← Back to PRs

#19665: feat(telegram): native sendMessageDraft streaming (Bot API 9.3)

by edonadei open 2026-02-18 02:30 View on GitHub →
channel: telegram size: S
## Summary Adds native support (Bot API 9.3) for animated streaming previews in Telegram bots, as a better alternative to the existing sendMessage + editMessageText loop. Closes #15714 ## Two Streaming Modes ### 1. Native Draft Mode (new, opt-in) Uses `bot.api.sendMessageDraft(chat_id, draft_id, text)` introduced in Telegram Bot API 9.3. Each call with the same `draft_id` updates the same animated draft bubble on the client side — no message is created, no "edited" marker appears, no message ID is consumed. **Constraint:** Only available for bots with **forum topic mode enabled** (`has_topics_enabled: true`). This is a Telegram API restriction. ### 2. Legacy Edit Mode (existing default, unchanged) The original behavior: sends an initial message via `sendMessage`, then edits it repeatedly via `editMessageText`. Active when `forumTopicsEnabled` is not set or is `false`. ## Automatic Fallback If `sendMessageDraft` fails for any reason (e.g., bot doesn't actually have forum topics enabled, network error), the stream automatically falls back to the legacy edit path for that session and logs a warning. Existing behavior is fully preserved. ## Changes ### `src/config/types.telegram.ts` - Added optional `forumTopicsEnabled?: boolean` to `TelegramAccountConfig` Users opt in explicitly via config rather than requiring a `getMe()` API call on every message: ```yaml telegram: forumTopicsEnabled: true # enable native sendMessageDraft streaming ``` ### `src/telegram/draft-stream.ts` - Added `useNativeDraft?: boolean` and `draftId?: number` params to `createTelegramDraftStream` - Native path: calls `api.sendMessageDraft(chatId, draftId, text, { message_thread_id? })` - `draftId` defaults to `Date.now() % 2147483647` (stable non-zero int32) - `messageId()` returns `undefined` in native mode (no real message created) - Automatic fallback to legacy path on any API failure - Added JSDoc comment explaining the two modes ### `src/telegram/bot-message-dispatch.ts` - Reads `telegramCfg.forumTopicsEnabled` to decide whether to use native drafts - Derives a stable `draftId` from `chatId XOR message_id` (so retried updates reuse the same draft slot) - Passes `useNativeDraft` and `draftId` to `createTelegramDraftStream` ## Notes - Native drafts do NOT support inline buttons — this only applies to the streaming preview, never the final send (which continues to use `deliverReplies` as before) - No breaking changes — `forumTopicsEnabled` defaults to `false`, preserving existing behavior for all users <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds opt-in support for Telegram Bot API 9.3's `sendMessageDraft` method, enabling animated streaming previews without "edited" markers or message ID consumption. The feature is gated behind a new `forumTopicsEnabled` config flag (default `false`), preserving backward compatibility. - Adds `forumTopicsEnabled?: boolean` to `TelegramAccountConfig` for explicit opt-in - Implements native draft path in `createTelegramDraftStream` with automatic fallback to the existing `sendMessage` + `editMessageText` loop on any API failure - Derives a stable `draftId` from `chatId XOR message_id` for retry consistency - In native draft mode, `messageId()` returns `undefined` — the finalization path in `bot-message-dispatch.ts` correctly skips preview-edit optimization and falls through to `deliverReplies` - `forceNewMessage()` does not regenerate `draftId`, meaning block-mode preview splitting reuses the same draft bubble instead of creating separate previews (behavioral difference from legacy mode worth documenting) <h3>Confidence Score: 4/5</h3> - Safe to merge — opt-in feature with automatic fallback; no changes to default behavior - The PR is well-structured with a safe default (off) and graceful fallback on any API error. The `forceNewMessage()` behavioral difference in native draft mode is a minor concern for block streaming users but not a regression since the feature requires explicit opt-in. No breaking changes to existing users. - `src/telegram/draft-stream.ts` — `forceNewMessage()` reuses the same `draftId` in native mode, creating a behavioral difference from legacy block-mode streaming <sub>Last reviewed commit: f6f7854</sub> <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs