InitRunner

Telegram Bot

Get a Telegram bot agent running in three steps. For the full trigger reference, see Triggers.

Old envelopes still load. Convert them with initrunner doctor --fix PATH. See Envelope Migration.

Prerequisites

  • InitRunner installed (pip install initrunner or uv tool install initrunner)
  • An API key for your provider (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.)
  • The Telegram optional dependency: uv sync --extra telegram (or pip install initrunner[telegram])

Step 1: Create a Bot with BotFather

  1. Open Telegram and search for @BotFather.
  2. Send /newbot and follow the prompts to choose a name and username.
  3. BotFather replies with a token — copy it. You'll need it in Step 2.

Step 2: Set Environment Variables

export TELEGRAM_BOT_TOKEN="your-token-here"
export OPENAI_API_KEY="your-api-key"   # or your provider's key

Or, to persist keys across sessions, add them to ~/.initrunner/.env:

TELEGRAM_BOT_TOKEN=your-token-here
OPENAI_API_KEY=your-api-key

A .env file next to your role.yaml also works. Running initrunner setup writes the provider key there automatically. Existing environment variables always take precedence over .env values.

Step 3: Create a Role and Run

Create a role.yaml:

name: telegram-assistant
description: A Telegram bot that responds to messages via long-polling
model:
  provider: openai
  name: gpt-5-mini
  temperature: 0.1
  max_tokens: 4096
prompt: |
  You are a helpful assistant responding to Telegram messages.
  Keep responses concise and well-formatted for mobile reading.
triggers:
  - type: telegram
    token_env: TELEGRAM_BOT_TOKEN
guardrails:
  max_tokens_per_run: 50000
  daemon_daily_token_budget: 200000

Start the daemon:

initrunner run role.yaml --daemon

You should see Telegram bot started polling in the logs.

Quick alternative: the bundled starter

To try a bot without writing a role file, run the starter that ships with InitRunner:

initrunner run telegram --daemon

It needs TELEGRAM_BOT_TOKEN and a provider key, and it installs its own extras on first run.

To customise it, copy it out and edit the YAML:

initrunner examples copy telegram -o ./my-bot/
initrunner run ./my-bot/role.yaml --daemon

The starter has no allowlist. Like the ephemeral bot it replaces, it answers anyone who finds the handle. Add allowed_user_ids to the trigger before you share it.

--bot telegram was removed in v2026.8.11. It never read triggers from the role: it built one from CLI arguments, dropping token_env, allow_all, channel_ids, and allowed_roles along the way. With no allowlist flags the result had allow_all: false and empty lists, which the adapter reads as no check at all, so the bot answered everyone. --daemon builds the trigger from YAML and adds hot reload, retry, and circuit breaking.

One behavior difference worth knowing: the starter runs a single agent turn per message, where run --bot forced the autonomous loop.

For production, use the role.yaml approach above for access control and budgets. See CLI Reference.

Testing

  • Send a plain text message to your bot in Telegram.
  • Long responses are automatically chunked at 4096-character boundaries.
  • /start, /help, and other commands are ignored — only plain text messages are processed.

Configuration Options

All options go under triggers[]:

FieldTypeDefaultDescription
token_envstr"TELEGRAM_BOT_TOKEN"Environment variable holding the bot token.
allowed_userslist[str][]Telegram usernames allowed to interact. Empty = allow everyone.
allowed_user_idslist[int][]Telegram user IDs allowed to interact. Empty = allow everyone.
allow_allboolfalseRespond to anyone even when no allowlist is set. Since v2026.6.5.
prompt_templatestr"{message}"Template for the prompt. {message} is replaced with the user's text.

Example with restrictions:

triggers:
  - type: telegram
    token_env: TELEGRAM_BOT_TOKEN
    allowed_users: ["alice", "bob"]
    allowed_user_ids: [123456789, 987654321]
    prompt_template: "Telegram user asks: {message}"

Security and Public Access

By default the bot responds to anyone who messages it. Lock it down before making it available to others:

  • Prefer allowed_user_ids over allowed_users. Usernames are mutable — users can change them at any time. User IDs are permanent. Find your ID via @userinfobot.
  • Use allowed_users to restrict access by Telegram username. When either allowed_users or allowed_user_ids is non-empty, messages from unmatched users are silently ignored.
  • Union semantics: access is granted if the user matches either allowed_users or allowed_user_ids. Both fields can be set together.
  • Acknowledge open access. Since v2026.6.5, a bot with no allowlist and allow_all unset logs a loud startup warning that it will respond to any user. Set an allowlist or allow_all: true to silence it. A future release will reject unconfigured bots by default (fail-closed).
  • Set daemon_daily_token_budget in guardrails to cap API costs. Without a budget, a public bot can run up unlimited charges.
  • Keep the bot token secret. Anyone with the token can impersonate the bot. Never commit it to version control — use environment variables or a secrets manager.
  • If the bot has access to tools (filesystem, HTTP, shell, etc.), restrict to known users only. An unrestricted bot lets strangers invoke those tools through the bot.

Troubleshooting

ModuleNotFoundError: No module named 'telegram'

The optional dependency is not installed. Run:

uv sync --extra telegram
# or
pip install initrunner[telegram]

Env var TELEGRAM_BOT_TOKEN not set

Export the token before starting the daemon:

export TELEGRAM_BOT_TOKEN="your-token-here"

Bot ignores messages

Only plain text messages are processed. /start, /help, and other slash commands are filtered out. Make sure you're sending a regular text message.

On this page