diff --git a/src/telegram.js b/src/telegram.js index d9ce8b4..242b2a3 100644 --- a/src/telegram.js +++ b/src/telegram.js @@ -1,4 +1,5 @@ var TelegramBotWebHook = require('./telegramWebHook'); +var TelegramBotPolling = require('./telegramPolling'); var debug = require('debug')('node-telegram-bot-api'); var EventEmitter = require('events').EventEmitter; var Promise = require("bluebird"); @@ -31,35 +32,20 @@ var requestPromise = Promise.promisify(request); var TelegramBot = function (token, options) { options = options || {}; this.token = token; - this.offset = 0; - this._webServer = null; + + var processUpdate = this._processUpdate.bind(this); if (options.polling) { - // By default polling for 4 seconds - var timeout = options.polling.timeout || 4; - this._polling(timeout); + this._polling = new TelegramBotPolling(token, options.polling, processUpdate); } if (options.webHook) { - var binded = this._processUpdate.bind(this); - this._WebHook = new TelegramBotWebHook(token, options.webHook, binded); + this._WebHook = new TelegramBotWebHook(token, options.webHook, processUpdate); } }; util.inherits(TelegramBot, EventEmitter); -TelegramBot.prototype._polling = function (timeout) { - var self = this; - this.getUpdates(timeout).then(function (data) { - self._processUpdates(data); - self._polling(timeout); - }).catch(function (err) { - debug('polling error: %j', err); - // Wait for 2 seconds before retry - setTimeout(self._polling.bind(self), 2000, timeout); - }); -}; - TelegramBot.prototype._processUpdate = function (update) { debug('Process Update', update); debug('Process Update message', update.message); @@ -68,12 +54,6 @@ TelegramBot.prototype._processUpdate = function (update) { } }; -TelegramBot.prototype._processUpdates = function (updates) { - for (var i = 0; i < updates.length; i++) { - this._processUpdate(updates[i]); - } -}; - TelegramBot.prototype._request = function (path, options) { if (!this.token) { throw new Error('Telegram Bot Token not provided!'); @@ -136,21 +116,13 @@ TelegramBot.prototype.setWebHook = function (url) { * @see https://core.telegram.org/bots/api#getupdates */ TelegramBot.prototype.getUpdates = function (timeout, limit, offset) { - var self = this; var query = { - offset: offset || this.offset+1, + offset: offset, limit: limit, timeout: timeout }; - return this._request('getUpdates', {qs: query}) - .then(function (result) { - var last = result[result.length-1]; - if (last) { - self.offset = last.update_id; - } - return result; - }); + return this._request('getUpdates', {qs: query}); }; /** diff --git a/src/telegramPolling.js b/src/telegramPolling.js new file mode 100644 index 0000000..add5931 --- /dev/null +++ b/src/telegramPolling.js @@ -0,0 +1,69 @@ +var debug = require('debug')('node-telegram-bot-api'); +var Promise = require("bluebird"); +var request = require('request'); +var URL = require('url'); + +var requestPromise = Promise.promisify(request); + +var TelegramBotPolling = function (token, options, callback) { + options = options || {}; + if (typeof options === "function") { + callback = options; + options = {}; + } + this.offset = 0; + this.token = token; + this.callback = callback; + this.timeout = options.timeout || 0; + this.interval = options.interval || 2000; + this._polling(); +}; + +TelegramBotPolling.prototype._polling = function () { + var self = this; + + this._getUpdates().then(function (updates) { + debug('polling data %j', updates); + updates.forEach(function (update, index) { + // If is the latest, update the offset. + if (index === updates.length - 1) { + self.offset = update.update_id; + debug('updated offset: %s', self.offset); + } + self.callback(update); + }); + }).catch(function (err) { + debug('polling error: %j', err); + }).finally(function () { + debug('setTimeout for %s miliseconds', self.interval); + setTimeout(self._polling.bind(self), self.interval); + }); +}; + +TelegramBotPolling.prototype._getUpdates = function () { + var opts = { + qs: { + offset: this.offset+1, + limit: this.limit, + timeout: this.timeout + }, + url: URL.format({ + protocol: 'https', + host: 'api.telegram.org', + pathname: '/bot'+this.token+'/getUpdates' + }) + }; + return requestPromise(opts).then(function (resp) { + if (resp[0].statusCode !== 200) { + throw new Error(resp[0].statusCode+' '+resp[0].body); + } + var data = JSON.parse(resp[0].body); + if (data.ok) { + return data.result; + } else { + throw new Error(data.error_code+' '+data.description); + } + }); +}; + +module.exports = TelegramBotPolling; diff --git a/test/index.js b/test/index.js index bf2ecee..cca988d 100644 --- a/test/index.js +++ b/test/index.js @@ -1,3 +1,4 @@ +var TelegramPolling = require('../src/telegramPolling'); var Telegram = require('../index'); var request = require('request'); var should = require('should'); @@ -31,27 +32,6 @@ describe('Telegram', function () { }); }); - describe('#Polling', function () { - it('should emit a `message` on polling', function (done) { - var bot = new Telegram(TOKEN); - bot.on('message', function (msg) { - msg.should.be.an.instanceOf(Object); - bot._polling = function () {}; - done(); - }); - bot.getUpdates = function() { - return { - then: function (cb) { - cb([{update_id: 0, message: {}}]); - return this; - }, - catch: function () {} - }; - }; - bot._polling(); - }); - }); - describe('#WebHook', function () { it('should reject request if same token not provided', function (done) { var bot = new Telegram(TOKEN, {webHook: true}); @@ -378,3 +358,27 @@ describe('Telegram', function () { }); }); // End Telegram + + +describe('#TelegramBotPolling', function () { + it('should call the callback on polling', function (done) { + function onUpdate (update) { + update.should.be.an.instanceOf(Object); + done(); + } + var polling = new TelegramPolling(null, {interval: 500}, onUpdate); + // Not the best way to mock, but it works + polling._getUpdates = function() { + return { + then: function (cb) { + cb([{update_id: 10, message: {}}]); + return this; + }, + catch: function () { + return this; + }, + finally: function () {} + }; + }; + }); +});