Telegram bots for your DocsGPT agents. One small binary runs any number of bots, each connected to one or more agents, with the answer experience Telegram now supports for AI bots: live streamed drafts with a Stop button, rich formatted answers (headings, tables, code, math), collapsible sources, file and photo input, voice notes, and files that your agent's tools produce sent back into the chat.
Version 3 runs on docsgpt-rs, the Rust crates shared with the Slack bot. Version 2 was the first Rust version; the Python bot lives on the legacy-python branch and the :1 image tag. See Upgrading to version 3.
- Streaming answers — private chats show the answer as it is generated, with Telegram's own Stop button; groups get a typing indicator and the final message.
- Rich messages — the agent's markdown is rendered natively: headings, lists, tables, code blocks, LaTeX, quotes. Sources are tucked into a collapsible block. Falls back to MarkdownV2, then plain text, if a message can't be rendered.
- Multi-turn memory — every chat (and every forum topic) keeps its own conversation with each agent;
/newstarts over. - Files in — photos and documents are uploaded to DocsGPT as attachments; voice notes and audio are transcribed and asked as questions (optionally answered with a voice note).
- Files out — when the agent runs tools (code execution, document or image generation) the resulting files and images are downloaded and sent to the chat.
- Many bots, many agents — a TOML file declares bots and their agents. Users pick an agent with
/agents,/agent <name>or a one-off#nameprefix. - Groups done properly — answers only when mentioned or replied to (configurable), one conversation per forum topic, private ("ephemeral") replies for
/helpand/agentsso the group isn't cluttered. - Telegram Business — answer customers inside a connected business account's chats.
- Guest mode — answer when mentioned in chats the bot is not a member of.
- Inline mode —
@yourbot question?from any chat. - Feedback — react 👍 or 👎 to an answer and DocsGPT records it for that exact answer; take the reaction back to clear it. The bot shows 👀 while it works.
- Polling or webhooks,
/healthz, structured logs, SQLite by default (or in memory), graceful shutdown that lets answers finish, a small distroless container image for amd64 and arm64.
You need a bot token from @BotFather and an agent API key from DocsGPT (Agents → your agent → API key).
git clone https://github.com/arc53/tg-bot-docsgpt-extenstion.git
cd tg-bot-docsgpt-extenstion
cp .env.example .env # fill in TELEGRAM_BOT_TOKEN and API_KEY
docker compose up -dOr without compose:
docker run -d --name docsgpt-tg --env-file .env -v botdata:/app/data arc53/tg-bot-docsgpt-extenstion:latestcargo build --release
./target/release/docsgpt-telegram --check # validates config and tokens
./target/release/docsgpt-telegramRust 1.88 or newer is required to build.
Same variables as version 1. Put them in .env next to the binary or pass them to the container.
| Variable | Purpose |
|---|---|
TELEGRAM_BOT_TOKEN |
Bot token from BotFather. Required. |
API_KEY |
DocsGPT agent API key for the default agent. |
API_KEY_<NAME> |
Additional agents, addressable as #name or via /agent name. |
API_BASE |
DocsGPT server URL (default https://gptcloud.arc53.com). |
SQLITE_PATH |
SQLite file (default data/docsgpt-telegram.db; /app/data/… in Docker). |
STORAGE_TYPE |
memory to keep state in memory instead of SQLite. |
GROUPS_MODE |
mention (default), all, or off. |
STREAMING |
false to disable live drafts. |
VOICE_REPLIES |
true to answer voice notes with a voice note. |
WELCOME_TEXT |
Text for /start; {name} and {agents} are expanded. |
MENU_BUTTON_URL |
Opens a Mini App (for example the DocsGPT web widget) from the menu button. |
WEBHOOK_PUBLIC_URL, WEBHOOK_SECRET, HTTP_BIND |
Webhook mode (see below). |
RUST_LOG, LOG_FORMAT=json |
Logging. |
Create docsgpt-tg.toml in the working directory (or set DOCSGPT_TG_CONFIG=/path/to/file). ${VAR} references are replaced from the environment, so secrets can stay in .env. See docsgpt-tg.example.toml for every option.
[[bots]]
name = "support"
token = "${TG_TOKEN_SUPPORT}"
[[bots.agents]]
name = "support"
api_key = "${DOCSGPT_KEY_SUPPORT}"
description = "Product and billing questions"
default = true
[[bots.agents]]
name = "sales"
api_key = "${DOCSGPT_KEY_SALES}"
description = "Pricing and plans"
[[bots]]
name = "internal-docs"
token = "${TG_TOKEN_INTERNAL}"
allowed_chats = [-1001234567890]
[[bots.agents]]
name = "docs"
api_key = "${DOCSGPT_KEY_DOCS}"Per-bot options: mode (polling/webhook), groups (mention/all/off), streaming, reactions, attachments, voice_replies, business, guest, inline, allowed_chats, welcome, description, short_description, menu_button_url, max_file_mb, api_base.
In Docker, mount the file: -v ./docsgpt-tg.toml:/app/docsgpt-tg.toml:ro.
DocsGPT keeps the conversation transcript; the bot only stores which conversation each chat is in, the active agent, which message holds which answer (for 👍/👎), and business-connection details.
sqlite(default) — a single file, kept in the/app/datavolume in Docker.memory— lost on restart; fine for trying things out.
Set mode = "webhook" on a bot (or WEBHOOK_PUBLIC_URL in the single-bot layout) and server.public_url. The bot registers <public_url>/webhook/<bot name> with a secret token and serves all webhook bots and GET /healthz on server.bind (default 0.0.0.0:8080). Polling needs no inbound connectivity and is the default.
All of these are toggled in @BotFather under Bot Settings:
- Groups — with Group Privacy on (the default) the bot only sees mentions and replies, which is exactly what
groups = "mention"needs. Turn privacy off only forgroups = "all". - Inline mode — enable it for
@yourbot question?. - Business mode — enable it so business accounts can connect the bot; the bot answers messages from customers (never the account owner's own messages) once the connection grants reply rights.
- Guest mode — enable it so the bot can be mentioned in chats it hasn't joined.
- Topics — enabling topics for the bot gives each private-chat topic its own conversation.
The bot sets its command menu, description and menu button itself at startup (from your config).
- Send a question. In private chats you'll see the answer stream in; press Stop to cut it short.
- Send a photo or document (with an optional caption as the question) — it's uploaded to DocsGPT and the agent answers about it. Albums are handled as one question.
- Send a voice note — it's transcribed and answered.
/newstarts a fresh conversation with the current agent./agentsshows a picker,/agent sales(or a message that is just#sales) switches,#sales what's the price?asks one agent just once.- React 👍 or 👎 to an answer to rate it (in groups, Telegram sends reactions to bots that are admins).
- In groups: mention
@yourbotor reply to one of its messages. Each forum topic keeps its own conversation. - Inline: type
@yourbot how do I reset my password?in any chat and pick the answer.
- MongoDB is no longer supported. Remove
STORAGE_TYPE=mongodband theMONGODB_*variables (orbackend = "mongodb"and itsuri/db_name/collectionkeys), and keep/app/dataon a volume. The bot refuses to start with a Mongo setting and says what to change. Chats continue in new DocsGPT conversations. - SQLite files from version 2 keep working: conversations and chosen agents carry over. Feedback needs to know each answer's position in its conversation, which version 2 didn't count; reactions in those carried-over conversations are ignored until the chat starts a new one (
/new). - Image tags:
:latestand:3are version 3;:2stays on the last version 2 build. - Small behaviour changes:
- A message that is just
#agentswitches the chat's agent. - An answer cut short by an error or Stop ends with a short note saying so.
- Error messages say whether the agent key was rejected, the assistant was busy, or it couldn't be reached.
- Plain-text answers keep
snake_caseand other symbols inside code.
- A message that is just
- The
:1image tag and thelegacy-pythonbranch keep the Python bot. - Your
.envworks as is, apart from MongoDB (see above). Deployments withoutSTORAGE_TYPEpersist to SQLite. - Answers now stream and render as rich messages; the
#agentprefix still works and/agentsis the new way to switch.
cargo test # unit + mock-server end-to-end tests
TELEGRAM_API_URL=http://localhost:8081 ... # point the bot at a local Bot API server or a mock
TG_RATE_LIMITS=off ... # disable outbound pacing (tests only)The DocsGPT client, storage, agent routing and the answer loop come from the docsgpt and docsgpt-bot crates; live tests against a real DocsGPT live there.
Layout:
src/config.rs: TOML and environment config.src/surface.rs: how an answer is drafted and delivered in Telegram.src/handlers/: messages, commands, attachments, business, guest, inline, callbacks, reactions.src/telegram/: the API wrapper, rendering, rate limits, polling and webhooks, and raw params for the newest Bot API fields.
MIT — see LICENSE.