@zero-bot.net/tg-bot-api 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,196 +0,0 @@
1
- const errors = require('./errors');
2
- const debug = require('debug')('@zero-bot.net/tg-bot-api');
3
- const deprecate = require('./utils').deprecate;
4
- const ANOTHER_WEB_HOOK_USED = 409;
5
-
6
- class TelegramBotPolling {
7
- /**
8
- * Handles polling against the Telegram servers.
9
- * @param {TelegramBot} bot
10
- * @see https://core.telegram.org/bots/api#getting-updates
11
- */
12
- constructor(bot) {
13
- this.bot = bot;
14
- this.options = typeof bot.options.polling === 'boolean' ? {} : bot.options.polling;
15
- this.options.interval = typeof this.options.interval === 'number' ? this.options.interval : 300;
16
- this.options.params = typeof this.options.params === 'object' ? this.options.params : {};
17
- this.options.params.offset = typeof this.options.params.offset === 'number' ? this.options.params.offset : 0;
18
- this.options.params.timeout = typeof this.options.params.timeout === 'number' ? this.options.params.timeout : 10;
19
- if (typeof this.options.timeout === 'number') {
20
- deprecate('`options.polling.timeout` is deprecated. Use `options.polling.params` instead.');
21
- this.options.params.timeout = this.options.timeout;
22
- }
23
- this._lastUpdate = 0;
24
- this._lastRequest = null;
25
- this._abort = false;
26
- this._pollingTimeout = null;
27
- }
28
-
29
- /**
30
- * Start polling
31
- * @param {Object} [options]
32
- * @param {Object} [options.restart]
33
- * @return {Promise}
34
- */
35
- start(options = {}) {
36
- if (this._lastRequest) {
37
- if (!options.restart) {
38
- return Promise.resolve();
39
- }
40
- return this.stop({
41
- cancel: true,
42
- reason: 'Polling restart'
43
- }).then(() => {
44
- return this._polling();
45
- });
46
- }
47
- return this._polling();
48
- }
49
-
50
- /**
51
- * Stop polling
52
- * @param {Object} [options] Options
53
- * @param {Boolean} [options.cancel] Cancel current request
54
- * @param {String} [options.reason] Reason for stopping polling
55
- * @return {Promise}
56
- */
57
- stop(options = {}) {
58
- if (!this._lastRequest) {
59
- return Promise.resolve();
60
- }
61
- const lastRequest = this._lastRequest;
62
- this._lastRequest = null;
63
- clearTimeout(this._pollingTimeout);
64
- if (options.cancel) {
65
- const reason = options.reason || 'Polling stop';
66
- lastRequest.cancel(reason);
67
- return Promise.resolve();
68
- }
69
- this._abort = true;
70
- return lastRequest.finally(() => {
71
- this._abort = false;
72
- });
73
- }
74
-
75
- /**
76
- * Return `true` if is polling. Otherwise, `false`.
77
- */
78
- isPolling() {
79
- return !!this._lastRequest;
80
- }
81
-
82
- /**
83
- * Handle error thrown during polling.
84
- * @private
85
- * @param {Error} error
86
- */
87
- _error(error) {
88
- if (!this.bot.listeners('polling_error').length) {
89
- return console.error('error: [polling_error] %j', error); // eslint-disable-line no-console
90
- }
91
- return this.bot.emit('polling_error', error);
92
- }
93
-
94
- /**
95
- * Invokes polling (with recursion!)
96
- * @return {Promise} promise of the current request
97
- * @private
98
- */
99
- _polling() {
100
- this._lastRequest = this._getUpdates().then(updates => {
101
- this._lastUpdate = Date.now();
102
- debug('polling data %j', updates);
103
- updates.forEach(update => {
104
- this.options.params.offset = update.update_id + 1;
105
- debug('updated offset: %s', this.options.params.offset);
106
- try {
107
- this.bot.processUpdate(update);
108
- } catch (err) {
109
- err._processing = true;
110
- throw err;
111
- }
112
- });
113
- return null;
114
- }).catch(err => {
115
- debug('polling error: %s', err.message);
116
- if (!err._processing) {
117
- return this._error(err);
118
- }
119
- delete err._processing;
120
- /*
121
- * An error occured while processing the items,
122
- * i.e. in `this.bot.processUpdate()` above.
123
- * We need to mark the already-processed items
124
- * to avoid fetching them again once the application
125
- * is restarted, or moves to next polling interval
126
- * (in cases where unhandled rejections do not terminate
127
- * the process).
128
- * See https://github.com/ZeroBot-net/@zero-bot.net/tg-bot-api/issues/36#issuecomment-268532067
129
- */
130
- if (!this.bot.options.badRejection) {
131
- return this._error(err);
132
- }
133
- const opts = {
134
- offset: this.options.params.offset,
135
- limit: 1,
136
- timeout: 0
137
- };
138
- return this.bot.getUpdates(opts).then(() => {
139
- return this._error(err);
140
- }).catch(requestErr => {
141
- /*
142
- * We have been unable to handle this error.
143
- * We have to log this to stderr to ensure devops
144
- * understands that they may receive already-processed items
145
- * on app restart.
146
- * We simply can not rescue this situation, emit "error"
147
- * event, with the hope that the application exits.
148
- */
149
- /* eslint-disable no-console */
150
- const bugUrl = 'https://github.com/ZeroBot-net/@zero-bot.net/tg-bot-api/issues/36#issuecomment-268532067';
151
- console.error('error: Internal handling of The Offset Infinite Loop failed');
152
- console.error(`error: Due to error '${requestErr}'`);
153
- console.error('error: You may receive already-processed updates on app restart');
154
- console.error(`error: Please see ${bugUrl} for more information`);
155
- /* eslint-enable no-console */
156
- return this.bot.emit('error', new errors.FatalError(err));
157
- });
158
- }).finally(() => {
159
- if (this._abort) {
160
- debug('Polling is aborted!');
161
- } else {
162
- debug('setTimeout for %s miliseconds', this.options.interval);
163
- this._pollingTimeout = setTimeout(() => this._polling(), this.options.interval);
164
- }
165
- });
166
- return this._lastRequest;
167
- }
168
-
169
- /**
170
- * Unset current webhook. Used when we detect that a webhook has been set
171
- * and we are trying to poll. Polling and WebHook are mutually exclusive.
172
- * @see https://core.telegram.org/bots/api#getting-updates
173
- * @private
174
- */
175
- _unsetWebHook() {
176
- debug('unsetting webhook');
177
- return this.bot._request('setWebHook');
178
- }
179
-
180
- /**
181
- * Retrieve updates
182
- */
183
- _getUpdates() {
184
- debug('polling with options: %j', this.options.params);
185
- return this.bot.getUpdates(this.options.params).catch(err => {
186
- if (err.response && err.response.statusCode === ANOTHER_WEB_HOOK_USED) {
187
- return this._unsetWebHook().then(() => {
188
- return this.bot.getUpdates(this.options.params);
189
- });
190
- }
191
- throw err;
192
- });
193
- }
194
- }
195
-
196
- module.exports = TelegramBotPolling;
@@ -1,156 +0,0 @@
1
- const errors = require('./errors');
2
- const debug = require('debug')('@zero-bot.net/tg-bot-api');
3
- const https = require('https');
4
- const http = require('http');
5
- const fs = require('fs');
6
- const bl = require('bl');
7
-
8
- class TelegramBotWebHook {
9
- /**
10
- * Sets up a webhook to receive updates
11
- * @param {TelegramBot} bot
12
- * @see https://core.telegram.org/bots/api#getting-updates
13
- */
14
- constructor(bot) {
15
- this.bot = bot;
16
- this.options = typeof bot.options.webHook === 'boolean' ? {} : bot.options.webHook;
17
- this.options.host = this.options.host || '0.0.0.0';
18
- this.options.port = this.options.port || 8443;
19
- this.options.https = this.options.https || {};
20
- this.options.healthEndpoint = this.options.healthEndpoint || '/healthz';
21
- this._healthRegex = new RegExp(this.options.healthEndpoint);
22
- this._webServer = null;
23
- this._open = false;
24
- this._requestListener = this._requestListener.bind(this);
25
- this._parseBody = this._parseBody.bind(this);
26
-
27
- if (this.options.key && this.options.cert) {
28
- debug('HTTPS WebHook enabled (by key/cert)');
29
- this.options.https.key = fs.readFileSync(this.options.key);
30
- this.options.https.cert = fs.readFileSync(this.options.cert);
31
- this._webServer = https.createServer(this.options.https, this._requestListener);
32
- } else if (this.options.pfx) {
33
- debug('HTTPS WebHook enabled (by pfx)');
34
- this.options.https.pfx = fs.readFileSync(this.options.pfx);
35
- this._webServer = https.createServer(this.options.https, this._requestListener);
36
- } else if (Object.keys(this.options.https).length) {
37
- debug('HTTPS WebHook enabled by (https)');
38
- this._webServer = https.createServer(this.options.https, this._requestListener);
39
- } else {
40
- debug('HTTP WebHook enabled');
41
- this._webServer = http.createServer(this._requestListener);
42
- }
43
- }
44
-
45
- /**
46
- * Open WebHook by listening on the port
47
- * @return {Promise}
48
- */
49
- open() {
50
- if (this.isOpen()) {
51
- return Promise.resolve();
52
- }
53
- return new Promise((resolve, reject) => {
54
- this._webServer.listen(this.options.port, this.options.host, () => {
55
- debug('WebHook listening on port %s', this.options.port);
56
- this._open = true;
57
- return resolve();
58
- });
59
-
60
- this._webServer.once('error', err => {
61
- reject(err);
62
- });
63
- });
64
- }
65
-
66
- /**
67
- * Close the webHook
68
- * @return {Promise}
69
- */
70
- close() {
71
- if (!this.isOpen()) {
72
- return Promise.resolve();
73
- }
74
- return new Promise((resolve, reject) => {
75
- this._webServer.close(error => {
76
- if (error) return reject(error);
77
- this._open = false;
78
- return resolve();
79
- });
80
- });
81
- }
82
-
83
- /**
84
- * Return `true` if server is listening. Otherwise, `false`.
85
- */
86
- isOpen() {
87
- // NOTE: Since `http.Server.listening` was added in v5.7.0
88
- // and we still need to support Node v4,
89
- // we are going to fallback to 'this._open'.
90
- // The following LOC would suffice for newer versions of Node.js
91
- // return this._webServer.listening;
92
- return this._open;
93
- }
94
-
95
- /**
96
- * Handle error thrown during processing of webhook request.
97
- * @private
98
- * @param {Error} error
99
- */
100
- _error(error) {
101
- if (!this.bot.listeners('webhook_error').length) {
102
- return console.error('error: [webhook_error] %j', error); // eslint-disable-line no-console
103
- }
104
- return this.bot.emit('webhook_error', error);
105
- }
106
-
107
- /**
108
- * Handle request body by passing it to 'callback'
109
- * @private
110
- */
111
- _parseBody(error, body) {
112
- if (error) {
113
- return this._error(new errors.FatalError(error));
114
- }
115
-
116
- let data;
117
- try {
118
- data = JSON.parse(body.toString());
119
- } catch (parseError) {
120
- return this._error(new errors.ParseError(parseError.message));
121
- }
122
-
123
- return this.bot.processUpdate(data);
124
- }
125
-
126
- /**
127
- * Listener for 'request' event on server
128
- * @private
129
- * @see https://nodejs.org/docs/latest/api/http.html#http_http_createserver_requestlistener
130
- * @see https://nodejs.org/docs/latest/api/https.html#https_https_createserver_options_requestlistener
131
- */
132
- _requestListener(req, res) {
133
- debug('WebHook request URL: %s', req.url);
134
- debug('WebHook request headers: %j', req.headers);
135
-
136
- if (req.url.indexOf(this.bot.token) !== -1) {
137
- if (req.method !== 'POST') {
138
- debug('WebHook request isn\'t a POST');
139
- res.statusCode = 418; // I'm a teabot!
140
- res.end();
141
- } else {
142
- req.pipe(bl(this._parseBody)).on('finish', () => res.end('OK'));
143
- }
144
- } else if (this._healthRegex.test(req.url)) {
145
- debug('WebHook health check passed');
146
- res.statusCode = 200;
147
- res.end('OK');
148
- } else {
149
- debug('WebHook request unauthorized');
150
- res.statusCode = 401;
151
- res.end();
152
- }
153
- }
154
- }
155
-
156
- module.exports = TelegramBotWebHook;
package/lib/utils.js DELETED
@@ -1,3 +0,0 @@
1
- const util = require('util');
2
- // Native deprecation warning
3
- exports.deprecate = msg => util.deprecate(() => {}, msg, '@zero-bot.net/tg-bot-api')();