@wasserstoff/mangi-tg-bot
Version:
A powerful Telegram Bot SDK with built-in authentication, session management, and database integration
182 lines • 7.68 kB
JavaScript
;
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