Skip to main content

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_SECRET and validates X-Telegram-Bot-Api-Secret-Token on 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_id values 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_id rate limit protects the assistant endpoint.
  • /start <otp> payloads are parsed and sent to data-service to complete verification.
  • Normal messages resolve chat_id to the connected user, build conversationId = telegram:<chat_id>, call assistant-orchestrator-service /assistant/ask, and return the answer with Telegram sendMessage.
  • If the assistant returns a document download link, the URL must already point to the public docs-service token endpoint. Telegram clients do not carry the browser session, so document links are consumed through /docs-service/dl/:token and expire quickly.
  • message-bot-gateway reads top-level assistant actions[] 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 as X-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/configs
  • POST /messaging/telegram/start-verification
  • POST /messaging/telegram/verify
  • DELETE /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/register
  • POST /telegram/webhook/delete
  • GET /telegram/webhook/status
  • GET /telegram/runtime/status
  • POST /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:

  1. Open Telegram and start a chat with @BotFather.
  2. Run /newbot.
  3. Choose the display name and username.
  4. Store the returned bot token as TELEGRAM_BOT_TOKEN.
  5. Store the username, without @, as TELEGRAM_BOT_USERNAME.
  6. Generate a random webhook secret and store it as TELEGRAM_WEBHOOK_SECRET.
  7. Set TELEGRAM_WEBHOOK_PUBLIC_URL to the public API base path, for example https://test.rainty.app/message-bot-gateway.
  8. 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_TOKEN
  • TELEGRAM_WEBHOOK_SECRET

Variables:

  • MESSAGE_BOT_GATEWAY_VERSION
  • TELEGRAM_BOT_USERNAME
  • TELEGRAM_WEBHOOK_PUBLIC_URL
  • TELEGRAM_RATE_LIMIT_MAX_MESSAGES (default 20)
  • TELEGRAM_RATE_LIMIT_WINDOW_MS (default 60000)
  • ASSISTANT_ORCHESTRATOR_SERVICE_URL
  • DATA_SERVICE_URL
  • INTERNAL_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

  1. UserInterface calls POST /data-service/messaging/telegram/start-verification.
  2. data-service creates a short OTP, stores only its hash, and returns a Telegram deep link.
  3. UserInterface renders the deep link as QR code and an Open Telegram link.
  4. Telegram sends /start <otp> to the bot.
  5. message-bot-gateway validates the webhook secret and calls data-service internal verification.
  6. data-service associates external_chat_id with the user and emits messaging.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_configs
  • messaging_pending_verifications

The schema is defined in db/init.sql and the incremental migration is db/migrations/010_messaging_gateway.sql.