nexus/server/src/routes/telegram.ts
Nexus Dev 69269fea14 feat(38-01): install grammY, create telegramService + telegramRoutes
- Install grammy v2 for long polling Telegram bot
- telegramService: text relay handler, agent prefix, session map, deleteWebhook lifecycle
- telegramRoutes: POST /telegram/token (getMe validation), GET /telegram/status
- telegram.ts under 500 lines (187 lines)
2026-04-04 03:55:50 +00:00

70 lines
2.2 KiB
TypeScript

import { Router } from "express";
import { Bot } from "grammy";
import { assertBoard } from "./authz.js";
import { nexusSettingsService } from "../services/nexus-settings.js";
import type { TelegramService } from "../services/telegram.js";
import type { Db } from "@paperclipai/db";
export function telegramRoutes(db: Db, svc: TelegramService): Router {
const router = Router();
// POST /telegram/token — validate and save bot token, restart bot
router.post("/telegram/token", async (req, res) => {
try {
assertBoard(req);
const { token } = req.body as { token?: string };
if (!token || typeof token !== "string" || token.trim() === "") {
res.status(400).json({ error: "token is required" });
return;
}
// Validate token by calling getMe
let botUsername: string;
try {
const tempBot = new Bot(token.trim());
const me = await tempBot.api.getMe();
botUsername = me.username ?? me.first_name;
} catch {
res.status(400).json({ error: "Invalid Telegram bot token" });
return;
}
// Save token to nexus settings
await nexusSettingsService().set({ telegramToken: token.trim() });
// Stop old bot and start new one with new token
await svc.stop();
svc.start(token.trim()).catch((err) => {
// Log but don't fail the request — token is saved
void err;
});
res.json({ ok: true, botUsername });
} catch (err: unknown) {
if (err && typeof err === "object" && "status" in err) {
const e = err as { status: number; message: string };
res.status(e.status).json({ error: e.message });
return;
}
res.status(500).json({ error: "Unexpected error" });
}
});
// GET /telegram/status — return running state
router.get("/telegram/status", async (req, res) => {
try {
assertBoard(req);
res.json({ running: svc.isRunning() });
} catch (err: unknown) {
if (err && typeof err === "object" && "status" in err) {
const e = err as { status: number; message: string };
res.status(e.status).json({ error: e.message });
return;
}
res.status(500).json({ error: "Unexpected error" });
}
});
return router;
}