Message Bot Gateway
message-bot-gateway is the transport boundary for external chat channels.
It receives channel-specific webhooks, validates the channel secret, normalizes
the message, and keeps channel behavior out of application services.
Telegram first version
- Webhook endpoint:
POST /webhook/telegram. - The endpoint requires
TELEGRAM_WEBHOOK_SECRETand validatesX-Telegram-Bot-Api-Secret-Tokenon every request. - Only private chats are accepted for user interaction.
- Group, supergroup, and channel updates receive:
This bot works only in Direct Message for privacy reasons.The bot then leaves the chat. update_idvalues are deduplicated in-memory for the first implementation.- Webhook responses are sanitized and do not echo OTPs or message text.
- Long answers are split under Telegram's message length limit before sending.
- A basic per-
chat_idrate limit protects the assistant endpoint. /start <otp>payloads are parsed and sent todata-serviceto complete verification.- Normal messages resolve
chat_idto the connected user, buildconversationId = telegram:<chat_id>, callassistant-orchestrator-service /assistant/ask, and return the answer with TelegramsendMessage. - If the assistant returns a document download link, the URL must already point
to the public
docs-servicetoken endpoint. Telegram clients do not carry the browser session, so document links are consumed through/docs-service/dl/:tokenand expire quickly. message-bot-gatewayreads top-level assistantactions[]and appends document download links to the Telegram text when the generated answer did not already include the URL.- Non-text Telegram messages are explicitly rejected for now. The gateway does not ingest attachments or other unstructured inbound content in this version.
- Each forwarded Telegram message carries a deterministic
correlationId(telegram:<chat_id>:<message_id>when Telegram provides a message id). The gateway sends it both in the assistant request body and asX-Correlation-Id, and logs it without storing the message text.
Internal normalized message shape:
{
"channel": "TELEGRAM",
"user_id": 42,
"external_chat_id": "123456",
"external_message_id": "987",
"text": "Ci sono rate scadute?",
"session_id": "telegram:123456",
"timestamp": "2026-05-20T10:00:00.000Z"
}
User-scoped data-service API
The frontend uses data-service for messaging settings. These routes are
authenticated through the standard user token and never trust a browser-supplied
user_id.
GET /messaging/configsPOST /messaging/telegram/start-verificationPOST /messaging/telegram/verifyDELETE /messaging/telegram
The Telegram webhook completes /start <otp> through the internal endpoint
POST /internal/messaging/telegram/verify-start. It is protected by internal
JWT with audience data-service and scope messaging:telegram:verify, and is
callable only by message-bot-gateway.
Connected Telegram chats are resolved through
GET /internal/messaging/telegram/chats/:chatId, protected by the same
internal JWT scope.
Operations
message-bot-gateway exposes protected operational endpoints:
POST /telegram/webhook/registerPOST /telegram/webhook/deleteGET /telegram/webhook/statusGET /telegram/runtime/statusPOST /telegram/test-message
The endpoints validate the caller through authService /auth/validate before
calling Telegram or returning operational state.
The AdminInterface page
/#/admin/microservice/message-bot-gateway exposes a dedicated
Operativi Telegram tab with:
- runtime configuration checks without token or secret values;
- Telegram webhook info from
getWebhookInfo; - register/delete webhook actions;
- manual test message sender for known test chat IDs;
- a checklist of missing AdminInterface capabilities for a fuller operational console.
The service also keeps the standard health/status endpoints provided by
serverFactory.
TELEGRAM_BOT_TOKEN and TELEGRAM_WEBHOOK_SECRET are read only from
environment/secret configuration. They must not be stored in database-backed
runtime settings.
start-verification generates a short OTP, stores only its SHA-256 hash in
messaging_pending_verifications, and returns:
{
"otp": "RAIN-ABC123",
"telegramDeepLink": "https://t.me/RaintyBot?start=RAIN-ABC123",
"expires_at": "2026-05-20 12:00:00"
}
The UserInterface renders the deep link as a local QR code and as an
Open Telegram link.
BotFather setup
Create one platform bot:
- Open Telegram and start a chat with
@BotFather. - Run
/newbot. - Choose the display name and username.
- Store the returned bot token as
TELEGRAM_BOT_TOKEN. - Store the username, without
@, asTELEGRAM_BOT_USERNAME. - Generate a random webhook secret and store it as
TELEGRAM_WEBHOOK_SECRET. - Set
TELEGRAM_WEBHOOK_PUBLIC_URLto the public API base path, for examplehttps://test.rainty.app/message-bot-gateway. - Register the webhook with
POST /message-bot-gateway/telegram/webhook/register.
The webhook target becomes:
https://test.rainty.app/message-bot-gateway/webhook/telegram.
To prevent group usage, BotFather privacy settings are not enough. The gateway also enforces direct-message-only behavior and leaves groups automatically.
Environment Variables
Secrets:
TELEGRAM_BOT_TOKENTELEGRAM_WEBHOOK_SECRET
Variables:
MESSAGE_BOT_GATEWAY_VERSIONTELEGRAM_BOT_USERNAMETELEGRAM_WEBHOOK_PUBLIC_URLTELEGRAM_RATE_LIMIT_MAX_MESSAGES(default20)TELEGRAM_RATE_LIMIT_WINDOW_MS(default60000)ASSISTANT_ORCHESTRATOR_SERVICE_URLDATA_SERVICE_URLINTERNAL_JWT_PRIVATE_KEY
For TEST and LIVE, configure these as GitHub environment Secrets/Variables. Do not commit real Telegram tokens or webhook secrets.
QR Flow
- UserInterface calls
POST /data-service/messaging/telegram/start-verification. data-servicecreates a short OTP, stores only its hash, and returns a Telegram deep link.- UserInterface renders the deep link as QR code and an
Open Telegramlink. - Telegram sends
/start <otp>to the bot. message-bot-gatewayvalidates the webhook secret and callsdata-serviceinternal verification.data-serviceassociatesexternal_chat_idwith the user and emitsmessaging.telegram.connected.
Adding Future Channels
Add a channel adapter inside message-bot-gateway, for example
WhatsAppAdapter, with the same responsibilities as TelegramAdapter:
- validate provider-specific webhook/authentication;
- reject unsupported conversation scopes;
- normalize incoming messages to the internal shape;
- resolve the external recipient through
data-service; - call
assistant-orchestrator-service; - send the provider-specific response;
- never implement rAInty domain logic in the gateway.
assistant-orchestrator-service should continue to receive only normalized
messages and should not know whether the source channel is Telegram, WhatsApp,
or another provider.
Tables
user_messaging_configsmessaging_pending_verifications
The schema is defined in db/init.sql and the incremental migration is
db/migrations/010_messaging_gateway.sql.