Discord: implement role-based agent routing in resolveAgentRoute

This commit is contained in:
Minidoracat
2026-01-29 14:30:22 +00:00
committed by Shadow
parent 4c0ce46ac3
commit 75fc8cf25c
+23 -45
View File
@@ -43,6 +43,7 @@ export type ResolvedAgentRoute = {
matchedBy: matchedBy:
| "binding.peer" | "binding.peer"
| "binding.peer.parent" | "binding.peer.parent"
| "binding.guild+roles"
| "binding.guild" | "binding.guild"
| "binding.team" | "binding.team"
| "binding.account" | "binding.account"
@@ -67,12 +68,8 @@ function normalizeAccountId(value: string | undefined | null): string {
function matchesAccountId(match: string | undefined, actual: string): boolean { function matchesAccountId(match: string | undefined, actual: string): boolean {
const trimmed = (match ?? "").trim(); const trimmed = (match ?? "").trim();
if (!trimmed) { if (!trimmed) return actual === DEFAULT_ACCOUNT_ID;
return actual === DEFAULT_ACCOUNT_ID; if (trimmed === "*") return true;
}
if (trimmed === "*") {
return true;
}
return trimmed === actual; return trimmed === actual;
} }
@@ -106,18 +103,12 @@ function listAgents(cfg: OpenClawConfig) {
function pickFirstExistingAgentId(cfg: OpenClawConfig, agentId: string): string { function pickFirstExistingAgentId(cfg: OpenClawConfig, agentId: string): string {
const trimmed = (agentId ?? "").trim(); const trimmed = (agentId ?? "").trim();
if (!trimmed) { if (!trimmed) return sanitizeAgentId(resolveDefaultAgentId(cfg));
return sanitizeAgentId(resolveDefaultAgentId(cfg));
}
const normalized = normalizeAgentId(trimmed); const normalized = normalizeAgentId(trimmed);
const agents = listAgents(cfg); const agents = listAgents(cfg);
if (agents.length === 0) { if (agents.length === 0) return sanitizeAgentId(trimmed);
return sanitizeAgentId(trimmed);
}
const match = agents.find((agent) => normalizeAgentId(agent.id) === normalized); const match = agents.find((agent) => normalizeAgentId(agent.id) === normalized);
if (match?.id?.trim()) { if (match?.id?.trim()) return sanitizeAgentId(match.id.trim());
return sanitizeAgentId(match.id.trim());
}
return sanitizeAgentId(resolveDefaultAgentId(cfg)); return sanitizeAgentId(resolveDefaultAgentId(cfg));
} }
@@ -126,9 +117,7 @@ function matchesChannel(
channel: string, channel: string,
): boolean { ): boolean {
const key = normalizeToken(match?.channel); const key = normalizeToken(match?.channel);
if (!key) { if (!key) return false;
return false;
}
return key === channel; return key === channel;
} }
@@ -143,9 +132,7 @@ function matchesPeer(
// Backward compat: normalize "dm" to "direct" in config match rules // Backward compat: normalize "dm" to "direct" in config match rules
const kind = normalizeChatType(m.kind); const kind = normalizeChatType(m.kind);
const id = normalizeId(m.id); const id = normalizeId(m.id);
if (!kind || !id) { if (!kind || !id) return false;
return false;
}
return kind === peer.kind && id === peer.id; return kind === peer.kind && id === peer.id;
} }
@@ -154,17 +141,13 @@ function matchesGuild(
guildId: string, guildId: string,
): boolean { ): boolean {
const id = normalizeId(match?.guildId); const id = normalizeId(match?.guildId);
if (!id) { if (!id) return false;
return false;
}
return id === guildId; return id === guildId;
} }
function matchesTeam(match: { teamId?: string | undefined } | undefined, teamId: string): boolean { function matchesTeam(match: { teamId?: string | undefined } | undefined, teamId: string): boolean {
const id = normalizeId(match?.teamId); const id = normalizeId(match?.teamId);
if (!id) { if (!id) return false;
return false;
}
return id === teamId; return id === teamId;
} }
@@ -176,12 +159,8 @@ export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentR
const teamId = normalizeId(input.teamId); const teamId = normalizeId(input.teamId);
const bindings = listBindings(input.cfg).filter((binding) => { const bindings = listBindings(input.cfg).filter((binding) => {
if (!binding || typeof binding !== "object") { if (!binding || typeof binding !== "object") return false;
return false; if (!matchesChannel(binding.match, channel)) return false;
}
if (!matchesChannel(binding.match, channel)) {
return false;
}
return matchesAccountId(binding.match?.accountId, accountId); return matchesAccountId(binding.match?.accountId, accountId);
}); });
@@ -214,9 +193,14 @@ export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentR
if (peer) { if (peer) {
const peerMatch = bindings.find((b) => matchesPeer(b.match, peer)); const peerMatch = bindings.find((b) => matchesPeer(b.match, peer));
if (peerMatch) { if (peerMatch) return choose(peerMatch.agentId, "binding.peer");
return choose(peerMatch.agentId, "binding.peer"); }
}
if (guildId && memberRoleIds.length > 0) {
const guildRolesMatch = bindings.find(
(b) => matchesGuild(b.match, guildId) && matchesRoles(b.match, memberRoleIds),
);
if (guildRolesMatch) return choose(guildRolesMatch.agentId, "binding.guild+roles");
} }
// Thread parent inheritance: if peer (thread) didn't match, check parent peer binding // Thread parent inheritance: if peer (thread) didn't match, check parent peer binding
@@ -239,26 +223,20 @@ export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentR
if (teamId) { if (teamId) {
const teamMatch = bindings.find((b) => matchesTeam(b.match, teamId)); const teamMatch = bindings.find((b) => matchesTeam(b.match, teamId));
if (teamMatch) { if (teamMatch) return choose(teamMatch.agentId, "binding.team");
return choose(teamMatch.agentId, "binding.team");
}
} }
const accountMatch = bindings.find( const accountMatch = bindings.find(
(b) => (b) =>
b.match?.accountId?.trim() !== "*" && !b.match?.peer && !b.match?.guildId && !b.match?.teamId, b.match?.accountId?.trim() !== "*" && !b.match?.peer && !b.match?.guildId && !b.match?.teamId,
); );
if (accountMatch) { if (accountMatch) return choose(accountMatch.agentId, "binding.account");
return choose(accountMatch.agentId, "binding.account");
}
const anyAccountMatch = bindings.find( const anyAccountMatch = bindings.find(
(b) => (b) =>
b.match?.accountId?.trim() === "*" && !b.match?.peer && !b.match?.guildId && !b.match?.teamId, b.match?.accountId?.trim() === "*" && !b.match?.peer && !b.match?.guildId && !b.match?.teamId,
); );
if (anyAccountMatch) { if (anyAccountMatch) return choose(anyAccountMatch.agentId, "binding.channel");
return choose(anyAccountMatch.agentId, "binding.channel");
}
return choose(resolveDefaultAgentId(input.cfg), "default"); return choose(resolveDefaultAgentId(input.cfg), "default");
} }