diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc873c3..110fa9f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased][Unreleased]
+Added:
+
+1. Add health-check endpoint (by @mironov)
* * *
diff --git a/doc/api.md b/doc/api.md
index 9fa7798..4da53b8 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -80,6 +80,7 @@ Emits `message` when a message arrives.
| [options.webHook.pfx] | String
| | Path to file with PFX private key and certificate chain for webHook server. The file is read **synchronously**! |
| [options.webHook.autoOpen] | Boolean
| true
| Open webHook immediately |
| [options.webHook.https] | Object
| | Options to be passed to `https.createServer()`. Note that `options.webHook.key`, `options.webHook.cert` and `options.webHook.pfx`, if provided, will be used to override `key`, `cert` and `pfx` in this object, respectively. See https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener for more information. |
+| [options.webHook.healthEndpoint] | String
| /healthz
| An endpoint for health checks that always responds with 200 OK |
| [options.onlyFirstMatch] | Boolean
| false
| Set to true to stop after first match. Otherwise, all regexps are executed |
| [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 |
diff --git a/src/telegram.js b/src/telegram.js
index 8f6af71..1c4be1c 100644
--- a/src/telegram.js
+++ b/src/telegram.js
@@ -59,6 +59,7 @@ class TelegramBot extends EventEmitter {
* Note that `options.webHook.key`, `options.webHook.cert` and `options.webHook.pfx`, if provided, will be
* used to override `key`, `cert` and `pfx` in this object, respectively.
* See https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener for more information.
+ * @param {String} [options.webHook.healthEndpoint=/healthz] An endpoint for health checks that always responds with 200 OK
* @param {Boolean} [options.onlyFirstMatch=false] Set to true to stop after first match. Otherwise, all regexps are executed
* @param {Object} [options.request] Options which will be added for all requests to telegram api.
* See https://github.com/request/request#requestoptions-callback for more information.
diff --git a/src/telegramWebHook.js b/src/telegramWebHook.js
index 6ee33f2..d35c649 100644
--- a/src/telegramWebHook.js
+++ b/src/telegramWebHook.js
@@ -13,6 +13,7 @@ class TelegramBotWebHook {
* @param {String} token Telegram API token
* @param {Boolean|Object} options WebHook options
* @param {Number} [options.port=8443] Port to bind to
+ * @param {String} [options.healthEndpoint=/healthz] An endpoint for health checks that always responds with 200 OK
* @param {Function} callback Function for process a new update
*/
constructor(token, options, callback) {
@@ -27,6 +28,7 @@ class TelegramBotWebHook {
this.options.https = options.https || {};
this.callback = callback;
this._regex = new RegExp(this.token);
+ this._healthRegex = new RegExp(options.healthEndpoint || '/healthz');
this._webServer = null;
this._open = false;
this._requestListener = this._requestListener.bind(this);
@@ -133,20 +135,24 @@ class TelegramBotWebHook {
debug('WebHook request URL: %s', req.url);
debug('WebHook request headers: %j', req.headers);
- // If there isn't token on URL
- if (!this._regex.test(req.url)) {
+ if (this._regex.test(req.url)) {
+ if (req.method !== 'POST') {
+ debug('WebHook request isn\'t a POST');
+ res.statusCode = 418; // I'm a teabot!
+ res.end();
+ } else {
+ req
+ .pipe(bl(this._parseBody))
+ .on('finish', () => res.end('OK'));
+ }
+ } else if (this._healthRegex.test(req.url)) {
+ debug('WebHook health check passed');
+ res.statusCode = 200;
+ res.end('OK');
+ } else {
debug('WebHook request unauthorized');
res.statusCode = 401;
res.end();
- } else if (req.method === 'POST') {
- req
- .pipe(bl(this._parseBody))
- .on('finish', () => res.end('OK'));
- } else {
- // Authorized but not a POST
- debug('WebHook request isn\'t a POST');
- res.statusCode = 418; // I'm a teabot!
- res.end();
}
}
}
diff --git a/test/telegram.js b/test/telegram.js
index b2c6059..7a5b89a 100644
--- a/test/telegram.js
+++ b/test/telegram.js
@@ -133,6 +133,12 @@ describe('TelegramBot', function telegramSuite() {
});
describe('WebHook', function webHookSuite() {
+ it('returns 200 OK for health endpoint', function test(done) {
+ utils.sendWebHookRequest(webHookPort2, '/healthz', { json: false }).then(resp => {
+ assert.equal(resp, 'OK');
+ return done();
+ });
+ });
it('returns 401 error if token is wrong', function test(done) {
utils.sendWebHookMessage(webHookPort2, 'wrong-token').catch(resp => {
assert.equal(resp.statusCode, 401);
diff --git a/test/utils.js b/test/utils.js
index c63c58e..044bf09 100644
--- a/test/utils.js
+++ b/test/utils.js
@@ -30,6 +30,17 @@ exports = module.exports = {
* @return {Promise}
*/
isPollingMockServer,
+ /**
+ * Send a message to the webhook at the specified port and path.
+ * @param {Number} port
+ * @param {String} path
+ * @param {Object} [options]
+ * @param {String} [options.method=POST] Method to use
+ * @param {Object} [options.message] Message to send. Default to a generic text message
+ * @param {Boolean} [options.https=false] Use https
+ * @return {Promise}
+ */
+ sendWebHookRequest,
/**
* Send a message to the webhook at the specified port.
* @param {Number} port
@@ -134,11 +145,11 @@ function hasOpenWebHook(port, reverse) {
}
-function sendWebHookMessage(port, token, options = {}) {
+function sendWebHookRequest(port, path, options = {}) {
assert.ok(port);
- assert.ok(token);
+ assert.ok(path);
const protocol = options.https ? 'https' : 'http';
- const url = `${protocol}://127.0.0.1:${port}/bot${token}`;
+ const url = `${protocol}://127.0.0.1:${port}${path}`;
return request({
url,
method: options.method || 'POST',
@@ -146,11 +157,19 @@ function sendWebHookMessage(port, token, options = {}) {
update_id: 1,
message: options.message || { text: 'test' }
},
- json: true,
+ json: options.json || true,
});
}
+function sendWebHookMessage(port, token, options = {}) {
+ assert.ok(port);
+ assert.ok(token);
+ const path = `/bot${token}`;
+ return sendWebHookRequest(port, path, options);
+}
+
+
function handleRatelimit(bot, methodName, suite) {
const backupMethodName = `__${methodName}`;
if (!bot[backupMethodName]) bot[backupMethodName] = bot[methodName];