Skip to content

Commit eb45ee5

Browse files
committed
fix: enforce requireMention setting for Feishu group chats
The `bridge_feishu_require_mention` setting existed in the config UI and was loaded from the database, but was never actually checked in the message handling pipeline. Group chat messages were always processed regardless of whether the bot was @mentioned. Changes: - Fetch bot identity (open_id) on plugin startup via getBotInfo() - Check `message.mentions` against botOpenId when requireMention is enabled and the message comes from a group chat (chatId starts with 'oc_') - Strip the @bot mention placeholder from message text so the LLM receives clean input - Return null (skip message) when requireMention is true and the bot is not mentioned
1 parent 8be94c6 commit eb45ee5

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

src/lib/channels/feishu/inbound.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,22 @@ import type { FeishuConfig } from './types';
99

1010
const LOG_TAG = '[feishu/inbound]';
1111

12+
/**
13+
* Check whether the bot is mentioned in the message's mentions array.
14+
*
15+
* Feishu events include `message.mentions` — an array of objects with
16+
* `{ id: { open_id }, key, name }` for each @-mention in the message.
17+
*/
18+
function isBotMentioned(mentions: any[] | undefined, botOpenId: string): boolean {
19+
if (!mentions || !Array.isArray(mentions) || !botOpenId) return false;
20+
return mentions.some((m) => m?.id?.open_id === botOpenId);
21+
}
22+
1223
/** Parse a raw Feishu im.message.receive_v1 event into an InboundMessage. */
1324
export function parseInboundMessage(
1425
eventData: any,
15-
_config: FeishuConfig,
26+
config: FeishuConfig,
27+
botOpenId?: string,
1628
): InboundMessage | null {
1729
try {
1830
const event = eventData?.event ?? eventData;
@@ -24,6 +36,15 @@ export function parseInboundMessage(
2436
const sender = event.sender?.sender_id?.open_id || '';
2537
const msgType = message.message_type;
2638

39+
// In group chats, skip messages that don't @mention the bot
40+
// when requireMention is enabled.
41+
const isGroupChat = chatId.startsWith('oc_');
42+
if (isGroupChat && config.requireMention) {
43+
if (!isBotMentioned(message.mentions, botOpenId || '')) {
44+
return null;
45+
}
46+
}
47+
2748
// Only handle text messages for now
2849
let text = '';
2950
if (msgType === 'text') {
@@ -40,6 +61,17 @@ export function parseInboundMessage(
4061

4162
if (!text.trim()) return null;
4263

64+
// Strip the @bot mention placeholder from text so the LLM sees clean input.
65+
// Feishu encodes mentions as placeholders like "@_user_1" in the text body.
66+
if (isGroupChat && botOpenId && message.mentions) {
67+
const botMention = (message.mentions as any[]).find(
68+
(m) => m?.id?.open_id === botOpenId,
69+
);
70+
if (botMention?.key) {
71+
text = text.replace(botMention.key, '').trim();
72+
}
73+
}
74+
4375
// Build thread-session address if applicable
4476
const rootId = message.root_id || '';
4577
const effectiveChatId = rootId ? `${chatId}:thread:${rootId}` : chatId;

src/lib/channels/feishu/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { FeishuConfig } from './types';
1111
import { loadFeishuConfig, validateFeishuConfig } from './config';
1212
import { FeishuGateway } from './gateway';
1313
import { parseInboundMessage } from './inbound';
14+
import { getBotInfo } from './identity';
1415
import { sendMessage, addReaction, removeReaction } from './outbound';
1516
import { isUserAuthorized } from './policy';
1617
import { createCardStreamController } from './card-controller';
@@ -23,6 +24,7 @@ export class FeishuChannelPlugin implements ChannelPlugin<FeishuConfig> {
2324

2425
private config: FeishuConfig | null = null;
2526
private gateway: FeishuGateway | null = null;
27+
private botOpenId: string = '';
2628
private messageQueue: InboundMessage[] = [];
2729
private waitResolve: ((msg: InboundMessage | null) => void) | null = null;
2830
/** Track last received messageId per chatId for reaction acknowledgment. */
@@ -66,7 +68,7 @@ export class FeishuChannelPlugin implements ChannelPlugin<FeishuConfig> {
6668

6769
// Register message handler — pushes to internal queue
6870
this.gateway.registerMessageHandler((data: unknown) => {
69-
const msg = parseInboundMessage(data, this.config!);
71+
const msg = parseInboundMessage(data, this.config!, this.botOpenId);
7072
if (!msg) return;
7173
this.enqueueMessage(msg);
7274
});
@@ -148,6 +150,16 @@ export class FeishuChannelPlugin implements ChannelPlugin<FeishuConfig> {
148150
});
149151

150152
await this.gateway.start();
153+
154+
// Fetch bot identity so we can check @mentions in group chats.
155+
const client = this.gateway.getRestClient();
156+
if (client) {
157+
const info = await getBotInfo(client);
158+
if (info?.openId) {
159+
this.botOpenId = info.openId;
160+
console.log('[feishu/plugin]', 'Bot identity resolved:', info.botName, info.openId);
161+
}
162+
}
151163
}
152164

153165
async stop(): Promise<void> {

0 commit comments

Comments
 (0)