UNPKG

4.78 kBJavaScriptView Raw
1const errors = require('./errors');
2const debug = require('debug')('node-telegram-bot-api');
3const https = require('https');
4const http = require('http');
5const fs = require('fs');
6const bl = require('bl');
7const Promise = require('bluebird');
8
9
10class TelegramBotWebHook {
11 /**
12 * Sets up a webhook to receive updates
13 * @param {TelegramBot} bot
14 * @see https://core.telegram.org/bots/api#getting-updates
15 */
16 constructor(bot) {
17 this.bot = bot;
18 this.options = (typeof bot.options.webHook === 'boolean') ? {} : bot.options.webHook;
19 this.options.host = this.options.host || '0.0.0.0';
20 this.options.port = this.options.port || 8443;
21 this.options.https = this.options.https || {};
22 this.options.healthEndpoint = this.options.healthEndpoint || '/healthz';
23 this._healthRegex = new RegExp(this.options.healthEndpoint);
24 this._webServer = null;
25 this._open = false;
26 this._requestListener = this._requestListener.bind(this);
27 this._parseBody = this._parseBody.bind(this);
28
29 if (this.options.key && this.options.cert) {
30 debug('HTTPS WebHook enabled (by key/cert)');
31 this.options.https.key = fs.readFileSync(this.options.key);
32 this.options.https.cert = fs.readFileSync(this.options.cert);
33 this._webServer = https.createServer(this.options.https, this._requestListener);
34 } else if (this.options.pfx) {
35 debug('HTTPS WebHook enabled (by pfx)');
36 this.options.https.pfx = fs.readFileSync(this.options.pfx);
37 this._webServer = https.createServer(this.options.https, this._requestListener);
38 } else if (Object.keys(this.options.https).length) {
39 debug('HTTPS WebHook enabled by (https)');
40 this._webServer = https.createServer(this.options.https, this._requestListener);
41 } else {
42 debug('HTTP WebHook enabled');
43 this._webServer = http.createServer(this._requestListener);
44 }
45 }
46
47 /**
48 * Open WebHook by listening on the port
49 * @return {Promise}
50 */
51 open() {
52 if (this.isOpen()) {
53 return Promise.resolve();
54 }
55 return new Promise(resolve => {
56 this._webServer.listen(this.options.port, this.options.host, () => {
57 debug('WebHook listening on port %s', this.options.port);
58 this._open = true;
59 return resolve();
60 });
61 });
62 }
63
64 /**
65 * Close the webHook
66 * @return {Promise}
67 */
68 close() {
69 if (!this.isOpen()) {
70 return Promise.resolve();
71 }
72 return new Promise((resolve, reject) => {
73 this._webServer.close(error => {
74 if (error) return reject(error);
75 this._open = false;
76 return resolve();
77 });
78 });
79 }
80
81 /**
82 * Return `true` if server is listening. Otherwise, `false`.
83 */
84 isOpen() {
85 // NOTE: Since `http.Server.listening` was added in v5.7.0
86 // and we still need to support Node v4,
87 // we are going to fallback to 'this._open'.
88 // The following LOC would suffice for newer versions of Node.js
89 // return this._webServer.listening;
90 return this._open;
91 }
92
93 /**
94 * Handle error thrown during processing of webhook request.
95 * @private
96 * @param {Error} error
97 */
98 _error(error) {
99 if (!this.bot.listeners('webhook_error').length) {
100 return console.error('error: [webhook_error] %j', error); // eslint-disable-line no-console
101 }
102 return this.bot.emit('webhook_error', error);
103 }
104
105 /**
106 * Handle request body by passing it to 'callback'
107 * @private
108 */
109 _parseBody(error, body) {
110 if (error) {
111 return this._error(new errors.FatalError(error));
112 }
113
114 let data;
115 try {
116 data = JSON.parse(body.toString());
117 } catch (parseError) {
118 return this._error(new errors.ParseError(parseError.message));
119 }
120
121 return this.bot.processUpdate(data);
122 }
123
124 /**
125 * Listener for 'request' event on server
126 * @private
127 * @see https://nodejs.org/docs/latest/api/http.html#http_http_createserver_requestlistener
128 * @see https://nodejs.org/docs/latest/api/https.html#https_https_createserver_options_requestlistener
129 */
130 _requestListener(req, res) {
131 debug('WebHook request URL: %s', req.url);
132 debug('WebHook request headers: %j', req.headers);
133
134 if (req.url.indexOf(this.bot.token) !== -1) {
135 if (req.method !== 'POST') {
136 debug('WebHook request isn\'t a POST');
137 res.statusCode = 418; // I'm a teabot!
138 res.end();
139 } else {
140 req
141 .pipe(bl(this._parseBody))
142 .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
156module.exports = TelegramBotWebHook;