telebot
Version:
The easy way to write Telegram bots.
344 lines (343 loc) • 13.2 kB
JavaScript
"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;