From 0f5ab771565292ba4c8a532384224c04351aa048 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Tue, 21 Apr 2026 19:55:57 -0400 Subject: [PATCH] fix: preserve discord argument reply visibility --- .../discord/src/monitor/native-command-ui.ts | 3 +- .../native-command.command-arg.test.ts | 97 +++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 extensions/discord/src/monitor/native-command.command-arg.test.ts diff --git a/extensions/discord/src/monitor/native-command-ui.ts b/extensions/discord/src/monitor/native-command-ui.ts index 4f54cc3e855..a969c7c1eaa 100644 --- a/extensions/discord/src/monitor/native-command-ui.ts +++ b/extensions/discord/src/monitor/native-command-ui.ts @@ -35,6 +35,7 @@ import { withTimeout, } from "openclaw/plugin-sdk/text-runtime"; import { resolveDiscordChannelNameSafe } from "./channel-access.js"; +import { resolveDiscordSlashCommandConfig } from "./commands.js"; import { resolveDiscordChannelInfo } from "./message-utils.js"; import { readDiscordModelPickerRecentModels, @@ -919,7 +920,7 @@ export async function handleDiscordCommandArgInteraction(params: { sessionPrefix: ctx.sessionPrefix, preferFollowUp: true, threadBindings: ctx.threadBindings, - responseEphemeral: true, + responseEphemeral: resolveDiscordSlashCommandConfig(ctx.discordConfig?.slashCommand).ephemeral, }); } diff --git a/extensions/discord/src/monitor/native-command.command-arg.test.ts b/extensions/discord/src/monitor/native-command.command-arg.test.ts new file mode 100644 index 00000000000..4e7b13d2343 --- /dev/null +++ b/extensions/discord/src/monitor/native-command.command-arg.test.ts @@ -0,0 +1,97 @@ +import type { ChatCommandDefinition } from "openclaw/plugin-sdk/command-auth"; +import * as commandRegistryModule from "openclaw/plugin-sdk/command-auth"; +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { + createDiscordCommandArgFallbackButton, + type DispatchDiscordCommandInteraction, +} from "./native-command-ui.js"; +import { createNoopThreadBindingManager } from "./thread-bindings.js"; + +type CommandArgContext = Parameters[0]["ctx"]; +type CommandArgButton = ReturnType; +type CommandArgInteraction = Parameters[0]; +type CommandArgData = Parameters[1]; + +function createCommandDefinition(): ChatCommandDefinition { + return { + key: "think", + nativeName: "think", + description: "Set thinking level", + textAliases: ["/think"], + acceptsArgs: true, + args: [ + { + name: "level", + description: "Thinking level", + type: "string", + required: true, + }, + ], + argsParsing: "none", + scope: "native", + }; +} + +function createContext( + discordConfig: NonNullable["discord"], +): CommandArgContext { + const cfg = { + channels: { + discord: discordConfig, + }, + } as OpenClawConfig; + return { + cfg, + discordConfig, + accountId: "default", + sessionPrefix: "discord:slash", + threadBindings: createNoopThreadBindingManager("default"), + }; +} + +function createInteraction(): CommandArgInteraction { + return { + user: { + id: "owner", + username: "tester", + globalName: "Tester", + }, + update: vi.fn().mockResolvedValue({ ok: true }), + } as unknown as CommandArgInteraction; +} + +async function safeInteractionCall(_label: string, fn: () => Promise): Promise { + return await fn(); +} + +describe("discord command argument fallback", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("preserves public slash command visibility for selected argument follow-ups", async () => { + const commandDefinition = createCommandDefinition(); + vi.spyOn(commandRegistryModule, "findCommandByNativeName").mockReturnValue(commandDefinition); + const dispatchSpy = vi.fn().mockResolvedValue(); + const button = createDiscordCommandArgFallbackButton({ + ctx: createContext({ slashCommand: { ephemeral: false } }), + safeInteractionCall, + dispatchCommandInteraction: dispatchSpy, + }); + + await button.run(createInteraction(), { + command: "think", + arg: "level", + value: "high", + user: "owner", + } satisfies CommandArgData); + + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + prompt: "/think high", + responseEphemeral: false, + }), + ); + }); +});