mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-05-01 22:21:18 +00:00
a2f67a7758
Code fixes: - trading/handlers + stats-handler: guard ctx.from?.id to prevent cross-user state corruption when channel posts or inline queries lack a sender - trading/prices + trading/symbols: encodeURIComponent on ticker before interpolating into TCBS API URLs - trading/stats-handler: parallelize per-stock price fetches with Promise.allSettled so N-stock portfolios don't stack serial latency - loldle/handlers: guard target champion lookup against champions.json refresh drift — start a fresh round or fall back to the stored id - wordle + loldle: explicitly initialize giveup:false in startFreshGame for stable state shape - wordle/lookup: fix stale JSDoc that claimed null return - biome: ignore auto-generated champions.json / champions-data.js / words-data.js - Apply formatter to src/index.js, loldle/handlers.js imports, and loldle/compare.test.js (previously red) Docs refresh: - README: 105+ tests -> 200+; wordle/loldle described as real modules - architecture: module tree updated, test count 105 -> 200, runtime ~500ms -> ~2s, stub list narrowed to misc only - codebase-summary: module table rewritten (wordle/loldle now Complete with real command lists and KV schema); test coverage table updated - loldle/README: full rewrite matching the current implementation (was describing the original stub) - New docs/development-roadmap.md tracking upcoming features (daily-mode for wordle + loldle, crypto/gold/forex trading, shared picker util, handler-level tests, coverage reporting, staging env) Tests: 200/200 passing. Lint: clean.
104 lines
2.8 KiB
JavaScript
104 lines
2.8 KiB
JavaScript
/**
|
|
* @file fetch entry point for the Cloudflare Worker.
|
|
*
|
|
* Routes:
|
|
* GET / — "miti99bot ok" health check (unauthenticated).
|
|
* POST /webhook — Telegram webhook. grammY validates the
|
|
* X-Telegram-Bot-Api-Secret-Token header against
|
|
* env.TELEGRAM_WEBHOOK_SECRET and replies 401 on mismatch.
|
|
* * — 404.
|
|
*
|
|
* There is NO admin HTTP surface. `setWebhook` + `setMyCommands` run at
|
|
* deploy time from `scripts/register.js`, not from the Worker.
|
|
*/
|
|
|
|
import { webhookCallback } from "grammy";
|
|
import { getBot, getRegistry } from "./bot.js";
|
|
import { dispatchScheduled } from "./modules/cron-dispatcher.js";
|
|
|
|
/** @type {ReturnType<typeof webhookCallback> | null} */
|
|
let cachedWebhookHandler = null;
|
|
|
|
/**
|
|
* @param {any} env
|
|
*/
|
|
async function getWebhookHandler(env) {
|
|
if (cachedWebhookHandler) return cachedWebhookHandler;
|
|
const bot = await getBot(env);
|
|
cachedWebhookHandler = webhookCallback(bot, "cloudflare-mod", {
|
|
secretToken: env.TELEGRAM_WEBHOOK_SECRET,
|
|
});
|
|
return cachedWebhookHandler;
|
|
}
|
|
|
|
export default {
|
|
/**
|
|
* Cloudflare Cron Trigger handler.
|
|
* Dispatches the scheduled event to all module cron handlers whose
|
|
* schedule matches event.cron.
|
|
*
|
|
* @param {any} event — ScheduledEvent ({ cron: string, scheduledTime: number })
|
|
* @param {any} env
|
|
* @param {{ waitUntil: (p: Promise<any>) => void }} ctx
|
|
*/
|
|
async scheduled(event, env, ctx) {
|
|
try {
|
|
const registry = await getRegistry(env);
|
|
dispatchScheduled(event, env, ctx, registry);
|
|
} catch (err) {
|
|
console.error("scheduled handler failed", err);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param {Request} request
|
|
* @param {any} env
|
|
* @param {any} _ctx
|
|
*/
|
|
async fetch(request, env, _ctx) {
|
|
const start = Date.now();
|
|
const { pathname } = new URL(request.url);
|
|
const method = request.method;
|
|
|
|
const response = await route(request, env, pathname);
|
|
|
|
// Structured request log for Workers Observability dashboard.
|
|
console.log(
|
|
JSON.stringify({
|
|
msg: "req",
|
|
method,
|
|
path: pathname,
|
|
status: response.status,
|
|
ms: Date.now() - start,
|
|
}),
|
|
);
|
|
return response;
|
|
},
|
|
};
|
|
|
|
/**
|
|
* @param {Request} request
|
|
* @param {any} env
|
|
* @param {string} pathname
|
|
*/
|
|
async function route(request, env, pathname) {
|
|
if (request.method === "GET" && pathname === "/") {
|
|
return new Response("miti99bot ok", {
|
|
status: 200,
|
|
headers: { "content-type": "text/plain" },
|
|
});
|
|
}
|
|
|
|
if (request.method === "POST" && pathname === "/webhook") {
|
|
try {
|
|
const handler = await getWebhookHandler(env);
|
|
return await handler(request);
|
|
} catch (err) {
|
|
console.error("webhook handler failed", err);
|
|
return new Response("internal error", { status: 500 });
|
|
}
|
|
}
|
|
|
|
return new Response("not found", { status: 404 });
|
|
}
|