Prerequisites
- Comis installed and running (Quickstart)
- A Telegram account
Setup
Create a bot with BotFather
/newbot and follow the prompts:- Choose a display name for your bot (for example, MyTeamBot)
- Choose a username ending in
bot(for example,my_team_comis_bot)
Copy the bot token
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11.
Copy it immediately and store it somewhere safe.This token is your bot’s password. Anyone who has it can control your bot.Configure Comis
~/.comis/config.yaml):TELEGRAM_BOT_TOKEN environment variable in your ~/.comis/.env file:Restart and verify
Configuration
These options go underchannels.telegram in your config.yaml:
| Option | Type | Default | What it does |
|---|---|---|---|
enabled | boolean | false | Turns on the Telegram adapter |
botToken | string / SecretRef | (required) | Bot authentication token from BotFather (env: TELEGRAM_BOT_TOKEN) |
webhookUrl | string | (optional) | Public webhook URL. When set, Telegram pushes updates over HTTPS instead of long-polling. |
allowFrom | string[] | [] (all) | Restrict to specific Telegram user IDs. Empty means all users can talk to the bot. |
mediaProcessing.transcribeAudio | boolean | true | Transcribe voice messages to text before passing to the agent |
mediaProcessing.analyzeImages | boolean | true | Describe images sent to the bot using AI vision |
mediaProcessing.describeVideos | boolean | true | Extract descriptions from video content |
mediaProcessing.extractDocuments | boolean | true | Extract text from PDFs, DOCX, and other documents |
mediaProcessing.understandLinks | boolean | true | Fetch and summarize URLs included in messages |
TELEGRAM_WEBHOOK_SECRET in your .env if you switch to
webhook delivery — Comis uses it to verify the X-Telegram-Bot-Api-Secret-Token
header on incoming requests.
What your agent can do
Once connected to Telegram, your agent can:- Send, edit, and delete messages in any chat it has access to
- React to messages with emoji (Telegram limits the supported emoji set)
- Send inline buttons for user choices
- Send rich cards with formatted content
- Use HTML formatting (bold, italic, code, links, spoilers, blockquotes, custom emoji)
- Apply message effects (spoiler reveal, silent send)
- Send file attachments up to 50 MB
- Send native voice messages (audio attachments uploaded as Telegram voice notes)
- Stream responses with live editing (updates every 300 ms)
- Mention users in group chats
- Send native polls and stickers via platform actions (
poll,sticker) - Receive location and venue messages — coordinates appear in the agent’s message metadata so the agent can react to “I’m at the office now”
- Pin and unpin messages, set chat title and description, ban or promote members, and manage forum topics (create, edit, close, reopen)
Platform limits
| Limit | Value | What Comis does about it |
|---|---|---|
| Message length | 4,096 characters | Automatically splits long responses into multiple messages at paragraph boundaries |
| Attachment size | 50 MB | Files over 50 MB are rejected with a user-friendly error message |
Groups
Telegram groups (and supergroups) are multi-party chats where many users talk together. The bot is just another member, and Telegram applies an extra filter - privacy mode - that determines which messages the bot can see at all. The Comis activation policy then decides which of those messages should trigger a reply. Both layers matter; misconfiguring either one results in the bot looking “broken.”How addressing works (default)
Out of the box, your agent activates in a group when any of these is true:- The message contains
@<your_bot_username>parsed as a Telegram mention entity - The message is a reply to one of the bot’s previous messages
- The message is a slash command targeted at the bot:
/cmd@<your_bot_username>
mention-gated activation mode and is the default for autoReplyEngine.groupActivation.
Setting up a Telegram group
Complete the base Setup section first
Add the bot to your group
@<your_bot_username> → tap the bot → Add. The bot does
not need to be an administrator unless you turn off privacy mode
(next step) or your agent uses platform actions like ban/promote/pin
that require admin rights.(Recommended) Disable BotFather privacy mode
groupActivation: "always" or "custom",
or when your agent passively monitors group chatter. If you only need
mention-gated behavior, skip this step and leave privacy on.See Disable BotFather privacy mode
immediately below for the full procedure (including the mandatory
kick-and-readd step).Restrict who can talk to the bot (optional)
allowFrom
in ~/.comis/config.yaml:allowFrom is sender-keyed, not chat-keyed. Adding the group’s chat ID
to allowFrom does not whitelist the group - every individual user
you want to enable must be listed by their Telegram user ID. To find a
user’s ID, ask them to send any message in the group while you tail
pm2 logs comis (the senderId field is logged on every inbound).
Leave allowFrom: [] (or omit it) to allow all users.Restart the daemon and verify
@<your_bot_username> hello (when typing @, tap the suggestion popup
so Telegram parses it as a mention entity - typing the text alone is not
enough). The bot should reply within seconds.Disable BotFather privacy mode
Permanently disable privacy mode so the bot sees all group messages reliably.Toggle the setting in BotFather
/mybots → select @<your_bot_username> → Bot Settings → Group Privacy → Turn offBotFather confirms Privacy mode is disabled for <bot>.Kick and re-add the bot in every existing group
- Open the group → tap the group name → Members
- Find the bot → swipe (iOS) or long-press (Android) → Remove
- Re-add the bot via Add Members →
@<your_bot_username>→ Add
Group activation modes
Comis decides whether to activate the agent based on theautoReplyEngine.groupActivation config (top level of config.yaml):
| Mode | When the agent activates | Privacy mode requirement |
|---|---|---|
mention-gated (default) | Message @-mentions the bot, replies to it, or uses /cmd@<botname> | Privacy on is fine |
always | Every group message | Privacy must be off |
custom | Message text matches one of customPatterns (regex) | Privacy must be off |
historyInjection: true silently records non-trigger group messages into a
ring buffer per session, so when the agent does activate, it has the recent
context. Set to false to drop them entirely.
custom example - wake the agent on either a regex trigger or any question:
Routing different groups to different agents
If you want a single bot to serve multiple agents - one per group, or one per user - use top-levelrouting bindings. The
Multi-agent routing section below has the full schema
and examples.
Finding a group’s chat ID
You’ll need the group’s numeric chat ID (looks like-1001234567890 or
-5152588116) for routing bindings, log filters, or sending posts
programmatically.
-100).
Channels
Telegram channels are one-way broadcast feeds: one or more administrators post; subscribers read. Channels are not chats - there are no member messages to respond to. Use a channel when you want the agent to publish status updates, daily summaries, monitoring alerts, or any kind of scheduled feed. Use a group instead if you want a conversational bot.Setting up a Telegram channel
Create the channel in Telegram
t.me/<handle> link, anyone can find it) or
Private (invite-only via link).Add the bot as an administrator
@<your_bot_username> → tap to
select. Grant at minimum Post Messages. For richer behavior also
enable Edit Messages, Delete Messages, and (if your agent
manages subscribers) Add Subscribers. Save.Find the channel's chat ID
-1001234567890.Have the agent post into the channel
message skill), background tasks, or
the gateway HTTP API. Use the channel’s numeric chat ID as the
target:channelType: "telegram" and
channelId: "<channel-id>".Inbound from channels
Telegram emitschannel_post updates for messages authored in the channel.
Comis normalizes these the same way as group messages: chatType: "channel"
in the prompt context, telegramChatType: "channel" in metadata. The auto-reply
gate also applies, so if you want the agent to react to channel posts (rare -
usually channels are write-only from the agent’s perspective), set
groupActivation: "always" or "custom" and target the channel’s chat ID
in your routing bindings.
Multi-agent routing
A single Telegram bot can serve multiple agents. Comis resolves which agent handles a given message via the top-levelrouting config, with bindings
ranked by specificity (peerId 8 > channelId 4 > guildId 2 > channelType 1).
mention-gated and allowFrom apply per resolved agent - first the bot
receives the message, then the router picks the agent, then the agent’s gate
runs. Multi-agent setups don’t change addressing rules; they just change which
agent answers.
Routing to separate agents is also how you isolate memory between people:
each agent keeps its own long-term memory, secrets, and budget, so two users on
different agents never share learned facts. For the full multi-user model - what
is private by default and what isn’t - see
Multiple users and teams.
If you need a literally separate bot identity per agent (different
@username, different command menus), run one Comis daemon per bot:
channels.telegram entry per daemon, so
multi-bot in a single daemon is not yet supported.
Forum Topics
Telegram supergroups can enable forum mode, which organizes conversations into topics — similar to channels within a Discord server. Each topic is a separate conversation thread with its own name, icon, and message history. When forum mode is enabled, your agent automatically detects which topic a message came from and routes its replies to the same topic.Setting up forum topics
Enable forum mode in your Telegram group
Make the bot an administrator
How it works
When a user sends a message in a forum topic:- Comis detects the topic and attaches thread metadata to the message
- The agent processes the message as usual
- All replies — including multi-part responses, media, and voice — go to the same topic
- Typing indicators appear in the correct topic
General Topic
Every forum group has a General topic that exists by default. Comis handles the General Topic automatically — no special configuration is needed. Messages sent to the General Topic work the same as messages in any other topic.Complete walkthrough: from zero to first reply
Here is a top-to-bottom run for a brand-new bot called MyTeamBot:Create the bot
/newbot, type MyTeamBot
as the display name, then my_team_comis_bot as the username.
BotFather replies with a token like
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11.Restart the daemon and verify
{"level":30,"module":"channel-telegram","msg":"Telegram adapter connected"}.Troubleshooting
The fastest diagnostic for “bot didn’t reply in a group” is to tail the daemon log while you send the test message:- No
Inbound messagelog at all → Telegram never forwarded the message to the bot. This is a Telegram-side filter (privacy mode, bot not in group, mention not parsed as an entity). See the first accordion below. Inbound messagefollowed byGroup message did not activate agent→ Comis received the message but the activation policy suppressed it. The log line includesisBotMentioned,replyToBot, andactivationModeso you can see exactly why. See the second accordion.Inbound message→autoreply:activated→Execution complete→Delivery complete→ Working as designed. If the user still doesn’t see a reply, see the delivery accordion.
No 'Inbound message' log at all (Telegram-side filter)
No 'Inbound message' log at all (Telegram-side filter)
- Privacy mode is on AND the message wasn’t a recognized mention,
reply, or
/cmd@<botname>. With privacy on, Telegram filters every other message before it reaches the bot. Typing@<your_bot_username>as plain text is not enough - your Telegram client must parse it as a mention entity at send time (look for the in-app suggestion popup when you type the@). - The bot isn’t actually in the group. Check the member list. Re-add it via the group’s Add Members flow.
- The bot was added to the group while privacy mode was on, then privacy was disabled. The flag is cached at join time - kick and re-add the bot. See Disable BotFather privacy mode.
"can_read_all_group_messages": false means privacy is on (the
BotFather default).Quick test that bypasses privacy mode entirely: send
/start@<your_bot_username> in the group. /cmd@<botname> always
reaches the bot regardless of privacy. If that fires the agent, your
code path is fine and the issue was Telegram-side filtering on the
earlier test.Inbound logged, then 'Group message did not activate agent'
Inbound logged, then 'Group message did not activate agent'
activationMode: "mention-gated"and the message didn’t@-mention the bot. Either type@<your_bot_username>properly (tap the suggestion) or switch togroupActivation: "always"(privacy must be off - see the Groups section).- The message was an edit. Telegram doesn’t always re-emit
mentionentities for edits; send a fresh message instead. - Your activation policy expects a
customregex that didn’t match. Inspect the log’sreasonfield.
Inbound logged, but sender is blocked by allowFrom
Inbound logged, but sender is blocked by allowFrom
channels.telegram.allowFrom.
You’ll see this log line:allowFrom, or remove
allowFrom (or set it to []) to allow all senders. allowFrom
only matches sender IDs - putting a group’s chat ID there does
nothing.Bot token rejected
Bot token rejected
/token to @BotFather, and select
your bot to regenerate the token. Update the token in your ~/.comis/.env
file and restart the daemon.Bot replies in DM but not in groups, even after disabling privacy
Bot replies in DM but not in groups, even after disabling privacy
Messages take a long time to arrive
Messages take a long time to arrive
