Skip to content

feat: add Slack and Telegram adapters with adapter-owned routing#16

Merged
Royal-lobster merged 11 commits intomainfrom
feat/slack-telegram-adapters
Apr 10, 2026
Merged

feat: add Slack and Telegram adapters with adapter-owned routing#16
Royal-lobster merged 11 commits intomainfrom
feat/slack-telegram-adapters

Conversation

@Royal-lobster
Copy link
Copy Markdown
Member

Summary

  • Breaking change: Moves routing from global RoutingConfig into per-adapter constructor options (channels, tags, mentions). Removes Router class, webhookUrl/pings from FormattedAlert.
  • New: SlackAdapter — Incoming Webhooks with Block Kit formatting, per-level channel routing, mention support, 429 retry
  • New: TelegramAdapter — Bot API with HTML formatting, per-level forum topic routing, tag-to-topic mapping, @username mentions, 429 retry

Migration

Move routing.channels, routing.tags, and routing.pings into adapter constructors:

// Before
AlertLogger.init({
  adapters: [new DiscordAdapter({ webhookUrl: '...' })],
  routing: { channels: { critical: '...' }, pings: { critical: ['<@&role>'] } },
})

// After
AlertLogger.init({
  adapters: [
    new DiscordAdapter({
      webhookUrl: '...',
      channels: { critical: '...' },
      mentions: { critical: ['<@&role>'] },
    }),
  ],
})

Usage

const logger = AlertLogger.init({
  serviceName: 'my-api',
  adapters: [
    new SlackAdapter({
      webhookUrl: 'https://hooks.slack.com/services/...',
      channels: { critical: 'https://hooks.slack.com/services/.../critical' },
      mentions: { critical: ['<@U0123>'] },
    }),
    new TelegramAdapter({
      botToken: '123:ABC...',
      chatId: '-1001234567890',
      topics: { critical: 42, warning: 43, info: 44 },
      mentions: { critical: ['@oncall_dev'] },
    }),
  ],
})

Test plan

  • 178 tests pass across 14 test files
  • TypeScript compiles cleanly
  • Package builds successfully (ESM + CJS + DTS)
  • Manual test with real Slack webhook
  • Manual test with real Telegram bot

🤖 Generated with Claude Code

Royal-lobster and others added 6 commits April 10, 2026 21:59
Covers adapter-owned routing (Approach A), message formatting,
rate limits, breaking changes to RoutingConfig/FormattedAlert,
and migration path from global routing to per-adapter config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BREAKING CHANGE: RoutingConfig, Router, and webhookUrl/pings on
FormattedAlert are removed. Routing is now configured per-adapter
via channels/tags/mentions constructor options. DiscordAdapter now
accepts channels, tags, and mentions directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supports per-level channel routing via webhook URLs, tag-based
routing, mentions, and 429 retry with Retry-After header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supports per-level forum topic routing, tag-based topic routing,
@username mentions, and 429 retry with body-based retry_after.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements Slack and Telegram adapters for the alert logger and refactors the routing system to be adapter-owned, removing the global Router and RoutingConfig. Core types like FormattedAlert were simplified by removing webhookUrl and pings to support this decentralized routing. Feedback focuses on aligning the new formatters with design specifications, such as ensuring the Slack context block is present in all alert phases and correcting emoji inconsistencies in the Telegram formatter. The reviewer also identified code duplication in the Telegram formatter and suggested formatting improvements for Slack fields and Telegram mentions. Additionally, several test files were flagged for violating project rules by explicitly importing Vitest globals.

Comment thread src/adapters/slack/formatter.ts
Comment thread docs/specs/2026-04-10-slack-telegram-plan.md
Comment thread src/adapters/slack/formatter.ts Outdated
Comment thread src/adapters/slack/slack-adapter.test.ts
Comment thread src/adapters/telegram/formatter.test.ts
Comment thread src/adapters/telegram/formatter.ts Outdated
Comment thread src/adapters/telegram/formatter.ts
Comment thread src/adapters/telegram/telegram-adapter.test.ts
Comment thread src/adapters/telegram/telegram-adapter.ts Outdated
Royal-lobster and others added 5 commits April 10, 2026 22:25
- Slack formatter: context block now appears in all phases, not just onset
- Slack formatter: field format changed to inline '*key:* value'
- Telegram formatter: info emoji changed from green to blue circle per design spec
- Telegram formatter: footer moved outside switch to reduce duplication
- Telegram adapter: mentions use double newline separator for readability

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CI was failing because new files used tabs instead of spaces
(biome.json specifies indentStyle: "space", indentWidth: 2).
Also fixes multi-line function signatures and import ordering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Telegram formatter: truncate stack traces before wrapping in <code>
  tags so HTML stays balanced when hitting the 4096 char limit. Drop
  code blocks entirely if message is still over limit.
- Telegram adapter: re-enforce 4096 limit after prepending mentions
  to prevent sendMessage 400 errors on near-limit alerts.
- Slack formatter: sanitize alert content in mrkdwn blocks to prevent
  mention injection (<@u123>, <!channel>, @everyone etc), matching
  the sanitization already present in the Discord formatter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Royal-lobster Royal-lobster merged commit bc50c1a into main Apr 10, 2026
3 checks passed
@github-actions github-actions bot mentioned this pull request Apr 10, 2026
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