From 8ad3f584699b3739ece06a02b54fba3522a0c04e Mon Sep 17 00:00:00 2001 From: neken Date: Fri, 10 Apr 2026 15:15:00 +0500 Subject: [PATCH] feat: add bot setup with owner middleware, commands, callbacks --- src/bot.ts | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/bot.ts diff --git a/src/bot.ts b/src/bot.ts new file mode 100644 index 0000000..d74cead --- /dev/null +++ b/src/bot.ts @@ -0,0 +1,74 @@ +import { Bot, type Context } from "grammy"; +import { Store } from "./state/store.js"; +import { SessionManager } from "./services/session-manager.js"; +import { ClaudeService } from "./services/claude.js"; +import { registerCommands } from "./handlers/commands.js"; +import { createMessageHandler } from "./handlers/message.js"; + +export function createBot(token: string, ownerId: number): Bot { + const bot = new Bot(token); + + // Owner-only middleware + bot.use(async (ctx, next) => { + if (ctx.from?.id !== ownerId) return; + await next(); + }); + + const store = new Store(); + const manager = new SessionManager(store); + const claude = new ClaudeService(); + + // Register command handlers + const commands: Record Promise> = {}; + registerCommands(manager, claude, commands); + + for (const [name, handler] of Object.entries(commands)) { + bot.command(name, handler); + } + + // Callback queries for inline buttons + bot.on("callback_query:data", async (ctx) => { + const data = ctx.callbackQuery.data; + + if (data.startsWith("switch:")) { + const name = data.slice("switch:".length); + if (manager.switchSession(name)) { + await ctx.answerCallbackQuery({ text: `Switched to "${name}"` }); + await ctx.reply(`Switched to "${name}".`); + } else { + await ctx.answerCallbackQuery({ text: `Session "${name}" not found` }); + } + } else if (data.startsWith("start:")) { + const name = data.slice("start:".length); + const resolved = manager.resolveProjectPath(name); + if (!resolved) { + await ctx.answerCallbackQuery({ text: "Project not found" }); + return; + } + if (manager.getAllSessions()[resolved.name]) { + manager.switchSession(resolved.name); + await ctx.answerCallbackQuery({ text: `Switched to "${resolved.name}"` }); + await ctx.reply(`Session "${resolved.name}" already exists. Switched to it.`); + return; + } + await ctx.answerCallbackQuery({ text: `Starting "${resolved.name}"...` }); + await ctx.reply(`Starting session "${resolved.name}" in ${resolved.path}...`); + + try { + const { sessionId, result } = await claude.createSession( + "You are now connected via Telegram. Briefly confirm you're ready and state the project directory.", + resolved.path, + ); + manager.createSession(resolved.name, resolved.path, sessionId); + await ctx.reply(`Session "${resolved.name}" created.\n\n${result}`); + } catch (err) { + await ctx.reply(`Failed to create session: ${err}`); + } + } + }); + + // Plain text messages + bot.on("message:text", createMessageHandler(manager, claude)); + + return bot; +}