Skip to content

feat(notify): Telegram Bot notifier — v1.1.0 (closes #22)#23

Merged
fxthiry merged 5 commits intomainfrom
feature/telegram-notifier
Apr 15, 2026
Merged

feat(notify): Telegram Bot notifier — v1.1.0 (closes #22)#23
fxthiry merged 5 commits intomainfrom
feature/telegram-notifier

Conversation

@fxthiry
Copy link
Copy Markdown
Owner

@fxthiry fxthiry commented Apr 15, 2026

Summary

Adds a native type: telegram notifier to valerter, alongside the existing Mattermost / Webhook / Email notifiers. Resolves issue #22.

  • Uses the Bot API sendMessage endpoint, one sequential HTTP call per chat_id (Telegram rate-limits at 1 msg/sec/chat so fan-out parallelism would not help).
  • parse_mode: HTML by default, UTF-8 codepoint-safe truncation at Telegram's 4096 hard limit.
  • Full 429 Retry-After handling with bounded delay [1s, 60s] and a pool of 3 retries shared with 5xx + network errors.
  • bot_token stored as SecretString. Full endpoint URL (which contains the token) is kept in SecretString and never logged — reqwest::Error is sanitized via .without_url() before tracing.
  • New Prometheus counter valerter_alerts_truncated_total{notifier_type}.

Validation

  • Tests: 459 passing (+30 vs 1.0.0) — unit + wiremock integration covering multi-chat fan-out, partial-success, total-failure, 4096 codepoint boundary (incl. 4-byte emoji), 429 header/JSON/fallback chain, token-leak prevention on Debug.
  • Adversarial review: 3 reviewers (blind hunter, edge-case hunter, acceptance auditor). All findings patched or deferred; deferred work tracked in _bmad-output/implementation-artifacts/deferred-work.md.
  • Real prod test: 2 real alerts delivered to a live Telegram bot from a real VictoriaLogs feed; HTML rendering and metrics clean.
  • Version bump: 1.0.0 → 1.1.0 in Cargo.toml, Cargo.lock, CHANGELOG.

Known limitation (documented in CHANGELOG)

Templates have a single body field shared across notifiers. Markdown-flavored bodies (written for Mattermost) render literally in Telegram's HTML mode. Workaround: override body_template on the Telegram notifier with HTML-friendly Jinja. A render-pipeline-per-notifier abstraction is planned for 1.2.

Test plan

  • cargo fmt --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test — 459 passed, 0 failed
  • cargo build --release
  • Live run against production VictoriaLogs feed with real Telegram bot
  • CI green on this PR
  • After merge: tag v1.1.0 → release workflow publishes x86_64/aarch64 tarballs + .deb

François-Xavier THIRY added 3 commits April 15, 2026 08:31
Native `type: telegram` notifier using the Bot API sendMessage endpoint.
Supports multi-chat delivery (one sequential HTTP call per chat_id),
HTML parse_mode by default, 429 Retry-After handling with bounded delay,
and codepoint-safe truncation at Telegram's 4096-character limit.

Safety:
- bot_token stored as SecretString; full endpoint URL (which contains
  the token) is kept in SecretString and never logged
- reqwest errors are sanitized via `.without_url()` before logging
- fail-fast on empty chat_ids, empty chat_id elements, unresolved or
  empty bot_token, invalid body_template
- Retry-After clamped to [1s, 60s] to prevent tight loops and
  adversarial delays

Observability:
- new `valerter_alerts_truncated_total` Prometheus counter
- existing sent/failed/error counters gain `notifier_type="telegram"`

Cargo.toml bumped to 1.1.0.
- Extend .gitignore to cover config/prod-*/ so local test variants
  (e.g. config/prod-test-telegram) cannot be accidentally committed.
- Document in CHANGELOG the known limitation that Markdown-flavored
  template bodies render literally in Telegram's HTML parse_mode,
  with the body_template workaround. A proper render-pipeline-per-
  notifier will land in 1.2.
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 15, 2026

Codecov Report

❌ Patch coverage is 89.94083% with 17 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/notify/telegram.rs 89.44% 17 Missing ⚠️

📢 Thoughts on this report? Let us know!

François-Xavier THIRY added 2 commits April 15, 2026 09:27
…aths

Patch coverage bumped from 81.65% by adding targeted tests for
previously uncovered lines:

- src/config/tests.rs: two tests exercising Config::validate() for a
  Telegram notifier with empty chat_ids (fails) and with populated
  chat_ids (passes).
- src/notify/tests.rs: two registry tests that instantiate a Telegram
  notifier from config and that verify validation errors propagate
  through NotifierRegistry::from_config.
- src/notify/telegram.rs: three wiremock scenarios covering the 5xx
  retry path (recovers after one 5xx), the MaxRetriesExceeded path on
  persistent 5xx, and the Err(reqwest::Error) branch on network
  failure when the server goes away.
… on CI

On CI, wiremock's random-port allocation could recycle a port from a
just-dropped server, letting requests from one test land on another
test's server. This produced two intermittent failures:

- send_network_error_exhausts_retries observed the dropped server's
  port reused by a live mock and saw Ok(()) instead of a connect error.
- send_payload_has_expected_fields saw a second request body leak in
  from a peer test, tripping the exact-count verification.

Fixes:
- Mark every send_* wiremock test with #[serial] so they run one at a
  time. Adds ~3s of total runtime, eliminates the flake.
- Replace the drop-server trick in send_network_error_exhausts_retries
  with a connection to TEST-NET-1 (192.0.2.1, RFC 5737) and a short
  connect_timeout, guaranteeing ECONNREFUSED / timeout regardless of
  port reuse.
@fxthiry fxthiry merged commit b1f97c5 into main Apr 15, 2026
5 checks passed
@fxthiry fxthiry deleted the feature/telegram-notifier branch April 15, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant