2017-02-09 16:12:22 +03:00
|
|
|
const errors = require('./errors');
|
2016-03-17 02:44:34 +03:00
|
|
|
const debug = require('debug')('node-telegram-bot-api');
|
|
|
|
const https = require('https');
|
|
|
|
const http = require('http');
|
|
|
|
const fs = require('fs');
|
|
|
|
const bl = require('bl');
|
2015-07-13 00:24:49 +02:00
|
|
|
|
2017-01-02 13:04:44 +03:00
|
|
|
class TelegramBotWebHook {
|
|
|
|
/**
|
|
|
|
* Sets up a webhook to receive updates
|
2017-02-09 16:12:22 +03:00
|
|
|
* @param {TelegramBot} bot
|
|
|
|
* @see https://core.telegram.org/bots/api#getting-updates
|
2017-01-02 13:04:44 +03:00
|
|
|
*/
|
2017-02-09 16:12:22 +03:00
|
|
|
constructor(bot) {
|
|
|
|
this.bot = bot;
|
|
|
|
this.options = (typeof bot.options.webHook === 'boolean') ? {} : bot.options.webHook;
|
|
|
|
this.options.host = this.options.host || '0.0.0.0';
|
|
|
|
this.options.port = this.options.port || 8443;
|
|
|
|
this.options.https = this.options.https || {};
|
|
|
|
this.options.healthEndpoint = this.options.healthEndpoint || '/healthz';
|
2017-01-30 13:44:07 +03:00
|
|
|
this._healthRegex = new RegExp(this.options.healthEndpoint);
|
2017-01-02 13:04:44 +03:00
|
|
|
this._webServer = null;
|
2017-01-09 16:49:06 +03:00
|
|
|
this._open = false;
|
2017-01-06 22:26:25 +03:00
|
|
|
this._requestListener = this._requestListener.bind(this);
|
|
|
|
this._parseBody = this._parseBody.bind(this);
|
2016-03-17 02:44:34 +03:00
|
|
|
|
2017-01-08 00:43:01 +03:00
|
|
|
if (this.options.key && this.options.cert) {
|
2017-01-07 17:58:01 +03:00
|
|
|
debug('HTTPS WebHook enabled (by key/cert)');
|
2017-01-08 00:43:01 +03:00
|
|
|
this.options.https.key = fs.readFileSync(this.options.key);
|
|
|
|
this.options.https.cert = fs.readFileSync(this.options.cert);
|
2017-01-07 17:58:01 +03:00
|
|
|
this._webServer = https.createServer(this.options.https, this._requestListener);
|
2017-01-08 00:43:01 +03:00
|
|
|
} else if (this.options.pfx) {
|
2017-01-07 17:58:01 +03:00
|
|
|
debug('HTTPS WebHook enabled (by pfx)');
|
2017-01-08 00:43:01 +03:00
|
|
|
this.options.https.pfx = fs.readFileSync(this.options.pfx);
|
2017-01-07 17:58:01 +03:00
|
|
|
this._webServer = https.createServer(this.options.https, this._requestListener);
|
2017-01-08 00:43:01 +03:00
|
|
|
} else if (Object.keys(this.options.https).length) {
|
2017-01-07 17:58:01 +03:00
|
|
|
debug('HTTPS WebHook enabled by (https)');
|
|
|
|
this._webServer = https.createServer(this.options.https, this._requestListener);
|
2016-03-17 02:44:34 +03:00
|
|
|
} else {
|
|
|
|
debug('HTTP WebHook enabled');
|
|
|
|
this._webServer = http.createServer(this._requestListener);
|
|
|
|
}
|
2017-01-09 15:57:34 +03:00
|
|
|
}
|
2016-03-17 02:44:34 +03:00
|
|
|
|
2017-01-09 15:57:34 +03:00
|
|
|
/**
|
|
|
|
* Open WebHook by listening on the port
|
|
|
|
* @return {Promise}
|
|
|
|
*/
|
|
|
|
open() {
|
2017-01-09 16:49:06 +03:00
|
|
|
if (this.isOpen()) {
|
2017-01-09 15:57:34 +03:00
|
|
|
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);
|
2017-01-09 16:49:06 +03:00
|
|
|
this._open = true;
|
2017-01-09 15:57:34 +03:00
|
|
|
return resolve();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Close the webHook
|
|
|
|
* @return {Promise}
|
|
|
|
*/
|
|
|
|
close() {
|
2017-01-09 16:49:06 +03:00
|
|
|
if (!this.isOpen()) {
|
2017-01-09 15:57:34 +03:00
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
this._webServer.close(error => {
|
|
|
|
if (error) return reject(error);
|
2017-01-09 16:49:06 +03:00
|
|
|
this._open = false;
|
2017-01-09 15:57:34 +03:00
|
|
|
return resolve();
|
|
|
|
});
|
2016-03-17 02:44:34 +03:00
|
|
|
});
|
2015-07-13 00:24:49 +02:00
|
|
|
}
|
|
|
|
|
2017-01-09 15:57:34 +03:00
|
|
|
/**
|
|
|
|
* Return `true` if server is listening. Otherwise, `false`.
|
|
|
|
*/
|
|
|
|
isOpen() {
|
2017-01-09 16:49:06 +03:00
|
|
|
// NOTE: Since `http.Server.listening` was added in v5.7.0
|
|
|
|
// and we still need to support Node v4,
|
|
|
|
// we are going to fallback to 'this._open'.
|
|
|
|
// The following LOC would suffice for newer versions of Node.js
|
|
|
|
// return this._webServer.listening;
|
|
|
|
return this._open;
|
2017-01-09 15:57:34 +03:00
|
|
|
}
|
|
|
|
|
2017-02-09 16:12:22 +03:00
|
|
|
/**
|
|
|
|
* Handle error thrown during processing of webhook request.
|
|
|
|
* @private
|
|
|
|
* @param {Error} error
|
|
|
|
*/
|
|
|
|
_error(error) {
|
|
|
|
if (!this.bot.listeners('webhook_error').length) {
|
2017-11-18 23:21:52 +03:00
|
|
|
return console.error('error: [webhook_error] %j', error); // eslint-disable-line no-console
|
2016-03-17 02:44:34 +03:00
|
|
|
}
|
2017-02-09 16:12:22 +03:00
|
|
|
return this.bot.emit('webhook_error', error);
|
2015-07-13 00:24:49 +02:00
|
|
|
}
|
|
|
|
|
2017-01-02 13:04:44 +03:00
|
|
|
/**
|
|
|
|
* Handle request body by passing it to 'callback'
|
|
|
|
* @private
|
|
|
|
*/
|
2017-02-09 16:12:22 +03:00
|
|
|
_parseBody(error, body) {
|
|
|
|
if (error) {
|
|
|
|
return this._error(new errors.FatalError(error));
|
2016-03-17 02:44:34 +03:00
|
|
|
}
|
2015-07-13 00:24:49 +02:00
|
|
|
|
2017-02-09 16:12:22 +03:00
|
|
|
let data;
|
|
|
|
try {
|
|
|
|
data = JSON.parse(body.toString());
|
|
|
|
} catch (parseError) {
|
|
|
|
return this._error(new errors.ParseError(parseError.message));
|
2016-03-17 02:44:34 +03:00
|
|
|
}
|
2015-07-13 00:24:49 +02:00
|
|
|
|
2017-02-09 16:12:22 +03:00
|
|
|
return this.bot.processUpdate(data);
|
2015-07-13 00:24:49 +02:00
|
|
|
}
|
2016-03-17 02:44:34 +03:00
|
|
|
|
2017-01-02 13:04:44 +03:00
|
|
|
/**
|
|
|
|
* Listener for 'request' event on server
|
|
|
|
* @private
|
|
|
|
* @see https://nodejs.org/docs/latest/api/http.html#http_http_createserver_requestlistener
|
|
|
|
* @see https://nodejs.org/docs/latest/api/https.html#https_https_createserver_options_requestlistener
|
|
|
|
*/
|
2017-01-06 22:26:25 +03:00
|
|
|
_requestListener(req, res) {
|
2016-07-13 19:52:50 +02:00
|
|
|
debug('WebHook request URL: %s', req.url);
|
2016-03-17 02:44:34 +03:00
|
|
|
debug('WebHook request headers: %j', req.headers);
|
|
|
|
|
2017-02-09 16:12:22 +03:00
|
|
|
if (req.url.indexOf(this.bot.token) !== -1) {
|
2017-01-30 13:24:15 +03:00
|
|
|
if (req.method !== 'POST') {
|
2017-01-29 21:59:40 +03:00
|
|
|
debug('WebHook request isn\'t a POST');
|
|
|
|
res.statusCode = 418; // I'm a teabot!
|
|
|
|
res.end();
|
2017-01-30 13:24:15 +03:00
|
|
|
} else {
|
|
|
|
req
|
|
|
|
.pipe(bl(this._parseBody))
|
|
|
|
.on('finish', () => res.end('OK'));
|
2017-01-29 21:59:40 +03:00
|
|
|
}
|
|
|
|
} else if (this._healthRegex.test(req.url)) {
|
|
|
|
debug('WebHook health check passed');
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.end('OK');
|
2017-01-30 13:24:15 +03:00
|
|
|
} else {
|
2016-03-17 02:44:34 +03:00
|
|
|
debug('WebHook request unauthorized');
|
|
|
|
res.statusCode = 401;
|
|
|
|
res.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-07-13 00:24:49 +02:00
|
|
|
|
|
|
|
module.exports = TelegramBotWebHook;
|