mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-13 16:20:36 +00:00
fix(discord): preserve native command session keys
This commit is contained in:
@@ -325,6 +325,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Telegram/DM streaming transport parity: use message preview transport for all DM streaming lanes so final delivery can edit the active preview instead of sending duplicate finals. Landed from contributor PR #38906 by @gambletan. Thanks @gambletan.
|
- Telegram/DM streaming transport parity: use message preview transport for all DM streaming lanes so final delivery can edit the active preview instead of sending duplicate finals. Landed from contributor PR #38906 by @gambletan. Thanks @gambletan.
|
||||||
- Telegram/send retry safety: retry non-idempotent send paths only for pre-connect failures and make custom retry predicates strict, preventing ambiguous reconnect retries from sending duplicate messages. Landed from contributor PR #34238 by @hal-crackbot. Thanks @hal-crackbot.
|
- Telegram/send retry safety: retry non-idempotent send paths only for pre-connect failures and make custom retry predicates strict, preventing ambiguous reconnect retries from sending duplicate messages. Landed from contributor PR #34238 by @hal-crackbot. Thanks @hal-crackbot.
|
||||||
- Discord/DM session-key normalization: rewrite legacy `discord:dm:*` and phantom direct-message `discord:channel:<user>` session keys to `discord:direct:*` when the sender matches, so multi-agent Discord DMs stop falling into empty channel-shaped sessions and resume replying correctly.
|
- Discord/DM session-key normalization: rewrite legacy `discord:dm:*` and phantom direct-message `discord:channel:<user>` session keys to `discord:direct:*` when the sender matches, so multi-agent Discord DMs stop falling into empty channel-shaped sessions and resume replying correctly.
|
||||||
|
- Discord/native slash session fallback: treat empty configured bound-session keys as missing so `/status` and other native commands fall back to the routed slash session and routed channel session instead of blanking Discord session keys in normal channel bindings.
|
||||||
|
|
||||||
## 2026.3.2
|
## 2026.3.2
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,61 @@ describe("Discord native plugin command dispatch", () => {
|
|||||||
expectBoundSessionDispatch(dispatchSpy, boundSessionKey);
|
expectBoundSessionDispatch(dispatchSpy, boundSessionKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("falls back to the routed slash and channel session keys when no bound session exists", async () => {
|
||||||
|
const guildId = "1459246755253325866";
|
||||||
|
const channelId = "1478836151241412759";
|
||||||
|
const cfg = {
|
||||||
|
commands: {
|
||||||
|
useAccessGroups: false,
|
||||||
|
},
|
||||||
|
bindings: [
|
||||||
|
{
|
||||||
|
agentId: "qwen",
|
||||||
|
match: {
|
||||||
|
channel: "discord",
|
||||||
|
accountId: "default",
|
||||||
|
peer: { kind: "channel", id: channelId },
|
||||||
|
guildId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
channels: {
|
||||||
|
discord: {
|
||||||
|
guilds: {
|
||||||
|
[guildId]: {
|
||||||
|
channels: {
|
||||||
|
[channelId]: { allow: true, requireMention: false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as OpenClawConfig;
|
||||||
|
const command = createStatusCommand(cfg);
|
||||||
|
const interaction = createInteraction({
|
||||||
|
channelType: ChannelType.GuildText,
|
||||||
|
channelId,
|
||||||
|
guildId,
|
||||||
|
guildName: "Ops",
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.spyOn(pluginCommandsModule, "matchPluginCommand").mockReturnValue(null);
|
||||||
|
const dispatchSpy = createDispatchSpy();
|
||||||
|
|
||||||
|
await (command as { run: (interaction: unknown) => Promise<void> }).run(interaction as unknown);
|
||||||
|
|
||||||
|
expect(dispatchSpy).toHaveBeenCalledTimes(1);
|
||||||
|
const dispatchCall = dispatchSpy.mock.calls[0]?.[0] as {
|
||||||
|
ctx?: { SessionKey?: string; CommandTargetSessionKey?: string };
|
||||||
|
};
|
||||||
|
expect(dispatchCall.ctx?.SessionKey).toBe("agent:qwen:discord:slash:owner");
|
||||||
|
expect(dispatchCall.ctx?.CommandTargetSessionKey).toBe(
|
||||||
|
"agent:qwen:discord:channel:1478836151241412759",
|
||||||
|
);
|
||||||
|
expect(persistentBindingMocks.resolveConfiguredAcpBindingRecord).toHaveBeenCalledTimes(1);
|
||||||
|
expect(persistentBindingMocks.ensureConfiguredAcpBindingSession).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("routes Discord DM native slash commands through configured ACP bindings", async () => {
|
it("routes Discord DM native slash commands through configured ACP bindings", async () => {
|
||||||
const channelId = "dm-1";
|
const channelId = "dm-1";
|
||||||
const boundSessionKey = "agent:codex:acp:binding:discord:default:dmfeedface";
|
const boundSessionKey = "agent:codex:acp:binding:discord:default:dmfeedface";
|
||||||
|
|||||||
@@ -1644,7 +1644,7 @@ async function dispatchDiscordCommandInteraction(params: {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const configuredBoundSessionKey = configuredRoute?.boundSessionKey ?? "";
|
const configuredBoundSessionKey = configuredRoute?.boundSessionKey?.trim() || undefined;
|
||||||
const boundSessionKey = threadBinding?.targetSessionKey?.trim() || configuredBoundSessionKey;
|
const boundSessionKey = threadBinding?.targetSessionKey?.trim() || configuredBoundSessionKey;
|
||||||
const boundAgentId = boundSessionKey ? resolveAgentIdFromSessionKey(boundSessionKey) : undefined;
|
const boundAgentId = boundSessionKey ? resolveAgentIdFromSessionKey(boundSessionKey) : undefined;
|
||||||
const effectiveRoute = boundSessionKey
|
const effectiveRoute = boundSessionKey
|
||||||
|
|||||||
Reference in New Issue
Block a user