UNPKG

telebot

Version:

The easy way to write Telegram bots.

344 lines (343 loc) 13.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const axios_1 = __importDefault(require("axios")); const telebot_1 = require("./types/telebot"); const errors_1 = require("./errors"); const devkit_1 = require("./telebot/devkit"); const events_1 = require("./telebot/events"); const processors_1 = require("./telebot/processors"); const webhook_1 = require("./telebot/webhook"); const utils_1 = require("./utils"); const TELEGRAM_BOT_API = (botToken) => `https://api.telegram.org/bot${botToken}`; exports.DEFAULT_POLLING = { offset: 0, interval: false, timeout: 0, limit: 100, retryTimeout: 3000, retryTimes: Infinity }; exports.DEFAULT_DEBUG = { levels: Object.values(devkit_1.Levels).filter(value => typeof value === "number") }; class TeleBot extends events_1.TeleBotEvents { constructor(options) { super(); this.polling = exports.DEFAULT_POLLING; this.allowedUpdates = []; this.debug = false; this.flags = { isRunning: false, canFetch: true, waitEvents: false }; this.runningInstanceScenario = telebot_1.TeleBotScenario.Pass; this.telegramFetchErrorScenario = telebot_1.TeleBotScenario.Reconnect; if (typeof options === "string") { options = { token: options }; } const { token, botAPI, polling, webhook, allowedUpdates, debug } = options; this.botToken = token; if (!this.botToken) { throw new errors_1.TeleBotError("Telegram bot token is not provided."); } this.botAPI = botAPI || TELEGRAM_BOT_API(this.botToken); this.webhook = webhook; if (polling) { const { offset, interval, timeout, limit, retryTimes, retryTimeout } = polling; if (offset !== undefined && Number.isInteger(offset)) { this.setOffset(offset); } if (interval !== undefined && interval > 0) { this.polling.interval = interval; } if (timeout !== undefined && timeout > 0) { this.polling.timeout = timeout; } if (limit !== undefined && limit > 0) { this.polling.limit = limit; } if (retryTimes !== undefined && Number.isInteger(retryTimes)) { this.polling.retryTimes = retryTimes; } if (retryTimeout !== undefined && Number.isInteger(retryTimeout)) { this.polling.retryTimeout = retryTimeout; } } else if (webhook) { const { url } = webhook; const { port: urlPort, protocol } = utils_1.parseUrl(url); const webhookPort = urlPort ? parseInt(urlPort) : 80; const isHttps = protocol === null || protocol === void 0 ? void 0 : protocol.toLowerCase().startsWith("https"); if (!isHttps) { throw new errors_1.TeleBotError(`Webhook '${url}' must be HTTPS secured.`); } if (!webhook_1.allowedWebhookPorts.includes(webhookPort)) { throw new errors_1.TeleBotError(`Webhook port of '${webhookPort}' is not supported by Telegram Bot API. Use allowed webhook ports: ${webhook_1.allowedWebhookPorts.join(", ")}`); } // if (host) { // throw new TeleBotError("Webhook host is required."); // } // // if (!port || !Number.isInteger(port) || port <= 0) { // throw new TeleBotError(`Invalid webhook port: '${port}'`); // } } if (allowedUpdates !== undefined && Array.isArray(allowedUpdates) && allowedUpdates.length) { this.allowedUpdates = allowedUpdates; } if (debug) { this.debug = this.debug !== true ? debug : exports.DEFAULT_DEBUG; } else if (debug === false) { this.debug = false; } this.dev = new devkit_1.TeleBotDev("telebot", this, this.debug); } getToken() { return this.botToken; } getOptions() { return { token: this.botToken, botAPI: this.botAPI, polling: this.polling, webhook: this.webhook, allowedUpdates: this.allowedUpdates }; } start() { return __awaiter(this, void 0, void 0, function* () { const { interval } = this.polling; const webhook = this.webhook; try { this.me = yield this.getMe(); } catch (error) { if (error instanceof errors_1.TeleBotRequestError) { if (error.response.error_code === 401) { return new errors_1.TeleBotError("Incorrect Telegram Bot token."); } } return error; } if (!this.hasFlag("isRunning")) { this.setFlag("isRunning"); try { const deleteWebhook = () => __awaiter(this, void 0, void 0, function* () { return yield this.deleteWebhook(); }); if (webhook) { const url = `${webhook.url}/${this.getToken()}`; yield this.setWebhook(url, { certificate: webhook.cert }); if (webhook.host && webhook.port) { yield webhook_1.webhookServer(this, webhook); } } else if (interval && interval > 0) { yield deleteWebhook(); this.startLifeInterval(interval); } else if (interval === false) { yield deleteWebhook(); this.startLifeCycle(); } } catch (error) { const e = error; this.dev.error("telebot", { error: e }); // eslint-disable-next-line no-console console.error("==== TELEBOT GLOBAL ERROR ====", e); return e; } } else { switch (this.runningInstanceScenario) { case telebot_1.TeleBotScenario.Restart: yield this.restart(); break; case telebot_1.TeleBotScenario.Terminate: yield this.stop(); throw new errors_1.TeleBotError(errors_1.ERROR_TELEBOT_ALREADY_RUNNING); case telebot_1.TeleBotScenario.Pass: default: break; } } }); } restart() { return __awaiter(this, void 0, void 0, function* () { this.dev.info("restart"); yield this.stop(); return yield this.start(); }); } stop() { return __awaiter(this, void 0, void 0, function* () { this.dev.info("stop"); this.unsetFlag("isRunning"); if (this.lifeIntervalFn) { clearInterval(this.lifeIntervalFn); this.lifeIntervalFn = undefined; } }); } getFlags() { return this.flags; } hasFlag(name) { return this.flags[name]; } setFlag(name) { this.dev.debug("setFlag", `${name} = true`); this.flags[name] = true; } unsetFlag(name) { this.dev.debug("unsetFlag", `${name} = false`); this.flags[name] = false; } setOffset(offset) { this.dev.debug("setOffset", offset); this.polling.offset = offset; } startLifeCycle() { this.dev.info("startLifeCycle"); this.lifeCycle(false); } // TODO: WIP startLifeInterval(interval) { this.dev.info("startLifeInterval", { message: { interval } }); this.lifeIntervalFn = setInterval(() => { this.dev.debug("lifeInterval"); if (this.hasFlag("isRunning") && this.hasFlag("canFetch")) { this.unsetFlag("canFetch"); this.lifeCycle(true).then(() => { this.setFlag("canFetch"); }).catch((error) => { this.dev.error("startLifeInterval", { error }); }); } }, interval); } lifeCycle(liveOnce = true) { this.dev.debug("lifeCycle", `liveOnce = ${liveOnce}`); if (this.hasFlag("isRunning")) { const promise = this.fetchTelegramUpdates(); return promise.then((data) => { this.dev.debug("lifeCycle.ok", { message: data }); return liveOnce ? Promise.resolve() : this.lifeCycle(false); }).catch((error) => { this.dev.error("lifeCycle", { error }); return Promise.reject(error); }); } else { this.dev.debug("lifeCycle", "not running"); return Promise.resolve(); } } fetchTelegramUpdates(offset = this.polling.offset, limit = this.polling.limit, timeout = this.polling.timeout) { this.dev.debug("fetchTelegramUpdates"); const promise = this.telegramRequest("getUpdates", { offset, limit, timeout }); return promise.then((updates) => { let processPromise = Promise.resolve(); this.dev.debug("fetchTelegramUpdates.updates", { message: updates }); if (updates) { processPromise = this.processTelegramUpdates(updates); } return processPromise.then(() => { if (updates && updates.length > 0) { this.setOffset(++updates[updates.length - 1].update_id); } }); }).catch((error) => { this.dev.error("fetchTelegramUpdates", { error }); return Promise.reject(error); }); } processTelegramUpdates(updates) { const processorPromises = []; this.dev.info("processTelegramUpdates", { data: updates }); updates.forEach((update) => { for (const processorName in processors_1.updateProcessors) { if (processorName in update) { const updateData = update[processorName]; processorPromises.push(processors_1.updateProcessors[processorName].call(this, updateData, {})); break; } } }); return Promise.all(processorPromises); } telegramRequest(endpoint, payload) { const url = `${this.botAPI}/${endpoint}`; this.dev.info("telegramRequest", { data: { url, payload } }); return axios_1.default.request({ url, data: payload, method: "post", responseType: "json" }) .then((response) => { const { ok, result } = response.data; this.dev.info("telegramRequest.response", { data: response.data }); if (!ok || result === undefined) { return Promise.reject(response.data); } return result; }) .catch((error) => { const e = errors_1.handleTelegramResponse(error); this.dev.error("telegramRequest.response", { error: e }); return Promise.reject(e); }); } telegramMethod({ method, required, optional }) { return __awaiter(this, void 0, void 0, function* () { const data = Object.assign({}, required, optional); this.dev.debug("telegramMethod", { message: `${method} ${utils_1.toString(data)}`, data: { required, optional } }); return this.telegramRequest(method, data); }); } } exports.TeleBot = TeleBot;