/** * api.ts, the slice of the Telegram Bot API the daemon actually calls. * * Deliberately small: getUpdates for polling ingress, the webhook trio for * webhook ingress, and sendMessage for onboarding replies. Outbound task * replies keep going through the channel delivery strategy, this client is * not a second delivery path. * * Every call returns the Telegram `result` payload or throws TelegramApiError * carrying `error_code`, because the supervisor's behaviour depends on WHICH * error it was: 409 means a webhook is registered and polling can never work * until it is removed, and 429 carries a server-dictated retry delay. Treating * those as generic failures produces a loop that backs off forever against a * condition that backing off cannot fix. */ /** A Telegram API failure with the fields the supervisor branches on. */ export declare class TelegramApiError extends Error { /** Telegram's `error_code` (409 conflict, 401 unauthorized, 429 flood, …). */ readonly errorCode: number | null; /** `parameters.retry_after` in seconds, when Telegram dictated a delay. */ readonly retryAfterSeconds: number | null; /** HTTP status, for transport-level failures with no Telegram body. */ readonly httpStatus: number | null; /** Telegram's own `description`, verbatim, two different 409s share a code. */ readonly description: string; constructor(message: string, options?: { readonly errorCode?: number | null; readonly retryAfterSeconds?: number | null; readonly httpStatus?: number | null; readonly description?: string | undefined; }); /** * True when ANOTHER PROCESS is already long-polling this same bot token. * * Telegram answers the second getUpdates with 409 and the description * "terminated by other getUpdates request", the SAME status code it uses * for a registered webhook, and a completely different situation. A webhook * conflict is ours to clear; this one is not, because the other consumer is * a real process that is genuinely receiving the user's messages. Treating * it as a webhook conflict is what produces two daemons that each keep * terminating the other's long poll while messages arrive nowhere. */ get isConcurrentConsumerConflict(): boolean; /** * True when a webhook is registered and therefore getUpdates cannot run. * Telegram reports this as 409 Conflict, but so does a concurrent consumer, * which is a different problem with a different remedy, so it is excluded * here rather than swept into the same branch. */ get isWebhookConflict(): boolean; /** True when the bot token is missing, revoked, or wrong, never retryable. */ get isUnauthorized(): boolean; } /** One Telegram Update, kept as an opaque record, the adapter parses it. */ export type TelegramUpdate = Record; export interface TelegramWebhookInfo { readonly url: string; readonly pendingUpdateCount: number; readonly lastErrorMessage: string | undefined; } /** Who the configured bot token belongs to, per Telegram's own getMe. */ export interface TelegramBotIdentity { readonly id: string; /** The @handle, WITHOUT the leading '@'. */ readonly username: string; readonly displayName: string; } /** Injectable fetch so tests drive the client without network access. */ export type TelegramFetch = (input: string, init: RequestInit) => Promise; export declare class TelegramBotApi { private readonly token; private readonly fetchImpl; private readonly baseUrl; constructor(token: string, fetchImpl?: TelegramFetch, baseUrl?: string); /** * Never interpolate the token into a log or error message. `describeToken` * exists so diagnostics can say WHICH token is configured without printing * it: bot ids are the prefix before the colon and are not secret. */ get botId(): string; private call; /** * Long-poll for updates. `timeoutSeconds` is Telegram's own hold-open * duration: the request stays open server-side until an update arrives or the * timeout expires, which is what makes polling cheap rather than a busy loop. */ getUpdates(options: { readonly offset?: number | undefined; readonly limit?: number | undefined; readonly timeoutSeconds: number; readonly signal?: AbortSignal | undefined; }): Promise; /** Point Telegram at a public delivery URL, with the shared secret header. */ setWebhook(url: string, secretToken?: string): Promise; /** * Remove any registered webhook. `dropPendingUpdates` defaults to false so a * switch from webhook to polling HANDS the queued updates to the poller * instead of discarding messages the user already sent. */ deleteWebhook(dropPendingUpdates?: boolean): Promise; /** * Ask Telegram who this token belongs to. * * The bot's username is not a fact only the operator knows, the token * identifies the bot and Telegram hands back the handle for free. Making the * user type it, and degrading silently when they do not, is the product * declining to answer a question it can answer itself. An empty handle breaks * @mention matching in groups, mis-strips `/goodvibes@thebot`, answers * `/start@someotherbot` as if addressed to us, and collapses every bot's route * bindings onto the literal surfaceId 'telegram'. */ getMe(): Promise; getWebhookInfo(): Promise; sendMessage(input: { readonly chatId: string; readonly text: string; readonly threadId?: string | undefined; }): Promise; } //# sourceMappingURL=api.d.ts.map