mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-04-17 15:20:58 +00:00
feat: scaffold plug-n-play telegram bot on cloudflare workers
grammY-based bot with a module plugin system loaded from the MODULES env var. Three command visibility levels (public/protected/private) share a unified command namespace with conflict detection at registry build. - 4 initial modules (util, wordle, loldle, misc); util fully implemented, others are stubs proving the plugin system end-to-end - util: /info (chat/thread/sender ids) + /help (pure renderer over the registry, HTML parse mode, escapes user-influenced strings) - KVStore interface with CFKVStore and a per-module prefixing factory; getJSON/putJSON convenience helpers; other backends drop in via one file - Webhook at POST /webhook with secret-token validation via grammY's webhookCallback; no admin HTTP surface - Post-deploy register script (npm run deploy = wrangler deploy && node --env-file=.env.deploy scripts/register.js) for setWebhook and setMyCommands; --dry-run flag for preview - 56 vitest unit tests across 7 suites covering registry, db wrapper, dispatcher, help renderer, validators, and HTML escaper - biome for lint + format; phased implementation plan under plans/
This commit is contained in:
56
src/bot.js
Normal file
56
src/bot.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @file bot — lazy, memoized grammY Bot factory.
|
||||
*
|
||||
* The Bot instance is constructed on the first request (env is not available
|
||||
* at module-load time in CF Workers) and reused on subsequent warm requests.
|
||||
* Dispatcher middleware is installed exactly once, as part of that same
|
||||
* lazy init, so the registry is built before any update is routed.
|
||||
*/
|
||||
|
||||
import { Bot } from "grammy";
|
||||
import { installDispatcher } from "./modules/dispatcher.js";
|
||||
|
||||
/** @type {Bot | null} */
|
||||
let botInstance = null;
|
||||
/** @type {Promise<Bot> | null} */
|
||||
let botInitPromise = null;
|
||||
|
||||
/**
|
||||
* Fail fast if any required env var is missing — better a 500 on first webhook
|
||||
* than a confusing runtime error inside grammY.
|
||||
*
|
||||
* @param {any} env
|
||||
*/
|
||||
function requireEnv(env) {
|
||||
const required = ["TELEGRAM_BOT_TOKEN", "TELEGRAM_WEBHOOK_SECRET", "MODULES"];
|
||||
const missing = required.filter((key) => !env?.[key]);
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`missing required env vars: ${missing.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} env
|
||||
* @returns {Promise<Bot>}
|
||||
*/
|
||||
export async function getBot(env) {
|
||||
if (botInstance) return botInstance;
|
||||
if (botInitPromise) return botInitPromise;
|
||||
|
||||
requireEnv(env);
|
||||
|
||||
botInitPromise = (async () => {
|
||||
const bot = new Bot(env.TELEGRAM_BOT_TOKEN);
|
||||
await installDispatcher(bot, env);
|
||||
botInstance = bot;
|
||||
return bot;
|
||||
})();
|
||||
|
||||
try {
|
||||
return await botInitPromise;
|
||||
} catch (err) {
|
||||
// Clear the failed promise so the next request retries init.
|
||||
botInitPromise = null;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user