Skip to content

Commit 0870684

Browse files
authored
src/polling: Fix the Offset Infinite Loop bug (#265)
References: * BR: #36
1 parent fb1f434 commit 0870684

File tree

5 files changed

+68
-6
lines changed

5 files changed

+68
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Changed:
2626

2727
Fixed:
2828

29+
1. (#265) Fix the offset infinite loop bug (#36) (by @GochoMugo)
2930
1. Fix game example (by @MCSH)
3031

3132

doc/api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ Emits `message` when a message arrives.
122122
| [options.request] | <code>Object</code> | | Options which will be added for all requests to telegram api. See https://github.com/request/request#requestoptions-callback for more information. |
123123
| [options.baseApiUrl] | <code>String</code> | <code>https://api.telegram.org</code> | API Base URl; useful for proxying and testing |
124124
| [options.filepath] | <code>Boolean</code> | <code>true</code> | 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. |
125+
| [options.badRejection] | <code>Boolean</code> | <code>false</code> | 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*. |
125126

126127
<a name="TelegramBot+on"></a>
127128

doc/usage.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ that emits the following events:
3636
1. `pre_checkout_query`: Received a new incoming pre-checkout query
3737
1. `polling_error`: Error occurred during polling. See [polling errors](#polling-errors).
3838
1. `webhook_error`: Error occurred handling a webhook request. See [webhook errors](#webhook-errors).
39+
1. `error`: Unexpected error occurred, usually fatal!
3940

4041
**Tip:** Its much better to listen a specific event rather than on
4142
`message` in order to stay safe from the content.

src/telegram.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ class TelegramBot extends EventEmitter {
174174
* @param {Boolean} [options.filepath=true] Allow passing file-paths as arguments when sending files,
175175
* such as photos using `TelegramBot#sendPhoto()`. See [usage information][usage-sending-files-performance]
176176
* for more information on this option and its consequences.
177+
* @param {Boolean} [options.badRejection=false] Set to `true`
178+
* **if and only if** the Node.js version you're using terminates the
179+
* process on unhandled rejections. This option is only for
180+
* *forward-compatibility purposes*.
177181
* @see https://core.telegram.org/bots/api
178182
*/
179183
constructor(token, options = {}) {
@@ -184,6 +188,7 @@ class TelegramBot extends EventEmitter {
184188
this.options.webHook = (typeof options.webHook === 'undefined') ? false : options.webHook;
185189
this.options.baseApiUrl = options.baseApiUrl || 'https://api.telegram.org';
186190
this.options.filepath = (typeof options.filepath === 'undefined') ? true : options.filepath;
191+
this.options.badRejection = (typeof options.badRejection === 'undefined') ? false : options.badRejection;
187192
this._textRegexpCallbacks = [];
188193
this._replyListenerId = 0;
189194
this._replyListeners = [];

src/telegramPolling.js

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const errors = require('./errors');
12
const debug = require('debug')('node-telegram-bot-api');
23
const deprecate = require('depd')('node-telegram-bot-api');
34
const ANOTHER_WEB_HOOK_USED = 409;
@@ -79,6 +80,18 @@ class TelegramBotPolling {
7980
return !!this._lastRequest;
8081
}
8182

83+
/**
84+
* Handle error thrown during polling.
85+
* @private
86+
* @param {Error} error
87+
*/
88+
_error(error) {
89+
if (!this.bot.listeners('polling_error').length) {
90+
return console.error('error: [polling_error] %j', error); // eslint-disable-line no-console
91+
}
92+
return this.bot.emit('polling_error', error);
93+
}
94+
8295
/**
8396
* Invokes polling (with recursion!)
8497
* @return {Promise} promise of the current request
@@ -93,18 +106,59 @@ class TelegramBotPolling {
93106
updates.forEach(update => {
94107
this.options.params.offset = update.update_id + 1;
95108
debug('updated offset: %s', this.options.params.offset);
96-
this.bot.processUpdate(update);
109+
try {
110+
this.bot.processUpdate(update);
111+
} catch (err) {
112+
err._processing = true;
113+
throw err;
114+
}
97115
});
98116
return null;
99117
})
100118
.catch(err => {
101119
debug('polling error: %s', err.message);
102-
if (this.bot.listeners('polling_error').length) {
103-
this.bot.emit('polling_error', err);
104-
} else {
105-
console.error('error: [polling_error] %j', err); // eslint-disable-line no-console
120+
if (!err._processing) {
121+
return this._error(err);
106122
}
107-
return null;
123+
delete err._processing;
124+
/*
125+
* An error occured while processing the items,
126+
* i.e. in `this.bot.processUpdate()` above.
127+
* We need to mark the already-processed items
128+
* to avoid fetching them again once the application
129+
* is restarted, or moves to next polling interval
130+
* (in cases where unhandled rejections do not terminate
131+
* the process).
132+
* See https://github.com/yagop/node-telegram-bot-api/issues/36#issuecomment-268532067
133+
*/
134+
if (!this.bot.options.badRejection) {
135+
return this._error(err);
136+
}
137+
const opts = {
138+
offset: this.options.params.offset,
139+
limit: 1,
140+
timeout: 0,
141+
};
142+
return this.bot.getUpdates(opts).then(() => {
143+
return this._error(err);
144+
}).catch(requestErr => {
145+
/*
146+
* We have been unable to handle this error.
147+
* We have to log this to stderr to ensure devops
148+
* understands that they may receive already-processed items
149+
* on app restart.
150+
* We simply can not rescue this situation, emit "error"
151+
* event, with the hope that the application exits.
152+
*/
153+
/* eslint-disable no-console */
154+
const bugUrl = 'https://github.com/yagop/node-telegram-bot-api/issues/36#issuecomment-268532067';
155+
console.error('error: Internal handling of The Offset Infinite Loop failed');
156+
console.error(`error: Due to error '${requestErr}'`);
157+
console.error('error: You may receive already-processed updates on app restart');
158+
console.error(`error: Please see ${bugUrl} for more information`);
159+
/* eslint-enable no-console */
160+
return this.bot.emit('error', new errors.FatalError(err));
161+
});
108162
})
109163
.finally(() => {
110164
if (this._abort) {

0 commit comments

Comments
 (0)