diff --git a/CHANGELOG.md b/CHANGELOG.md index cc1a56314..962159f2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Docs: https://docs.openclaw.ai - Plugin command/runtime hardening: validate and normalize plugin command name/description at registration boundaries, and guard Telegram native menu normalization paths so malformed plugin command specs cannot crash startup (`trim` on undefined). (#31997) Fixes #31944. Thanks @liuxiaopai-ai. - Telegram: guard duplicate-token checks and gateway startup token normalization when account tokens are missing, preventing `token.trim()` crashes during status/start flows. (#31973) Thanks @ningding97. - Discord/lifecycle startup status: push an immediate `connected` status snapshot when the gateway is already connected before lifecycle debug listeners attach, with abort-guarding to avoid contradictory status flips during pre-aborted startup. (#32336) Thanks @mitchmcalister. +- Feishu/multi-app mention routing: guard mention detection in multi-bot groups by validating mention display name alongside bot `open_id`, preventing false-positive self-mentions from Feishu WebSocket remapping so only the actually mentioned bot responds under `requireMention`. (#30315) Thanks @teaguexiao. - Feishu/session-memory hook parity: trigger the shared `before_reset` session-memory hook path when Feishu `/new` and `/reset` commands execute so reset flows preserve memory behavior consistent with other channels. (#31437) Thanks @Linux2010. - Feishu/LINE group system prompts: forward per-group `systemPrompt` config into inbound context `GroupSystemPrompt` for Feishu and LINE group/room events so configured group-specific behavior actually applies at dispatch time. (#31713) Thanks @whiskyboy. - Mentions/Slack formatting hardening: add null-safe guards for runtime text normalization paths so malformed/undefined text payloads do not crash mention stripping or mrkdwn conversion. (#31865) Thanks @stone-jin. diff --git a/extensions/feishu/src/bot.ts b/extensions/feishu/src/bot.ts index f9cd3670c..4b688b1da 100644 --- a/extensions/feishu/src/bot.ts +++ b/extensions/feishu/src/bot.ts @@ -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; accountId?: string; }): Promise { - 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;