mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(telegram): exclude forum topic system messages from implicitMention
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>
This commit is contained in:
committed by
Peter Steinberger
parent
bb60687b89
commit
8fdd1d2f05
98
src/telegram/bot-message-context.implicit-mention.test.ts
Normal file
98
src/telegram/bot-message-context.implicit-mention.test.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildTelegramMessageContextForTest } from "./bot-message-context.test-harness.js";
|
||||
|
||||
describe("buildTelegramMessageContext implicitMention forum system messages", () => {
|
||||
/**
|
||||
* 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
|
||||
* bot. Callers control whether the reply target looks like a system
|
||||
* message (empty text) or a real bot reply (non-empty text).
|
||||
*/
|
||||
async function buildGroupReplyCtx(params: {
|
||||
replyToMessageText?: string;
|
||||
replyFromIsBot?: boolean;
|
||||
replyFromId?: number;
|
||||
}) {
|
||||
const BOT_ID = 7; // matches test harness primaryCtx.me.id
|
||||
return await buildTelegramMessageContextForTest({
|
||||
message: {
|
||||
message_id: 100,
|
||||
chat: { id: -1001234567890, type: "supergroup", title: "Forum Group" },
|
||||
date: 1700000000,
|
||||
text: "hello everyone",
|
||||
from: { id: 42, first_name: "Alice" },
|
||||
reply_to_message: {
|
||||
message_id: 1,
|
||||
text: params.replyToMessageText ?? undefined,
|
||||
from: {
|
||||
id: params.replyFromId ?? BOT_ID,
|
||||
first_name: "OpenClaw",
|
||||
is_bot: params.replyFromIsBot ?? true,
|
||||
},
|
||||
},
|
||||
},
|
||||
resolveGroupActivation: () => true,
|
||||
resolveGroupRequireMention: () => true,
|
||||
resolveTelegramGroupConfig: () => ({
|
||||
groupConfig: { requireMention: true },
|
||||
topicConfig: undefined,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
it("does NOT trigger implicitMention for forum topic system messages (empty-text bot message)", async () => {
|
||||
// System message: bot created the topic → text is empty, from.is_bot = true
|
||||
const ctx = await buildGroupReplyCtx({
|
||||
replyToMessageText: undefined,
|
||||
replyFromIsBot: true,
|
||||
});
|
||||
|
||||
// With requireMention and no explicit @mention, the message should be
|
||||
// skipped (null) because implicitMention should NOT fire.
|
||||
expect(ctx).toBeNull();
|
||||
});
|
||||
|
||||
it("does NOT trigger implicitMention for empty-string text system messages", async () => {
|
||||
const ctx = await buildGroupReplyCtx({
|
||||
replyToMessageText: "",
|
||||
replyFromIsBot: true,
|
||||
});
|
||||
|
||||
expect(ctx).toBeNull();
|
||||
});
|
||||
|
||||
it("DOES trigger implicitMention for real bot replies (non-empty text)", async () => {
|
||||
const ctx = await buildGroupReplyCtx({
|
||||
replyToMessageText: "Here is my answer",
|
||||
replyFromIsBot: true,
|
||||
});
|
||||
|
||||
// Real bot reply → implicitMention fires → message is NOT skipped.
|
||||
expect(ctx).not.toBeNull();
|
||||
expect(ctx?.ctxPayload?.WasMentioned).toBe(true);
|
||||
});
|
||||
|
||||
it("DOES trigger implicitMention for bot reply with whitespace-only text", async () => {
|
||||
// A bot message that has actual whitespace text is NOT a system message,
|
||||
// so it should still count as an implicit mention. (Telegram's forum
|
||||
// system messages have undefined / empty text, not whitespace.)
|
||||
const ctx = await buildGroupReplyCtx({
|
||||
replyToMessageText: " ",
|
||||
replyFromIsBot: true,
|
||||
});
|
||||
|
||||
expect(ctx).not.toBeNull();
|
||||
expect(ctx?.ctxPayload?.WasMentioned).toBe(true);
|
||||
});
|
||||
|
||||
it("does NOT trigger implicitMention when reply is from a different user", async () => {
|
||||
const ctx = await buildGroupReplyCtx({
|
||||
replyToMessageText: "some message",
|
||||
replyFromIsBot: false,
|
||||
replyFromId: 999,
|
||||
});
|
||||
|
||||
// Different user's message → not an implicit mention → skipped.
|
||||
expect(ctx).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -471,9 +471,15 @@ export const buildTelegramMessageContext = async ({
|
||||
return null;
|
||||
}
|
||||
// Reply-chain detection: replying to a bot message acts like an implicit mention.
|
||||
// Exclude forum-topic system messages (auto-generated "Topic created" messages by the
|
||||
// bot that have empty text) so that every message inside a bot-created topic does not
|
||||
// incorrectly bypass requireMention (#32256).
|
||||
const botId = primaryCtx.me?.id;
|
||||
const replyFromId = msg.reply_to_message?.from?.id;
|
||||
const implicitMention = botId != null && replyFromId === botId;
|
||||
const replyToBotMessage = botId != null && replyFromId === botId;
|
||||
const isReplyToSystemMessage =
|
||||
replyToBotMessage && msg.reply_to_message?.from?.is_bot === true && !msg.reply_to_message?.text;
|
||||
const implicitMention = replyToBotMessage && !isReplyToSystemMessage;
|
||||
const canDetectMention = Boolean(botUsername) || mentionRegexes.length > 0;
|
||||
const mentionGate = resolveMentionGatingWithBypass({
|
||||
isGroup,
|
||||
|
||||
Reference in New Issue
Block a user