UNPKG

@wasserstoff/mangi-tg-bot

Version:

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

182 lines 7.68 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.createBot = void 0; const grammy_1 = require("grammy"); const storage_redis_1 = require("@grammyjs/storage-redis"); const grammy_middlewares_1 = require("grammy-middlewares"); const CustomContext_1 = require("./context/CustomContext"); const logger_1 = require("../logger"); const session_1 = __importStar(require("./middlewares/session")); const updateLogger_1 = require("./middlewares/updateLogger"); const errorHandler_1 = require("./helper/errorHandler"); const auth_1 = require("./middlewares/auth"); const adminAuth_1 = __importStar(require("./middlewares/adminAuth")); const createBot = (BOT_TOKEN, redisInstance, config) => { const sdkLogger = (0, logger_1.createSdkLogger)(config.isDev); const bot = new grammy_1.Bot(BOT_TOKEN, { ContextConstructor: (0, CustomContext_1.createContextConstructor)({ logger: sdkLogger }), client: { canUseWebhookReply: (method) => method === "sendMessage", }, }); // Inject config into ctx for all updates bot.use(async (ctx, next) => { ctx.config = config; await next(); }); // Create Redis storage adapter const storage = new storage_redis_1.RedisAdapter({ instance: redisInstance, }); // Handle errors in Bot const protectedBot = bot.errorBoundary(errorHandler_1.errorHandler); if (config.isDev) { protectedBot.use((0, updateLogger_1.updateLogger)()); } // Configure session middleware with proper typing protectedBot.use((0, grammy_1.session)({ initial: () => ({ jwtToken: undefined, custom: {}, setCustom: () => { }, getCustom: () => undefined, updateCustom: () => { }, deleteCustom: () => { }, }), storage, getSessionKey(ctx) { // Ensure we have a valid chat ID if (!ctx.chat?.id) { if (config.isDev) { logger_1.logger.warn("No chat ID available for session key"); } return undefined; } const sessionKey = `bot:${ctx.me.username}:session:${ctx.chat.id}`; if (config.isDev) { logger_1.logger.debug(`Generated session key: ${sessionKey}`); } // Store the session key on the context for our middleware ctx.__sessionKey = sessionKey; return sessionKey; }, })); // Add middleware to expose the storage adapter on the context protectedBot.use(async (ctx, next) => { ctx.__storageAdapter = storage; // Patch the session with a forced save method that directly uses the storage adapter if (ctx.session) { const origSession = ctx.session; const sessionKey = ctx.__sessionKey; // Create a shadow save method that ensures data is written to Redis if (sessionKey) { ctx.session.forceFlush = async () => { if (config.isDev) { logger_1.logger.debug(`Force flushing session to Redis with key ${sessionKey}`); } return storage.write(sessionKey, origSession); }; } } await next(); // Automatically save session after request is complete if (ctx.session && ctx.__sessionKey) { try { await storage.write(ctx.__sessionKey, ctx.session); if (config.isDev) { logger_1.logger.debug(`Session auto-saved after request with key ${ctx.__sessionKey}`); } } catch (err) { if (config.isDev) { logger_1.logger.error('Error auto-saving session:', err); } } } }); // Add our custom session middleware to extend the session with custom properties protectedBot.use(session_1.default); // Ensure ctx.session, ctx.chat, and ctx.from are always present for all handlers protectedBot.use(session_1.requireSessionAndChat); // Add sequentialize middleware protectedBot.use((0, grammy_middlewares_1.sequentialize)()); // Add debug middleware to log session data if (config.isDev) { protectedBot.use(async (ctx, next) => { await next(); }); } // Remove the inline auth middleware and use global auth only when config.useAuth is "fully" if (config.useAuth === "fully" && config.jwtSecret) { protectedBot.use((0, auth_1.createAuthMiddleware)(config.jwtSecret)); } // Ensure session is always initialized and sessionKey is set for every update protectedBot.use(async (ctx, next) => { if (!ctx.session) ctx.session = { jwtToken: undefined, custom: {}, setCustom: () => { }, getCustom: () => undefined, updateCustom: () => { }, deleteCustom: () => { }, }; if (!ctx.session.custom) ctx.session.custom = {}; // Ensure sessionKey is set for all updates if (!ctx.__sessionKey && ctx.chat?.id && ctx.me?.username) { ctx.__sessionKey = `bot:${ctx.me.username}:session:${ctx.chat.id}`; } await next(); // Always save session after every update if (ctx.session && ctx.__sessionKey && ctx.__storageAdapter) { await ctx.__storageAdapter.write(ctx.__sessionKey, ctx.session); } }); // Add middleware to expose the raw Redis client on the context for adminAuth protectedBot.use(async (ctx, next) => { ctx.__rawRedis = redisInstance; await next(); }); // Register admin authentication middleware and callback handler if enabled (AFTER session/redis middlewares) if (config.adminAuthentication && config.adminChatIds && config.adminChatIds.length > 0) { protectedBot.use(adminAuth_1.adminAuthCallbackHandler); // Handle approval/deny callbacks first protectedBot.use(adminAuth_1.default); // Then check user status for all updates } return bot; }; exports.createBot = createBot; //# sourceMappingURL=index.js.map