Skip to main content

Telegram Setup

This page sets up the Telegram channel end-to-end: create the bot credentials, write the channel config, start the resident gateway, verify with doctor checks, and tune access policy, sessions, media, commands, and scheduled (cron) delivery.

For the broader picture of how channels fit together, see the Messaging Gateway overview. For the full list of Telegram config fields, see the Channels Reference.

Overview

Anyy connects to Telegram as a bot account and talks to the Bot API over HTTPS. The adapter uses long polling (getUpdates), so Telegram does not need to reach your host and you do not need a public URL, webhook, or reverse proxy. Direct messages and group chats are both supported, along with inbound photos, documents, and voice notes, plus a typing indicator while a turn is running.

Identity and routing follow the standard channel model: each Telegram chat maps to a stable Anyy identity of the form telegram:<account_id>:<chat_id>, and each chat gets its own session.

note

Anyy always presents itself as Anyy in conversations. The Bot API token only authenticates your bot to Telegram; it does not change the assistant's identity.

Prerequisites

  • Anyy installed, with the anyy CLI on your PATH (anyy --version).
  • A Telegram account and the official Telegram app (mobile or desktop) to talk to @BotFather.
  • A bot token from BotFather (created in the next section). Treat the token as a secret.
  • A host where the resident gateway can run continuously — see Running as a Resident Service.

Setup

The flow is: create the bot with BotFather, write the channel config with the setup command, start the gateway, then run doctor checks.

Create credentials

  1. In Telegram, open a chat with @BotFather.
  2. Send /newbot and follow the prompts: pick a display name, then a username ending in bot (for example my_anyy_bot).
  3. BotFather replies with an HTTP API token that looks like 123456789:AA.... Copy it. This is the value you will pass as <BOT_TOKEN> below.
  4. Optional, recommended settings (still in the BotFather chat):
    • To let the bot read all group messages (not only replies/mentions), send /setprivacy, choose your bot, and select Disable. Leave privacy Enabled if you only want the bot to act on mentions and replies in groups.
    • Send /setjoingroups to control whether the bot can be added to groups.
    • Use /setcommands to publish the slash commands you expose (see Commands).
warning

The bot token grants full control of your bot. Never paste it into chats, commit it to version control, or include it in logs, screenshots, or bug reports. If it leaks, send /revoke (or /token) to BotFather to rotate it, then update the stored secret. Anyy stores the token as a profile-local secret file (mode 0600) and only keeps a secret: reference in config.yaml; the raw token is never written into the config.

Configure Anyy

Write the Telegram config block and store the token as a managed secret with one command. Replace <BOT_TOKEN> with the token from BotFather:

anyy setup channels telegram \
--secret token='<BOT_TOKEN>' \
--write-config

What this does:

  • Stores the token as a profile-local secret at telegram/personal/token and records the reference token_ref: secret:telegram/personal/token in the config.
  • Writes a channels.telegram block into config.yaml (because of --write-config).
  • Prints a JSON summary including the resolved config_path and the exact doctor_command to run next.

The generated block uses these defaults:

channels:
telegram:
enabled: true
account_id: personal
token_ref: secret:telegram/personal/token
dm_policy: pairing
group_policy: mention
allow_from: []
allow_groups: []
media_max_bytes: 5242880

Useful flags (the --help output prints only a terse usage line, so flags such as --force are accepted but not listed there):

  • --account-id ID — local account label (default personal). It becomes part of the identity and secret path, so changing it changes where the token is stored.
  • --secret token=<BOT_TOKEN> — the bot token. May be repeated for other secrets, but Telegram only needs token.
  • --set NAME=VALUE — override or add a string config field, e.g. --set dm_policy=allowlist or --set bot_username=my_anyy_bot. May be repeated. You cannot override token_ref with --set; use --secret for credentials.
  • --write-config — actually write the block. Without it, the command performs a dry run and only prints what it would write (the secret is still written).
  • --force — replace an existing channels.telegram block instead of failing.
  • --home PATH / global --profile NAME — target a specific Anyy home/profile.
tip

Running without --write-config previews only the config block — it still writes the secret file. The --write-config flag gates the config.yaml block, not the secret; the token is stored either way. To stage the secret first and add the config block later, run once without --write-config, then re-run with it.

Set bot_username (via --set bot_username=... or by editing the config) to your bot's exact username without the @. In groups under the default mention policy, this is how Anyy recognizes that a message mentions it (@my_anyy_bot).

Start Gateway

Channels run inside the resident gateway. Start (or restart, after a config change) the gateway:

anyy gateway start
anyy gateway status

gateway status should report the service running. If the gateway is already running and you only changed channel config, you can apply it without a full restart:

anyy channels reload

To keep the gateway alive across reboots, install it as a system service — see Running as a Resident Service.

Run doctor checks

Verify the Telegram wiring with the channel doctor:

anyy channels doctor telegram

Add --json for machine-readable output, or --home PATH to target a specific home. The human output has three sections:

  • Channel doctor — the resolved home and config file.
  • Checks — per-channel configuration probes (config present, secret reference resolvable, etc.).
  • Live smoke — whether the channel is configured and enabled, and whether a live smoke run would contact the Telegram API (Will contact). The doctor itself does not send a message; it reports the plan.

The setup command also prints the exact doctor_command (including the resolved --home) in its JSON output, so you can copy it directly.

Access Policy

Two independent policies decide which inbound messages are accepted: dm_policy for direct (private) chats and group_policy for groups and supergroups. Both are enforced before a session is created, and every drop is recorded in channel status for observability.

Direct messages

dm_policy accepts one of:

  • open — accept DMs from anyone who messages the bot.
  • allowlist — accept only senders whose numeric Telegram user ID is listed in allow_from.
  • pairing — accept only already-known (paired) identities.
  • disabled — reject all DMs.

