anthropics / anthropics/claude-plugins-official

feat(telegram): native streaming via sendMessageDraft (Bot API 9.3+)

オープン
#3,071 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
Python
スター
36.3k
フォーク
4.1k
平均マージ
2日 14時間
マージ済み PR(30日)
539

説明

## Summary

The Telegram plugin currently delivers model output via `bot.api.sendMessage` (and, for long-running tasks, `editMessageText` to edit a placeholder in place). That works, but it's the pre-2026 pattern: every interim update is a real `editMessageText` round-trip against Bot API global rate limits (1 msg/sec per chat, 30 msg/sec total), and the UX is choppy compared to ChatGPT/Claude.ai web, where text streams character-by-character as the model generates.

Telegram's Bot API added native streaming over the last six months specifically for AI-bot use cases. The plugin should adopt it so Claude-in-Telegram feels as live as Claude-in-the-web-app, without paying for it in 429s.

## Telegram Bot API timeline (relevant changelog entries)

| Date | Version | Change |
|---|---|---|
| 2025-12-31 | 9.3 | Introduced `sendMessageDraft` — partial-message streaming during generation. Initially gated to bots in the Telegram Business beta. ([changelog](https://core.telegram.org/bots/api#december-31-2025)) |
| 2026-03-01 | 9.5 | `sendMessageDraft` opened to **all** bots, no allow-list. ([changelog](https://core.telegram.org/bots/api#march-1-2026)) |
| 2026-05-08 | 10.0 | `sendMessageDraft` accepts an empty `text` — can be sent as a placeholder before any tokens are generated. ([changelog](https://core.telegram.org/bots/api#may-8-2026)) |
| 2026-06-11 | 10.1 | `sendRichMessageDraft` — streaming variant for rich messages (formatted text, entities, inline keyboards). ([changelog](https://core.telegram.org/bots/api#june-11-2026)) |

Mechanism: the draft is an ephemeral message (TTL ~30s) that the client renders incrementally as the bot calls `sendMessageDraft` with the latest cumulative text. Draft updates do **not** consume the per-chat sendMessage budget. When generation completes, the bot issues a final `sendMessage` (or `sendRichMessage`) for persistence, and the draft is replaced atomically.

## Current implementation

- `server.ts:435` — the `reply` tool schema. No streaming option today; the contract is single-shot text + optional files.
- `server.ts:510-576` — the `reply` handler. Splits text into chunks and calls `await bot.api.sendMessage(...)` per chunk (line 541). Each chunk is a fully-persisted message; long Claude outputs land as 3-5 separate messages in the chat.
- `server.ts:761`, `server.ts:777` — `ctx.editMessageText` for the permission flow. This is where the "edit a placeholder repeatedly" pattern is used today; in practice users (and other plugins built on top of this) extend it for progress updates and hit rate-limit ceilings under load.
- `package.json` — depends on `grammy ^1.21.0`. grammY's auto-generated API surface already exposes `bot.api.sendMessageDraft` and `bot.api.sendRichMessageDraft` (see https://grammy.dev/ref/core/api), so no new transport work is needed — only wiring.

## Proposal

Add streaming as an opt-in to the `reply` tool. Two equivalent shapes:

**Option A — extend `reply` with a `stream` flag:**

```jsonc
{
"name": "reply",
"inputSchema": {
"properties": {
"chat_id": { "type": "string" },
"text": { "type": "string" },
"stream": {
"type": "boolean",
"description": "When true, the message is delivered as a sendMessageDraft stream that updates as text grows, then committed with a final sendMessage. Provides ChatGPT-style live rendering and does not consume the per-chat rate-limit budget."
},
// ... existing fields
}
}
}
```

**Option B — separate `reply_stream` tool.** Clearer contract (caller passes incremental `text` deltas + a final `commit: true`), at the cost of one extra tool entry.

Either way, the server-side flow is:

1. First call → `bot.api.sendMessageDraft(chat_id, text)` returns a `draft_id`.
2. Subsequent calls during the same generation → `bot.api.sendMessageDraft(chat_id, text, { draft_id })` with cumulative text. No rate-limit budget consumed.
3. Final call → `bot.api.sendMessage(chat_id, text, { ...reply_parameters, parse_mode })` to persist. Draft is auto-replaced by the client.

If the draft expires mid-generation (~30s TTL with no update), fall back to the current chunked `sendMessage` path so the message is never lost.

## Compatibility

Fully backward compatible. Callers that don't pass `stream: true` (or don't use the new tool) hit the existing `sendMessage` path unchanged. The `format`, `reply_to`, `files`, and chunking semantics in `server.ts:510-576` stay as-is. grammY version bump may be needed only if a future grammY release renames the method — current `^1.21.0` resolves to a version that already exposes both draft methods.

## Why this matters

The Telegram plugin is the primary "Claude on my phone" path for a lot of users. Right now a 30-second Claude response feels like staring at a "Typing..." indicator and then receiving a wall of text. With `sendMessageDraft` it feels like the same Claude session you have in the browser — tokens flow in, and the message commits when generation ends. That's the difference between "useful when I'm at a desk" and "useful as a daily driver from a phone."

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

Start in server.ts:435 and server.ts:510-576, then inspect package.json and grammY's sendMessageDraft API. Determine which proposed reply contract fits the existing tool lifecycle while preserving the non-streaming path. Done means opted-in streaming updates cumulative text, commits the final output, and retains format, reply_to, files, chunking, and expiry fallback behavior.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
typescript
領域
api, backend
issue の種類
機能追加
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
48/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。