Rebased and landed contributor work from @chengzhichao-xydt for the
Telegram multi-account DM regression in #32351.
Co-authored-by: Zhichao Cheng <cheng.zhichao@xydigit.com>
* fix(telegram): resolve session entry for /stop in forum topics
Fixes#38675
- Export normalizeStoreSessionKey from store.ts for reuse
- Use it in resolveSessionEntryForKey so topic session keys (lowercase
in store) are found when handling /stop
- Add test for forum topic session key lookup
* fix(telegram): share native topic routing with inbound messages
* fix: land telegram topic routing follow-up (#38871)
---------
Co-authored-by: xialonglee <li.xialong@xydigit.com>
- Export pickFirstExistingAgentId and use it to validate topic agentId
- Properly update mainSessionKey when overriding route agent
- Fix docs example showing incorrect session key for topic 3
Fixes issue where non-existent agentId would create orphaned sessions.
Fixes issue where DM topic replies would route to wrong agent.
This feature allows different topics within a Telegram forum supergroup to route
to different agents, each with isolated workspace, memory, and sessions.
Key changes:
- Add agentId field to TelegramTopicConfig type for per-topic routing
- Add zod validation for agentId in topic config schema
- Implement routing logic to re-derive session key with topic's agent
- Add debug logging for topic agent overrides
- Add unit tests for routing behavior (forum topics + DM topics)
- Add config validation tests
- Document feature in docs/channels/telegram.md
This builds on the approach from PR #31513 by @Sid-Qin with additional fixes
for security (preserved account fail-closed guard) and test coverage.
Closes#31473
Stickers, voice notes, and captionless photos from the bot also lack
text and caption fields, so the previous check incorrectly classified
them as system messages and suppressed implicitMention.
Switch to checking for Telegram's forum_topic_* / general_forum_topic_*
service-message fields which only appear on actual service messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address Greptile review feedback: bot media messages (photo/video) use
caption instead of text, so they would be incorrectly classified as
system messages. Add !caption guard to the system message check.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a Telegram Forum topic is created by the bot, Telegram generates a
system message with from.id=botId and empty text. Every subsequent user
message in that topic has reply_to_message pointing to this system
message, causing the implicitMention check to fire and bypassing
requireMention for every single message.
Add a guard that recognises system messages (is_bot=true with no text)
and excludes them from implicit mention detection, so that only genuine
replies to bot messages trigger the bypass.
Closes#32256
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add shared status reaction controller
* feat: add statusReactions config schema
* feat: wire status reactions for Discord and Telegram
* fix: restore original 10s/30s stall defaults for Discord compatibility
* Status reactions: fix stall timers and gating
* Format status reaction imports
---------
Co-authored-by: Matt <mateus.carniatto@gmail.com>
The preflight transcription condition only triggered for group chats
(isGroup && requireMention), so voice notes sent in direct messages
were never transcribed — they arrived as raw <media:audio> placeholders.
This patch widens the condition to fire whenever there is audio and no
accompanying text, regardless of chat type.
It also adds a fallback path: if the standard media pipeline returns no
transcript (e.g. format mismatch, missing config), OpenClaw now calls
the configured whisper CLI command directly with the audio file, using
the same {{MediaPath}}/{{OutputBase}} template variables from config.
Co-Authored-By: TH <tzhsn.huang@gmail.com>
- Move hasAudio detection before bodyText building
- Move preflight transcription before bodyText building
- If audio has transcript, use transcript as bodyText
- Otherwise use <media:audio> placeholder
Fixes#16772: Telegram voice messages leak raw audio binary into chat context
Co-authored-by: Limitless2023 <limitless@users.noreply.github.com>
* fix(pairing): show actual code in approval command instead of placeholder
The pairing reply shown to new users included the approval command with
a literal '<code>' placeholder. Users had to manually copy the code
from one line and substitute it into the command.
Now shows the ready-to-copy command with the real pairing code:
Before: openclaw pairing approve telegram <code>
After: openclaw pairing approve telegram abc123
Fixed in both the shared pairing message builder and the Telegram
inline pairing reply.
* test(pairing): update test to expect actual code instead of placeholder
---------
Co-authored-by: Echo Ito <echoito@MacBook-Air.local>