mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-21 16:41:56 +00:00
fix(feishu): fix mention detection for post messages with embedded docs
Parse "at" elements from post content when message.mentions is empty to detect bot mentions in rich text messages containing documents.
This commit is contained in:
committed by
Peter Steinberger
parent
cd4f7524e3
commit
c315246971
@@ -61,4 +61,46 @@ describe("parseFeishuMessageEvent – mentionedBot", () => {
|
||||
const ctx = parseFeishuMessageEvent(event as any, "");
|
||||
expect(ctx.mentionedBot).toBe(false);
|
||||
});
|
||||
|
||||
it("returns mentionedBot=true for post message with at (no top-level mentions)", () => {
|
||||
const BOT_OPEN_ID = "ou_bot_123";
|
||||
const postContent = JSON.stringify({
|
||||
content: [
|
||||
[{ tag: "at", user_id: BOT_OPEN_ID, user_name: "claw" }],
|
||||
[{ tag: "text", text: "What does this document say" }],
|
||||
],
|
||||
});
|
||||
const event = {
|
||||
sender: { sender_id: { user_id: "u1", open_id: "ou_sender" } },
|
||||
message: {
|
||||
message_id: "msg_1",
|
||||
chat_id: "oc_chat1",
|
||||
chat_type: "group",
|
||||
message_type: "post",
|
||||
content: postContent,
|
||||
mentions: [],
|
||||
},
|
||||
};
|
||||
const ctx = parseFeishuMessageEvent(event as any, BOT_OPEN_ID);
|
||||
expect(ctx.mentionedBot).toBe(true);
|
||||
});
|
||||
|
||||
it("returns mentionedBot=false for post message with no at", () => {
|
||||
const postContent = JSON.stringify({
|
||||
content: [[{ tag: "text", text: "hello" }]],
|
||||
});
|
||||
const event = {
|
||||
sender: { sender_id: { user_id: "u1", open_id: "ou_sender" } },
|
||||
message: {
|
||||
message_id: "msg_1",
|
||||
chat_id: "oc_chat1",
|
||||
chat_type: "group",
|
||||
message_type: "post",
|
||||
content: postContent,
|
||||
mentions: [],
|
||||
},
|
||||
};
|
||||
const ctx = parseFeishuMessageEvent(event as any, "ou_bot_123");
|
||||
expect(ctx.mentionedBot).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -185,10 +185,17 @@ function parseMessageContent(content: string, messageType: string): string {
|
||||
}
|
||||
|
||||
function checkBotMentioned(event: FeishuMessageEvent, botOpenId?: string): boolean {
|
||||
const mentions = event.message.mentions ?? [];
|
||||
if (mentions.length === 0) return false;
|
||||
if (!botOpenId) return false;
|
||||
return mentions.some((m) => m.id.open_id === botOpenId);
|
||||
const mentions = event.message.mentions ?? [];
|
||||
if (mentions.length > 0) {
|
||||
return mentions.some((m) => m.id.open_id === botOpenId);
|
||||
}
|
||||
// Post (rich text) messages may have empty message.mentions when they contain docs/paste
|
||||
if (event.message.message_type === "post") {
|
||||
const mentionedIds = getMentionedOpenIdsFromPost(event.message.content);
|
||||
return mentionedIds.includes(botOpenId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function stripBotMention(
|
||||
@@ -237,9 +244,37 @@ function parseMediaKeys(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract mentioned user open_ids from post (rich text) content.
|
||||
* Feishu may not populate message.mentions for post messages (e.g. when pasting docs);
|
||||
* the "at" elements in post body use user_id (open_id). Returns non-empty only for post content.
|
||||
*/
|
||||
function getMentionedOpenIdsFromPost(content: string): string[] {
|
||||
try {
|
||||
const parsed = JSON.parse(content);
|
||||
const contentBlocks =
|
||||
parsed.content ??
|
||||
parsed.zh_cn?.content ??
|
||||
([] as Array<Array<{ tag?: string; user_id?: string }>>);
|
||||
const ids: string[] = [];
|
||||
for (const paragraph of contentBlocks) {
|
||||
if (!Array.isArray(paragraph)) continue;
|
||||
for (const element of paragraph) {
|
||||
if (element.tag === "at" && element.user_id && element.user_id !== "all") {
|
||||
ids.push(element.user_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse post (rich text) content and extract embedded image keys.
|
||||
* Post structure: { title?: string, content: [[{ tag, text?, image_key?, ... }]] }
|
||||
* or { zh_cn: { title?, content: [...] } } when received from Feishu.
|
||||
*/
|
||||
function parsePostContent(content: string): {
|
||||
textContent: string;
|
||||
@@ -247,8 +282,9 @@ function parsePostContent(content: string): {
|
||||
} {
|
||||
try {
|
||||
const parsed = JSON.parse(content);
|
||||
const title = parsed.title || "";
|
||||
const contentBlocks = parsed.content || [];
|
||||
const locale = parsed.zh_cn ?? parsed;
|
||||
const title = locale.title || "";
|
||||
const contentBlocks = locale.content || [];
|
||||
let textContent = title ? `${title}\n\n` : "";
|
||||
const imageKeys: string[] = [];
|
||||
|
||||
@@ -273,11 +309,11 @@ function parsePostContent(content: string): {
|
||||
}
|
||||
|
||||
return {
|
||||
textContent: textContent.trim() || "[富文本消息]",
|
||||
textContent: textContent.trim() || "[Rich text message]",
|
||||
imageKeys,
|
||||
};
|
||||
} catch {
|
||||
return { textContent: "[富文本消息]", imageKeys: [] };
|
||||
return { textContent: "[Rich text message]", imageKeys: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user