Skip to content

Telegram, Discord & Slack

The three primary messaging platforms with streaming modes, reactions, and history logging.

Telegram, Discord, and Slack are the three messaging platforms most Revka operators reach for. All three are real-time, need no public inbound port (each pulls messages over an outbound connection, so they work behind NAT and on a Raspberry Pi), and ship in the default build with no extra Cargo features. This page is the per-platform reference: configuration keys, streaming modes, acknowledgement reactions, the Discord history-logging variant, and Slack permalink expansion.

Every channel lives under [channels_config] in ~/.revka/config.toml and runs inside revka daemon. If you have not connected a channel before, read Connect a messaging channel for the allowlist model and the polling-vs-webhook distinction, and see the Channels overview for the shared trait model. For a guided, step-by-step Telegram walkthrough (including the /bind pairing flow), see Set up Telegram & Matrix.

Telegram and Discord share the stream_mode key (a StreamMode enum). Slack uses a boolean stream_drafts instead, because it implements draft updates but not multi-message splitting.

ModeValueBehaviorPlatforms
Off"off"Send the complete response as a single message once generation finishes. Default.Telegram, Discord
Partial"partial"Post an editable draft and live-edit it as tokens arrive, then finalize with the full text. Throttled by draft_update_interval_ms.Telegram, Discord, Slack (stream_drafts = true)
Multi-message"multi_message"Deliver the response as separate messages split on paragraph boundaries (\n\n), spaced by multi_message_delay_ms. Never splits a code fence.Discord

Acknowledgement (ack) reactions tell a sender their message was received and is being worked on, before any reply text exists. They are controlled by a global switch under [channels_config]:

[channels_config]
ack_reactions = true # default: add 👀 on receipt, ✅/⚠️ on completion
show_tool_calls = false # default: don't forward per-tool 🔧 notices to chat
KeyTypeDefaultMeaning
ack_reactionsbooltrueAdd a 👀 reaction on receipt and ✅ / ⚠️ on completion to incoming channel messages.
show_tool_callsboolfalseForward per-tool notices (e.g. 🔧 web_search_tool: …) to channel users. When false, tool calls are still logged server-side.

Telegram layers its own behavior on top: when it starts working on a message it adds a randomized emoji reaction drawn from ⚡️, 👌, 👀, 🔥, 👍 via setMessageReaction. Telegram also accepts a per-channel ack_reactions override (see below) that takes precedence over the global value.

Telegram is the fastest channel to stand up: create a bot with @BotFather, paste the token, and you are talking to your agent in minutes. It receives messages via getUpdates long-poll and replies via sendMessage.

[channels_config.telegram]
bot_token = "123456:TELEGRAM-TOKEN" # required, from @BotFather
allowed_users = ["*"] # tighten after testing
stream_mode = "off" # off | partial
draft_update_interval_ms = 1000 # edit throttle for partial mode
mention_only = false # only respond to @mentions in groups
interrupt_on_new_message = false # cancel in-flight reply on a new message
# ack_reactions = true # optional per-channel override of the global setting
# proxy_url = "socks5://127.0.0.1:9050"
# notification_chat_id = "123456789" # target for workflow notify/approval prompts
FieldType / valuesDefaultMeaning
bot_tokenstring (required)Bot API token from @BotFather.
allowed_userslist[] (deny all)Telegram usernames (without @) or numeric user IDs, or "*".
stream_mode"off" | "partial""off"Reply delivery mode.
draft_update_interval_msinteger1000Edit throttle while streaming a draft in partial mode.
mention_onlyboolfalseRequire an @mention before responding in group chats. DMs are always processed.
interrupt_on_new_messageboolfalseCancel the in-flight generation when the same sender messages again in the same chat.
ack_reactionsbool (optional)inherits globalPer-channel override of [channels_config].ack_reactions.
proxy_urlstringPer-channel HTTP/HTTPS/SOCKS5 proxy, overriding the global [proxy].
notification_chat_idstringChat ID for workflow notify steps and approval prompts targeting "telegram".

