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
- 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.
- Bot commands for triggers - Map EDDI
ChannelTarget triggers to Telegram bot commands (/architect question). Also support plain mentions (@bot architect: question).
- 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.
- Topic routing (supergroups) - Support
message_thread_id for Telegram supergroup topics (optional, forward-looking).
- MarkdownV2 conversion - Telegram requires escaping
., -, !, (, ), >, #, +, =, {, }, |, ~. Create convertMarkdownToTelegramV2() following the pattern in SlackWebApiClient.convertMarkdownToSlackMrkdwn().
sendChatAction("typing") - Send typing indicator while waiting for EDDI response.
- Loop prevention - Filter messages where
from.is_bot == true. Dedup by update_id (Telegram guarantees uniqueness).
- Rate limits - Telegram allows ~30 messages/sec to different chats, but only ~1 message/sec to the same chat. Implement per-chat pacing.
Deliverables
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
Problem / Motivation
Telegram (950M+ monthly active users) is the architecturally closest platform to Slack for EDDI integration:
X-Telegram-Bot-Api-Secret-Tokenheader (simpler than Slack's HMAC-SHA256)sendMessage,sendChatActionwith JSON bodiesreply_to_message_idcreates visual reply chainsThis makes Telegram the recommended first non-Slack adapter.
Architecture Principle
Proposed Solution
Create
ai.labs.eddi.integrations.telegrampackage:RestTelegramWebhook@POST /integrations/telegram/webhook. Verifies secret token header, dispatches events async. Returns 200 immediately.RestSlackWebhookTelegramSecretVerifierX-Telegram-Bot-Api-Secret-Tokenheader against configured secret (constant-time string comparison).SlackSignatureVerifierTelegramEventHandlerChannelTargetRouter. Maps Telegram chats -> EDDI conversations viaIUserConversationStore. Bot-message filtering, dedup byupdate_id.SlackEventHandlerTelegramApiClientjava.net.http.HttpClient-based client callingapi.telegram.org/bot<token>/sendMessage. Converts Markdown -> MarkdownV2. Retry with exponential backoff.SlackWebApiClientTelegramGroupDiscussionListenerGroupDiscussionEventListener. Posts agent contributions as reply chains usingreply_to_message_id.SlackGroupDiscussionListenerTelegramDeliveryExceptionSlackDeliveryExceptionConfiguration 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
setWebhookAPI to register EDDI's endpoint. Consistent with Slack's HTTP webhook pattern. Document thecurlcommand for webhook registration in the setup guide.ChannelTargettriggers to Telegram bot commands (/architect question). Also support plain mentions (@bot architect: question).reply_to_message_idcreates visual reply chains. For group discussions, each agent's contribution replies to the original question message.message_thread_idfor Telegram supergroup topics (optional, forward-looking)..,-,!,(,),>,#,+,=,{,},|,~. CreateconvertMarkdownToTelegramV2()following the pattern inSlackWebApiClient.convertMarkdownToSlackMrkdwn().sendChatAction("typing")- Send typing indicator while waiting for EDDI response.from.is_bot == true. Dedup byupdate_id(Telegram guarantees uniqueness).Deliverables
ai.labs.eddi.integrations.telegramSlackEventHandlerTestpattern - pure unit tests, no CDI)"telegram"toREGISTERED_CHANNEL_TYPESinRestChannelIntegrationStoredocs/telegram-integration.md(followingdocs/slack-integration.mdstructure)Alternatives Considered
java.net.http.HttpClient(consistent with Slack adapter's no-SDK approach)Additional Context
Acceptance Criteria
SlackEventHandlerTestpattern - pure unit tests, no CDI container)"telegram"added toREGISTERED_CHANNEL_TYPESinRestChannelIntegrationStore./mvnw testpasses with zero failuresdocs/telegram-integration.mdexists (followingdocs/slack-integration.mdstructure)