























@@ -0,0 +1,147 @@
1+import { describe, expect, it } from "vitest";
2+import { buildTelegramMessageContextForTest } from "./bot-message-context.test-harness.js";
3+import { TELEGRAM_FORUM_SERVICE_FIELDS } from "./forum-service-message.js";
4+5+describe("buildTelegramMessageContext implicitMention forum service messages", () => {
6+/**
7+ * Build a group message context where the user sends a message inside a
8+ * forum topic that has `reply_to_message` pointing to a message from the
9+ * bot. Callers control whether the reply target looks like a forum service
10+ * message (carries `forum_topic_created` etc.) or a real bot reply.
11+ */
12+async function buildGroupReplyCtx(params: {
13+replyToMessageText?: string;
14+replyToMessageCaption?: string;
15+replyFromIsBot?: boolean;
16+replyFromId?: number;
17+/** Extra fields on reply_to_message (e.g. forum_topic_created). */
18+replyToMessageExtra?: Record<string, unknown>;
19+}) {
20+const BOT_ID = 7; // matches test harness primaryCtx.me.id
21+return await buildTelegramMessageContextForTest({
22+message: {
23+message_id: 100,
24+chat: { id: -1001234567890, type: "supergroup", title: "Forum Group" },
25+date: 1700000000,
26+text: "hello everyone",
27+from: { id: 42, first_name: "Alice" },
28+reply_to_message: {
29+message_id: 1,
30+text: params.replyToMessageText ?? undefined,
31+ ...(params.replyToMessageCaption != null
32+ ? { caption: params.replyToMessageCaption }
33+ : {}),
34+from: {
35+id: params.replyFromId ?? BOT_ID,
36+first_name: "OpenClaw",
37+is_bot: params.replyFromIsBot ?? true,
38+},
39+ ...params.replyToMessageExtra,
40+},
41+},
42+resolveGroupActivation: () => true,
43+resolveGroupRequireMention: () => true,
44+resolveTelegramGroupConfig: () => ({
45+groupConfig: { requireMention: true },
46+topicConfig: undefined,
47+}),
48+});
49+}
50+51+it("does NOT trigger implicitMention for forum_topic_created service message", async () => {
52+// Bot auto-generated "Topic created" message carries forum_topic_created.
53+const ctx = await buildGroupReplyCtx({
54+replyToMessageText: undefined,
55+replyFromIsBot: true,
56+replyToMessageExtra: {
57+forum_topic_created: { name: "New Topic", icon_color: 0x6fb9f0 },
58+},
59+});
60+61+// With requireMention and no explicit @mention, the message should be
62+// skipped (null) because implicitMention should NOT fire.
63+expect(ctx).toBeNull();
64+});
65+66+it.each(TELEGRAM_FORUM_SERVICE_FIELDS)(
67+"does NOT trigger implicitMention for %s service message",
68+async (field) => {
69+const ctx = await buildGroupReplyCtx({
70+replyToMessageText: undefined,
71+replyFromIsBot: true,
72+replyToMessageExtra: { [field]: {} },
73+});
74+75+expect(ctx).toBeNull();
76+},
77+);
78+79+it("does NOT trigger implicitMention for forum_topic_closed service message", async () => {
80+const ctx = await buildGroupReplyCtx({
81+replyToMessageText: undefined,
82+replyFromIsBot: true,
83+replyToMessageExtra: { forum_topic_closed: {} },
84+});
85+86+expect(ctx).toBeNull();
87+});
88+89+it("does NOT trigger implicitMention for general_forum_topic_hidden service message", async () => {
90+const ctx = await buildGroupReplyCtx({
91+replyToMessageText: undefined,
92+replyFromIsBot: true,
93+replyToMessageExtra: { general_forum_topic_hidden: {} },
94+});
95+96+expect(ctx).toBeNull();
97+});
98+99+it("DOES trigger implicitMention for real bot replies (non-empty text)", async () => {
100+const ctx = await buildGroupReplyCtx({
101+replyToMessageText: "Here is my answer",
102+replyFromIsBot: true,
103+});
104+105+// Real bot reply → implicitMention fires → message is NOT skipped.
106+expect(ctx).not.toBeNull();
107+expect(ctx?.ctxPayload?.WasMentioned).toBe(true);
108+});
109+110+it("DOES trigger implicitMention for bot media messages with caption", async () => {
111+// Media messages from the bot have caption but no text — they should
112+// still count as real bot replies, not service messages.
113+const ctx = await buildGroupReplyCtx({
114+replyToMessageText: undefined,
115+replyToMessageCaption: "Check out this image",
116+replyFromIsBot: true,
117+});
118+119+expect(ctx).not.toBeNull();
120+expect(ctx?.ctxPayload?.WasMentioned).toBe(true);
121+});
122+123+it("DOES trigger implicitMention for bot sticker/voice (no text, no caption, no service field)", async () => {
124+// Stickers, voice notes, and captionless photos have neither text nor
125+// caption, but they are NOT service messages — they are legitimate bot
126+// replies that should trigger implicitMention.
127+const ctx = await buildGroupReplyCtx({
128+replyToMessageText: undefined,
129+replyFromIsBot: true,
130+// No forum_topic_* fields → not a service message
131+});
132+133+expect(ctx).not.toBeNull();
134+expect(ctx?.ctxPayload?.WasMentioned).toBe(true);
135+});
136+137+it("does NOT trigger implicitMention when reply is from a different user", async () => {
138+const ctx = await buildGroupReplyCtx({
139+replyToMessageText: "some message",
140+replyFromIsBot: false,
141+replyFromId: 999,
142+});
143+144+// Different user's message → not an implicit mention → skipped.
145+expect(ctx).toBeNull();
146+});
147+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。