Files
miti99bot/src/modules/misc/index.js
tiennm99 c4314f21df 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/
2026-04-11 09:49:06 +07:00

55 lines
1.4 KiB
JavaScript

/**
* @file misc module stub — proves the plugin system end-to-end AND
* demonstrates DB usage via getJSON/putJSON in /ping.
*/
/** @type {import("../../db/kv-store-interface.js").KVStore | null} */
let db = null;
/** @type {import("../registry.js").BotModule} */
const miscModule = {
name: "misc",
init: async ({ db: store }) => {
db = store;
},
commands: [
{
name: "ping",
visibility: "public",
description: "Health check — replies pong and records last ping",
handler: async (ctx) => {
// Best-effort write — if KV is unavailable, still reply.
try {
await db?.putJSON("last_ping", { at: Date.now() });
} catch (err) {
console.warn("misc /ping: putJSON failed", String(err));
}
await ctx.reply("pong");
},
},
{
name: "mstats",
visibility: "protected",
description: "Show the timestamp of the last /ping",
handler: async (ctx) => {
const last = await db?.getJSON("last_ping");
if (last?.at) {
await ctx.reply(`last ping: ${new Date(last.at).toISOString()}`);
} else {
await ctx.reply("last ping: never");
}
},
},
{
name: "fortytwo",
visibility: "private",
description: "Easter egg — the answer",
handler: async (ctx) => {
await ctx.reply("The answer.");
},
},
],
};
export default miscModule;