feat: TelegramNotifier — recommended default notifier (rc2)#20
Merged
Conversation
Telegram beats SMTP / SES / Slack on every axis that matters for single-dev and small-team ops: two-minute @Botfather setup, no app password / verified-identity dance, per-flow routing via chat_id or forum message_thread_id, mobile push for free, clean token rotation. This rc promotes it to the recommended default; SMTP / SES (via the existing EmailNotifier) / Slack / GitHub / S3 stay as alternates for consumers that need them. - flow_doctor/notify/telegram.py: TelegramNotifier (Bot API sendMessage POST, urllib-only to keep core dep-free, 4096-char Telegram-limit truncation with [truncated] sentinel, /getMe preflight that respects FLOW_DOCTOR_SKIP_PREFLIGHT). send() returns the non-secret target identifier "telegram:<chat_id>[:<thread>]" so action.target never persists the bot token. - flow_doctor/notify/configs.py: TelegramNotifierConfig joins the Field(discriminator="type") union. Accepts chat_id as int (typical, negative for supergroups / channels) or str ("@channelusername" for public channels). - flow_doctor/core/config.py: NotifyChannelConfig extended with bot_token / chat_id / message_thread_id / parse_mode (default Markdown) / disable_notification fields so yaml-driven configs and the to_channel_config() lift path both work. _parse_notify_dicts forwards the new keys. - flow_doctor/core/client.py: _init_notifiers handles type="telegram" with bot_token + chat_id ConfigError messages pointing at @Botfather and the getUpdates lookup. FLOW_DOCTOR_TELEGRAM_BOT_TOKEN + FLOW_DOCTOR_TELEGRAM_CHAT_ID env fallbacks (with TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID as conventional aliases); numeric env chat_ids coerce to int, @channelusername stays str. _send_notifications dispatches to ActionType.TELEGRAM_ALERT. - flow_doctor/core/models.py: TELEGRAM_ALERT joins the ActionType enum. - flow_doctor/{__init__,notify/__init__}.py: TelegramNotifierConfig re-exported at both package roots. - Version bump 0.5.0rc1 → 0.5.0rc2 in pyproject.toml + __init__.py. CHANGELOG entry documents the rationale + setup recipe. 20 new tests cover: round-trip through legacy NotifyChannelConfig, builder integration, env-var coercion (numeric → int, @channel → str), missing-field ConfigError shape, urllib POST URL + payload (chat_id / text / parse_mode / message_thread_id / disable_notification gating), target-id format on success, never-raises behavior on API ok=false and network failures, truncation, ActionType dispatch end-to-end through _send_notifications, and preflight skip + auth failure paths. Suite: 376/376 pass (356 prior + 20 new). One pre-existing assertion in test_builder used "telegram" as an example unknown channel type — updated to "pagerduty" since telegram is now a first-class member of the discriminated union. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Adds Telegram as the recommended default notifier in flow-doctor 0.5.0rc2.
Why Telegram becomes the default: for single-dev and small-team ops, Telegram beats SMTP / SES / Slack on setup cost (two-minute
@BotFatherflow vs. SMTP app password / SES verified identity / Slack workspace admin), routing (per-chat or per-thread is built in), and UX (mobile push is automatic). One bot fans out to N flows viachat_idand optionalmessage_thread_id(forum topics in supergroups). Bot tokens rotate cleanly via@BotFather.SMTP / SES (via existing
EmailNotifier) / Slack / GitHub / S3 stay as alternates — the change is which notifier the README + builder examples lead with. No deprecations.This also kills the orphan
telegram_webhook_urlfield that was hiding inRemediationConfigwith no first-class notifier behind it (will be cleaned up in a follow-up rc once we audit consumers).Usage
Or via env-only:
Implementation notes
sendMessagevia urllib (norequestsdep — keeps the install lean)./getMepreflight at__init__so a revoked token fails fast; bypassed byFLOW_DOCTOR_SKIP_PREFLIGHT=1(same env-var contract as the other notifiers).[truncated]sentinel.send()returns"telegram:<chat_id>[:<thread>]"as the action target — never the bot token. Stored inactions.targetfor the audit trail.FLOW_DOCTOR_TELEGRAM_CHAT_IDenv value auto-coerces toint(negative for supergroups / channels);@channelusernamestyle staysstr.Test plan
pytest tests/→ 376/376 pass (356 prior + 20 new Telegram tests).NotifyChannelConfig, env-var coercion (int vs@channelstr), missing-fieldConfigErrorshape (mentions@BotFather+getUpdates), urllib POST URL + payload (chat_id / text / parse_mode / message_thread_id / disable_notification gating), target-id format, never-raises on APIok=false+ network failure, message-length truncation,ActionType.TELEGRAM_ALERTdispatch end-to-end, preflight skip + 401-style auth failure.test_builderused"telegram"as an example unknown channel type — updated to"pagerduty".🤖 Generated with Claude Code