Add the channel and start the daemon:

Terminal window
revka channel add telegram '{"bot_token":"123456:TELEGRAM-TOKEN","name":"my-bot"}'
revka daemon

Because the allowlist denies everyone by default, a new sender is refused and Revka replies with the exact revka channel bind-telegram <id> command to allowlist them. See Set up Telegram & Matrix for the full /bind pairing flow.

These commands are available to allowed Telegram (and Discord) senders. They are sender-scoped and need no daemon restart:

CommandEffect
/modelsShow available providers and current selection.
/models <provider>Switch provider for this sender’s session.
/modelShow the current model.
/model <model-id>Switch model for this sender’s session.
/newClear this sender’s conversation history and start fresh.

Discord connects over the Gateway WebSocket (op IDENTIFY), receives guild and DM messages in real time, and sends replies via REST. It is the richest of the three for streaming, supporting all three stream_mode values, typing indicators, emoji reactions, and approval-keyword intercept.

[channels_config.discord]
bot_token = "DISCORD-BOT-TOKEN" # required
guild_id = "123456789012345678" # optional: restrict to one guild
allowed_users = ["*"] # tighten after testing
listen_to_bots = false # process messages from other bots
mention_only = false # require an @mention
interrupt_on_new_message = false # cancel in-flight reply on a new message
stream_mode = "multi_message" # off | partial | multi_message
draft_update_interval_ms = 1000 # edit throttle (partial mode)
multi_message_delay_ms = 800 # delay between chunks (multi_message mode)
# proxy_url = "socks5://127.0.0.1:9050"
# notification_channel_id = "123456789012345678"
FieldType / valuesDefaultMeaning
bot_tokenstring (required)Bot token from the Discord Developer Portal.
guild_idstringRestrict the bot to a single guild (server).
allowed_userslist[] (deny all)Discord user IDs, or "*".
listen_to_botsboolfalseProcess messages from other bots. The bot always ignores its own messages.
mention_onlyboolfalseRequire an @mention; other guild messages are ignored.
interrupt_on_new_messageboolfalseCancel the in-flight generation when the same sender messages again in the same channel.
stream_mode"off" | "partial" | "multi_message""off"Reply delivery mode.
draft_update_interval_msinteger1000Edit throttle in partial mode (PATCH /channels/{id}/messages/{id}).
multi_message_delay_msinteger800Delay between paragraph chunks in multi_message mode.
proxy_urlstringPer-channel HTTP/HTTPS/SOCKS5 proxy.
notification_channel_idstringChannel ID for workflow notify steps targeting "discord".

Add and start:

Terminal window
revka channel add discord '{"bot_token":"DISCORD-BOT-TOKEN","name":"my-discord"}'
revka daemon

The Discord History channel is a distinct variant of Discord, configured under its own [channels_config.discord_history] key. It logs all non-bot messages to a dedicated discord.db memory backend while only forwarding @mention messages to the agent — turning Revka into a persistent guild archive that still answers when called. Use it when a team wants a searchable message history alongside the bot.

[channels_config.discord_history]
bot_token = "DISCORD-BOT-TOKEN" # required
guild_id = "123456789012345678" # optional: restrict logging to one guild
allowed_users = ["*"] # empty = allow all (open logging)
channel_ids = [] # channels to watch; empty = all
store_dms = true # persist DM messages to discord.db
respond_to_dms = true # forward DM @mentions to the agent
# proxy_url = "socks5://127.0.0.1:9050"
FieldType / valuesDefaultMeaning
bot_tokenstring (required)Bot token from the Discord Developer Portal.
guild_idstringRestrict logging to a single guild.
allowed_userslist[]allow allDiscord user IDs, or "*". Note: unlike standard Discord, an empty list here means open logging, not deny-all.
channel_idslist[] (watch all)Channel IDs to watch.
store_dmsbooltruePersist DM messages to discord.db.
respond_to_dmsbooltrueForward @mentions received in DMs to the agent.
proxy_urlstringPer-channel proxy.

