import { type AlertThrottle } from "./notify.js"; /** The slice of a gramio `Bot` the worker cap drives — structural, so versions don't pin. */ export type WorkerBot = { info?: { username?: string; }; updates: { handleUpdate(update: unknown): Promise; }; api: { setWebhook(params: { url: string; secret_token: string; allowed_updates: string[]; }): Promise; deleteWebhook(): Promise; getWebhookInfo(): Promise<{ url?: string; [k: string]: unknown; }>; /** For the lifecycle DMs (notifyAdmins rides the same bot). */ sendMessage(params: { chat_id: number; text: string; }): Promise; }; }; type WaitUntilCtx = { waitUntil(promise: Promise): void; }; /** * True when the update is the bot's own voice: a message it authored, or a forward of one of * its messages (forward_origin type "user" pointing back at it). Replies TO the bot are NOT * echoes — they're the conversation. */ export declare function isSelfEcho(update: unknown, selfId: number): boolean; /** The slice of a Durable Object namespace binding the durable ingress touches. */ export type UpdateRunnerNamespace = { idFromName(name: string): unknown; get(id: unknown): { fetch(input: string, init?: RequestInit): Promise; }; }; export type DurableUpdates = { /** The namespace bound to the class built by {@link telegramUpdateRunner}. */ namespace: UpdateRunnerNamespace; /** * Route SELECTED updates outside their conversation queue — for a time-critical * callback that must not wait behind slow work already running for its chat. * Return a partition name, or null/undefined for the default * `telegramUpdatePartition` (one Durable Object per conversation). */ partition?: (update: unknown) => string | null | undefined; }; export type BotWorkerRuntime = { bot: WorkerBot; /** Telegram webhook secret; unset → webhook/setup respond 500 (a misconfigured prod must scream). */ webhookSecret?: string; /** * The bot's own user id (the digits before the token's colon). When set, updates that are * the bot's OWN VOICE echoed back — a message it authored (channels/business echo their own * posts) or a FORWARD of one of its messages (a forwarded summary still carries live links) — * are acked and dropped before dispatch: a bot must never listen to itself, or it loops. */ selfId?: number; /** Bearer for /pause, /resume, /webhook-status; unset → those 404 (surface disabled). */ operatorSecret?: string; /** Deployment label echoed in responses/DMs (e.g. the wrangler env name). */ mode?: string; /** Live admin resolver for the lifecycle DMs + error alerts. Default: nobody (DMs off). */ adminIds?: () => Promise; /** Awaited inside waitUntil after each update — hand `d1Storage`'s flush here. */ flush?: () => Promise; /** Extra fields merged into /webhook-status (e.g. a live feature flag). */ statusExtra?: () => Promise>; /** Consumer endpoints, tried BEFORE the built-ins. Return null to fall through. */ routes?: (request: Request, url: URL, ctx: WaitUntilCtx) => Promise | Response | null; /** Error-alert throttle (share one to share its budget). Default: own 60s window. */ errorThrottle?: AlertThrottle; /** Durable webhook ingress: persist-before-ack into per-conversation runner DOs. */ durableUpdates?: DurableUpdates; }; /** * Build the Worker `fetch` handler. `resolve` runs per request — memoize your * bot construction inside it (the classic `let botPromise` pattern) so the * isolate builds once. */ export declare function botWorkerFetch(resolve: (env: Env) => Promise | BotWorkerRuntime): (request: Request, env: Env, ctx: WaitUntilCtx) => Promise; /** The slice of a Durable Object storage transaction the runner touches. */ type RunnerTxn = { get(key: string): Promise; put(key: string, value: unknown): Promise; delete(key: string | string[]): Promise; }; /** The slice of DurableObjectState the runner touches — satisfied by the real binding. */ export type UpdateRunnerState = { storage: RunnerTxn & { getAlarm(): Promise; setAlarm(time: number): Promise; transaction(fn: (txn: RunnerTxn) => Promise): Promise; }; }; export interface UpdateRunnerOptions { /** Application retries per update before it is dropped as completed. Default 3. */ maxAttempts?: number; /** Backoff (ms) before retry N+1; one entry per non-final attempt. Default [5s, 30s]. */ retryMs?: readonly number[]; /** Completed update ids retained for webhook de-duplication. Default 512. */ dedupeLimit?: number; /** Wall-clock bound per attempt. A HANG is worse than a failure: an attempt that never * settles re-runs the same alarm forever, wedging the conversation with ZERO logs (tails * only print completed invocations). The bound turns a hang into a screaming, retried, * eventually-dropped failure. Generous by default (slow transcript/LLM work is the point * of the alarm design). Default 180s. */ attemptTimeoutMs?: number; } /** The Durable Object surface the factory-built runner class exposes. */ export type TelegramUpdateRunnerClass = new (state: UpdateRunnerState, env: Env) => { fetch(request: Request): Promise; alarm(): Promise; }; /** * Build the durable, per-conversation Telegram update runner class — bind it in * wrangler and hand its namespace to the runtime's `durableUpdates`. The webhook * ingress only persists a job and sets an alarm; each alarm handles ONE update * and checkpoints before scheduling the next. Unlike an HTTP waitUntil tail, an * alarm handler owns its full invocation, so slow transcript/LLM work is not * cancelled ~30 seconds after Telegram receives its acknowledgement. * * `resolve` is the same (memoized) runtime loader given to `botWorkerFetch`: * * const loadRuntime = async (env: Env) => ({ bot: await getBot(env), ... }) * export class TelegramUpdateRunner extends telegramUpdateRunner(loadRuntime) {} * export default { fetch: botWorkerFetch(loadRuntime) } */ export declare function telegramUpdateRunner(resolve: (env: Env) => Promise | BotWorkerRuntime, options?: UpdateRunnerOptions): TelegramUpdateRunnerClass; export {}; //# sourceMappingURL=worker.d.ts.map