Add support for WebHooks
This commit is contained in:
parent
4ac725ea58
commit
b55fb3c7d6
@ -39,7 +39,7 @@ API.prototype.request = function request(method, data) {
|
||||
return (0, _fetch2['default'])(this.token + '/' + method, data);
|
||||
};
|
||||
|
||||
var methods = ['getMe', 'sendMessage', 'forwardMessage', 'sendPhoto', 'sendAudio', 'sendDocument', 'sendSticker', 'sendVideo', 'sendLocation', 'sendChatAction', 'getUserProfilePhotos', 'getUpdates'];
|
||||
var methods = ['getMe', 'sendMessage', 'forwardMessage', 'sendPhoto', 'sendAudio', 'sendDocument', 'sendSticker', 'sendVideo', 'sendLocation', 'sendChatAction', 'getUserProfilePhotos', 'getUpdates', 'setWebhook'];
|
||||
|
||||
methods.forEach(function (method) {
|
||||
API.prototype[method] = function (data) {
|
||||
|
@ -4,6 +4,7 @@ Object.defineProperty(exports, '__esModule', {
|
||||
value: true
|
||||
});
|
||||
exports['default'] = fetch;
|
||||
exports.getBody = getBody;
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
||||
|
||||
@ -19,8 +20,6 @@ function fetch(path, data) {
|
||||
var post = _qs2['default'].stringify(data);
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var res = '';
|
||||
|
||||
var req = _https2['default'].request({
|
||||
hostname: 'api.telegram.org',
|
||||
method: data ? 'POST' : 'GET',
|
||||
@ -29,19 +28,15 @@ function fetch(path, data) {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
}, function (response) {
|
||||
response.on('data', function (chunk) {
|
||||
res += chunk;
|
||||
});
|
||||
|
||||
response.on('end', function () {
|
||||
return getBody(response).then(function (res) {
|
||||
try {
|
||||
var json = JSON.parse(res);
|
||||
resolve(json);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
})['catch'](reject);
|
||||
});
|
||||
}).on('error', reject);
|
||||
|
||||
if (post) {
|
||||
req.write(post);
|
||||
@ -52,4 +47,18 @@ function fetch(path, data) {
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = exports['default'];
|
||||
function getBody(stream) {
|
||||
var data = '';
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
stream.on('data', function (chunk) {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
stream.on('end', function () {
|
||||
resolve(data);
|
||||
});
|
||||
|
||||
stream.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
116
build/index.js
116
build/index.js
@ -20,6 +20,14 @@ var _api = require('./api');
|
||||
|
||||
var _api2 = _interopRequireDefault(_api);
|
||||
|
||||
var _webhook = require('./webhook');
|
||||
|
||||
var _webhook2 = _interopRequireDefault(_webhook);
|
||||
|
||||
var _poll = require('./poll');
|
||||
|
||||
var _poll2 = _interopRequireDefault(_poll);
|
||||
|
||||
var _events = require('events');
|
||||
|
||||
var DEFAULTS = {
|
||||
@ -75,62 +83,38 @@ var Bot = (function (_EventEmitter) {
|
||||
key: 'start',
|
||||
|
||||
/**
|
||||
* Gets information about the bot and then starts polling updates from API
|
||||
* Gets information about the bot and then
|
||||
* 1) starts polling updates from API
|
||||
* 2) sets a webhook as defined by the first parameter and listens for updates
|
||||
* Emits an `update` event after polling with the response from server
|
||||
* Returns a promise which is resolved after the bot information is received
|
||||
* and set to it's `info` property i.e. bot.info
|
||||
*
|
||||
* @param {object} hook An object containg options passed to webhook
|
||||
* properties:
|
||||
* - url: HTTPS url to listen on POST requests coming
|
||||
* from the Telegram API
|
||||
* - port: the port to listen to, defaults to 443
|
||||
* - server: An object passed to https.createServer
|
||||
*
|
||||
* @return {promise} A promise which is resolved with the response of getMe
|
||||
*/
|
||||
value: function start() {
|
||||
var _this2 = this;
|
||||
|
||||
var poll = (function () {
|
||||
value: function start(hook) {
|
||||
var _this = this;
|
||||
|
||||
return this.api.getUpdates(this.update).then(function (response) {
|
||||
var again = wait(_this.update.timeout * 1000).then(poll);
|
||||
|
||||
var result = response.result;
|
||||
if (!result.length) {
|
||||
return again;
|
||||
if (hook) {
|
||||
return (0, _webhook2['default'])(hook, this);
|
||||
}
|
||||
|
||||
if (!_this.update.offset) {
|
||||
var updateId = result[result.length - 1].update_id;
|
||||
_this.update.offset = updateId;
|
||||
}
|
||||
if (_this.update) {
|
||||
_this.update.offset += 1;
|
||||
}
|
||||
|
||||
_this.emit('update', result);
|
||||
|
||||
result.forEach(function (res) {
|
||||
var text = res.message.text;
|
||||
if (text.startsWith('/')) {
|
||||
// Commands are sent in /command@botusername format in groups
|
||||
var regex = new RegExp('@' + _this.info.username + '$');
|
||||
text = text.replace(regex, '');
|
||||
}
|
||||
|
||||
var ev = _this._userEvents.find(function (_ref) {
|
||||
var pattern = _ref.pattern;
|
||||
return pattern.test(text);
|
||||
});
|
||||
|
||||
if (!ev) {
|
||||
return;
|
||||
}
|
||||
ev.listener(res.message);
|
||||
});
|
||||
|
||||
return again;
|
||||
});
|
||||
}).bind(this);
|
||||
|
||||
return this.api.getMe().then(function (response) {
|
||||
_this2.info = response.result;
|
||||
return poll();
|
||||
_this.info = response.result;
|
||||
|
||||
_this.on('update', _this._update);
|
||||
|
||||
if (hook) {
|
||||
return (0, _webhook2['default'])(hook, _this);
|
||||
} else {
|
||||
return (0, _poll2['default'])(_this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
@ -186,16 +170,42 @@ var Bot = (function (_EventEmitter) {
|
||||
value: function send(message) {
|
||||
return message.send(this)['catch'](console.error);
|
||||
}
|
||||
}, {
|
||||
key: '_update',
|
||||
value: function _update(update) {
|
||||
var _this2 = this;
|
||||
|
||||
if (!this.update.offset) {
|
||||
var updateId = update[update.length - 1].update_id;
|
||||
this.update.offset = updateId;
|
||||
}
|
||||
if (this.update) {
|
||||
this.update.offset += 1;
|
||||
}
|
||||
|
||||
update.forEach(function (res) {
|
||||
var text = res.message.text;
|
||||
if (text.startsWith('/')) {
|
||||
// Commands are sent in /command@thisusername format in groups
|
||||
var regex = new RegExp('@' + _this2.info.username + '$');
|
||||
text = text.replace(regex, '');
|
||||
}
|
||||
|
||||
var ev = _this2._userEvents.find(function (_ref) {
|
||||
var pattern = _ref.pattern;
|
||||
return pattern.test(text);
|
||||
});
|
||||
|
||||
if (!ev) {
|
||||
return;
|
||||
}
|
||||
ev.listener(res.message);
|
||||
});
|
||||
}
|
||||
}]);
|
||||
|
||||
return Bot;
|
||||
})(_events.EventEmitter);
|
||||
|
||||
exports['default'] = Bot;
|
||||
|
||||
var wait = function wait(miliseconds) {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(resolve, miliseconds);
|
||||
});
|
||||
};
|
||||
module.exports = exports['default'];
|
||||
|
27
build/poll.js
Normal file
27
build/poll.js
Normal file
@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', {
|
||||
value: true
|
||||
});
|
||||
exports['default'] = poll;
|
||||
|
||||
function poll(bot) {
|
||||
return bot.api.getUpdates(bot.update).then(function (response) {
|
||||
var again = wait(bot.update.timeout * 1000, bot).then(poll);
|
||||
if (!response.result.length) {
|
||||
return again;
|
||||
}
|
||||
bot.emit('update', response.result);
|
||||
|
||||
return again;
|
||||
});
|
||||
}
|
||||
|
||||
var wait = function wait(miliseconds, value) {
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(function () {
|
||||
resolve(value);
|
||||
}, miliseconds);
|
||||
});
|
||||
};
|
||||
module.exports = exports['default'];
|
42
build/webhook.js
Normal file
42
build/webhook.js
Normal file
@ -0,0 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', {
|
||||
value: true
|
||||
});
|
||||
exports['default'] = webhook;
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
||||
|
||||
var _http = require('http');
|
||||
|
||||
var _http2 = _interopRequireDefault(_http);
|
||||
|
||||
var _qs = require('qs');
|
||||
|
||||
var _qs2 = _interopRequireDefault(_qs);
|
||||
|
||||
var _fetch = require('./fetch');
|
||||
|
||||
var DEFAULTS = {
|
||||
server: {},
|
||||
port: 443
|
||||
};
|
||||
|
||||
function webhook(options, bot) {
|
||||
if (options === undefined) options = {};
|
||||
|
||||
options = Object.assign(DEFAULTS, options);
|
||||
|
||||
return bot.api.setWebhook(options.url).then(function () {
|
||||
|
||||
_http2['default'].createServer(options.server, function (req, res) {
|
||||
return (0, _fetch.getBody)(req).then(function (data) {
|
||||
bot.emit('update', _qs2['default'].parse(data).result);
|
||||
|
||||
res.end('OK');
|
||||
});
|
||||
}).listen(options.port);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = exports['default'];
|
2
demo.js
2
demo.js
@ -5,7 +5,7 @@ var Message = require('telegram-api/types/Message');
|
||||
var Question = require('telegram-api/types/Question');
|
||||
|
||||
var bot = new Bot({
|
||||
token: 'YOUR_KEY'
|
||||
token: '121143906:AAE6pcpBoARNZZjr3fUpvKuLInJ5Eee5Ajk'
|
||||
});
|
||||
|
||||
bot.start().catch(err => {
|
||||
|
@ -26,7 +26,7 @@ API.prototype.request = function request(method, data) {
|
||||
const methods = ['getMe', 'sendMessage', 'forwardMessage', 'sendPhoto',
|
||||
'sendAudio', 'sendDocument', 'sendSticker', 'sendVideo',
|
||||
'sendLocation', 'sendChatAction', 'getUserProfilePhotos',
|
||||
'getUpdates'];
|
||||
'getUpdates', 'setWebhook'];
|
||||
|
||||
methods.forEach(method => {
|
||||
API.prototype[method] = function(data) {
|
||||
|
26
lib/fetch.js
26
lib/fetch.js
@ -5,8 +5,6 @@ export default function fetch(path, data) {
|
||||
const post = qs.stringify(data);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let res = '';
|
||||
|
||||
const req = https.request({
|
||||
hostname: 'api.telegram.org',
|
||||
method: data ? 'POST' : 'GET',
|
||||
@ -15,19 +13,15 @@ export default function fetch(path, data) {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
}, response => {
|
||||
response.on('data', chunk => {
|
||||
res += chunk;
|
||||
});
|
||||
|
||||
response.on('end', () => {
|
||||
return getBody(response).then(res => {
|
||||
try {
|
||||
let json = JSON.parse(res);
|
||||
resolve(json);
|
||||
} catch(e) {
|
||||
reject(e);
|
||||
}
|
||||
}).catch(reject);
|
||||
});
|
||||
}).on('error', reject);
|
||||
|
||||
if (post) {
|
||||
req.write(post);
|
||||
@ -37,3 +31,19 @@ export default function fetch(path, data) {
|
||||
console.error('Error sending request', err);
|
||||
});
|
||||
}
|
||||
|
||||
export function getBody(stream) {
|
||||
let data = '';
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
stream.on('data', chunk => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
resolve(data);
|
||||
});
|
||||
|
||||
stream.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
101
lib/index.js
101
lib/index.js
@ -1,5 +1,7 @@
|
||||
import 'babel/polyfill';
|
||||
import API from './api';
|
||||
import webhook from './webhook';
|
||||
import poll from './poll';
|
||||
import {EventEmitter} from 'events';
|
||||
|
||||
const DEFAULTS = {
|
||||
@ -44,55 +46,38 @@ export default class Bot extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about the bot and then starts polling updates from API
|
||||
* Gets information about the bot and then
|
||||
* 1) starts polling updates from API
|
||||
* 2) sets a webhook as defined by the first parameter and listens for updates
|
||||
* Emits an `update` event after polling with the response from server
|
||||
* Returns a promise which is resolved after the bot information is received
|
||||
* and set to it's `info` property i.e. bot.info
|
||||
*
|
||||
* @param {object} hook An object containg options passed to webhook
|
||||
* properties:
|
||||
* - url: HTTPS url to listen on POST requests coming
|
||||
* from the Telegram API
|
||||
* - port: the port to listen to, defaults to 443
|
||||
* - server: An object passed to https.createServer
|
||||
*
|
||||
* @return {promise} A promise which is resolved with the response of getMe
|
||||
*/
|
||||
start() {
|
||||
let poll = function() {
|
||||
return this.api.getUpdates(this.update).then(response => {
|
||||
const again = wait(this.update.timeout * 1000).then(poll);
|
||||
|
||||
const result = response.result;
|
||||
if (!result.length) {
|
||||
return again;
|
||||
start(hook) {
|
||||
if (hook) {
|
||||
return webhook(hook, this);
|
||||
}
|
||||
|
||||
if (!this.update.offset) {
|
||||
const updateId = result[result.length - 1].update_id;
|
||||
this.update.offset = updateId;
|
||||
}
|
||||
if (this.update) {
|
||||
this.update.offset += 1;
|
||||
}
|
||||
|
||||
this.emit('update', result);
|
||||
|
||||
result.forEach(res => {
|
||||
let text = res.message.text;
|
||||
if (text.startsWith('/')) {
|
||||
// Commands are sent in /command@botusername format in groups
|
||||
const regex = new RegExp(`@${this.info.username}$`);
|
||||
text = text.replace(regex, '');
|
||||
}
|
||||
|
||||
let ev = this._userEvents.find(({pattern}) => pattern.test(text));
|
||||
|
||||
if (!ev) {
|
||||
return;
|
||||
}
|
||||
ev.listener(res.message);
|
||||
});
|
||||
|
||||
return again;
|
||||
});
|
||||
}.bind(this);
|
||||
|
||||
return this.api.getMe().then(response => {
|
||||
this.info = response.result;
|
||||
return poll();
|
||||
|
||||
this.on('update', this._update);
|
||||
|
||||
if (hook) {
|
||||
return webhook(hook, this);
|
||||
} else {
|
||||
return poll(this);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -142,10 +127,36 @@ export default class Bot extends EventEmitter {
|
||||
send(message) {
|
||||
return message.send(this).catch(console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal update event listener, used to parse messages and fire
|
||||
* command/get events - YOU SHOULD NOT USE THIS
|
||||
*
|
||||
* @param {object} update
|
||||
*/
|
||||
_update(update) {
|
||||
if (!this.update.offset) {
|
||||
const updateId = update[update.length - 1].update_id;
|
||||
this.update.offset = updateId;
|
||||
}
|
||||
if (this.update) {
|
||||
this.update.offset += 1;
|
||||
}
|
||||
|
||||
const wait = (miliseconds) => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, miliseconds);
|
||||
update.forEach(res => {
|
||||
let text = res.message.text;
|
||||
if (text.startsWith('/')) {
|
||||
// Commands are sent in /command@thisusername format in groups
|
||||
const regex = new RegExp(`@${this.info.username}$`);
|
||||
text = text.replace(regex, '');
|
||||
}
|
||||
|
||||
let ev = this._userEvents.find(({pattern}) => pattern.test(text));
|
||||
|
||||
if (!ev) {
|
||||
return;
|
||||
}
|
||||
ev.listener(res.message);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
19
lib/poll.js
Normal file
19
lib/poll.js
Normal file
@ -0,0 +1,19 @@
|
||||
export default function poll(bot) {
|
||||
return bot.api.getUpdates(bot.update).then(response => {
|
||||
const again = wait(bot.update.timeout * 1000, bot).then(poll);
|
||||
if (!response.result.length) {
|
||||
return again;
|
||||
}
|
||||
bot.emit('update', response.result);
|
||||
|
||||
return again;
|
||||
});
|
||||
}
|
||||
|
||||
const wait = (miliseconds, value) => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve(value);
|
||||
}, miliseconds);
|
||||
});
|
||||
};
|
24
lib/webhook.js
Normal file
24
lib/webhook.js
Normal file
@ -0,0 +1,24 @@
|
||||
import https from 'http';
|
||||
import qs from 'qs';
|
||||
import {getBody} from './fetch';
|
||||
|
||||
const DEFAULTS = {
|
||||
server: {},
|
||||
port: 443
|
||||
};
|
||||
|
||||
export default function webhook(options = {}, bot) {
|
||||
options = Object.assign(DEFAULTS, options);
|
||||
|
||||
return bot.api.setWebhook(options.url).then(() => {
|
||||
|
||||
https.createServer(options.server, (req, res) => {
|
||||
return getBody(req).then(data => {
|
||||
bot.emit('update', qs.parse(data).result);
|
||||
|
||||
res.end('OK');
|
||||
});
|
||||
}).listen(options.port);
|
||||
|
||||
});
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "telegram-api",
|
||||
"version": "0.2.2",
|
||||
"version": "0.3.2",
|
||||
"description": "Control Telegram bots easily using the new Telegram API",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
Loading…
Reference in New Issue
Block a user