/** * `createBot` — one bot file from ideation to production. * * The composer makes the wiring foot-guns unrepresentable: it constructs * the storage + session pair ONCE and threads it into every feature (menu, * language, access, payments), so "must be the SAME instance you passed to * session()" is structural instead of a doc warning. * * The same file runs in every stage — storage and transport are environment * decisions, never code shape: * * ideation BOT_TOKEN=… tsx bot.ts memory session, long-poll * experiment BOT_PERSIST=./bot.sqlite tsx bot.ts sqlite session, long-poll * prod worker wrangler deploy (D1 binding `DB`) D1 session, webhook (bot/worker) * prod server systemd/launchd unit running poll() sqlite/redis, long-poll * * @example * const app = createBot({ * language: { supported: ["en", "es"] as const, default: "en" }, * menu: { adminContact: "@you", items: [...] }, * handlers: (bot) => bot.command("start", (ctx) => ctx.say({ en: "hi", es: "hola" })), * }) * export default app // Worker: webhook + /setup + /pause + deploy DMs * if (app.isMain(import.meta)) app.poll() // Node: `tsx bot.ts` long-polls * * Worker-safe by construction: the Node-only conveniences (sqlite/redis * adapters) load lazily on the poll path only; `bot/kit` stays out of the * graph entirely (wrap `app.poll` with `gracefulStart` yourself if you want * signal-handled start/stop DMs on a server). */ import { type Storage } from "@gramio/storage"; import { Bot } from "gramio"; import { type AccessControlOptions } from "./access-control.js"; import { type Admins } from "./admin.js"; import { type LanguageOptions } from "./language.js"; import { type BotMenuOptions, type PersonalDataOptions } from "./menu.js"; import { type BotPaymentsConfig } from "./payments/index.js"; import { type BotWorkerRuntime } from "./worker.js"; type EnvLike = Record; export type CreateBotOptions = Record> = { /** Display name for startup logs. Default: the bot's username once known. */ name?: string; /** * Bot token. Default: the `BOT_TOKEN` environment value (process env on * Node, the Worker env in workerd). Missing token throws at build — a bot * with no token must scream, not idle. */ token?: string | ((env: EnvLike) => string | undefined); /** * Session storage OVERRIDE — an instance, or `(env) => Storage` when the * choice depends on the environment (e.g. a D1 binding NOT named `DB`). * Default resolution (the lifecycle story): workerd → the `DB` D1 binding; * Node → `BOT_PERSIST` (a path = sqlite via `@gramio/storage-sqlite`, * `redis://…` = `@gramio/storage-redis`), else in-memory (ephemeral, * announced at startup). */ storage?: Storage | ((env: EnvLike) => Storage | Promise); /** Initial session record for a new user. Types `S` through to `handlers`' session accessor. */ initial?: () => S; /** Admin user ids (static or a LIVE resolver) — enables access/payments admin flows + lifecycle DMs. */ admins?: Admins; /** UI language feature (`ctx.lang`/`ctx.say`, read-time stored→hint→default). */ language?: Omit, "session">; /** * /settings menu. `personalData` is auto-wired to the composer's storage — * pass `personalData: { onForget }` to also wipe your own tables, or * `personalData: false` to drop Forget/Export entirely. When `language` is * configured its picker item is appended automatically (opt out with * `languagePicker: false`). */ menu?: Omit & { personalData?: Pick | false; languagePicker?: boolean; }; /** Gate the bot to admins + approved users (requires `admins`). */ access?: Omit; /** Telegram Stars monetization (requires `admins` — the refund approver). */ payments?: Omit, "session" | "storage">; /** * Your commands/handlers — runs LAST, after every feature is wired. * `session(ctx)` is the TYPED accessor for your `S` fields (gramio derive * types don't flow into generic handlers; this beats casting per call site). */ handlers?: (bot: Bot, api: { session: (ctx: unknown) => S; }) => void; /** Extra options forwarded to `bot/worker` in the Worker cap (routes, statusExtra, mode…). */ worker?: (env: EnvLike) => Partial; }; export type BotApp = Record> = { /** Build (memoized) — exposed for tests and custom runtimes. */ build(env?: EnvLike): Promise<{ bot: Bot; storage: Storage; flush?: () => Promise; }>; /** Typed accessor for your session fields (`S`), usable anywhere a ctx exists. */ session(ctx: unknown): S; /** Long-poll (Node/Bun/Deno). The ideation loop AND a legitimate prod mode on your own hardware. */ poll(): Promise; /** True when this module is the process entrypoint (`tsx bot.ts`). Always false on workerd. */ isMain(meta: ImportMeta): boolean; /** The Worker fetch handler — `export default app` is a complete workerd bot. */ fetch(request: Request, env: EnvLike, ctx: { waitUntil(p: Promise): void; }): Promise; }; export declare function createBot = Record>(opts?: CreateBotOptions): BotApp; export {}; //# sourceMappingURL=create.d.ts.map