feat(channels): Telegram channel (custom bot) - #6679
Conversation
…tion
Adds a Telegram channel for a customer's own bot, mirroring the Slack adapter.
- Adapter: getMe verification, X-Telegram-Bot-Api-Secret-Token webhook check,
per-bot-path connection locator, update parsing (message and callback_query),
sendMessage/editMessage, deactivation on kicked/left. No history (backfill
off) and no chat listing (discover_spaces empty), which Telegram cannot do.
- Ingress: a per-bot route /telegram/events/{routing_token}/, added to the
public auth allowlist by prefix (the stored webhook secret authorises).
- Activation: a new adapter.activate_connection hook runs setWebhook after the
row is stored (setWebhook writes on Telegram's side, so it cannot run in the
pre-store verify step). The webhook secret is minted and vaulted at create.
- Secrets: a telegram ChannelSecretKind and a webhook_secret credential field.
- Tests: unit tests for the adapter and the interface method list.
The placeholder shown while a turn runs now reads "Thinking…" in every channel (Slack, Telegram, Agenta), from the one shared INDICATOR_TEXT constant.
…ended turn_ended is published twice (SessionTurnsService.complete_turn and the records worker post-commit), so on_turn_ended can run twice for one turn. On an edit channel the second call was a wasted "message is not modified" that overwrote the SENT row; on a post-only channel (Telegram) it posted a duplicate message the user saw. _send now skips a row already SENT with identical content.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📘 Docs preview
This comment updates in place on every push. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 SummarySummary by CodeRabbit
WalkthroughThe pull request adds a Telegram custom-bot channel adapter with webhook ingress, secret handling, message parsing, outbound delivery, tests, supporting design documents, and a first-pass Slack and Telegram settings interface. ChangesTelegram channel runtime
Channel design and settings UI
Priority: ➖ Normal Estimated code review effort: 5 (Critical) | ~120 minutes Merge Risk: 🟡 Moderate · up to Several reachable Telegram failure and update paths can stop inbound messages or produce duplicate or unintended replies, so these issues should be resolved before merge. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 29.03% which is insufficient. The required threshold is 60.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 93 functions across 30 files. (8 skipped: 8 unsupported.) ✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
|
Railway Preview Environment
|
Fixes from the two reviews of PR #6679 (no architecture change): P1 security/correctness: - Redact the channel secondary secrets (Slack signing_secret, Telegram webhook_secret) in public vault responses; they survived redaction before. - Preserve those secondary secrets on credential rotation (a token-only update no longer drops the webhook secret and breaks inbound verification). - Roll back a Telegram connection whose setWebhook activation fails, instead of leaving a row that looks live but receives nothing. - Do not deactivate the whole bot connection on a per-chat kick/leave. - Render the approval card (tool + arguments) as text on Telegram; it was dropped, so an approval showed only Approve/Deny with no explanation. - Stop classifying forum topics and advertising topic support: the chat-only thread key would misroute a topic reply. v1 keeps one conversation per chat. P2: - Split raw answer text before html-escaping so a split never cuts an entity. - Translate an invalid-bot-token getMe error to a setup failure, not a 500. - Match a bot mention by substring (Telegram entity offsets are UTF-16); admit a /command only when bare or addressed to this bot. - Acknowledge a button press (answerCallbackQuery) so Telegram clears its spinner. - Identify the turn indicator by an explicit marker, not its display text, so a real answer equal to the indicator text is never swallowed. Tests updated and added for each; 844 channels+secrets unit tests pass.
|
Addressed an independent Codex (gpt-astra, xhigh) review and a code-quality review. All P1 and P2 findings are fixed in the latest commit, with no change to JP's architecture: secondary channel-secret redaction and rotation carry-over, failed-activation rollback, per-chat kick no longer deactivates the whole connection, the approval card renders as text, forum topics are not misrouted (one conversation per chat in v1), split-before-escape, invalid-token maps to a setup error, mention-by-substring, callback acknowledgement, and the indicator is marked explicitly rather than by its display text. 844 channels+secrets unit tests pass; live-verified continuity and single-reply on a real Telegram account. |
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 12
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Advanced
Run ID: d872753a-3e86-4496-944d-4d868f7130af
📒 Files selected for processing (39)
api/entrypoints/channel_adapters.pyapi/oss/src/apis/fastapi/channels/ingress.pyapi/oss/src/core/channels/adapters/interface.pyapi/oss/src/core/channels/adapters/slack/adapter.pyapi/oss/src/core/channels/adapters/telegram/__init__.pyapi/oss/src/core/channels/adapters/telegram/adapter.pyapi/oss/src/core/channels/adapters/telegram/capabilities.pyapi/oss/src/core/channels/adapters/telegram/mapping.pyapi/oss/src/core/channels/adapters/telegram/signature.pyapi/oss/src/core/channels/render/dtos.pyapi/oss/src/core/channels/render/render.pyapi/oss/src/core/channels/service.pyapi/oss/src/core/secrets/dtos.pyapi/oss/src/core/secrets/enums.pyapi/oss/src/core/secrets/redaction.pyapi/oss/src/core/secrets/services.pyapi/oss/src/middlewares/auth.pyapi/oss/src/tasks/asyncio/channels/outbox.pyapi/oss/tests/pytest/unit/channels/telegram/__init__.pyapi/oss/tests/pytest/unit/channels/telegram/test_telegram_adapter.pyapi/oss/tests/pytest/unit/channels/test_channel_adapter_interface.pyapi/oss/tests/pytest/unit/channels/test_channels_outbox_worker.pyapi/oss/tests/pytest/unit/secrets/test_dtos.pyapi/oss/tests/pytest/unit/secrets/test_write_only.pydocs/design/channels-research/v2/design-brief.mddocs/design/channels-research/v2/lane3-hosted-telegram-plan.mddocs/design/channels-research/v2/media-input.mddocs/design/channels-research/v2/ops-todo.mddocs/design/channels-research/v2/overnight-progress.mddocs/design/channels-research/v2/review-telegram-quality.mddocs/design/channels-research/v2/telegram-requirements.mddocs/design/channels-research/v2/ui-firstpass/channels-components/ChannelConnectFlow.tsxdocs/design/channels-research/v2/ui-firstpass/channels-components/ChannelManagePanel.tsxdocs/design/channels-research/v2/ui-firstpass/channels-components/ChannelsPage.tsxdocs/design/channels-research/v2/ui-firstpass/channels-components/helpers.tsdocs/design/channels-research/v2/ui-firstpass/channels-components/icons.tsxdocs/design/channels-research/v2/ui-firstpass/channels-components/index.tsdocs/design/channels-research/v2/ui-firstpass/channels-components/types.tsdocs/design/channels-research/v2/ui-firstpass/channels-connect-ui.patch
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.
- adapter: best-effort typing and callback calls also swallow httpx transport errors, not only Bot API errors, so a timeout cannot block the answer or the button routing. - mapping: match a bot mention on a username boundary, so @agenta_bot does not also match @agenta_bot2 and answer an unrequested turn. - signature: compare the secret token as bytes, so a non-ASCII header raises ChannelSignatureInvalid (401) instead of TypeError (500). - secrets: reject a blank secondary credential (e.g. an empty webhook_secret), the same rule as the primary; a blank would break inbound verification. - service: re-run activation after a Telegram credential rotation, so a rotated bot token re-points its webhook; surface a failure instead of a silent break. Deferred: the outbox atomic-claim finding is a property of JP's shared outbox worker for all channels, not introduced here; the _send idempotency guard already prevents the observed duplicate. Left as a separate concern about the worker.
…ferred decision Group chats need the bot's BotFather group-privacy turned off for Telegram to deliver group messages; added that to the Telegram setup instructions (Option 1). Recorded Mahmoud's decision that images/voice are out of scope this release, and that the eventual image flow uploads via the attachments store (same as the UI), not inline data URLs.
What
Adds Telegram as a channel, for a customer's own bot (the custom-bot path), mirroring the Slack adapter. Also fixes two shared delivery bugs surfaced by Telegram.
Why
Telegram is the next platform after Slack. The goal is one agent answering in a chat, with memory, behind the feature flag.
Changes
The Telegram adapter (
core/channels/adapters/telegram/)getMe; verifies each webhook with theX-Telegram-Bot-Api-Secret-Tokenheader.Ingress and activation
/telegram/events/{routing_token}/, on the public auth allowlist by prefix. The stored webhook secret is what authorises.activate_connectionadapter hook runssetWebhookafter the row is stored, because that call writes on Telegram's side and cannot run in the pre-store verify step. The webhook secret is minted and vaulted at create time.Secrets
telegramsecret kind and awebhook_secretcredential field.Two shared fixes (affect all channels)
turn_endedis published twice, which posted a duplicate message on a post-only channel and a wasted "not modified" edit on Slack. A row already SENT with identical content is now skipped.Testing
Not in this PR