discord_search tool currently unavailable

Section titled “discord_search tool ”

A discord_search agent tool was designed to query the discord.db history backend — returning messages matching a keyword query, optionally filtered by channel_id or a since/until RFC 3339 time range. The tool code remains in the tree, but it is currently unwired: the SQLite memory backend it depended on was removed, and the tool is registered only when a discord-backed Memory handle is supplied. Until it is reintroduced on top of Kumiho graph memory, the tool will not appear in the agent’s tool list. For persistent, cross-session message recall today, use the Kumiho memory tools instead.

Slack connects through Socket Mode using an app-level token (xapp-...) for real-time events, falling back to conversations.history polling when no app token is present. It supports draft streaming via chat.update, cancel-by-reaction, typing indicators, file attachments, and permalink expansion.

[channels_config.slack]
bot_token = "xoxb-..." # required, bot OAuth token
app_token = "xapp-..." # app-level token for Socket Mode
channel_id = "C1234567890" # single channel; "*" or omit = all accessible
channel_ids = ["C123...", "D456..."] # explicit list; takes precedence over channel_id
allowed_users = ["*"] # tighten after testing
thread_replies = true # reply in the originating thread
mention_only = false # require an @mention in groups; DMs always allowed
use_markdown_blocks = true # use the markdown block type (12k char limit)
stream_drafts = false # progressive draft updates via chat.update
draft_update_interval_ms = 1200 # edit throttle for draft streaming
cancel_reaction = "x" # emoji name (no colons) that cancels an in-flight request
interrupt_on_new_message = false # newer message from same sender cancels and restarts
# proxy_url = "socks5://127.0.0.1:9050"
# notification_channel_id = "C1234567890"
FieldType / valuesDefaultMeaning
bot_tokenstring (required)Bot OAuth token (xoxb-...).
app_tokenstringApp-level token (xapp-...) enabling Socket Mode. Without it, Slack falls back to polling.
channel_idstringSingle channel ID. Omit or use "*" to auto-discover all accessible channels.
channel_idslist[]Explicit channel/DM IDs to watch. Takes precedence over channel_id.
allowed_userslist[] (deny all)Slack user IDs, or "*".
thread_repliesbooltrueReply in the originating thread. When false, replies post to the channel root.
mention_onlyboolfalseRequire an @mention in groups. DMs remain allowed.
use_markdown_blocksboolfalseUse the newer markdown block type (12,000-char limit) instead of section blocks (3,000). Enable only if your workspace supports it.
stream_draftsboolfalseEnable progressive draft streaming via chat.update.
draft_update_interval_msinteger1200Edit throttle while streaming a draft.
cancel_reactionstringEmoji name (no colons), e.g. "x", that cancels an in-flight request when a user reacts with it. Leave unset to disable.
interrupt_on_new_messageboolfalseA newer message from the same sender in the same channel cancels the in-flight request and restarts with preserved history.
proxy_urlstringPer-channel proxy.
notification_channel_idstringChannel ID for workflow notify steps and approval prompts targeting "slack".

Add and start:

Terminal window
revka channel add slack '{"bot_token":"xoxb-...","app_token":"xapp-...","name":"my-slack"}'
revka daemon

When an inbound Slack message contains links to other Slack messages, Revka resolves them so the agent sees the referenced content without a separate tool call. It parses https://<workspace>.slack.com/archives/<channel>/p<timestamp> permalinks and fetches their context:

  • Up to 3 permalinks per message are expanded (deduplicated by channel + timestamp).
  • A thread permalink pulls in surrounding replies, up to the most recent 20; earlier replies are summarized as … N earlier thread messages omitted ….
  • Combined expanded content is capped at 8,000 characters per message.

Expansion is transparent: the resolved Message: or Thread: text is prepended to what the agent reads. This pairs well with the cross-channel Link Enricher for non-Slack URLs.