Files
hub/docs/telegram.md
T

4.0 KiB
Raw Blame History

Telegram Channel

Connect a Telegram bot to an agent so the agent can read incoming messages and reply. Telegram is the only channel implemented today; the machinery lives in backend/src/app_mixins/channel.py (logic) and backend/src/server/telegram.py (webhook route).

An agent that owns a channel/telegram resource is the agent that answers. You can bind the same bot token to several agents, and one agent can be bound to several bots.

1. Create a bot

Talk to @BotFather, create a bot, and copy its token (looks like 123456:ABC-DEF...). Treat the token as a secret.

2. Choose a delivery mode

Telegram can push updates to a webhook, or the hub can pull them with long polling. The choice is driven entirely by the HUB_HOST environment variable:

  • Webhook — set HUB_HOST to a publicly reachable host. Telegram requires HTTPS on ports 443, 80, 88 or 8443, so the hub must be reachable there.
  • Long polling — leave HUB_HOST unset. The hub prints a warning and polls getUpdates instead. No public endpoint or TLS certificate is needed.

The POST /channel/telegram/<agent_id> route is always served regardless of mode; in polling mode it simply never receives traffic.

Webhooks and polling are mutually exclusive. A webhook is never registered when HUB_HOST is unset, and the poller clears any existing webhook (deleteWebhook) before it starts.

3. Bind the bot to an agent

Add a resource to the responding agent (via the resources UI or the save-agent-resource action):

kind  = "channel"
key   = "telegram"
value = {"token": "<bot token>", "secret": "<random webhook secret>", "name": "<display>"}
  • token — the BotFather token. Required.
  • secret — a random string of A-Z a-z 0-9 _ -, 1–256 characters. Optional, and only meaningful in webhook mode. When present, Telegram sends it back on every request in the X-Telegram-Bot-Api-Secret-Token header and the route rejects mismatches with 403.
  • name — a display label. Optional.

This is the only state the channel persists. Conversations are not stored — no Chat, Thread or Message rows are created. The rolling transcript is held in memory on the hub and discarded when the process exits.

4. Restart the hub

On startup the hub queries all channel/telegram resources and reconciles the provider:

  • Webhook mode: it calls getWebhookInfo for each binding and, when the registered URL does not match https://<HUB_HOST>/channel/telegram/<agent_id>, calls setWebhook with that URL, the secret_token, and allowed_updates=["message"]. Verify with getWebhookInfo.
  • Polling mode: it starts one background task per binding. Confirm the bot is reachable with getMe, then send a test message.

5. Talk to the bot

Message the bot. On each text message the hub:

  1. Appends the message to the conversation's in-memory transcript.
  2. Runs the agent with a flattened input — an instruction, a status message (channel, chat id/title/type, current time, sender), and the rendered conversation.
  3. The agent replies by calling the send_message tool, which issues a sendMessage call to Telegram.

Non-text updates (edited messages, media, callbacks) are ignored. Messages authored by other bots are ignored.

6. Troubleshooting

Symptom Cause
Webhook not set HUB_HOST unset, or the host is not reachable over HTTPS. Check the startup log.
Polling active when you expected a webhook HUB_HOST is unset.
403 from the webhook route X-Telegram-Bot-Api-Secret-Token does not match the resource's secret.
404 from the webhook route No channel/telegram resource on that agent.
No reply The agent has no model/tool configuration, or the run errored — check the hub log for [channel] lines.
Conflict: can't use getUpdates method while webhook is active A stale webhook from a previous deployment. Remove it (deleteWebhook) or set HUB_HOST so webhook mode takes over.