refactor(discord): compose native command routes

This commit is contained in:
Peter Steinberger
2026-03-08 01:23:56 +00:00
parent 547436bca7
commit 6016e22cc0
3 changed files with 87 additions and 23 deletions

View File

@@ -85,8 +85,7 @@ import {
import { buildDiscordNativeCommandContext } from "./native-command-context.js";
import { resolveDiscordNativeCommandSessionTargets } from "./native-command-session-targets.js";
import {
buildDiscordRoutePeer,
resolveDiscordConversationRoute,
resolveDiscordBoundConversationRoute,
resolveDiscordEffectiveRoute,
} from "./route-resolution.js";
import { resolveDiscordSenderIdentity } from "./sender-identity.js";
@@ -451,25 +450,19 @@ async function resolveDiscordModelPickerRoute(params: {
threadParentId = parentInfo.id;
}
const route = resolveDiscordConversationRoute({
const threadBinding = isThreadChannel
? params.threadBindings.getByThreadId(rawChannelId)
: undefined;
return resolveDiscordBoundConversationRoute({
cfg,
accountId,
guildId: interaction.guild?.id ?? undefined,
memberRoleIds,
peer: buildDiscordRoutePeer({
isDirectMessage,
isGroupDm,
directUserId: interaction.user?.id ?? rawChannelId,
conversationId: rawChannelId,
}),
isDirectMessage,
isGroupDm,
directUserId: interaction.user?.id ?? rawChannelId,
conversationId: rawChannelId,
parentConversationId: threadParentId,
});
const threadBinding = isThreadChannel
? params.threadBindings.getByThreadId(rawChannelId)
: undefined;
return resolveDiscordEffectiveRoute({
route,
boundSessionKey: threadBinding?.targetSessionKey,
});
}
@@ -1605,18 +1598,18 @@ async function dispatchDiscordCommandInteraction(params: {
const isGuild = Boolean(interaction.guild);
const channelId = rawChannelId || "unknown";
const interactionId = interaction.rawData.id;
const route = resolveDiscordConversationRoute({
const route = resolveDiscordBoundConversationRoute({
cfg,
accountId,
guildId: interaction.guild?.id ?? undefined,
memberRoleIds,
peer: buildDiscordRoutePeer({
isDirectMessage,
isGroupDm,
directUserId: user.id,
conversationId: channelId,
}),
isDirectMessage,
isGroupDm,
directUserId: user.id,
conversationId: channelId,
parentConversationId: threadParentId,
// Configured ACP routes apply after raw route resolution, so do not pass
// bound/configured overrides here.
});
const threadBinding = isThreadChannel ? threadBindings.getByThreadId(rawChannelId) : undefined;
const configuredRoute =

View File

@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import type { ResolvedAgentRoute } from "../../routing/resolve-route.js";
import {
resolveDiscordBoundConversationRoute,
buildDiscordRoutePeer,
resolveDiscordConversationRoute,
resolveDiscordEffectiveRoute,
@@ -104,4 +105,39 @@ describe("discord route resolution helpers", () => {
matchedBy: "binding.peer",
});
});
it("composes route building with effective-route overrides", () => {
const cfg: OpenClawConfig = {
agents: {
list: [{ id: "worker" }],
},
bindings: [
{
agentId: "worker",
match: {
channel: "discord",
accountId: "default",
peer: { kind: "direct", id: "user-1" },
},
},
],
};
expect(
resolveDiscordBoundConversationRoute({
cfg,
accountId: "default",
isDirectMessage: true,
isGroupDm: false,
directUserId: "user-1",
conversationId: "dm-1",
boundSessionKey: "agent:worker:discord:direct:user-1",
matchedBy: "binding.channel",
}),
).toMatchObject({
agentId: "worker",
sessionKey: "agent:worker:discord:direct:user-1",
matchedBy: "binding.channel",
});
});
});

View File

@@ -41,6 +41,41 @@ export function resolveDiscordConversationRoute(params: {
});
}
export function resolveDiscordBoundConversationRoute(params: {
cfg: OpenClawConfig;
accountId?: string | null;
guildId?: string | null;
memberRoleIds?: string[];
isDirectMessage: boolean;
isGroupDm: boolean;
directUserId?: string | null;
conversationId: string;
parentConversationId?: string | null;
boundSessionKey?: string | null;
configuredRoute?: { route: ResolvedAgentRoute } | null;
matchedBy?: ResolvedAgentRoute["matchedBy"];
}): ResolvedAgentRoute {
const route = resolveDiscordConversationRoute({
cfg: params.cfg,
accountId: params.accountId,
guildId: params.guildId,
memberRoleIds: params.memberRoleIds,
peer: buildDiscordRoutePeer({
isDirectMessage: params.isDirectMessage,
isGroupDm: params.isGroupDm,
directUserId: params.directUserId,
conversationId: params.conversationId,
}),
parentConversationId: params.parentConversationId,
});
return resolveDiscordEffectiveRoute({
route,
boundSessionKey: params.boundSessionKey,
configuredRoute: params.configuredRoute,
matchedBy: params.matchedBy,
});
}
export function resolveDiscordEffectiveRoute(params: {
route: ResolvedAgentRoute;
boundSessionKey?: string | null;