import { type Bot } from "../bot.ts";import { type Context } from "../context.ts";import { type WebhookReplyEnvelope } from "../core/client.ts";import { debug as d } from "../platform.deno.ts";import { type Update } from "../types.ts";import { adapters as nativeAdapters, defaultAdapter,} from "./frameworks.deno.ts";const debugErr = d("grammy:error");
const callbackAdapter: FrameworkAdapter = ( update: Update, callback: (json: string) => unknown, header: string, unauthorized = () => callback('"unauthorized"'),) => ({ update: Promise.resolve(update), respond: callback, header, unauthorized,});const adapters = { ...nativeAdapters, callback: callbackAdapter };
type SupportedFrameworks = keyof typeof adapters;
interface ReqResHandler { update: Promise<Update>; header?: string; end?: () => void; respond: (json: string) => unknown | Promise<unknown>; unauthorized: () => unknown | Promise<unknown>; handlerReturn?: any;}export type FrameworkAdapter = (...args: any[]) => ReqResHandler;
export interface WebhookOptions { onTimeout?: "throw" | "return" | ((...args: any[]) => unknown); timeoutMilliseconds?: number; secretToken?: string;}
export function webhookCallback<C extends Context = Context>( bot: Bot<C>, adapter?: SupportedFrameworks | FrameworkAdapter, onTimeout?: WebhookOptions["onTimeout"], timeoutMilliseconds?: WebhookOptions["timeoutMilliseconds"], secretToken?: WebhookOptions["secretToken"],): (...args: any[]) => any;export function webhookCallback<C extends Context = Context>( bot: Bot<C>, adapter?: SupportedFrameworks | FrameworkAdapter, webhookOptions?: WebhookOptions,): (...args: any[]) => any;export function webhookCallback<C extends Context = Context>( bot: Bot<C>, adapter: SupportedFrameworks | FrameworkAdapter = defaultAdapter, onTimeout: | WebhookOptions | WebhookOptions["onTimeout"] = "throw", timeoutMilliseconds: WebhookOptions["timeoutMilliseconds"] = 10_000, secretToken?: WebhookOptions["secretToken"],) { const { onTimeout: timeout = "throw", timeoutMilliseconds: ms = 10_000, secretToken: token, } = typeof onTimeout === "object" ? onTimeout : { onTimeout, timeoutMilliseconds, secretToken }; let initialized = false; const server: FrameworkAdapter = typeof adapter === "string" ? adapters[adapter] : adapter; return async (...args: any[]) => { if (!initialized) { await bot.init(); initialized = true; } const { update, respond, unauthorized, end, handlerReturn, header } = server(...args); if (header !== token) { await unauthorized(); console.log(handlerReturn); return handlerReturn; } let usedWebhookReply = false; const webhookReplyEnvelope: WebhookReplyEnvelope = { async send(json) { usedWebhookReply = true; await respond(json); }, }; await timeoutIfNecessary( bot.handleUpdate(await update, webhookReplyEnvelope), typeof timeout === "function" ? () => timeout(...args) : timeout, ms, ); if (!usedWebhookReply) end?.(); return handlerReturn; };}
function timeoutIfNecessary( task: Promise<void>, onTimeout: "throw" | "return" | (() => unknown), timeout: number,): Promise<void> { if (timeout === Infinity) return task; return new Promise((resolve, reject) => { const handle = setTimeout(() => { if (onTimeout === "throw") { reject(new Error(`Request timed out after ${timeout} ms`)); } else { if (typeof onTimeout === "function") onTimeout(); resolve(); } const now = Date.now(); task.finally(() => { const diff = Date.now() - now; debugErr(`Request completed ${diff} ms after timeout!`); }); }, timeout); task.then(resolve) .catch(reject) .finally(() => clearTimeout(handle)); });}