From 9794c7f38ec3f57e51d5fbecbd700ba2714a9f41 Mon Sep 17 00:00:00 2001 From: GochoMugo Date: Wed, 25 Jan 2017 17:26:57 +0300 Subject: [PATCH] src/polling: Fix the Offset Infinite Loop bug References: * Issue #36: https://github.com/yagop/node-telegram-bot-api/issues/36 * answer/solution: https://github.com/yagop/node-telegram-bot-api/issues/36#issuecomment-268532067 --- doc/api.md | 1 + src/telegram.js | 5 +++++ src/telegramPolling.js | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/doc/api.md b/doc/api.md index adb8c25c..f5ae4d22 100644 --- a/doc/api.md +++ b/doc/api.md @@ -122,6 +122,7 @@ Emits `message` when a message arrives. | [options.request] | Object | | Options which will be added for all requests to telegram api. See https://github.com/request/request#requestoptions-callback for more information. | | [options.baseApiUrl] | String | https://api.telegram.org | API Base URl; useful for proxying and testing | | [options.filepath] | Boolean | true | Allow passing file-paths as arguments when sending files, such as photos using `TelegramBot#sendPhoto()`. See [usage information][usage-sending-files-performance] for more information on this option and its consequences. | +| [options.badRejection] | Boolean | false | Set to `true` **if and only if** the Node.js version you're using terminates the process on unhandled rejections. This option is only for *forward-compatibility purposes*. | diff --git a/src/telegram.js b/src/telegram.js index b69fa59e..df94cd86 100644 --- a/src/telegram.js +++ b/src/telegram.js @@ -174,6 +174,10 @@ class TelegramBot extends EventEmitter { * @param {Boolean} [options.filepath=true] Allow passing file-paths as arguments when sending files, * such as photos using `TelegramBot#sendPhoto()`. See [usage information][usage-sending-files-performance] * for more information on this option and its consequences. + * @param {Boolean} [options.badRejection=false] Set to `true` + * **if and only if** the Node.js version you're using terminates the + * process on unhandled rejections. This option is only for + * *forward-compatibility purposes*. * @see https://core.telegram.org/bots/api */ constructor(token, options = {}) { @@ -184,6 +188,7 @@ class TelegramBot extends EventEmitter { this.options.webHook = (typeof options.webHook === 'undefined') ? false : options.webHook; this.options.baseApiUrl = options.baseApiUrl || 'https://api.telegram.org'; this.options.filepath = (typeof options.filepath === 'undefined') ? true : options.filepath; + this.options.badRejection = (typeof options.badRejection === 'undefined') ? false : options.badRejection; this._textRegexpCallbacks = []; this._replyListenerId = 0; this._replyListeners = []; diff --git a/src/telegramPolling.js b/src/telegramPolling.js index d3f102fb..b4fe884e 100644 --- a/src/telegramPolling.js +++ b/src/telegramPolling.js @@ -79,6 +79,18 @@ class TelegramBotPolling { return !!this._lastRequest; } + /** + * Handle error thrown during polling. + * @private + * @param {Error} error + */ + _error(error) { + if (!this.bot.listeners('polling_error').length) { + return console.error('error: [polling_error] %j', err); // eslint-disable-line no-console + } + return this.bot.emit('polling_error', error); + } + /** * Invokes polling (with recursion!) * @return {Promise} promise of the current request @@ -99,12 +111,25 @@ class TelegramBotPolling { }) .catch(err => { debug('polling error: %s', err.message); - if (this.bot.listeners('polling_error').length) { - this.bot.emit('polling_error', err); - } else { - console.error('error: [polling_error] %j', err); // eslint-disable-line no-console + /** + * We need to mark the already-processed items + * to avoid fetching them again once the application + * is restarted, or moves to next polling interval + * (in cases where unhandled rejections do not terminate + * the process). + * See https://github.com/yagop/node-telegram-bot-api/issues/36#issuecomment-268532067 + */ + if (!this.bot.options.badRejection) { + return this._error(err); } - return null; + const opts = { + offset: this.options.params.offset, + limit: 1, + timeout: 0, + }; + return this.bot.getUpdates(opts).then(() => { + return this._error(err); + }); }) .finally(() => { if (this._abort) {