feat(feishu): add requireMentionInThread to allow thread replies without @mention
When requireMention is true for a group, thread replies still require an explicit @mention — even though thread context makes the intent unambiguous. This adds a requireMentionInThread config option at both the global and per-group level, letting users opt out of the mention requirement for thread replies while keeping it for top-level group messages. Closes #40475 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4c60956d8e
commit
80daf16550
@ -414,6 +414,7 @@ export async function handleFeishuMessage(params: {
|
||||
|
||||
({ requireMention } = resolveFeishuReplyPolicy({
|
||||
isDirectMessage: false,
|
||||
isThreadReply: groupSession?.threadReply,
|
||||
globalConfig: feishuCfg,
|
||||
groupConfig,
|
||||
}));
|
||||
|
||||
@ -141,6 +141,7 @@ const ReplyInThreadSchema = z.enum(["disabled", "enabled"]).optional();
|
||||
export const FeishuGroupSchema = z
|
||||
.object({
|
||||
requireMention: z.boolean().optional(),
|
||||
requireMentionInThread: z.boolean().optional(),
|
||||
tools: ToolPolicySchema,
|
||||
skills: z.array(z.string()).optional(),
|
||||
enabled: z.boolean().optional(),
|
||||
@ -164,6 +165,7 @@ const FeishuSharedConfigShape = {
|
||||
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
||||
groupSenderAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
||||
requireMention: z.boolean().optional(),
|
||||
requireMentionInThread: z.boolean().optional(),
|
||||
groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(),
|
||||
historyLimit: z.number().int().min(0).optional(),
|
||||
dmHistoryLimit: z.number().int().min(0).optional(),
|
||||
|
||||
@ -3,8 +3,9 @@ import {
|
||||
isFeishuGroupAllowed,
|
||||
resolveFeishuAllowlistMatch,
|
||||
resolveFeishuGroupConfig,
|
||||
resolveFeishuReplyPolicy,
|
||||
} from "./policy.js";
|
||||
import type { FeishuConfig } from "./types.js";
|
||||
import type { FeishuConfig, FeishuGroupConfig } from "./types.js";
|
||||
|
||||
describe("feishu policy", () => {
|
||||
describe("resolveFeishuGroupConfig", () => {
|
||||
@ -100,6 +101,79 @@ describe("feishu policy", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveFeishuReplyPolicy", () => {
|
||||
it("does not require mention for DMs", () => {
|
||||
expect(resolveFeishuReplyPolicy({ isDirectMessage: true })).toEqual({
|
||||
requireMention: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("requires mention in group by default", () => {
|
||||
expect(resolveFeishuReplyPolicy({ isDirectMessage: false })).toEqual({
|
||||
requireMention: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("respects group-level requireMention override", () => {
|
||||
expect(
|
||||
resolveFeishuReplyPolicy({
|
||||
isDirectMessage: false,
|
||||
groupConfig: { requireMention: false } as FeishuGroupConfig,
|
||||
}),
|
||||
).toEqual({ requireMention: false });
|
||||
});
|
||||
|
||||
it("still requires mention for thread replies when no thread override is set", () => {
|
||||
expect(
|
||||
resolveFeishuReplyPolicy({
|
||||
isDirectMessage: false,
|
||||
isThreadReply: true,
|
||||
}),
|
||||
).toEqual({ requireMention: true });
|
||||
});
|
||||
|
||||
it("skips mention for thread replies when requireMentionInThread is false (global)", () => {
|
||||
expect(
|
||||
resolveFeishuReplyPolicy({
|
||||
isDirectMessage: false,
|
||||
isThreadReply: true,
|
||||
globalConfig: { requireMentionInThread: false } as FeishuConfig,
|
||||
}),
|
||||
).toEqual({ requireMention: false });
|
||||
});
|
||||
|
||||
it("skips mention for thread replies when requireMentionInThread is false (group)", () => {
|
||||
expect(
|
||||
resolveFeishuReplyPolicy({
|
||||
isDirectMessage: false,
|
||||
isThreadReply: true,
|
||||
groupConfig: { requireMentionInThread: false } as FeishuGroupConfig,
|
||||
}),
|
||||
).toEqual({ requireMention: false });
|
||||
});
|
||||
|
||||
it("group-level requireMentionInThread overrides global", () => {
|
||||
expect(
|
||||
resolveFeishuReplyPolicy({
|
||||
isDirectMessage: false,
|
||||
isThreadReply: true,
|
||||
globalConfig: { requireMentionInThread: false } as FeishuConfig,
|
||||
groupConfig: { requireMentionInThread: true } as FeishuGroupConfig,
|
||||
}),
|
||||
).toEqual({ requireMention: true });
|
||||
});
|
||||
|
||||
it("does not apply thread override for non-thread messages", () => {
|
||||
expect(
|
||||
resolveFeishuReplyPolicy({
|
||||
isDirectMessage: false,
|
||||
isThreadReply: false,
|
||||
globalConfig: { requireMentionInThread: false } as FeishuConfig,
|
||||
}),
|
||||
).toEqual({ requireMention: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe("isFeishuGroupAllowed", () => {
|
||||
it("matches group IDs with chat: prefix", () => {
|
||||
expect(
|
||||
|
||||
@ -105,6 +105,7 @@ export function isFeishuGroupAllowed(params: {
|
||||
|
||||
export function resolveFeishuReplyPolicy(params: {
|
||||
isDirectMessage: boolean;
|
||||
isThreadReply?: boolean;
|
||||
globalConfig?: FeishuConfig;
|
||||
groupConfig?: FeishuGroupConfig;
|
||||
}): { requireMention: boolean } {
|
||||
@ -115,5 +116,13 @@ export function resolveFeishuReplyPolicy(params: {
|
||||
const requireMention =
|
||||
params.groupConfig?.requireMention ?? params.globalConfig?.requireMention ?? true;
|
||||
|
||||
if (requireMention && params.isThreadReply) {
|
||||
const threadOverride =
|
||||
params.groupConfig?.requireMentionInThread ?? params.globalConfig?.requireMentionInThread;
|
||||
if (threadOverride !== undefined) {
|
||||
return { requireMention: threadOverride };
|
||||
}
|
||||
}
|
||||
|
||||
return { requireMention };
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user