diff --git a/CHANGELOG.md b/CHANGELOG.md index 1781d04a5e3..02a66d73e8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -131,6 +131,7 @@ Docs: https://docs.openclaw.ai - Synology Chat/gateway lifecycle: keep `startAccount` pending until abort for inactive and active account paths to prevent webhook route restart loops under gateway supervision. (#23074) Thanks @druide67. - Discord/dispatch + Slack formatting: restore parallel outbound dispatch across Discord channels with per-channel queues while preserving in-channel ordering, and run Slack preview/stream update text through mrkdwn normalization for consistent formatting. (#31927) Thanks @Sid-Qin. - Telegram/inbound media filenames: preserve original `file_name` metadata for document/audio/video/animation downloads (with fetch/path fallbacks), so saved inbound attachments keep sender-provided names instead of opaque Telegram file paths. (#31837) Thanks @Kay-051. +- Telegram/implicit mention forum handling: exclude Telegram forum system service messages (`forum_topic_*`, `general_forum_topic_*`) from reply-chain implicit mention detection so `requireMention` does not get bypassed inside bot-created topic lifecycle events. (#32262) Thanks @scoootscooob. - Telegram/models picker callbacks: keep long model buttons selectable by falling back to compact callback payloads and resolving provider ids on selection (with provider re-prompt on ambiguity), avoiding Telegram 64-byte callback truncation failures. (#31857) Thanks @bmendonca3. - WhatsApp/inbound self-message context: propagate inbound `fromMe` through the web inbox pipeline and annotate direct self messages as `(self)` in envelopes so agents can distinguish owner-authored turns from contact turns. (#32167) Thanks @scoootscooob. - Slack/thread context payloads: only inject thread starter/history text on first thread turn for new sessions while preserving thread metadata, reducing repeated context-token bloat on long-lived thread sessions. (#32133) Thanks @sourman. diff --git a/src/telegram/bot-message-context.implicit-mention.test.ts b/src/telegram/bot-message-context.implicit-mention.test.ts index a5dc9862ede..c6ece03108b 100644 --- a/src/telegram/bot-message-context.implicit-mention.test.ts +++ b/src/telegram/bot-message-context.implicit-mention.test.ts @@ -2,6 +2,15 @@ import { describe, expect, it } from "vitest"; import { buildTelegramMessageContextForTest } from "./bot-message-context.test-harness.js"; describe("buildTelegramMessageContext implicitMention forum service messages", () => { + const TELEGRAM_FORUM_SERVICE_FIELDS = [ + "forum_topic_created", + "forum_topic_edited", + "forum_topic_closed", + "forum_topic_reopened", + "general_forum_topic_hidden", + "general_forum_topic_unhidden", + ] as const; + /** * Build a group message context where the user sends a message inside a * forum topic that has `reply_to_message` pointing to a message from the @@ -62,6 +71,19 @@ describe("buildTelegramMessageContext implicitMention forum service messages", ( expect(ctx).toBeNull(); }); + it.each(TELEGRAM_FORUM_SERVICE_FIELDS)( + "does NOT trigger implicitMention for %s service message", + async (field) => { + const ctx = await buildGroupReplyCtx({ + replyToMessageText: undefined, + replyFromIsBot: true, + replyToMessageExtra: { [field]: {} }, + }); + + expect(ctx).toBeNull(); + }, + ); + it("does NOT trigger implicitMention for forum_topic_closed service message", async () => { const ctx = await buildGroupReplyCtx({ replyToMessageText: undefined, diff --git a/src/telegram/bot-message-context.ts b/src/telegram/bot-message-context.ts index 4879b96821a..536ed711fcc 100644 --- a/src/telegram/bot-message-context.ts +++ b/src/telegram/bot-message-context.ts @@ -888,9 +888,10 @@ const FORUM_SERVICE_FIELDS = [ * `forum_topic_*` / `general_forum_topic_*` fields and should not count as * regular bot replies for implicit-mention purposes. */ -function isTelegramForumServiceMessage(msg: Record | undefined | null): boolean { - if (!msg) { +function isTelegramForumServiceMessage(msg: unknown): boolean { + if (!msg || typeof msg !== "object") { return false; } - return FORUM_SERVICE_FIELDS.some((f) => msg[f] != null); + const record = msg as Record; + return FORUM_SERVICE_FIELDS.some((f) => record[f] != null); }