UNPKG

8.61 kBJavaScriptView Raw
1'use strict';
2
3var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
4
5var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
6
7function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
8
9var errors = require('./errors');
10var debug = require('debug')('node-telegram-bot-api');
11var deprecate = require('depd')('node-telegram-bot-api');
12var ANOTHER_WEB_HOOK_USED = 409;
13
14var TelegramBotPolling = function () {
15 /**
16 * Handles polling against the Telegram servers.
17 * @param {TelegramBot} bot
18 * @see https://core.telegram.org/bots/api#getting-updates
19 */
20 function TelegramBotPolling(bot) {
21 _classCallCheck(this, TelegramBotPolling);
22
23 this.bot = bot;
24 this.options = typeof bot.options.polling === 'boolean' ? {} : bot.options.polling;
25 this.options.interval = typeof this.options.interval === 'number' ? this.options.interval : 300;
26 this.options.params = _typeof(this.options.params) === 'object' ? this.options.params : {};
27 this.options.params.offset = typeof this.options.params.offset === 'number' ? this.options.params.offset : 0;
28 this.options.params.timeout = typeof this.options.params.timeout === 'number' ? this.options.params.timeout : 10;
29 if (typeof this.options.timeout === 'number') {
30 deprecate('`options.polling.timeout` is deprecated. Use `options.polling.params` instead.');
31 this.options.params.timeout = this.options.timeout;
32 }
33 this._lastUpdate = 0;
34 this._lastRequest = null;
35 this._abort = false;
36 this._pollingTimeout = null;
37 }
38
39 /**
40 * Start polling
41 * @param {Object} [options]
42 * @param {Object} [options.restart]
43 * @return {Promise}
44 */
45
46
47 _createClass(TelegramBotPolling, [{
48 key: 'start',
49 value: function start() {
50 var _this = this;
51
52 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
53
54 if (this._lastRequest) {
55 if (!options.restart) {
56 return Promise.resolve();
57 }
58 return this.stop({
59 cancel: true,
60 reason: 'Polling restart'
61 }).then(function () {
62 return _this._polling();
63 });
64 }
65 return this._polling();
66 }
67
68 /**
69 * Stop polling
70 * @param {Object} [options] Options
71 * @param {Boolean} [options.cancel] Cancel current request
72 * @param {String} [options.reason] Reason for stopping polling
73 * @return {Promise}
74 */
75
76 }, {
77 key: 'stop',
78 value: function stop() {
79 var _this2 = this;
80
81 var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
82
83 if (!this._lastRequest) {
84 return Promise.resolve();
85 }
86 var lastRequest = this._lastRequest;
87 this._lastRequest = null;
88 clearTimeout(this._pollingTimeout);
89 if (options.cancel) {
90 var reason = options.reason || 'Polling stop';
91 lastRequest.cancel(reason);
92 return Promise.resolve();
93 }
94 this._abort = true;
95 return lastRequest.finally(function () {
96 _this2._abort = false;
97 });
98 }
99
100 /**
101 * Return `true` if is polling. Otherwise, `false`.
102 */
103
104 }, {
105 key: 'isPolling',
106 value: function isPolling() {
107 return !!this._lastRequest;
108 }
109
110 /**
111 * Handle error thrown during polling.
112 * @private
113 * @param {Error} error
114 */
115
116 }, {
117 key: '_error',
118 value: function _error(error) {
119 if (!this.bot.listeners('polling_error').length) {
120 return console.error('error: [polling_error] %j', error); // eslint-disable-line no-console
121 }
122 return this.bot.emit('polling_error', error);
123 }
124
125 /**
126 * Invokes polling (with recursion!)
127 * @return {Promise} promise of the current request
128 * @private
129 */
130
131 }, {
132 key: '_polling',
133 value: function _polling() {
134 var _this3 = this;
135
136 this._lastRequest = this._getUpdates().then(function (updates) {
137 _this3._lastUpdate = Date.now();
138 debug('polling data %j', updates);
139 updates.forEach(function (update) {
140 _this3.options.params.offset = update.update_id + 1;
141 debug('updated offset: %s', _this3.options.params.offset);
142 try {
143 _this3.bot.processUpdate(update);
144 } catch (err) {
145 err._processing = true;
146 throw err;
147 }
148 });
149 return null;
150 }).catch(function (err) {
151 debug('polling error: %s', err.message);
152 if (!err._processing) {
153 return _this3._error(err);
154 }
155 delete err._processing;
156 /*
157 * An error occured while processing the items,
158 * i.e. in `this.bot.processUpdate()` above.
159 * We need to mark the already-processed items
160 * to avoid fetching them again once the application
161 * is restarted, or moves to next polling interval
162 * (in cases where unhandled rejections do not terminate
163 * the process).
164 * See https://github.com/yagop/node-telegram-bot-api/issues/36#issuecomment-268532067
165 */
166 if (!_this3.bot.options.badRejection) {
167 return _this3._error(err);
168 }
169 var opts = {
170 offset: _this3.options.params.offset,
171 limit: 1,
172 timeout: 0
173 };
174 return _this3.bot.getUpdates(opts).then(function () {
175 return _this3._error(err);
176 }).catch(function (requestErr) {
177 /*
178 * We have been unable to handle this error.
179 * We have to log this to stderr to ensure devops
180 * understands that they may receive already-processed items
181 * on app restart.
182 * We simply can not rescue this situation, emit "error"
183 * event, with the hope that the application exits.
184 */
185 /* eslint-disable no-console */
186 var bugUrl = 'https://github.com/yagop/node-telegram-bot-api/issues/36#issuecomment-268532067';
187 console.error('error: Internal handling of The Offset Infinite Loop failed');
188 console.error('error: Due to error \'' + requestErr + '\'');
189 console.error('error: You may receive already-processed updates on app restart');
190 console.error('error: Please see ' + bugUrl + ' for more information');
191 /* eslint-enable no-console */
192 return _this3.bot.emit('error', new errors.FatalError(err));
193 });
194 }).finally(function () {
195 if (_this3._abort) {
196 debug('Polling is aborted!');
197 } else {
198 debug('setTimeout for %s miliseconds', _this3.options.interval);
199 _this3._pollingTimeout = setTimeout(function () {
200 return _this3._polling();
201 }, _this3.options.interval);
202 }
203 });
204 return this._lastRequest;
205 }
206
207 /**
208 * Unset current webhook. Used when we detect that a webhook has been set
209 * and we are trying to poll. Polling and WebHook are mutually exclusive.
210 * @see https://core.telegram.org/bots/api#getting-updates
211 * @private
212 */
213
214 }, {
215 key: '_unsetWebHook',
216 value: function _unsetWebHook() {
217 debug('unsetting webhook');
218 return this.bot._request('setWebHook');
219 }
220
221 /**
222 * Retrieve updates
223 */
224
225 }, {
226 key: '_getUpdates',
227 value: function _getUpdates() {
228 var _this4 = this;
229
230 debug('polling with options: %j', this.options.params);
231 return this.bot.getUpdates(this.options.params).catch(function (err) {
232 if (err.response && err.response.statusCode === ANOTHER_WEB_HOOK_USED) {
233 return _this4._unsetWebHook().then(function () {
234 return _this4.bot.getUpdates(_this4.options.params);
235 });
236 }
237 throw err;
238 });
239 }
240 }]);
241
242 return TelegramBotPolling;
243}();
244
245module.exports = TelegramBotPolling;
\No newline at end of file