fix(feishu): default requireMention to false for groupPolicy open

Groups configured with groupPolicy: open are expected to respond to all
messages. Previously, requireMention defaulted to true regardless of
groupPolicy, causing image (and other non-text) messages to be silently
dropped because they cannot carry @-mentions.

Fix: when groupPolicy is 'open' and requireMention is not explicitly
configured, resolve it to false instead of true. Users who want
mention-required behaviour in open groups can still set requireMention: true
explicitly.

Adds three regression tests covering the new default, explicit override, and
the unchanged allowlist-policy behaviour.

Closes #52553
This commit is contained in:
lbo728
2026-03-25 08:03:40 +09:00
committed by Peter Steinberger
parent 89b7fee352
commit 69195f7e9d
4 changed files with 112 additions and 2 deletions

View File

@@ -1074,6 +1074,103 @@ describe("handleFeishuMessage command authorization", () => {
expect(mockDispatchReplyFromConfig).not.toHaveBeenCalled();
});
it("dispatches group image message when groupPolicy is open (requireMention defaults to false)", async () => {
mockShouldComputeCommandAuthorized.mockReturnValue(false);
const cfg: ClawdbotConfig = {
channels: {
feishu: {
groupPolicy: "open",
// requireMention is NOT set — should default to false for open policy
},
},
} as ClawdbotConfig;
const event: FeishuMessageEvent = {
sender: {
sender_id: { open_id: "ou-sender" },
},
message: {
message_id: "msg-group-image-open",
chat_id: "oc-group-open",
chat_type: "group",
message_type: "image",
content: JSON.stringify({ image_key: "img_v3_test" }),
},
};
await dispatchMessage({ cfg, event });
expect(mockDispatchReplyFromConfig).toHaveBeenCalledTimes(1);
});
it("drops group image message when groupPolicy is open but requireMention is explicitly true", async () => {
mockShouldComputeCommandAuthorized.mockReturnValue(false);
const cfg: ClawdbotConfig = {
channels: {
feishu: {
groupPolicy: "open",
requireMention: true, // explicit override — user opts into mention-required even for open
},
},
} as ClawdbotConfig;
const event: FeishuMessageEvent = {
sender: {
sender_id: { open_id: "ou-sender" },
},
message: {
message_id: "msg-group-image-open-explicit-mention",
chat_id: "oc-group-open",
chat_type: "group",
message_type: "image",
content: JSON.stringify({ image_key: "img_v3_test" }),
},
};
await dispatchMessage({ cfg, event });
expect(mockFinalizeInboundContext).not.toHaveBeenCalled();
expect(mockDispatchReplyFromConfig).not.toHaveBeenCalled();
});
it("drops group image message when groupPolicy is allowlist and requireMention is not set (defaults to true)", async () => {
mockShouldComputeCommandAuthorized.mockReturnValue(false);
const cfg: ClawdbotConfig = {
channels: {
feishu: {
groupPolicy: "allowlist",
// requireMention not set — for non-open policy defaults to true
groups: {
"oc-allowlist-group": {
allow: true,
},
},
},
},
} as ClawdbotConfig;
const event: FeishuMessageEvent = {
sender: {
sender_id: { open_id: "ou-sender" },
},
message: {
message_id: "msg-group-image-allowlist",
chat_id: "oc-allowlist-group",
chat_type: "group",
message_type: "image",
content: JSON.stringify({ image_key: "img_v3_test" }),
},
};
await dispatchMessage({ cfg, event });
expect(mockFinalizeInboundContext).not.toHaveBeenCalled();
expect(mockDispatchReplyFromConfig).not.toHaveBeenCalled();
});
it("drops message when groupConfig.enabled is false", async () => {
const cfg: ClawdbotConfig = {
channels: {

View File

@@ -416,6 +416,7 @@ export async function handleFeishuMessage(params: {
isDirectMessage: false,
globalConfig: feishuCfg,
groupConfig,
groupPolicy,
}));
if (requireMention && !ctx.mentionedBot) {

View File

@@ -221,7 +221,7 @@ export const FeishuConfigSchema = z
dmPolicy: DmPolicySchema.optional().default("pairing"),
reactionNotifications: ReactionNotificationModeSchema.optional().default("own"),
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
requireMention: z.boolean().optional().default(true),
requireMention: z.boolean().optional(),
groupSessionScope: GroupSessionScopeSchema,
topicSessionMode: TopicSessionModeSchema,
// Dynamic agent creation for DM users

View File

@@ -107,13 +107,25 @@ export function resolveFeishuReplyPolicy(params: {
isDirectMessage: boolean;
globalConfig?: FeishuConfig;
groupConfig?: FeishuGroupConfig;
/**
* Effective group policy resolved for this chat. When "open", requireMention
* defaults to false so that non-text messages (e.g. images) that cannot carry
* @-mentions are still delivered to the agent.
*/
groupPolicy?: string;
}): { requireMention: boolean } {
if (params.isDirectMessage) {
return { requireMention: false };
}
// When groupPolicy is "open" and requireMention is not explicitly configured,
// default to false: an open group should respond to all messages including
// images and files that cannot carry @-mentions.
const requireMentionDefault = params.groupPolicy === "open" ? false : true;
const requireMention =
params.groupConfig?.requireMention ?? params.globalConfig?.requireMention ?? true;
params.groupConfig?.requireMention ??
params.globalConfig?.requireMention ??
requireMentionDefault;
return { requireMention };
}