UNPKG

@wasserstoff/mangi-tg-bot

Version:

A powerful Telegram Bot SDK with built-in authentication, session management, and database integration

342 lines 14.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BotManager = void 0; const grammy_1 = require("grammy"); const logger_1 = require("../logger"); const _1 = require("."); const auth_1 = require("./middlewares/auth"); class BotManager { bot; config; composer; registeredCommands = []; sdkLogger; messageHandlers = []; callbackHandlers = []; messageHandlersWithAuth = []; callbackHandlersWithAuth = []; constructor(botToken, redisInstance, config) { this.bot = (0, _1.createBot)(botToken, redisInstance, config); this.composer = new grammy_1.Composer(); this.config = config; this.sdkLogger = (0, logger_1.createSdkLogger)(config.isDev); this.bot.use(this.composer); // Register a single message dispatcher (non-auth) this.composer.on("message", async (ctx, next) => { // If this is a command, skip custom message handlers if (ctx.message && ctx.message.text && ctx.message.text.startsWith("/")) { // Let .command() handlers run return await next(); } for (const { filter, handler } of this.messageHandlers) { if (filter(ctx)) { if (this.config.isDev) { this.sdkLogger.info(`Message handler matched and executed`); } try { await handler(ctx); } catch (error) { if (this.config.isDev) { this.sdkLogger.error("Error handling message:", error); } await ctx.reply("Sorry, there was an error processing your message."); } return; // Only the first matching handler runs } } await next(); }); // Register a single callback dispatcher (non-auth) this.composer.on("callback_query", async (ctx, next) => { for (const { filter, handler } of this.callbackHandlers) { if (filter(ctx)) { if (this.config.isDev) { this.sdkLogger.info(`Callback handler matched and executed`); } try { // Answer the callback query immediately to prevent timeout await ctx.answerCallbackQuery(); // Now execute the handler function await handler(ctx); } catch (error) { if (this.config.isDev) { this.sdkLogger.error("Error handling callback query:", error); } // Only try to answer callback query if it hasn't been answered yet try { await ctx.answerCallbackQuery({ text: "Error processing callback query", }); } catch (callbackError) { // Ignore callback query errors as they might be due to timeout if (this.config.isDev) { this.sdkLogger.error("Error answering callback query:", callbackError); } } } return; // Only the first matching handler runs } } await next(); }); // Register a single message dispatcher (auth) this.composer.on("message", async (ctx, next) => { if (!this.config.jwtSecret) return await next(); // If this is a command, skip custom message handlers if (ctx.message && ctx.message.text && ctx.message.text.startsWith("/")) { return await next(); } const authMiddleware = (0, auth_1.createAuthMiddleware)(this.config.jwtSecret); for (const { filter, handler } of this.messageHandlersWithAuth) { if (filter(ctx)) { if (this.config.isDev) { this.sdkLogger.info(`Auth message handler matched and executed`); } try { await authMiddleware(ctx, async () => handler(ctx)); } catch (error) { if (this.config.isDev) { this.sdkLogger.error("Error handling auth message:", error); } await ctx.reply("Sorry, there was an error processing your message with auth."); } return; } } await next(); }); // Register a single callback dispatcher (auth) this.composer.on("callback_query", async (ctx, next) => { if (!this.config.jwtSecret) return await next(); const authMiddleware = (0, auth_1.createAuthMiddleware)(this.config.jwtSecret); for (const { filter, handler } of this.callbackHandlersWithAuth) { if (filter(ctx)) { if (this.config.isDev) { this.sdkLogger.info(`Auth callback handler matched and executed`); } try { // Answer the callback query immediately to prevent timeout await ctx.answerCallbackQuery(); // Now execute the auth middleware and handler function await authMiddleware(ctx, async () => handler(ctx)); } catch (error) { if (this.config.isDev) { this.sdkLogger.error("Error handling auth callback:", error); } // Only try to answer callback query if it hasn't been answered yet try { await ctx.answerCallbackQuery({ text: "Error processing auth callback" }); } catch (callbackError) { // Ignore callback query errors as they might be due to timeout if (this.config.isDev) { this.sdkLogger.error("Error answering auth callback query:", callbackError); } } } return; } } await next(); }); } handleCommand(command, handler, message, buttons) { if (this.config.isDev) { this.sdkLogger.info(`Registering command /${command}`); } // Only register command once. if (!this.registeredCommands.some((cmd) => cmd.command === command)) { this.registeredCommands.push({ command: command, description: `Execute /${command} command`, }); } this.composer.command(command, async (ctx) => { try { await handler(ctx); if (message) { await ctx.reply(message, { parse_mode: "HTML", reply_markup: buttons ? { inline_keyboard: buttons } : undefined, }); } if (this.config.isDev) { this.sdkLogger.info(`Command /${command} executed successfully`); } } catch (error) { if (this.config.isDev) { this.sdkLogger.error(`Error executing command /${command}:`, error); } await ctx.reply("Sorry, there was an error executing this command."); } }); } async setMyCommands(commands) { try { await this.bot.api.setMyCommands(commands); } catch (error) { if (this.config.isDev) { this.sdkLogger.error("Error setting command menu:", error); } } } handleCallback(filter, handler) { if (this.config.isDev) { this.sdkLogger.info(`Registering callback handler`); } this.callbackHandlers.push({ filter, handler }); } handleMessage(filter, handler) { if (this.config.isDev) { this.sdkLogger.info(`Registering message handler`); } this.messageHandlers.push({ filter, handler }); } async start() { try { if (this.config.botMode === "webhook") { if (this.config.isDev) { this.sdkLogger.info("Starting bot in webhook mode..."); } await this.bot.init(); await this.bot.api.setWebhook(this.config.botWebhookUrl, { allowed_updates: this.config.botAllowedUpdates, }); } else if (this.config.botMode === "polling") { if (this.config.isDev) { this.sdkLogger.info("Starting bot in polling mode..."); } await new Promise((resolve, reject) => { this.bot .start({ allowed_updates: this.config.botAllowedUpdates, onStart: ({ username }) => { if (this.config.isDev) { this.sdkLogger.info("Bot running...", { username }); } resolve(); }, }) .catch((error) => { if (this.config.isDev) { this.sdkLogger.error("Error starting bot:", error); } reject(error); }); }); } } catch (error) { if (this.config.isDev) { this.sdkLogger.error("Error starting BotManager:", error); } throw error; } } async stop() { try { await this.bot.stop(); if (this.config.isDev) { this.sdkLogger.info("Bot stopped successfully"); } } catch (error) { if (this.config.isDev) { this.sdkLogger.error("Error stopping bot:", error); } throw error; } } getBot() { return this.bot; } handleCommandWithAuth(command, handler, message, buttons) { if (!this.config.jwtSecret) { if (this.config.isDev) { this.sdkLogger.error("JWT secret not configured for authentication. Cannot register auth command."); } return; } if (this.config.isDev) { this.sdkLogger.info(`Registering command with auth /${command}`); } if (!this.registeredCommands.some(cmd => cmd.command === command)) { this.registeredCommands.push({ command: command, description: `Execute /${command} command with authentication`, }); } const authMiddleware = (0, auth_1.createAuthMiddleware)(this.config.jwtSecret); this.composer.command(command, async (ctx) => { await authMiddleware(ctx, async () => Promise.resolve()); try { await handler(ctx); if (message) { await ctx.reply(message, { parse_mode: "HTML", reply_markup: buttons ? { inline_keyboard: buttons } : undefined, }); } if (this.config.isDev) { this.sdkLogger.info(`Auth command /${command} executed successfully`); } } catch (error) { if (this.config.isDev) { this.sdkLogger.error(`Error executing auth command /${command}:`, error); } await ctx.reply("Sorry, there was an error executing this command with auth."); } }); } handleCallbackWithAuth(filter, handler) { if (!this.config.jwtSecret) { if (this.config.isDev) { this.sdkLogger.error("JWT secret not configured for authentication. Cannot register auth callback."); } return; } if (this.config.isDev) { this.sdkLogger.info(`Registering auth callback handler`); } this.callbackHandlersWithAuth.push({ filter, handler }); } handleMessageWithAuth(filter, handler) { if (!this.config.jwtSecret) { if (this.config.isDev) { this.sdkLogger.error("JWT secret not configured for authentication. Cannot register auth message handler."); } return; } if (this.config.isDev) { this.sdkLogger.info(`Registering auth message handler`); } this.messageHandlersWithAuth.push({ filter, handler }); } /** * Register a handler for any event type (e.g., 'chat_member', 'my_chat_member', etc.) * This is the recommended way to add group or channel event listeners. * * @param event - grammY filter query string (e.g. 'message', 'chat_member', 'message:text', etc.) * Type-safe and autocompleted in editors. * @param handler - async function to handle the event */ handleEvent(event, handler) { if (this.config.isDev) { this.sdkLogger.info(`Registering handler for event: ${event}`); } this.composer.on(event, handler); } } exports.BotManager = BotManager; //# sourceMappingURL=BotManager.js.map