UNPKG

vvlad1973-telegram-framework

Version:
1,563 lines (1,356 loc) 62.4 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: base_telegram_bot/base_telegram_bot.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: base_telegram_bot/base_telegram_bot.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/** * @module base_telegram_bot */ 'use strict'; import EventEmitter from 'events'; import { Worker } from 'worker_threads'; import path from 'path'; import got from 'got'; import { jt, getCallerName } from 'vvlad1973-utils'; import { ValidationError } from 'vvlad1973-error-definitions'; import SimpleLogger from 'vvlad1973-simple-logger'; import { createFormData } from '../../helpers/utils.js'; import { ParseModes, UpdateTypes, MessageTypes, TelegramBotApiMethods, StatusCodes, TelegramBotApiMethodsWithMedia, MediaProperties, PollTypes, ServiceMessages, } from '../enums.js'; import { CallbackQuery } from '../callback_query.js'; import { Message } from '../message.js'; const telegramBotApiUrl = 'https://api.telegram.org/bot', telegramBotFileUrl = 'https://api.telegram.org/file/bot', instance = got.extend({ handlers: [ (options, next) => { Error.captureStackTrace(options.context); return next(options); }, ], hooks: { beforeError: [ (error) => { error.source = error.options.context.stack.split('\n'); return error; }, ], }, }); /** * @class Implements Interface to Telegram Bot API * * @description This object provide access to methods to communicate with * the Telegram * @readonly * @property {integer} id - Read only. Unique identifier for this user or bot. This number * may have more than 32 significant bits and some programming languages * may have difficulty/silent defects in interpreting it. But it has at * most 52 significant bits, so a 64-bit integer or double-precision float * type are safe for storing this identifier * @readonly * @property {string} firstName - Read only. User's or bot's first name * @readonly * @property {string} [username] - Read only. User's or bot's username * @readonly * @property {string} [lastName] - Read only. User's or bot's last name * @readonly * @property {string} [languageCode] - Read only. ETF language tag of the user's language * @readonly * @property {boolean} [canJoinGroups] - Read only. True, if the bot can be invited to * groups * @readonly * @property {boolean} [canReadAllGroupMessages] - Read only. True, if privacy mode is * disabled for the bot * @readonly * @property {boolean} [supportsInlineQueries] - Read only. True, if the bot supports * inline queries * @property {module:enums.ParseModes} [defaultParseMode] - Default value mode * for parsing messages wchich send by the bot * @property {Object} [logger] - Object implemented multilevel logging routines. If not * specified, will use default logger * @readonly * @property {Worker} sender - Read only. The bot's sender which will send data to * Telegram by using sending queue of messages * @property {boolean} [sendByQueue] - If True the bot will send data to * Telegram by using sending queue of messages * @readonly * @property {integer} lastMessageId - Read only. ID of the last sent message * @property {integer} [maxConnections] - The maximum allowed number of * simultaneous HTTPS connections to the webhook for update delivery, 1-100. * Defaults to 40. Use lower values to limit the load on your bot's server, * and higher values to increase your bot's throughput * @property {string} [secretToken] - A secret token to be sent in a header * “X-Telegram-Bot-Api-Secret-Token” in every webhook request, 1-256 * characters. Only characters A-Z, a-z, 0-9, _ and - are allowed. The * header is useful to ensure that the request comes from a webhook set * by you * @property {Object} [certificate] - Upload your public key certificate so * that the root certificate in use can be checked. See our self-signed * guide for details * @property {string} [certificate.file] - Filename of the certificate * @property {Array.&lt;string>} [allowedUpdates] - A JSON-serialized list of the * update types you want your bot to receive. For example, specify * [“message”, “edited_channel_post”, “callback_query”] to only receive * updates of these types. See Update for a complete list of available * update types. Specify an empty list to receive all update types except * chat_member (default). If not specified, the previous setting will be * used. * Please note that this parameter doesn't affect updates created before * the call to the setWebhook, so unwanted updates may be received for a * short period of time * @property {boolean} [dropPendingUpdates] - Pass True to drop all pending * updates * @property {string} [ipAddress] - The fixed IP address which will be used * to send webhook requests instead of the IP address resolved through DNS */ export class BaseTelegramBot extends EventEmitter { #token; #secretToken #defaultParseMode; #senderPath; #id; #username; #firstName; #lastName; #languageCode; #canJoinGroups; #canReadAllGroupMessages; #supportsInlineQueries; webAppUrl; lastMessageId; totalSent = 0; totalReceived = 0; queueSize = 0; maxConnections; ipAddress; allowedUpdates; dropPendingUpdates; certificate; /** * @param {string} token - Each bot is given a unique authentication token * when it is created. The token looks something like * 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 * @param {Object} [options] - some optional parameters to pass to * @param {string} [options.url] - HTTPS URL to send updates to. Use an * empty string to remove webhook integration * @param {string} [options.secretToken] - A secret token to be sent in a * header “X-Telegram-Bot-Api-Secret-Token” in every webhook request, * 1-256 characters. Only characters A-Z, a-z, 0-9, _ and - are allowed. * The header is useful to ensure that the request comes from a webhook * set by you * @param {integer} [options.maxConnections] - The maximum allowed number of * simultaneous HTTPS connections to the webhook for update delivery, * 1-100. Defaults to 40. Use lower values to limit the load on your * bot's server, and higher values to increase your bot's throughput * @param {boolean} [options.sendByQueue] - If True the bot will send data to * Telegram by using sending queue of messages * @param {Object} [options.certificate] - Upload your public key certificate so * that the root certificate in use can be checked. See our self-signed * guide for details * @param {string} options.certificate.file - Frilename of the certificate * @param {Array.&lt;string>} [options.allowedUpdates] - A JSON-serialized list of the * update types you want your bot to receive. For example, specify * [“message”, “edited_channel_post”, “callback_query”] to only receive * updates of these types. See Update for a complete list of available * update types. Specify an empty list to receive all update types except * chat_member (default). If not specified, the previous setting will be * used. * Please note that this parameter doesn't affect updates created before * the call to the setWebhook, so unwanted updates may be received for a * short period of time * @param {boolean} [options.dropPendingUpdates] - Pass True to drop all pending * updates * @param {string} [options.ipAddress] - The fixed IP address which will be used * to send webhook requests instead of the IP address resolved through DNS * @param {module:enums.ParseModes} [defaultParseMode] - Default value mode * for parsing messages wchich send by the bot * @param {Object} [options.sender] - Params for the bot's sender which will send * data to Telegram by using sending queue of messages * @param {integer} [options.sender.capacityLimit] - Limit of amount of messages * to send during one interval of time. Default value is 30 * @param {integer} [options.sender.capacityInterval] - Duration of one time * interval in milliseconds for message flow control. Default value is 1000 * @param {integer} [options.sender.queueSize] - Limit of size of message queue. * Default value is 10 000 * @param {Object} [options.sender.logger] - Object implemented multilevel logging * routines. If not specified, will use default logger * @returns {module:base_telegram_bot.BaseTelegramBot} Instance of the * BaseTelegramBot class */ constructor( token, options = {} ) { super(); this.#token = token; this.#secretToken = options.secretToken; this.ipAddress = options.ipAddress; this.allowedUpdates = options.allowedUpdates; this.url = options.url ?? ''; this.dropPendingUpdates = options.dropPendingUpdates; this.certificate = options.certificate; this.sendByQueue = (typeof options.sendByQueue === 'undefined') ? true : options.sendByQueue; this.maxConnections = options.maxConnections ?? 40; this.logger = options.logger ?? new SimpleLogger(); Object.defineProperties(this, { id: { get: () => { return this.#id; }, }, username: { get: () => { return this.#username; }, }, firstName: { get: () => { return this.#firstName; }, }, lastName: { get: () => { return this.#lastName; }, }, languageCode: { get: () => { return this.#languageCode; }, }, canJoinGroups: { get: () => { return this.#canJoinGroups; }, }, canReadAllGroupMessages: { get: () => { return this.#canReadAllGroupMessages; }, }, supportsInlineQueries: { get: () => { return this.#supportsInlineQueries; }, }, defaultParseMode: { get: () => { return this.#defaultParseMode; }, set: (x) => { if (Object.values(ParseModes).includes(x)) { this.#defaultParseMode = x; } else { this.#defaultParseMode = ParseModes.NONE; } }, }, apiUrl: { get: () => { return `${telegramBotApiUrl}${this.#token}/`; }, }, fileUrl: { get: () => { return `${telegramBotFileUrl}${this.#token}/`; }, }, webhookUrl: { get: () => { let result; if (this.url) result = `${this.url}/${this.#token}`; return result; }, set: (x) => { this.url = x; }, }, }); this.defaultParseMode = options.defaultParseMode ?? ParseModes.NONE; if (process.env.TESTMODE) this.#senderPath = './classes/base_telegram_bot/sender.js'; else this.#senderPath = path.resolve( 'node_modules/vvlad1973-telegram-framework/classes/base_telegram_bot/sender.js' ); this.sender = new Worker(this.#senderPath, { workerData: { url: this.apiUrl, capacity: { limit: options.sender?.capacityLimit ?? 30, interval: options.sender?.capacityInterval ?? 1000 }, queue: { size: options.sender?.queueSize ?? 10000 } }, }); this.sender.logger = options.sender?.logger ?? this.logger; this.sender.on('message', (data) => { if (data?.error) this.sender.logger.error( data.error, `[TelegramBot.Sender] ${data.error.message}` ); else if (data?.debug) this.sender.logger.debug(data.debug, `[TelegramBot.Sender]`); else if (data?.warn) this.sender.logger.warn(data.warn, `[TelegramBot.Sender]`); else if (data?.info) this.sender.logger.info(data.info, `[TelegramBot.Sender]`); else if (data?.trace) this.sender.logger.trace(data.trace, `[TelegramBot.Sender]`); else { if ( typeof data.result === 'object' &amp;&amp; typeof data.request?.id === 'string' ) { if (data?.result?.ok &amp;&amp; data?.response?.result?.message_id) this.lastMessageId = data?.response?.result?.message_id; this.emit(data.request.id, data); } else { throw new TypeError( `Unexpected content of Sender response: ${jt(data)}` ); } } }); } #emitIfExists(type, contents, params, chatId, user, data) { let userId = String(user?.id); this.eventNames().forEach((item) => { let filter; try { filter = JSON.parse(item); } catch (error) { } if (typeof filter === 'object') { let fIsDefined = typeof filter.type !== 'undefined' || typeof filter.contents !== 'undefined' || typeof filter.params !== 'undefined' || typeof filter.chat !== 'undefined' || typeof filter.user !== 'undefined'; if ( fIsDefined &amp;&amp; (typeof filter.type === 'undefined' || (typeof filter.type &amp;&amp; typeof type !== 'undefined' &amp;&amp; type.match(filter.type))) &amp;&amp; (typeof filter.contents === 'undefined' || (typeof filter.contents &amp;&amp; typeof contents !== 'undefined' &amp;&amp; contents.match(filter.contents))) &amp;&amp; (typeof filter.params === 'undefined' || (typeof filter.params &amp;&amp; typeof params !== 'undefined' &amp;&amp; params.match(filter.params))) &amp;&amp; (typeof filter.chat === 'undefined' || (typeof filter.chat &amp;&amp; typeof chatId !== 'undefined' &amp;&amp; chatId.match(filter.chat))) &amp;&amp; (typeof filter.user === 'undefined' || (typeof filter.user &amp;&amp; typeof userId !== 'undefined' &amp;&amp; userId.match(filter.user))) ) { this.emit(item, type, contents, params, chatId, user, data); } } }); } #getNextId() { this.totalSent++; return `${Date.now()}-${this.totalSent}`; } #logEntry(functionName) { functionName = functionName ?? getCallerName(); this.logger.trace(`Function ${functionName}() execution...`); } #logExit(functionName) { functionName = functionName ?? getCallerName(); this.logger.trace(`Function ${functionName}() complete`); } #getEventName(event) { let result; if (typeof event === 'object') { if ( typeof event.type !== 'undefined' || typeof event.contents !== 'undefined' || typeof event.params !== 'undefined' || typeof event.chat !== 'undefined' || typeof event.user != 'undefined' ) { result = JSON.stringify(event); } else { throw new ValidationError('Filter is incorrect'); } } else { result = event; } return result; } on(event, listener) { let eventName = this.#getEventName(event); super.on(eventName, listener); } once(event, listener) { let eventName = this.#getEventName(event); super.once(eventName, listener); } init() { this.#logEntry(); return new Promise((resolve, reject) => { this.getMe() .then( (response) => new Promise((resolve, reject) => { this.logger.trace( response.response?.result, `Bot initialisation data:` ); this.#id = response.response?.result?.id; this.#firstName = response.response?.result?.first_name; this.#lastName = response.response?.result?.last_name; this.#username = response.response?.result?.username; this.#languageCode = response.response?.result?.language_code; this.#canJoinGroups = response.response?.result?.can_join_groups; this.#canReadAllGroupMessages = response.response?.result?.can_read_all_group_messages; this.#supportsInlineQueries = response.response?.result?.supports_inline_queries; this.getWebhookInfo() .then((response) => { this.logger.trace(response, "Got bot's webhook info:"); return resolve(response); }) .catch((error) => reject(error)); }) ) .then( (response) => new Promise((resolve, reject) => { if (this.webhookUrl !== response.response.result.url) this.setWebhook() .then((response) => { this.logger.info(response, "Bot's webhook was reset"); return resolve(); }) .catch((error) => { return reject(error); }); else return resolve(response); }) ) .then((response) => { this.#logExit('init'); return resolve(response); }) .catch((error) => reject(error)); }); } processUpdate(data) { this.#logEntry(); this.logger.debug(data, `Process update. Contents:`); return new Promise((resolve, reject) => { let object, parts, bot, command, type, contents = data?.message?.text ?? data?.message?.caption ?? data?.edited_message?.text ?? data?.edited_message?.caption ?? data?.channel_post?.text ?? data?.channel_post?.caption ?? data?.edited_channel_post?.text ?? data?.edited_channel_post?.caption ?? data?.callback_query?.data ?? data?.inline_query?.query ?? data?.chosenInlineResult?.result_id ?? data?.poll?.question ?? data?.poll_answer?.option_ids, chatId = data?.message?.chat?.id ?? data?.edited_message?.chat?.id ?? data?.channel_post?.chat?.id ?? data?.edited_channel_post?.chat?.id ?? data?.callback_query?.message?.chat?.id ?? data?.my_chat_member?.chat?.id ?? data?.chat_join_request?.chat?.id ?? data?.chat_member?.chat?.id, user = data?.message?.from ?? data?.edited_message?.from ?? data?.channel_post?.from ?? data?.edited_channel_post?.from ?? data?.callback_query?.from ?? data?.inline_query?.from ?? data?.chosen_inline_result?.from ?? data?.shipping_query?.from ?? data?.pre_checkout_query?.from ?? data?.inline_query?.from ?? data?.poll_answer?.user ?? data?.my_chat_member?.from ?? data?.chat_join_request?.from ?? data?.chat_member?.from, message = data?.message ? new Message(data?.message, this) : undefined, editedMessage = data?.edited_message ? new Message(data?.message, this) : undefined, channelPost = data?.channel_post ? new Message(data?.message, this) : undefined, editedСhannelPost = data?.edited_channel_post ? new Message(data?.message, this) : undefined, callbackQuery = data?.callback_query ? new CallbackQuery(data?.callback_query, this) : undefined, inlineQuery = data?.inline_query, chosenInlineResult = data?.chosen_inline_result, shippingQuery = data?.shipping_query, preCheckoutQuery = data?.pre_checkout_query, poll = data?.poll, pollAnswer = data?.poll_answer, myChatMember = data?.my_chat_member, chatMember = data?.chat_member, chatJoinRequest = data?.chat_join_request, params = message?.fileId ?? data?.callback_query?.id ?? data?.inline_query?.id ?? data?.chosenInlineResult?.query ?? data?.poll?.options ?? data?.chat_join_request?.invite_link ?? data?.poll_answer?.poll_id; if (message) { object = message; type = UpdateTypes.MESSAGE; if (typeof contents === 'string') parts = contents.match(/^[\\/]([^@\s]+)(@([\S]+))?( (.*))?$/); if (parts) { command = parts[1]; params = parts[5] ? parts[5] : ''; bot = parts[3] ? parts[3] : ''; } type = message.type; } else if (callbackQuery) { object = callbackQuery; type = UpdateTypes.CALLBACK_QUERY; } else if (editedMessage) { object = editedMessage; type = UpdateTypes.EDITED_MESSAGE; } else if (channelPost) { object = channelPost; type = UpdateTypes.CHANNEL_POST; } else if (editedСhannelPost) { object = editedСhannelPost; type = UpdateTypes.EDITED_CHANNEL_POST; } else if (inlineQuery) { object = inlineQuery; type = UpdateTypes.INLINE_QUERY; } else if (chosenInlineResult) { object = chosenInlineResult; type = UpdateTypes.CHOSEN_INLINE_RESULT; } else if (preCheckoutQuery) { object = preCheckoutQuery; type = UpdateTypes.PRE_CHECKOUT_QUERY; } else if (poll) { object = poll; type = UpdateTypes.POLL; } else if (pollAnswer) { object = pollAnswer; type = UpdateTypes.POLL_ANSWER; } else if (myChatMember) { object = myChatMember; type = UpdateTypes.MY_CHAT_MEMBER; } else if (chatMember) { object = chatMember; type = UpdateTypes.CHAT_MEMBER; } else if (chatJoinRequest) { object = chatJoinRequest; type = UpdateTypes.CHAT_JOIN_REQUEST; } else if (shippingQuery) { object = shippingQuery; type = UpdateTypes.SHIPPING_QUERY; } if (object) { this.totalReceived++; if (chatId === user?.id) this.#emitIfExists(type, contents, params, 'private', user, object); else this.#emitIfExists(type, contents, params, chatId, user, object); if (command) { if (bot === '' || bot === this.username) { this.emit( MessageTypes.COMMAND, command, params, chatId, user, object ); this.emit(command, contents, params, chatId, user, object); if (chatId === user?.id) { this.emit( `${MessageTypes.COMMAND}@private`, command, params, chatId, user, object ); this.emit( `${command}@private`, contents, params, chatId, user, object ); } } type = 'command'; contents = command; } if (ServiceMessages.includes(type)) this.emit('service_message', contents, params, chatId, user, object); this.emit('*', type, contents, params, chatId, user, object); if (chatId === user?.id) this.emit('*@private', type, contents, params, chatId, user, object); } this.#logExit('processUpdate'); return resolve(); }); } #sendByQueue(data) { this.#logEntry(); return new Promise(async (resolve, reject) => { let id = this.#getNextId(); this.sender.postMessage({ id: id, data: data }); this.logger.debug(data, `[queueId=${id}] Data was enqueued:`); this.once(id, (data) => { if (data.result.ok) { this.logger.debug(data, `[queueId=${id}] Data was sent:`); if (data.queue_size) this.queueSize = data.queue_size; this.#logExit('#sendByQueue'); return resolve(data); } else { return reject(data); } }); }); } #callApi(data) { this.#logEntry(); let self = this; return new Promise(async (resolve, reject) => { let url = this.apiUrl, result, _options = {}, response; if (data.multipart) { let form = createFormData(data.payload); _options = { headers: form.getHeaders(), body: form, }; } else _options.json = data.payload; _options.responseType = 'json'; instance .post(url, _options) .then((result) => { result = { result: { ok: true, code: StatusCodes.OK, }, response: result.body, request: data, }; self.logger.debug(result, `Data was sent:`); self.#logExit('callApi'); return resolve(result); }) .catch((error) => { let code; switch (error.code) { case 'ENOTFOUND': { code = StatusCodes.NOT_FOUND; break; } default: code = StatusCodes.BAD_REQUEST; } result = { result: { ok: false, code: code, message: error.message, }, response: error.response?.body, request: data, }; return reject(result); }); }); } #baseTelegramMethod(required, ...optional) { this.#logEntry(); return new Promise(async (resolve, reject) => { let data, hasMedia = false, result, obj = Object.assign(required, ...optional); if (obj._options) { obj = Object.assign(obj, obj._options); delete obj._options; } if (obj[MediaProperties.MEDIA]) { if (Array.isArray(obj[MediaProperties.MEDIA])) for (let item of obj[MediaProperties.MEDIA]) if (item.media.file || item.thumbnail?.file) hasMedia = true; } else if ( Object.values(TelegramBotApiMethodsWithMedia).includes(obj.method) ) { for (let item of Object.values(MediaProperties)) { if (Object.keys(obj).includes(item) &amp;&amp; obj[item].file) { hasMedia = true; } } } data = { multipart: hasMedia, payload: obj, }; try { if (this.sendByQueue) { result = await this.#sendByQueue(data); } else { result = await this.#callApi(data); } this.totalSent++; this.#logExit('#baseTelegramMethod'); return resolve(result); } catch (error) { return reject(error); } }); } getMe() { this.#logEntry(); return new Promise(async (resolve, reject) => { let data = { method: TelegramBotApiMethods.GET_ME, }; this.#baseTelegramMethod(data) .then((response) => { this.#logExit('getMe'); return resolve(response); }) .catch((error) => reject(error)); }); } getWebhookInfo() { this.#logEntry(); return new Promise(async (resolve, reject) => { let data = { method: TelegramBotApiMethods.GET_WEBHOOK_INFO, }; this.#baseTelegramMethod(data) .then((response) => { this.#logExit('getWebhookInfo'); return resolve(response); }) .catch((error) => reject(error)); }); } deleteWebhook(dropPendingUpdates = true) { this.#logEntry(); return new Promise(async (resolve, reject) => { let data = { method: TelegramBotApiMethods.DELETE_WEBHOOK, }; if (!dropPendingUpdates) data['drop_pending_updates'] = false; this.#baseTelegramMethod(data) .then((response) => { this.#logExit('deleteWebhook'); return resolve(response); }) .catch((error) => reject(error)); }); } /** * Use this method to specify a URL and receive incoming updates via an * outgoing webhook. Whenever there is an update for the bot, we will send * an HTTPS POST request to the specified URL, containing a JSON-serialized * Update. In case of an unsuccessful request, we will give up after a * reasonable amount of attempts. Returns True on success. * If you'd like to make sure that the webhook was set by you, you can * specify secret data in the parameter secret_token. If specified, the * request will contain a header “X-Telegram-Bot-Api-Secret-Token” with the * secret token as content * @param {string} [url] - HTTPS URL to send updates to. Use an * empty string to remove webhook integration. Default value is empty * string * @param {Object} [options] - some optional parameters to pass to * @param {string} [options.secretToken] - A secret token to be sent in a * header “X-Telegram-Bot-Api-Secret-Token” in every webhook request, * 1-256 characters. Only characters A-Z, a-z, 0-9, _ and - are allowed. * The header is useful to ensure that the request comes from a webhook * set by you * @param {integer} [options.maxConnections] - The maximum allowed number of * simultaneous HTTPS connections to the webhook for update delivery, * 1-100. Defaults to 40. Use lower values to limit the load on your * bot's server, and higher values to increase your bot's throughput * @param {boolean} [options.sendByQueue] - If True the bot will send data to * Telegram by using sending queue of messages * @param {Object} [options.certificate] - Upload your public key certificate so * that the root certificate in use can be checked. See our self-signed * guide for details * @param {string} options.certificate.file - Frilename of the certificate * @param {Array.&lt;string>} [options.allowedUpdates] - A JSON-serialized list of the * update types you want your bot to receive. For example, specify * [“message”, “edited_channel_post”, “callback_query”] to only receive * updates of these types. See Update for a complete list of available * update types. Specify an empty list to receive all update types except * chat_member (default). If not specified, the previous setting will be * used. * Please note that this parameter doesn't affect updates created before * the call to the setWebhook, so unwanted updates may be received for a * short period of time * @param {boolean} [options.dropPendingUpdates] - Pass True to drop all pending * updates * @param {string} [options.ipAddress] - The fixed IP address which will be used * to send webhook requests instead of the IP address resolved through DNS * @returns {Object} Response with method executing result */ setWebhook(url, options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (typeof url === 'string') this.webhookUrl = url else url = this.webhookUrl; if (typeof this.webhookUrl === 'undefined') this.webhookUrl = ''; if (options.maxConnections) this.maxConnections = options.maxConnections; if (options.secretToken) this.#secretToken = options.secretToken; if (options.ipAddress) this.ipAddress = options.ipAddress; if (options.allowedUpdates) this.allowedUpdates = options.allowedUpdates; if (options.dropPendingUpdates) this.dropPendingUpdates = options.dropPendingUpdates; if (options.certificate) this.certificate = options.certificate; let data = { method: TelegramBotApiMethods.SET_WEBHOOK, url: this.webhookUrl, max_connections: this.maxConnections, secret_token: this.#secretToken, ip_address: this.ipAddress, allowed_updates: this.allowedUpdates, drop_pending_updates: this.dropPendingUpdates, certificate: this.certificate }; this.#baseTelegramMethod(data) .then((response) => { this.#logExit('setWebhook'); return resolve(response); }) .catch((error) => reject(error)); }); } sendMessage(chatId, text, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; text) { let data = { method: TelegramBotApiMethods.SEND_MESSAGE, chat_id: chatId, text: text, }; _options['parse_mode'] = _options['parse_mode'] ?? this.defaultParseMode; return this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('sendMessage'); return resolve(response); }) .catch((error) => reject(error)); } else return reject(new ValidationError('ChatId and text are required')); }); } sendPhoto(chatId, photo, caption, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; photo) { let data = { method: TelegramBotApiMethods.SEND_PHOTO, chat_id: chatId, photo: photo, caption: caption, }; _options['parse_mode'] = _options['parse_mode'] ?? this.defaultParseMode; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('sendPhoto'); return resolve(response); }) .catch((error) => reject(error)); } else return reject(new ValidationError('ChatId and photo are required')); }); } sendMediaGroup(chatId, media, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; media) { let data = { method: TelegramBotApiMethods.SEND_MEDIA_GROUP, chat_id: chatId, media: media, }; for (let mediaItem of media) mediaItem['parse_mode'] = mediaItem['parse_mode'] ?? this.defaultParseMode; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('sendMediaGroup'); return resolve(response); }) .catch((error) => reject(error)); } else return reject( new ValidationError('ChatId and media group are required') ); }); } forwardMessage(chatId, fromChatId, messageId, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; fromChatId &amp;&amp; messageId) { let data = { method: TelegramBotApiMethods.FORWARD_MESSAGE, chat_id: chatId, from_chat_id: fromChatId, message_id: messageId, }; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('forwardMessage'); return resolve(response); }) .catch((error) => reject(error)); } else return reject( new ValidationError('ChatId, fromChatId and messageId are required') ); }); } copyMessage(chatId, fromChatId, messageId, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; fromChatId &amp;&amp; messageId) { let data = { method: TelegramBotApiMethods.COPY_MESSAGE, chat_id: chatId, from_chat_id: fromChatId, message_id: messageId, }; _options['parse_mode'] = _options['parse_mode'] ?? this.defaultParseMode; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('copyMessage'); return resolve(response); }) .catch((error) => reject(error)); } else return reject( new ValidationError('ChatId, fromChatId and messageId are required') ); }); } deleteMessage(chatId, messageId) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; messageId) { let data = { method: TelegramBotApiMethods.DELETE_MESSAGE, chat_id: chatId, message_id: messageId, }; this.#baseTelegramMethod(data) .then((response) => { this.#logExit('deleteMessage'); return resolve(response); }) .catch((error) => reject(error)); } else return reject(new ValidationError('ChatId and messageId are required')); }); } pinChatMessage(chatId, messageId, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; messageId) { let data = { method: TelegramBotApiMethods.PIN_CHAT_MESSAGE, chat_id: chatId, message_id: messageId, }; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('pinChatMessage'); return resolve(response); }) .catch((error) => reject(error)); } else return Promise.reject( new ValidationError('ChatId and messageId are required') ); }); } unpinChatMessage(chatId, messageId) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; messageId) { let data = { method: TelegramBotApiMethods.UNPIN_CHAT_MESSAGE, chat_id: chatId, message_id: messageId, }; this.#baseTelegramMethod(data) .then((response) => { this.#logExit('unpinChatMessage'); return resolve(response); }) .catch((error) => reject(error)); } else return reject(new ValidationError('ChatId and messageId are required')); }); } unpinAllChatMessages(chatId) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId) { let data = { method: TelegramBotApiMethods.UNPIN_ALL_CHAT_MESSAGES, chat_id: chatId, }; this.#baseTelegramMethod(data) .then((response) => { this.#logExit('unpinAllChatMessages'); return resolve(response); }) .catch((error) => reject(error)); } else return reject(new ValidationError('ChatId is required')); }); } answerCallbackQuery(callbackQueryId, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (callbackQueryId) { let data = { method: TelegramBotApiMethods.ANSWER_CALLBACK_QUERY, callback_query_id: callbackQueryId, }; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('answerCallbackQuery'); return resolve(response); }) .catch((error) => reject(error)); } else return reject(new ValidationError('callbackQueryId is required')); }); } sendDice(chatId, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId) { let data = { method: TelegramBotApiMethods.SEND_DICE, chat_id: chatId, }; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('sendDice'); return resolve(response); }) .catch((error) => reject(error)); } else return Promise.reject(new ValidationError('ChatId is required')); }); } sendChatAction(chatId, action) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; action) { let data = { method: 'sendChatAction', chat_id: chatId, action: action, }; this.#baseTelegramMethod(data) .then((response) => { this.#logExit('sendChatAction'); return resolve(response); }) .catch((error) => reject(error)); } else return Promise.reject( new ValidationError('ChatId and action are required') ); }); } getMyCommands(_options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { let data = { method: TelegramBotApiMethods.GET_MY_COMMANDS, }; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('getMyCommands'); return resolve(response); }) .catch((error) => reject(error)); }); } deleteMyCommands(_options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { let data = { method: TelegramBotApiMethods.DELETE_MY_COMMANDS, }; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('deleteMyCommands'); return resolve(response); }) .catch((error) => reject(error)); }); } setMyCommands(commands, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (commands) { let data = { method: TelegramBotApiMethods.SET_MY_COMMANDS, commands: JSON.stringify(commands), }; _options['scope'] = jt(_options['scope']); this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('setMyCommands'); return resolve(response); }) .catch((error) => reject(error)); } else return reject(new ValidationError('Commands are required')); }); } editMessageText(text, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if ( text &amp;&amp; (_options.inline_message_id || (_options.chat_id &amp;&amp; _options.message_id)) ) { let data = { method: TelegramBotApiMethods.EDIT_MESSAGE_TEXT, text: text, }; _options['parse_mode'] = _options['parse_mode'] ?? this.defaultParseMode; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('editMessageText'); return resolve(response); }) .catch((error) => reject(error)); } else return reject( new ValidationError( 'Text and inlineMessageId or chatId with ' + 'messageId must be defined' ) ); }); } editMessageCaption(caption, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if ( _options.inline_message_id || (_options.chat_id &amp;&amp; _options.message_id) ) { let data = { method: TelegramBotApiMethods.EDIT_MESSAGE_CAPTION, caption: caption, }; _options['parse_mode'] = _options['parse_mode'] ?? this.defaultParseMode; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('editMessageCaption'); return resolve(response); }) .catch((error) => reject(error)); } else return reject( new ValidationError( 'inlineMessageId or chatId with messageId must be defined' ) ); }); } editMessageReplyMarkup(_options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if ( _options.inline_message_id || (_options.chat_id &amp;&amp; _options.message_id) ) { let data = { method: TelegramBotApiMethods.EDIT_MESSAGE_REPLY_MARKUP, }; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('editMessageReplyMarkup'); return resolve(response); }) .catch((error) => reject(error)); } else return reject( new ValidationError( 'inlineMessageId or chatId with messageId must be defined' ) ); }); } editMessageMedia(media, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if ( _options.inline_message_id || (_options.chat_id &amp;&amp; _options.message_id) ) { media.parse_mode = media.parse_mode ?? this.defaultParseMode; let data = { method: TelegramBotApiMethods.EDIT_MESSAGE_MEDIA, media: jt(media), }; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('editMessageMedia'); return resolve(response); }) .catch((error) => reject(error)); } else return reject( new ValidationError( 'inlineMessageId or chatId with messageId must be defined' ) ); }); } sendVideo(chatId, video, caption, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; video) { let data = { method: TelegramBotApiMethods.SEND_VIDEO, chat_id: chatId, video: video, caption: caption, }; _options['parse_mode'] = _options['parse_mode'] ?? this.defaultParseMode; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('sendVideo'); return resolve(response); }) .catch((error) => reject(error)); } else return reject(new ValidationError('ChatId and video are required')); }); } sendContact(chatId, phoneNumber, firstName, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; phoneNumber &amp;&amp; firstName) { let data = { method: TelegramBotApiMethods.SEND_CONTACT, chat_id: chatId, phone_number: phoneNumber, first_name: firstName, }; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('sendContact'); return resolve(response); }) .catch((error) => reject(error)); } else return reject( new ValidationError('ChatId, phoneNumber and firstName are required') ); }); } sendAudio(chatId, audio, caption, _options = {}) { this.#logEntry(); return new Promise(async (resolve, reject) => { if (chatId &amp;&amp; audio) { let data = { method: TelegramBotApiMethods.SEND_AUDIO, chat_id: chatId, audio: audio, caption: caption, }; _options['parse_mode'] = _options['parse_mode'] ?? this.defaultParseMode; this.#baseTelegramMethod(data, _options) .then((response) => { this.#logExit('sendAudio'); return resolve(response); }) .catch((error) => reject(error)); } else return reject(new ValidationError('ChatId and audio are required')); }); } sendVoice(chatId, voice, caption, _options = {}) { this.#logEntry(); return new Promise(async (res