From eef247b7a4bcbac0ef43d70f6ecbab5a97dc8a9b Mon Sep 17 00:00:00 2001 From: Clawdbot Date: Mon, 2 Feb 2026 16:46:19 +0100 Subject: [PATCH] fix: auto-inject Telegram forum topic threadId in message tool When using Telegram DM topics (forum topics), messages sent via the message tool (media, buttons, etc.) land in General Topic instead of the user's current topic. This happens because Slack has resolveSlackAutoThreadId for auto-threading but Telegram had no equivalent. Add resolveTelegramAutoThreadId that mirrors the Slack pattern: - When channel is telegram and no explicit threadId is provided - Check if toolContext.currentThreadTs (the topic ID) is set - Verify the target matches the originating chat - Inject the threadId into params so the Telegram plugin action handler picks it up for sendMessage/sendMedia The subagent announce path already correctly passes threadId via requesterOrigin (set from agentThreadId in sessions-spawn-tool), so no changes needed there. --- src/infra/outbound/message-action-runner.ts | 39 ++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/infra/outbound/message-action-runner.ts b/src/infra/outbound/message-action-runner.ts index e60a01e87..f75f94246 100644 --- a/src/infra/outbound/message-action-runner.ts +++ b/src/infra/outbound/message-action-runner.ts @@ -20,6 +20,7 @@ import { parseReplyDirectives } from "../../auto-reply/reply/reply-directives.js import { dispatchChannelMessageAction } from "../../channels/plugins/message-actions.js"; import { extensionForMime } from "../../media/mime.js"; import { parseSlackTarget } from "../../slack/targets.js"; +import { parseTelegramTarget } from "../../telegram/targets.js"; import { isDeliverableMessageChannel, normalizeMessageChannel, @@ -244,6 +245,32 @@ function resolveSlackAutoThreadId(params: { return context.currentThreadTs; } +/** + * Auto-inject Telegram forum topic thread ID when the message tool targets + * the same chat the session originated from. Mirrors the Slack auto-threading + * pattern so media, buttons, and other tool-sent messages land in the correct + * topic instead of the General Topic. + */ +function resolveTelegramAutoThreadId(params: { + to: string; + toolContext?: ChannelThreadingToolContext; +}): string | undefined { + const context = params.toolContext; + if (!context?.currentThreadTs || !context.currentChannelId) { + return undefined; + } + // Parse both targets to extract base chat IDs, ignoring topic suffixes and + // internal prefixes (e.g. "telegram:group:123:topic:456" → "123"). + // This mirrors Slack's parseSlackTarget approach — compare canonical chat IDs + // so auto-threading applies even when representations differ. + const parsedTo = parseTelegramTarget(params.to); + const parsedChannel = parseTelegramTarget(context.currentChannelId); + if (parsedTo.chatId.toLowerCase() !== parsedChannel.chatId.toLowerCase()) { + return undefined; + } + return context.currentThreadTs; +} + function resolveAttachmentMaxBytes(params: { cfg: OpenClawConfig; channel: ChannelId; @@ -792,6 +819,16 @@ async function handleSendAction(ctx: ResolvedActionContext): Promise