Skip to content

feat: Telegram Bot API channel integration adapter #580

Description

@ginccc

Problem / Motivation

Telegram (950M+ monthly active users) is the architecturally closest platform to Slack for EDDI integration:

  • HTTP webhook model - Telegram sends POST requests to a registered URL (identical to Slack's Events API)
  • Simple secret verification - String comparison of X-Telegram-Bot-Api-Secret-Token header (simpler than Slack's HMAC-SHA256)
  • Simple REST API - sendMessage, sendChatAction with JSON bodies
  • Reply threading - reply_to_message_id creates visual reply chains
  • Generous message limit - 4096 characters (vs Slack's ~4000)
  • No OAuth complexity - Single bot token, no workspace/tenant scoping

This makes Telegram the recommended first non-Slack adapter.

Architecture Principle

No platform SDKs. All adapters use java.net.http.HttpClient + Jackson for raw HTTP/JSON. No TelegramBots library. Every messaging platform's API is just REST under the hood. Raw HTTP keeps the dependency tree lean, the single-JAR deployment simple, and gives full control over retry logic, error handling, and message formatting. See the existing SlackWebApiClient (245 lines, zero external dependencies) as the reference pattern.

Proposed Solution

Create ai.labs.eddi.integrations.telegram package:

Class Responsibility Slack Equivalent
RestTelegramWebhook JAX-RS @POST /integrations/telegram/webhook. Verifies secret token header, dispatches events async. Returns 200 immediately. RestSlackWebhook
TelegramSecretVerifier Compares X-Telegram-Bot-Api-Secret-Token header against configured secret (constant-time string comparison). SlackSignatureVerifier
TelegramEventHandler Routes Telegram messages/commands to EDDI via ChannelTargetRouter. Maps Telegram chats -> EDDI conversations via IUserConversationStore. Bot-message filtering, dedup by update_id. SlackEventHandler
TelegramApiClient java.net.http.HttpClient-based client calling api.telegram.org/bot<token>/sendMessage. Converts Markdown -> MarkdownV2. Retry with exponential backoff. SlackWebApiClient
TelegramGroupDiscussionListener Implements GroupDiscussionEventListener. Posts agent contributions as reply chains using reply_to_message_id. SlackGroupDiscussionListener
TelegramDeliveryException Retryable delivery failure. SlackDeliveryException

Configuration Model

{
  "name": "Community Support Bot",
  "channelType": "telegram",
  "platformConfig": {
    "chatId": "-1001234567890",
    "botToken": "${vault:telegram-bot-token}",
    "webhookSecret": "${vault:telegram-webhook-secret}"
  },
  "defaultTargetName": "assistant",
  "targets": [...]
}

Key Design Decisions

  1. Webhook mode, not polling - Use Telegram's setWebhook API to register EDDI's endpoint. Consistent with Slack's HTTP webhook pattern. Document the curl command for webhook registration in the setup guide.
  2. Bot commands for triggers - Map EDDI ChannelTarget triggers to Telegram bot commands (/architect question). Also support plain mentions (@bot architect: question).
  3. Reply-based threading - Telegram doesn't have true threads like Slack, but reply_to_message_id creates visual reply chains. For group discussions, each agent's contribution replies to the original question message.
  4. Topic routing (supergroups) - Support message_thread_id for Telegram supergroup topics (optional, forward-looking).
  5. MarkdownV2 conversion - Telegram requires escaping ., -, !, (, ), >, #, +, =, {, }, |, ~. Create convertMarkdownToTelegramV2() following the pattern in SlackWebApiClient.convertMarkdownToSlackMrkdwn().
  6. sendChatAction("typing") - Send typing indicator while waiting for EDDI response.
  7. Loop prevention - Filter messages where from.is_bot == true. Dedup by update_id (Telegram guarantees uniqueness).
  8. Rate limits - Telegram allows ~30 messages/sec to different chats, but only ~1 message/sec to the same chat. Implement per-chat pacing.

Deliverables

  • 6 Java classes in ai.labs.eddi.integrations.telegram
  • Unit tests for all classes (following SlackEventHandlerTest pattern - pure unit tests, no CDI)
  • Add "telegram" to REGISTERED_CHANNEL_TYPES in RestChannelIntegrationStore
  • Documentation page: docs/telegram-integration.md (following docs/slack-integration.md structure)

Alternatives Considered

  • TelegramBots Java library - Adds dependency; Telegram's API is simple enough for raw java.net.http.HttpClient (consistent with Slack adapter's no-SDK approach)
  • Long polling - Requires persistent connection; HTTP webhook matches EDDI's architecture

Additional Context

Acceptance Criteria

  • All classes listed in Deliverables are implemented and compile without warnings
  • Unit tests for every class (following SlackEventHandlerTest pattern - pure unit tests, no CDI container)
  • "telegram" added to REGISTERED_CHANNEL_TYPES in RestChannelIntegrationStore
  • ./mvnw test passes with zero failures
  • Code coverage meets project targets: >90% instruction coverage, >80% branch coverage (measured by JaCoCo)
  • Documentation page docs/telegram-integration.md exists (following docs/slack-integration.md structure)
  • PR description includes screenshots or curl transcripts demonstrating:
    • Webhook receiving a Telegram message and routing it to an EDDI agent
    • EDDI agent response delivered back to the Telegram chat
    • Secret token verification rejecting an invalid request
  • PR submitted for review - do NOT merge without approval

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementhelp wantedintegrationChannel integration adapters (Slack, Teams, Discord, Telegram, WhatsApp)javaPull requests that update java codesize: LLarge: full day+, significant scope, design discussion likely

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions