← Back to PRs

#14367: feat(telegram): add message read via inbound message store

by michaelquinlan88 open 2026-02-12 01:14 View on GitHub →
docs channel: telegram agents size: M
## Summary - add in-memory inbound message store (`src/telegram/inbound-message-store.ts`) — per-chat ring buffer (~200 msgs, 24h TTL) that records messages as they flow through the bot handler pipeline - rewrite `readMessagesTelegram` to read from local store instead of calling `getUpdates` (which conflicts with the running long-poll loop and doesn't support per-chat history) - wire up `read`/`readMessages` action in `telegram-actions.ts` with `before`/`after` pagination by message_id - add `messages` to `TelegramActionConfig` for action gating - update `docs/cli/message.md` with Telegram read docs - add 10 unit tests for the inbound store ## Context The Telegram Bot API has no per-chat message history endpoint (unlike Discord's REST API). The previous approach using `getUpdates` was fundamentally broken: 1. **409 Conflict** — `getUpdates` from a second `Bot` instance conflicts with the running `@grammyjs/runner` long-poll loop 2. **Consumes updates** — calling `getUpdates` with an offset acknowledges updates, causing the main bot to miss messages 3. **Not chat-specific** — returns ALL unprocessed updates across all chats, not history for a specific chat The fix: record messages into a lightweight in-memory store as they arrive, then read from that store. No API calls needed. Closes #12756 ## Test ```bash pnpm vitest run src/telegram/inbound-message-store.test.ts ``` 10 tests covering: record/read, dedup, chat isolation, limit/before/after pagination, capacity eviction, field normalization, stats, edge cases. ## Design Decisions - **In-memory only, no disk persistence** — avoids storing user message content on disk without explicit opt-in. Trade-off: messages lost on restart. - **Records before access checks** — store reflects what the bot received (consistent with `sent-message-cache.ts`). Action gating controls read access separately. - **Synchronous reads** — no API call, just a filtered slice from the buffer. - **`before`/`after` by message_id** — matches Discord's pagination model. ## Limitations - Only messages received while the process is running - 24h eviction, ~200 per chat capacity - No full chat history retrieval (Telegram platform constraint for bots) ## AI Disclosure 🤖 AI-assisted (Claude Opus 4.6 via OpenClaw). Fully tested, code reviewed by human. Both contributors understand the implementation and the Telegram Bot API constraints that motivated this approach. <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR adds an in-memory inbound message store for Telegram and updates the Telegram action tool to support reading messages from that store (instead of using `getUpdates`). It also adds config gating (`actions.messages`) and docs/test coverage for the store. However, the new Telegram `read`/`readMessages` support is not wired into the channel action adapter used by `openclaw message read`. The Telegram plugin adapter does not implement the `read` action, so the CLI will still throw `Action read is not supported for provider telegram`. Additionally, the new handler expects `chatId`, while the CLI target plumbing maps `--target` to `to` for the `read` action, so even after wiring it, the parameter name mismatch would still break reads unless aligned. <h3>Confidence Score: 2/5</h3> - This PR should not be merged until the Telegram read action is correctly wired end-to-end. - Core inbound store logic looks coherent, but the advertised `openclaw message read` support for Telegram is currently broken: the Telegram channel action adapter does not implement `read`, and the new handler expects `chatId` while CLI target plumbing provides `to`. This makes the feature non-functional as shipped. - src/channels/plugins/actions/telegram.ts, src/agents/tools/telegram-actions.ts, src/infra/outbound/message-action-spec.ts, src/infra/outbound/channel-target.ts, docs/cli/message.md <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <details><summary><h4>Context used (3)</h4></summary> - Context from `dashboard` - docs/cli/agents.md ([source](https://app.greptile.com/review/custom-context?memory=057a11aa-5c5f-48bb-8d53-91b27b0fe3a2)) - Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8)) - Context from `dashboard` - AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=0d0c8278-ef8e-4d6c-ab21-f5527e322f13)) </details> <!-- /greptile_comment -->

Most Similar PRs