fix(feishu): guard against false-positive @mentions in multi-app groups (#30315)
* fix(feishu): guard against false-positive @mentions in multi-app groups When multiple Feishu bot apps share a group chat, Feishu's WebSocket event delivery remaps the open_id in mentions[] per-app. This causes checkBotMentioned() to return true for ALL bots when only one was actually @mentioned, making requireMention ineffective. Add a botName guard: if the mention's open_id matches this bot but the mention's display name differs from this bot's configured botName, treat it as a false positive and skip. botName is already available via account.config.botName (set during onboarding). Closes #24249 * fix(feishu): support @all mention in multi-bot groups When a user sends @all (@_all in Feishu message content), treat it as mentioning every bot so all agents respond when requireMention is true. Feishu's @all does not populate the mentions[] array, so this needs explicit content-level detection. * fix(feishu): auto-fetch bot display name from API for reliable mention matching Instead of relying on the manually configured botName (which may differ from the actual Feishu bot display name), fetch the bot's display name from the Feishu API at startup via probeFeishu(). This ensures checkBotMentioned() always compares against the correct display name, even when the config botName doesn't match (e.g. config says 'Wanda' but Feishu shows '绯红女巫'). Changes: - monitor.ts: fetchBotOpenId → fetchBotInfo (returns both openId and name) - monitor.ts: store botNames map, pass botName to handleFeishuMessage - bot.ts: accept botName from params, prefer it over config fallback * Changelog: note Feishu multi-app mention false-positive guard --------- Co-authored-by: Teague Xiao <teaguexiao@TeaguedeMac-mini.local> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -455,11 +455,20 @@ function formatSubMessageContent(content: string, contentType: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
function checkBotMentioned(event: FeishuMessageEvent, botOpenId?: string): boolean {
|
||||
function checkBotMentioned(event: FeishuMessageEvent, botOpenId?: string, botName?: string): boolean {
|
||||
if (!botOpenId) return false;
|
||||
// Check for @all (@_all in Feishu) — treat as mentioning every bot
|
||||
const rawContent = event.message.content ?? "";
|
||||
if (rawContent.includes("@_all")) return true;
|
||||
const mentions = event.message.mentions ?? [];
|
||||
if (mentions.length > 0) {
|
||||
return mentions.some((m) => m.id.open_id === botOpenId);
|
||||
return mentions.some((m) => {
|
||||
if (m.id.open_id !== botOpenId) return false;
|
||||
// Guard against Feishu WS open_id remapping in multi-app groups:
|
||||
// if botName is known and mention name differs, this is a false positive.
|
||||
if (botName && m.name && m.name !== botName) return false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
// Post (rich text) messages may have empty message.mentions when they contain docs/paste
|
||||
if (event.message.message_type === "post") {
|
||||
@@ -747,9 +756,10 @@ export function buildBroadcastSessionKey(
|
||||
export function parseFeishuMessageEvent(
|
||||
event: FeishuMessageEvent,
|
||||
botOpenId?: string,
|
||||
botName?: string,
|
||||
): FeishuMessageContext {
|
||||
const rawContent = parseMessageContent(event.message.content, event.message.message_type);
|
||||
const mentionedBot = checkBotMentioned(event, botOpenId);
|
||||
const mentionedBot = checkBotMentioned(event, botOpenId, botName);
|
||||
const content = stripBotMention(rawContent, event.message.mentions);
|
||||
const senderOpenId = event.sender.sender_id.open_id?.trim();
|
||||
const senderUserId = event.sender.sender_id.user_id?.trim();
|
||||
@@ -823,11 +833,12 @@ export async function handleFeishuMessage(params: {
|
||||
cfg: ClawdbotConfig;
|
||||
event: FeishuMessageEvent;
|
||||
botOpenId?: string;
|
||||
botName?: string;
|
||||
runtime?: RuntimeEnv;
|
||||
chatHistories?: Map<string, HistoryEntry[]>;
|
||||
accountId?: string;
|
||||
}): Promise<void> {
|
||||
const { cfg, event, botOpenId, runtime, chatHistories, accountId } = params;
|
||||
const { cfg, event, botOpenId, botName, runtime, chatHistories, accountId } = params;
|
||||
|
||||
// Resolve account with merged config
|
||||
const account = resolveFeishuAccount({ cfg, accountId });
|
||||
@@ -850,7 +861,7 @@ export async function handleFeishuMessage(params: {
|
||||
return;
|
||||
}
|
||||
|
||||
let ctx = parseFeishuMessageEvent(event, botOpenId);
|
||||
let ctx = parseFeishuMessageEvent(event, botOpenId, botName ?? account.config?.botName);
|
||||
const isGroup = ctx.chatType === "group";
|
||||
const isDirect = !isGroup;
|
||||
const senderUserId = event.sender.sender_id.user_id?.trim() || undefined;
|
||||
|
||||
Reference in New Issue
Block a user