# 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](https://t.me/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/` 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": "", "secret": "", "name": ""} ``` - `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:///channel/telegram/`, 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. |