Skip to content

Commit 406e7ab

Browse files
teaguexiaoTeague XiaoTakhoffman
authored
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 <[email protected]> Co-authored-by: Tak Hoffman <[email protected]>
1 parent cad06fa commit 406e7ab

File tree

2 files changed

+17
-5
lines changed

2 files changed

+17
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Docs: https://docs.openclaw.ai
3939
- 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.
4040
- 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.
4141
- 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.
42+
- 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.
4243
- 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.
4344
- 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.
4445
- 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.

extensions/feishu/src/bot.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -455,11 +455,20 @@ function formatSubMessageContent(content: string, contentType: string): string {
455455
}
456456
}
457457

458-
function checkBotMentioned(event: FeishuMessageEvent, botOpenId?: string): boolean {
458+
function checkBotMentioned(event: FeishuMessageEvent, botOpenId?: string, botName?: string): boolean {
459459
if (!botOpenId) return false;
460+
// Check for @all (@_all in Feishu) — treat as mentioning every bot
461+
const rawContent = event.message.content ?? "";
462+
if (rawContent.includes("@_all")) return true;
460463
const mentions = event.message.mentions ?? [];
461464
if (mentions.length > 0) {
462-
return mentions.some((m) => m.id.open_id === botOpenId);
465+
return mentions.some((m) => {
466+
if (m.id.open_id !== botOpenId) return false;
467+
// Guard against Feishu WS open_id remapping in multi-app groups:
468+
// if botName is known and mention name differs, this is a false positive.
469+
if (botName && m.name && m.name !== botName) return false;
470+
return true;
471+
});
463472
}
464473
// Post (rich text) messages may have empty message.mentions when they contain docs/paste
465474
if (event.message.message_type === "post") {
@@ -747,9 +756,10 @@ export function buildBroadcastSessionKey(
747756
export function parseFeishuMessageEvent(
748757
event: FeishuMessageEvent,
749758
botOpenId?: string,
759+
botName?: string,
750760
): FeishuMessageContext {
751761
const rawContent = parseMessageContent(event.message.content, event.message.message_type);
752-
const mentionedBot = checkBotMentioned(event, botOpenId);
762+
const mentionedBot = checkBotMentioned(event, botOpenId, botName);
753763
const content = stripBotMention(rawContent, event.message.mentions);
754764
const senderOpenId = event.sender.sender_id.open_id?.trim();
755765
const senderUserId = event.sender.sender_id.user_id?.trim();
@@ -823,11 +833,12 @@ export async function handleFeishuMessage(params: {
823833
cfg: ClawdbotConfig;
824834
event: FeishuMessageEvent;
825835
botOpenId?: string;
836+
botName?: string;
826837
runtime?: RuntimeEnv;
827838
chatHistories?: Map<string, HistoryEntry[]>;
828839
accountId?: string;
829840
}): Promise<void> {
830-
const { cfg, event, botOpenId, runtime, chatHistories, accountId } = params;
841+
const { cfg, event, botOpenId, botName, runtime, chatHistories, accountId } = params;
831842

832843
// Resolve account with merged config
833844
const account = resolveFeishuAccount({ cfg, accountId });
@@ -850,7 +861,7 @@ export async function handleFeishuMessage(params: {
850861
return;
851862
}
852863

853-
let ctx = parseFeishuMessageEvent(event, botOpenId);
864+
let ctx = parseFeishuMessageEvent(event, botOpenId, botName ?? account.config?.botName);
854865
const isGroup = ctx.chatType === "group";
855866
const isDirect = !isGroup;
856867
const senderUserId = event.sender.sender_id.user_id?.trim() || undefined;

0 commit comments

Comments
 (0)