mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-04-17 13:21:31 +00:00
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/
2.8 KiB
2.8 KiB
Researcher Report: grammY on Cloudflare Workers
Date: 2026-04-11 Scope: grammY entry point, webhook adapter, secret-token verification, setMyCommands usage.
Key findings
Adapter
- Use
"cloudflare-mod"adapter for ES module (fetch handler) Workers. Source: grammYsrc/convenience/frameworks.ts. - The legacy
"cloudflare"adapter targets service-worker style Workers. Do NOT use — CF has moved on to module workers. - Import path (npm, not Deno):
import { Bot, webhookCallback } from "grammy";
Minimal fetch handler
import { Bot, webhookCallback } from "grammy";
export default {
async fetch(request, env, ctx) {
const bot = new Bot(env.TELEGRAM_BOT_TOKEN);
// ... register handlers
const handle = webhookCallback(bot, "cloudflare-mod", {
secretToken: env.TELEGRAM_WEBHOOK_SECRET,
});
return handle(request);
},
};
Secret-token verification
webhookCallbackacceptssecretTokenin itsWebhookOptions. When set, grammY validates the incomingX-Telegram-Bot-Api-Secret-Tokenheader and rejects mismatches with 401.- No need to manually read the header — delegate to grammY.
- The same secret must be passed to Telegram when calling
setWebhook(secret_tokenfield).
Bot instantiation cost
new Bot()per request is acceptable for Workers (no persistent state between requests anyway). Global-scope instantiation also works and caches across warm invocations. Prefer global-scope for reuse but be aware env bindings are not available at module load — must instantiate lazily insidefetch. Recommended pattern: memoizeBotin a module-scope variable initialized on first request.
setMyCommands
- Call via
bot.api.setMyCommands([{ command, description }, ...]). - Should be called on demand, not on every webhook request (rate-limit risk, latency). Two options:
- Dedicated admin HTTP route (e.g.
POST /admin/setup) guarded by a second secret. Runs on demand. - One-shot
wranglerscript. Adds tooling complexity.
- Dedicated admin HTTP route (e.g.
- Recommendation: admin route. Keeps deploy flow in one place (
wrangler deploy+curl). No extra script.
Init flow
bot.init()is NOT required if you only usewebhookCallback; grammY handles lazy init.- For
/admin/setupthat directly callsbot.api.*, callawait bot.init()once to populatebot.botInfo.
Resolved technical answers
| Question | Answer |
|---|---|
| Adapter string | "cloudflare-mod" |
| Import | import { Bot, webhookCallback } from "grammy" |
| Secret verify | pass secretToken in webhookCallback options |
| setMyCommands trigger | admin HTTP route guarded by separate secret |
Unresolved questions
- None blocking. grammY version pin: recommend
^1.30.0or latest stable at implementation time; phase-01 shouldnpm view grammy versionto confirm.