refactor(acp): generalize message-channel binds

This commit is contained in:
Peter Steinberger
2026-03-28 02:52:31 +00:00
parent 491969efb0
commit 68416fdf83
22 changed files with 644 additions and 394 deletions

View File

@@ -14,6 +14,7 @@ import {
createAllowlistProviderGroupPolicyWarningCollector,
projectConfigAccountIdWarningCollector,
} from "openclaw/plugin-sdk/channel-policy";
import { getSessionBindingService } from "openclaw/plugin-sdk/conversation-runtime";
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
import {
createChannelDirectoryAdapter,
@@ -44,7 +45,12 @@ import {
import { FEISHU_CARD_INTERACTION_VERSION } from "./card-interaction.js";
import { createFeishuClient } from "./client.js";
import { FeishuConfigSchema } from "./config-schema.js";
import { parseFeishuConversationId } from "./conversation-id.js";
import {
buildFeishuConversationId,
parseFeishuConversationId,
parseFeishuDirectConversationId,
parseFeishuTargetId,
} from "./conversation-id.js";
import { listFeishuDirectoryPeers, listFeishuDirectoryGroups } from "./directory.static.js";
import { resolveFeishuGroupToolPolicy } from "./policy.js";
import { getFeishuRuntime } from "./runtime.js";
@@ -309,6 +315,99 @@ function matchFeishuAcpConversation(params: {
};
}
function resolveFeishuSenderScopedCommandConversation(params: {
accountId: string;
parentConversationId?: string;
threadId?: string;
senderId?: string;
sessionKey?: string;
parentSessionKey?: string;
}): string | undefined {
const parentConversationId = params.parentConversationId?.trim();
const threadId = params.threadId?.trim();
const senderId = params.senderId?.trim();
if (!parentConversationId || !threadId || !senderId) {
return undefined;
}
const expectedScopePrefix = `feishu:group:${parentConversationId.toLowerCase()}:topic:${threadId.toLowerCase()}:sender:`;
const isSenderScopedSession = [params.sessionKey, params.parentSessionKey].some((candidate) => {
const normalized = typeof candidate === "string" ? candidate.trim().toLowerCase() : "";
if (!normalized) {
return false;
}
const scopedRest = normalized.replace(/^agent:[^:]+:/, "");
return scopedRest.startsWith(expectedScopePrefix);
});
const senderScopedConversationId = buildFeishuConversationId({
chatId: parentConversationId,
scope: "group_topic_sender",
topicId: threadId,
senderOpenId: senderId,
});
if (isSenderScopedSession) {
return senderScopedConversationId;
}
if (!params.sessionKey?.trim()) {
return undefined;
}
const boundConversation = getSessionBindingService()
.listBySession(params.sessionKey)
.find((binding) => {
if (
binding.conversation.channel !== "feishu" ||
binding.conversation.accountId !== params.accountId
) {
return false;
}
return binding.conversation.conversationId === senderScopedConversationId;
});
return boundConversation?.conversation.conversationId;
}
function resolveFeishuCommandConversation(params: {
accountId: string;
threadId?: string;
senderId?: string;
sessionKey?: string;
parentSessionKey?: string;
originatingTo?: string;
commandTo?: string;
fallbackTo?: string;
}) {
if (params.threadId) {
const parentConversationId =
parseFeishuTargetId(params.originatingTo) ??
parseFeishuTargetId(params.commandTo) ??
parseFeishuTargetId(params.fallbackTo);
if (!parentConversationId) {
return null;
}
const senderScopedConversationId = resolveFeishuSenderScopedCommandConversation({
accountId: params.accountId,
parentConversationId,
threadId: params.threadId,
senderId: params.senderId,
sessionKey: params.sessionKey,
parentSessionKey: params.parentSessionKey,
});
return {
conversationId:
senderScopedConversationId ??
buildFeishuConversationId({
chatId: parentConversationId,
scope: "group_topic",
topicId: params.threadId,
}),
parentConversationId,
};
}
const conversationId =
parseFeishuDirectConversationId(params.originatingTo) ??
parseFeishuDirectConversationId(params.commandTo) ??
parseFeishuDirectConversationId(params.fallbackTo);
return conversationId ? { conversationId } : null;
}
function jsonActionResult(details: Record<string, unknown>) {
return {
content: [{ type: "text" as const, text: JSON.stringify(details) }],
@@ -942,6 +1041,26 @@ export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount, FeishuProbeResul
conversationId,
parentConversationId,
}),
resolveCommandConversation: ({
accountId,
threadId,
senderId,
sessionKey,
parentSessionKey,
originatingTo,
commandTo,
fallbackTo,
}) =>
resolveFeishuCommandConversation({
accountId,
threadId,
senderId,
sessionKey,
parentSessionKey,
originatingTo,
commandTo,
fallbackTo,
}),
},
setup: feishuSetupAdapter,
setupWizard: feishuSetupWizard,