mirror of https://github.com/openclaw/openclaw.git
fix(feishu): honor sender-scoped ACP bindings
This commit is contained in:
parent
76782f4409
commit
4f9c85c02d
|
|
@ -123,14 +123,23 @@ function resolveConfiguredBindingRecord(params: {
|
||||||
bindings: AgentAcpBinding[];
|
bindings: AgentAcpBinding[];
|
||||||
channel: ConfiguredAcpBindingChannel;
|
channel: ConfiguredAcpBindingChannel;
|
||||||
accountId: string;
|
accountId: string;
|
||||||
selectConversation: (
|
selectConversation: (binding: AgentAcpBinding) => {
|
||||||
binding: AgentAcpBinding,
|
conversationId: string;
|
||||||
) => { conversationId: string; parentConversationId?: string } | null;
|
parentConversationId?: string;
|
||||||
|
matchPriority?: number;
|
||||||
|
} | null;
|
||||||
}): ResolvedConfiguredAcpBinding | null {
|
}): ResolvedConfiguredAcpBinding | null {
|
||||||
let wildcardMatch: {
|
let wildcardMatch: {
|
||||||
binding: AgentAcpBinding;
|
binding: AgentAcpBinding;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
parentConversationId?: string;
|
parentConversationId?: string;
|
||||||
|
matchPriority: number;
|
||||||
|
} | null = null;
|
||||||
|
let exactMatch: {
|
||||||
|
binding: AgentAcpBinding;
|
||||||
|
conversationId: string;
|
||||||
|
parentConversationId?: string;
|
||||||
|
matchPriority: number;
|
||||||
} | null = null;
|
} | null = null;
|
||||||
for (const binding of params.bindings) {
|
for (const binding of params.bindings) {
|
||||||
if (normalizeBindingChannel(binding.match.channel) !== params.channel) {
|
if (normalizeBindingChannel(binding.match.channel) !== params.channel) {
|
||||||
|
|
@ -147,23 +156,40 @@ function resolveConfiguredBindingRecord(params: {
|
||||||
if (!conversation) {
|
if (!conversation) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const matchPriority = conversation.matchPriority ?? 0;
|
||||||
|
if (accountMatchPriority === 2) {
|
||||||
|
if (!exactMatch || matchPriority > exactMatch.matchPriority) {
|
||||||
|
exactMatch = {
|
||||||
|
binding,
|
||||||
|
conversationId: conversation.conversationId,
|
||||||
|
parentConversationId: conversation.parentConversationId,
|
||||||
|
matchPriority,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!wildcardMatch || matchPriority > wildcardMatch.matchPriority) {
|
||||||
|
wildcardMatch = {
|
||||||
|
binding,
|
||||||
|
conversationId: conversation.conversationId,
|
||||||
|
parentConversationId: conversation.parentConversationId,
|
||||||
|
matchPriority,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exactMatch) {
|
||||||
const spec = toConfiguredBindingSpec({
|
const spec = toConfiguredBindingSpec({
|
||||||
cfg: params.cfg,
|
cfg: params.cfg,
|
||||||
channel: params.channel,
|
channel: params.channel,
|
||||||
accountId: params.accountId,
|
accountId: params.accountId,
|
||||||
conversationId: conversation.conversationId,
|
conversationId: exactMatch.conversationId,
|
||||||
parentConversationId: conversation.parentConversationId,
|
parentConversationId: exactMatch.parentConversationId,
|
||||||
binding,
|
binding: exactMatch.binding,
|
||||||
});
|
});
|
||||||
if (accountMatchPriority === 2) {
|
return {
|
||||||
return {
|
spec,
|
||||||
spec,
|
record: toConfiguredAcpBindingRecord(spec),
|
||||||
record: toConfiguredAcpBindingRecord(spec),
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!wildcardMatch) {
|
|
||||||
wildcardMatch = { binding, ...conversation };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!wildcardMatch) {
|
if (!wildcardMatch) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -426,6 +452,7 @@ export function resolveConfiguredAcpBindingRecord(params: {
|
||||||
parsed.scope === "group_topic" || parsed.scope === "group_topic_sender"
|
parsed.scope === "group_topic" || parsed.scope === "group_topic_sender"
|
||||||
? parsed.chatId
|
? parsed.chatId
|
||||||
: undefined,
|
: undefined,
|
||||||
|
matchPriority: matchesCanonicalConversation ? 2 : 1,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,34 @@ describe("resolveConfiguredAcpBindingRecord", () => {
|
||||||
expect(resolved?.spec.agentId).toBe("claude");
|
expect(resolved?.spec.agentId).toBe("claude");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prefers sender-scoped Feishu bindings over topic inheritance", () => {
|
||||||
|
const cfg = createCfgWithBindings([
|
||||||
|
createFeishuBinding({
|
||||||
|
agentId: "codex",
|
||||||
|
conversationId: "oc_group_chat:topic:om_topic_root",
|
||||||
|
accountId: "work",
|
||||||
|
}),
|
||||||
|
createFeishuBinding({
|
||||||
|
agentId: "claude",
|
||||||
|
conversationId: "oc_group_chat:topic:om_topic_root:sender:ou_sender_1",
|
||||||
|
accountId: "work",
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const resolved = resolveConfiguredAcpBindingRecord({
|
||||||
|
cfg,
|
||||||
|
channel: "feishu",
|
||||||
|
accountId: "work",
|
||||||
|
conversationId: "oc_group_chat:topic:om_topic_root:sender:ou_sender_1",
|
||||||
|
parentConversationId: "oc_group_chat",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resolved?.spec.conversationId).toBe(
|
||||||
|
"oc_group_chat:topic:om_topic_root:sender:ou_sender_1",
|
||||||
|
);
|
||||||
|
expect(resolved?.spec.agentId).toBe("claude");
|
||||||
|
});
|
||||||
|
|
||||||
it("prefers exact account binding over wildcard for the same discord conversation", () => {
|
it("prefers exact account binding over wildcard for the same discord conversation", () => {
|
||||||
const cfg = createCfgWithBindings([
|
const cfg = createCfgWithBindings([
|
||||||
createDiscordBinding({
|
createDiscordBinding({
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,24 @@ describe("commands-acp context", () => {
|
||||||
expect(resolveAcpCommandConversationId(params)).toBe("ou_sender_1");
|
expect(resolveAcpCommandConversationId(params)).toBe("ou_sender_1");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("resolves Feishu DM conversation ids from user_id fallback targets", () => {
|
||||||
|
const params = buildCommandTestParams("/acp status", baseCfg, {
|
||||||
|
Provider: "feishu",
|
||||||
|
Surface: "feishu",
|
||||||
|
OriginatingChannel: "feishu",
|
||||||
|
OriginatingTo: "user:user_123",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resolveAcpCommandBindingContext(params)).toEqual({
|
||||||
|
channel: "feishu",
|
||||||
|
accountId: "default",
|
||||||
|
threadId: undefined,
|
||||||
|
conversationId: "user_123",
|
||||||
|
parentConversationId: undefined,
|
||||||
|
});
|
||||||
|
expect(resolveAcpCommandConversationId(params)).toBe("user_123");
|
||||||
|
});
|
||||||
|
|
||||||
it("does not infer a Feishu DM parent conversation id during fallback binding lookup", () => {
|
it("does not infer a Feishu DM parent conversation id during fallback binding lookup", () => {
|
||||||
const params = buildCommandTestParams("/acp status", baseCfg, {
|
const params = buildCommandTestParams("/acp status", baseCfg, {
|
||||||
Provider: "feishu",
|
Provider: "feishu",
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,21 @@ function parseFeishuTargetId(raw: unknown): string | undefined {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseFeishuDirectConversationId(raw: unknown): string | undefined {
|
function parseFeishuDirectConversationId(raw: unknown): string | undefined {
|
||||||
const id = parseFeishuTargetId(raw);
|
const target = normalizeConversationText(raw);
|
||||||
|
if (!target) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const withoutProvider = target.replace(/^(feishu|lark):/i, "").trim();
|
||||||
|
if (!withoutProvider) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const lowered = withoutProvider.toLowerCase();
|
||||||
|
for (const prefix of ["user:", "dm:", "open_id:"]) {
|
||||||
|
if (lowered.startsWith(prefix)) {
|
||||||
|
return normalizeConversationText(withoutProvider.slice(prefix.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const id = parseFeishuTargetId(target);
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue