2
0
mirror of https://github.com/yagop/node-telegram-bot-api synced 2025-09-02 07:15:34 +00:00

[webhook,polling] Improve starting, stopping of webhook, polling

Feature:

  The different mechanisms of fetching updates, i.e. polling
  and webhook, have had their implementations improved:

  * the TelegramBot instance needs to create the polling and
    webhook instances once, and when necessary
  * returning promises from TelegramBot#openWebHook() and
    TelegramBot#startPolling() allows more precise control

  Also,

  * TelegramBot#initPolling() is being deprecated in favor of
    TelegramBot#startPolling() to ensure consistency (as the
    opposite action of TelegramBot#stopPolling())
This commit is contained in:
GochoMugo
2017-01-09 15:57:34 +03:00
parent 4735518116
commit 97c8130d93
5 changed files with 159 additions and 61 deletions

View File

@@ -71,10 +71,11 @@ TelegramBot
* [TelegramBot](#TelegramBot) * [TelegramBot](#TelegramBot)
* [new TelegramBot(token, [options])](#new_TelegramBot_new) * [new TelegramBot(token, [options])](#new_TelegramBot_new)
* [.initPolling()](#TelegramBot+initPolling) * [.startPolling([options])](#TelegramBot+startPolling) ⇒ <code>Promise</code>
* ~~[.initPolling([options])](#TelegramBot+initPolling) ⇒ <code>Promise</code>~~
* [.stopPolling()](#TelegramBot+stopPolling) ⇒ <code>Promise</code> * [.stopPolling()](#TelegramBot+stopPolling) ⇒ <code>Promise</code>
* [.isPolling()](#TelegramBot+isPolling) ⇒ <code>Boolean</code> * [.isPolling()](#TelegramBot+isPolling) ⇒ <code>Boolean</code>
* [.openWebHook()](#TelegramBot+openWebHook) * [.openWebHook()](#TelegramBot+openWebHook)<code>Promise</code>
* [.closeWebHook()](#TelegramBot+closeWebHook) ⇒ <code>Promise</code> * [.closeWebHook()](#TelegramBot+closeWebHook) ⇒ <code>Promise</code>
* [.hasOpenWebHook()](#TelegramBot+hasOpenWebHook) ⇒ <code>Boolean</code> * [.hasOpenWebHook()](#TelegramBot+hasOpenWebHook) ⇒ <code>Boolean</code>
* [.getMe()](#TelegramBot+getMe) ⇒ <code>Promise</code> * [.getMe()](#TelegramBot+getMe) ⇒ <code>Promise</code>
@@ -144,19 +145,39 @@ Emits `message` when a message arrives.
| [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. | | [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. |
| [options.baseApiUrl] | <code>String</code> | <code>https://api.telegram.org</code> | API Base URl; useful for proxying and testing | | [options.baseApiUrl] | <code>String</code> | <code>https://api.telegram.org</code> | API Base URl; useful for proxying and testing |
<a name="TelegramBot+initPolling"></a> <a name="TelegramBot+startPolling"></a>
### telegramBot.initPolling() ### telegramBot.startPolling([options]) ⇒ <code>Promise</code>
Start polling Start polling.
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code> **Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [options] | <code>Object</code> | | |
| [options.restart] | <code>Boolean</code> | <code>true</code> | Consecutive calls to this method causes polling to be restarted |
<a name="TelegramBot+initPolling"></a>
### ~~telegramBot.initPolling([options]) ⇒ <code>Promise</code>~~
***Deprecated***
Alias of `TelegramBot#startPolling()`. This is **deprecated**.
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
| Param | Type |
| --- | --- |
| [options] | <code>Object</code> |
<a name="TelegramBot+stopPolling"></a> <a name="TelegramBot+stopPolling"></a>
### telegramBot.stopPolling() ⇒ <code>Promise</code> ### telegramBot.stopPolling() ⇒ <code>Promise</code>
Stops polling after the last polling request resolves Stops polling after the last polling request resolves.
Multiple invocations do nothing if polling is already stopped.
Returning the promise of the last polling request is **deprecated**.
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code> **Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
**Returns**: <code>Promise</code> - promise Promise, of last polling request
<a name="TelegramBot+isPolling"></a> <a name="TelegramBot+isPolling"></a>
### telegramBot.isPolling() ⇒ <code>Boolean</code> ### telegramBot.isPolling() ⇒ <code>Boolean</code>
@@ -165,14 +186,16 @@ Return true if polling. Otherwise, false.
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code> **Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
<a name="TelegramBot+openWebHook"></a> <a name="TelegramBot+openWebHook"></a>
### telegramBot.openWebHook() ### telegramBot.openWebHook() ⇒ <code>Promise</code>
Open webhook Open webhook.
Multiple invocations do nothing if webhook is already open.
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code> **Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
<a name="TelegramBot+closeWebHook"></a> <a name="TelegramBot+closeWebHook"></a>
### telegramBot.closeWebHook() ⇒ <code>Promise</code> ### telegramBot.closeWebHook() ⇒ <code>Promise</code>
Close webhook after closing all current connections Close webhook after closing all current connections.
Multiple invocations do nothing if webhook is already closed.
**Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code> **Kind**: instance method of <code>[TelegramBot](#TelegramBot)</code>
**Returns**: <code>Promise</code> - promise **Returns**: <code>Promise</code> - promise

View File

@@ -72,11 +72,13 @@ class TelegramBot extends EventEmitter {
this.options.baseApiUrl = options.baseApiUrl || 'https://api.telegram.org'; this.options.baseApiUrl = options.baseApiUrl || 'https://api.telegram.org';
this._textRegexpCallbacks = []; this._textRegexpCallbacks = [];
this._onReplyToMessages = []; this._onReplyToMessages = [];
this._polling = null;
this._webHook = null;
if (options.polling) { if (options.polling) {
const autoStart = options.polling.autoStart; const autoStart = options.polling.autoStart;
if (typeof autoStart === 'undefined' || autoStart === true) { if (typeof autoStart === 'undefined' || autoStart === true) {
this.initPolling(); this.startPolling();
} }
} }
@@ -223,29 +225,41 @@ class TelegramBot extends EventEmitter {
} }
/** /**
* Start polling * Start polling.
* @param {Object} [options]
* @param {Boolean} [options.restart=true] Consecutive calls to this method causes polling to be restarted
* @return {Promise}
*/ */
initPolling() { startPolling(options = {}) {
if (this._polling) { options.restart = typeof options.restart === 'undefined' ? true : options.restart;
this._polling.stopPolling({ if (!this._polling) {
cancel: true, this._polling = new TelegramBotPolling(this._request.bind(this), this.options.polling, this.processUpdate.bind(this));
reason: 'Polling restart',
});
} }
this._polling = new TelegramBotPolling(this._request.bind(this), this.options.polling, this.processUpdate.bind(this)); return this._polling.start(options);
} }
/** /**
* Stops polling after the last polling request resolves * Alias of `TelegramBot#startPolling()`. This is **deprecated**.
* @return {Promise} promise Promise, of last polling request * @param {Object} [options]
* @return {Promise}
* @deprecated
*/
initPolling() {
deprecate('TelegramBot#initPolling() is deprecated');
return this.startPolling();
}
/**
* Stops polling after the last polling request resolves.
* Multiple invocations do nothing if polling is already stopped.
* Returning the promise of the last polling request is **deprecated**.
* @return {Promise}
*/ */
stopPolling() { stopPolling() {
if (!this._polling) { if (!this._polling) {
return Promise.resolve(); return Promise.resolve();
} }
const polling = this._polling; return this._polling.stop();
delete this._polling;
return polling.stopPolling();
} }
/** /**
@@ -253,30 +267,31 @@ class TelegramBot extends EventEmitter {
* @return {Boolean} * @return {Boolean}
*/ */
isPolling() { isPolling() {
return !!this._polling; return this._polling ? this._polling.isPolling() : false;
} }
/** /**
* Open webhook * Open webhook.
* Multiple invocations do nothing if webhook is already open.
* @return {Promise}
*/ */
openWebHook() { openWebHook() {
if (this._webHook) { if (!this._webHook) {
return; this._webHook = new TelegramBotWebHook(this.token, this.options.webHook, this.processUpdate.bind(this));
} }
this._webHook = new TelegramBotWebHook(this.token, this.options.webHook, this.processUpdate.bind(this)); return this._webHook.open();
} }
/** /**
* Close webhook after closing all current connections * Close webhook after closing all current connections.
* Multiple invocations do nothing if webhook is already closed.
* @return {Promise} promise * @return {Promise} promise
*/ */
closeWebHook() { closeWebHook() {
if (!this._webHook) { if (!this._webHook) {
return Promise.resolve(); return Promise.resolve();
} }
const webHook = this._webHook; return this._webHook.close();
delete this._webHook;
return webHook.close();
} }
/** /**
@@ -285,7 +300,7 @@ class TelegramBot extends EventEmitter {
* @return {Boolean} * @return {Boolean}
*/ */
hasOpenWebHook() { hasOpenWebHook() {
return !!this._webHook; return this._webHook ? this._webHook.isOpen() : false;
} }
/** /**

View File

@@ -33,7 +33,27 @@ class TelegramBotPolling {
this._lastRequest = null; this._lastRequest = null;
this._abort = false; this._abort = false;
this._pollingTimeout = null; this._pollingTimeout = null;
this._polling(); }
/**
* Start polling
* @param {Object} [options]
* @param {Object} [options.restart]
* @return {Promise}
*/
start(options = {}) {
if (this._lastRequest) {
if (!options.restart) {
return Promise.resolve();
}
return this.stop({
cancel: true,
reason: 'Polling restart',
}).then(() => {
return this._polling();
});
}
return this._polling();
} }
/** /**
@@ -41,20 +61,36 @@ class TelegramBotPolling {
* @param {Object} [options] * @param {Object} [options]
* @param {Boolean} [options.cancel] Cancel current request * @param {Boolean} [options.cancel] Cancel current request
* @param {String} [options.reason] Reason for stopping polling * @param {String} [options.reason] Reason for stopping polling
* @return {Promise}
*/ */
stopPolling(options = {}) { stop(options = {}) {
this._abort = true; if (!this._lastRequest) {
return Promise.resolve();
}
const lastRequest = this._lastRequest;
this._lastRequest = null;
clearTimeout(this._pollingTimeout); clearTimeout(this._pollingTimeout);
if (options.cancel) { if (options.cancel) {
const reason = options.reason || 'Polling stop'; const reason = options.reason || 'Polling stop';
return this._lastRequest.cancel(reason); lastRequest.cancel(reason);
return Promise.resolve();
} }
// wait until the last request is fulfilled this._abort = true;
return this._lastRequest; return lastRequest.finally(() => {
this._abort = false;
});
}
/**
* Return `true` if is polling. Otherwise, `false`.
*/
isPolling() {
return !!this._lastRequest;
} }
/** /**
* Invokes polling (with recursion!) * Invokes polling (with recursion!)
* @return {Promise} promise of the current request
* @private * @private
*/ */
_polling() { _polling() {
@@ -81,6 +117,7 @@ class TelegramBotPolling {
this._pollingTimeout = setTimeout(() => this._polling(), this.options.interval); this._pollingTimeout = setTimeout(() => this._polling(), this.options.interval);
} }
}); });
return this._lastRequest;
} }
/** /**

View File

@@ -47,12 +47,47 @@ class TelegramBotWebHook {
debug('HTTP WebHook enabled'); debug('HTTP WebHook enabled');
this._webServer = http.createServer(this._requestListener); this._webServer = http.createServer(this._requestListener);
} }
}
this._webServer.listen(this.options.port, this.options.host, () => { /**
debug('WebHook listening on port %s', this.options.port); * Open WebHook by listening on the port
* @return {Promise}
*/
open() {
if (this._webServer.listening) {
return Promise.resolve();
}
return new Promise(resolve => {
this._webServer.listen(this.options.port, this.options.host, () => {
debug('WebHook listening on port %s', this.options.port);
return resolve();
});
}); });
} }
/**
* Close the webHook
* @return {Promise}
*/
close() {
if (!this._webServer.listening) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
this._webServer.close(error => {
if (error) return reject(error);
return resolve();
});
});
}
/**
* Return `true` if server is listening. Otherwise, `false`.
*/
isOpen() {
return this._webServer.listening;
}
// used so that other funcs are not non-optimizable // used so that other funcs are not non-optimizable
_safeParse(json) { _safeParse(json) {
try { try {
@@ -106,20 +141,6 @@ class TelegramBotWebHook {
res.end(); res.end();
} }
} }
/**
* Close the webHook
* @return {Promise}
*/
close() {
const self = this;
return new Promise(function closePromise(resolve, reject) {
self._webServer.close(function closeCb(error) {
if (error) return reject(error);
return resolve();
});
});
}
} }
module.exports = TelegramBotWebHook; module.exports = TelegramBotWebHook;

View File

@@ -178,10 +178,11 @@ describe('TelegramBot', function telegramSuite() {
}); });
}); });
describe('#initPolling', function initPollingSuite() { describe('#startPolling', function initPollingSuite() {
it('initiates polling', function test() { it('initiates polling', function test() {
testbot.initPolling(); return testbot.startPolling().then(() => {
return utils.isPollingMockServer(pollingPort); return utils.isPollingMockServer(pollingPort);
});
}); });
}); });
@@ -213,8 +214,9 @@ describe('TelegramBot', function telegramSuite() {
describe('#openWebHook', function openWebHookSuite() { describe('#openWebHook', function openWebHookSuite() {
it('opens webhook', function test() { it('opens webhook', function test() {
testbot.openWebHook(); return testbot.openWebHook().then(() => {
return utils.hasOpenWebHook(webHookPort); return utils.hasOpenWebHook(webHookPort);
});
}); });
}); });