import { Bot, Context, session, SessionFlavor } from "grammy";
import { run } from "@grammyjs/runner";
// ============================================
// Types
// ============================================
interface SessionData {
step: "idle" | "waiting_for_name";
counter: number;
}
type MyContext = Context & SessionFlavor<SessionData>;
// ============================================
// Setup
// ============================================
const token = process.env.TELEGRAM_BOT_TOKEN;
if (!token) throw new Error("TELEGRAM_BOT_TOKEN is missing");
const bot = new Bot<MyContext>(token);
// ============================================
// Middleware
// ============================================
// Session middleware
bot.use(
session({
initial: (): SessionData => ({ step: "idle", counter: 0 }),
}),
);
// Logging middleware
bot.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`Response time: ${ms}ms`);
});
// ============================================
// Commands
// ============================================
bot.command("start", async (ctx) => {
await ctx.reply("Welcome! Use /help to see available commands.");
});
bot.command("help", async (ctx) => {
await ctx.reply(
"Available commands:\n" +
"/start - Start the bot\n" +
"/count - Increment counter\n" +
"/reset - Reset counter\n" +
"/ask - Interactive conversation",
);
});
bot.command("count", async (ctx) => {
ctx.session.counter++;
await ctx.reply(`Counter: ${ctx.session.counter}`);
});
bot.command("reset", async (ctx) => {
ctx.session.counter = 0;
await ctx.reply("Counter reset to 0");
});
bot.command("ask", async (ctx) => {
ctx.session.step = "waiting_for_name";
await ctx.reply("What is your name?");
});
// ============================================
// Message Handler (Conversation logic)
// ============================================
bot.on("message:text", async (ctx) => {
const text = ctx.message.text;
if (ctx.session.step === "waiting_for_name") {
await ctx.reply(`Nice to meet you, ${text}!`);
ctx.session.step = "idle";
return;
}
// Default response
// await ctx.reply('I did not understand that. Try /help');
});
// ============================================
// Error Handling
// ============================================
bot.catch((err) => {
console.error(`Error while handling update ${err.ctx.update.update_id}:`);
console.error(err.error);
});
// ============================================
// Start
// ============================================
// For Long Polling (Development)
if (process.env.NODE_ENV !== "production") {
console.log("🤖 Bot started (Long Polling)");
run(bot);
}
// Export for Webhook (Production)
export const webhookCallback = (req: any, res: any) => {
// Adapter logic for your specific server (Express, Hono, etc.)
// return webhookCallback(bot, 'express')(req, res);
};
export { bot };