The setup default is pairing.

warning

Interactive pairing-token issuance is not yet wired up in this build: the /pair command and the pairing RPC are reserved and return "pairing is not configured". Until pairing issuance ships, use allowlist (with your numeric user ID in allow_from) or open so your own DMs are accepted.

For a private, single-user assistant, the simplest working setup is an allowlist:

channels:
telegram:
dm_policy: allowlist
allow_from:
- "123456789" # your numeric Telegram user ID

You can find your numeric user ID by messaging a bot such as @userinfobot, or by reading it from channel status/audit after sending a message (the drop record carries the sender_id). After editing, run anyy channels reload.

Groups

group_policy accepts one of:

  • disabled — ignore all group messages.
  • mention — respond only when the bot is mentioned (@<bot_username>) — the default.
  • allowlist — respond only in groups whose numeric chat ID is listed in allow_groups.
  • open — respond to every message in groups the bot is in.
  • pairing — respond only in already-known (paired) groups (subject to the same pairing limitation noted above).

Under the default mention policy, set bot_username so mention detection works, and add the bot to the group. Telegram's bot privacy mode also matters: with privacy enabled (the BotFather default), the bot only receives messages that mention it or reply to it, which pairs naturally with mention. Disable privacy in BotFather if you want open to see every message.

channels:
telegram:
group_policy: allowlist
allow_groups:
- "-1001234567890" # numeric (super)group chat ID

Admins

There is no separate "Telegram admin" role at the channel layer; access is governed entirely by the policies and allowlists above. Operational control (gateway lifecycle, config, secrets, approvals) is exercised through the anyy CLI on the host, and approval and audit semantics apply to state-changing operations regardless of which channel initiated them.

In-chat approvals are available to anyone whose messages the policy admits: a paired or allowlisted user can run /approve and /deny from Telegram (see Commands). Keep dm_policy/group_policy tight if you rely on this, since it effectively grants approval authority.

Sessions

Each Telegram conversation maps to its own Anyy session, keyed by chat:

  • A DM uses the session key anyy:telegram:dm:<chat_id>.
  • A group/supergroup uses anyy:telegram:group:<chat_id>.

Sessions persist across messages, so context carries forward within a chat. Different chats never share a session. To start fresh in a chat, send /new. Duplicate updates and the bot's own echoes are filtered automatically, so retries and re-delivered updates do not create duplicate turns.

Media Support

Inbound media

Anyy downloads and safely stages supported inbound attachments before the turn runs:

  • Photos — the largest available size is fetched and staged as an image.
  • Documents — staged as a file; image documents are validated as images.
  • Voice notes — staged as a voice attachment (with duration metadata).

Images are content-sniffed and rejected on a type mismatch. A per-attachment size cap applies: media_max_bytes (default 5242880 bytes = 5 MiB). Oversized or undownloadable attachments are skipped; if a message had only media and none could be staged, Anyy substitutes a short placeholder note so the turn still has context. Any caption on the media is used as the message text.

Raise or lower the cap as needed, then reload:

channels:
telegram:
media_max_bytes: 10485760 # 10 MiB

Outbound media

Outbound replies are delivered as text messages. Long replies are automatically split into multiple messages by character count (default chunk size 3500 characters; tune with send_chunk_chars). While a turn is running, the bot shows the Telegram "typing…" indicator in that chat.

Commands

Slash commands typed in a Telegram chat are interpreted by Anyy (they are independent of any commands you publish via BotFather's /setcommands, which only populate Telegram's UI hints). The commands available from a channel chat include:

  • /new — start a new session in this chat.
  • /status — show runtime status.
  • /stop — stop the running turn.
  • /approve / /deny — approve or deny a pending request.
  • /voice — manage voice replies for this conversation.

Commands that manage host-level runtime (provider, skills, cron, secrets, and similar) are exposed on the CLI/TUI rather than from channel chats. Run them with anyy on the host.

Cron Delivery

Scheduled tasks can deliver their output back to a Telegram chat. When you create a cron task from within a Telegram conversation (via the runtime), the task records the originating channel and identity (telegram:<account_id>:<chat_id>) as its delivery route, so the scheduled run's output is sent back to that same chat through the gateway.

This means the gateway must be running for scheduled deliveries to arrive, and the target chat must still be admitted by the access policy. For the delivery-route fields and how cron output binds to a channel identity, see the Channels Reference and your cron configuration.

Troubleshooting

  • Doctor reports not configured / disabled — confirm channels.telegram.enabled: true and that you pointed at the right home (--home / --profile). Re-run anyy setup channels telegram --write-config if the block is missing.
  • Secret can't be resolved — the token_ref points at a profile secret that wasn't written. Re-run the setup command with --secret token='<BOT_TOKEN>', then anyy channels reload.
  • Bot never replies to your DMs — most often dm_policy: pairing is blocking you and pairing issuance isn't configured. Switch to allowlist (add your numeric user ID to allow_from) or open, then reload.
  • Bot silent in a group — under mention it only answers when mentioned; verify bot_username matches the real username, add the bot to the group, and check BotFather privacy mode. For allowlist, confirm the numeric chat ID is in allow_groups.
  • Messages received but no session/turn — check that the gateway is running (anyy gateway status) and that the message wasn't dropped (channel status records the policy reason, e.g. pairing_required, mention_required, sender_not_allowed).
  • Attachments ignored — they likely exceeded media_max_bytes or failed validation. Raise the cap or resend a smaller file.
  • Config changes not taking effect — run anyy channels reload (or restart the gateway). Reloads require a running gateway.
  • Token compromised — rotate it via BotFather (/revoke), re-run setup with the new --secret token=..., then reload.