feat(channels): add Telegram as a first-class chat channel (WeChat peer)#54
Merged
Conversation
Adds a Telegram bot channel that runs side-by-side with WeChat through the same PolicyGate -> SessionDispatcher pipeline. It long-polls getUpdates (outbound only, no webhook or open ports); the bot token lives only in the client URL path and is never logged. - telegram/client.py: async Bot API client (getMe/getUpdates/sendMessage/deleteWebhook) with bounded, token-free errors and a 4096-char send cap - telegram/adapter.py: long-poll loop with offset tracking, backoff, 409->deleteWebhook, 401->stop, 429->retry_after; handler errors never kill the loop - config.py: TelegramInstanceConfig + [[channels.telegram]] parsing; extracted a shared _resolve_token_source helper (WeChat behavior unchanged) - channels start/doctor/init now drive WeChat + Telegram together - 45 new tests; README + init template document Telegram as a WeChat peer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
The
channelsportal/pipeline was WeChat-only — there was no peer entry point for other messengers. This adds Telegram as a first-class chat channel, running side-by-side with WeChat through the samePolicyGate → SessionDispatcherpipeline. Onecoding-bridge channels startnow drives both at once.Telegram needs no gateway: the daemon long-polls the official Bot API (
getUpdates, outbound-only, no webhook, no open ports). The bot token lives only in the client URL path (/bot<token>/…) and is never logged or surfaced in any error string.Changes
channels/telegram/client.py— async Bot API client:getMe/getUpdates/sendMessage/deleteWebhook. Bounded, token-free errors;sendMessagecaps text at 4096 chars;TelegramErrorcarrieserror_code+retry_after.channels/telegram/adapter.py— long-poll loop: offset tracking (update_id + 1), backoff 0.5→30s (reset on success),409→deleteWebhook+continue,401→stop,429→honorretry_after. Handler exceptions never kill the loop.aclose()closes the owned client to interrupt an in-flight poll.channels/config.py— newTelegramInstanceConfig+[[channels.telegram]]parsing/validation (unknown-key rejection,token_envXORtoken_file,api_basescheme + no embedded-credentials, allowlist/rate/dedup bounds,default_provider∈KNOWN_PROVIDERS). Extracted a shared_resolve_token_source()helper and refactoredWeChatInstanceConfig.resolve_tokento delegate to it — WeChat behavior/messages unchanged.channels_cli.py—channels startbuilds WeChat and Telegram adapters on one shared stop event;channels doctoradds a TelegramgetMeprobe;channels inittemplate +--helpmention Telegram.pyproject.toml—telegram = []marker extra (parity withwechat).README.md— channels section now documents WeChat + Telegram as peers.tests/test_channels_telegram.py— 45 new tests (update parsing, client viahttpx.MockTransport, adapter loop edge cases, config validation, WeChat+Telegram coexistence).Security
exc.__class__.__name__; adapter logserror_codeints only; doctor printsbot @usernameonly. Tests assert the token string never appears in any error.enabled = false); same gated inbound (trigger prefix / sender + group allowlist / rate limit / dedup) as WeChat; provider turns still run locally.Testing
ruff checkclean.test_capabilities,test_fs,test_locking— filesystem/PID assertions that pass on CI Linux; unrelated to this change).🤖 对抗评审
Reviewer: Claude
general-purposesubagent (Codex not available on this host), read-only, one round.VERDICT: CLEAN — no real defects found. Classes of bug explicitly checked and confirmed handled:
_calltransport error,send_message, adapter logs, doctor probe) surface only exception class names /error_codeints /@username; never the token or base URL. Tests assert the token never appears inSendResult.error.update_id + 1for every update including skipped/unparseable ones, so a permanently-bad update cannot wedge the loop. Verified by tests.aclose()sets the stop event and closes the owned client, which fails the in-flightgetUpdates; theTelegramError/stop check then returns immediately. Injected clients are intentionally not closed (caller-owned) — covered by a test.deleteWebhook+continue (same offset), stop, andretry_afterrespectively — all verified._resolve_token_sourceis byte-for-byte equivalent to the old inline WeChat logic; error substrings unchanged; existing WeChat tests still pass.api_base), including bool/int/NaN/Inf rejection and provider allowlist.channels startwiring — both adapters share one stop event; all adapters/dispatchers closed infinally._parse_update— drops non-message/edited/non-text/bad-chat updates;sender_idfalls back tochat_id; supergroup/channel →"group"soallowed_groupsapplies.No
RISKitems were raised, so none required fix-or-rebuttal.