import type { TelegramTransport, EditTransport, SendOptions } from "./outbound.js"; import type { InboxCapture } from "./handlers/capture.js"; import type { PrioritizeFn } from "./handlers/prioritize.js"; import type { PendingCallbackState } from "./handlers/approvals.js"; import type { PendingUploadState } from "./handlers/upload.js"; import type { InlineKeyboardSpec } from "./keyboard.js"; import type { SynthesisReader } from "./fleet-view.js"; /** * Minimal Telegram Update shape consumed by the poll loop. * Defined locally so that grammy's generated types never leak outside this file * (provider-agnostic principle, .bober/principles.md:28). * The concrete adapter casts grammy's Update[] to TelegramUpdate[] (compatible subset). */ export interface TelegramUpdate { update_id: number; message?: { message_id: number; from?: { id: number; }; chat: { id: number; }; text?: string; /** Present when the update is a document upload (mirrors @grammyjs/types Document subset). */ document?: { file_id: string; file_name?: string; mime_type?: string; }; }; /** Inline-keyboard button tap — present when a user clicks an inline button. */ callback_query?: { id: string; from: { id: number; }; message?: { chat: { id: number; }; }; data?: string; }; } /** * Extended transport used by the poll loop: outbound TelegramTransport + EditTransport * plus a getUpdates polling method, inline-keyboard sender, and callback acknowledgement. * Tests inject a fake BotTransport so the loop is testable without any SDK dependency. * Extensions stay here (not in outbound.ts) per outbound.ts:8-9. * Sprint 6: extends EditTransport so GrammyTransport satisfies both text and edit funnels. */ export interface BotTransport extends TelegramTransport, EditTransport { getUpdates(offset: number): Promise; /** Send a message with an inline keyboard. spec is the provider-neutral shape from keyboard.ts. */ sendKeyboard(chatId: number, text: string, keyboard: InlineKeyboardSpec): Promise; /** Acknowledge a callback query — dismisses the loading spinner on the client. */ answerCallback(callbackQueryId: string, text?: string): Promise; /** * Download a Telegram file to a local path. * Uses grammy's getFile API then fetches via the Telegram file endpoint. * Implemented only in GrammyTransport — grammy types stay in this file. */ downloadDocument(fileId: string, destPath: string): Promise; } /** * Concrete BotTransport backed by grammy's Bot.api. * This class is the sole SDK consumer — the loop, outbound funnel, and CLI * handler all depend on BotTransport / TelegramTransport, not on grammy directly. */ export declare class GrammyTransport implements BotTransport { private readonly bot; constructor(token: string); /** * Send a plain-text message. Maps the provider-neutral `silent` option to * grammy's `disable_notification` so callers (via sendSafe) stay SDK-agnostic. */ sendMessage(chatId: number, text: string, opts?: SendOptions): Promise; /** * Send a message and return its Telegram message_id. * Used exclusively by the streaming funnel (sendSafeForEdit) to capture the id * of the initial status message for subsequent in-place edits. * grammy's sendMessage returns Message.TextMessage which carries message_id (api.d.ts:156). */ sendReturningId(chatId: number, text: string, opts?: SendOptions): Promise; /** * Edit an existing message in place. Used exclusively by the streaming funnel * (sendSafeEdit) — never posts a new message, only updates the one sent by sendReturningId. * grammy's editMessageText signature: editMessageText(chat_id, message_id, text, other?, signal?). */ editMessage(chatId: number, messageId: number, text: string): Promise; /** * Send a message with an inline keyboard. Converts the provider-neutral * InlineKeyboardSpec to a grammy InlineKeyboard inside this class only. */ sendKeyboard(chatId: number, text: string, keyboard: InlineKeyboardSpec): Promise; /** * Acknowledge a callback query. Must be called for every tap to dismiss * the loading spinner on the Telegram client, even on denied/ghost taps. */ answerCallback(callbackQueryId: string, text?: string): Promise; /** * Fetches the next batch of updates using getUpdates long-polling (timeout=30s). * offset ensures already-acknowledged updates are not returned again. * callback_query updates are included by default (no allowed_updates filter needed). */ getUpdates(offset: number): Promise; /** * Download a Telegram document to a local path. * Uses getFile (grammy api) to resolve file_path, then fetches from the Telegram * file endpoint and writes bytes via node:fs/promises (no @grammyjs/files plugin needed). * Stays in this class so grammy types never leak outside bot.ts (principles.md:28). */ downloadDocument(fileId: string, destPath: string): Promise; } /** * Returns the /start help reply sent to whitelisted senders. * Handlers return a content string; the loop passes it through sendSafe. */ export declare function helpReply(): string; /** * Runs a getUpdates long-polling loop until the AbortSignal fires (e.g. SIGINT). * TELEGRAM_ALLOWED_USERS is read from process.env at loop start. * * Invariant: all outbound text goes through sendSafe — the loop never calls * transport.sendMessage directly (nonGoal #5, evaluatorNotes). * Keyboard messages go through transport.sendKeyboard (also on BotTransport, * still not bypassing the transport layer). * * New params are optional/defaulted so existing callers (telegram.ts:50) that * pass only (transport, signal) continue to compile unchanged. * * bober: single-process synchronous poll; extend to concurrent processing or * grammY's bot.start() if throughput becomes a bottleneck (later sprint). */ export declare function startPollLoop(transport: BotTransport, signal: AbortSignal, capture?: InboxCapture, prioritize?: PrioritizeFn, pending?: PendingCallbackState, uploads?: PendingUploadState, fleetReader?: SynthesisReader): Promise; //# sourceMappingURL=bot.d.ts.map