mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 04:52:43 +00:00
Slack: preserve slash options receiver binding
This commit is contained in:
@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Slack/Slash commands: preserve the Bolt app receiver when registering external select options handlers so monitor startup does not crash on runtimes that require bound `app.options` calls. (#23209) Thanks @0xgaia.
|
||||||
- Agents/Ollama: preserve unsafe integer tool-call arguments as exact strings during NDJSON parsing, preventing large numeric IDs from being rounded before tool execution. (#23170) Thanks @BestJoester.
|
- Agents/Ollama: preserve unsafe integer tool-call arguments as exact strings during NDJSON parsing, preventing large numeric IDs from being rounded before tool execution. (#23170) Thanks @BestJoester.
|
||||||
- Cron/Gateway: keep `cron.list` and `cron.status` responsive during startup catch-up by avoiding a long-held cron lock while missed jobs execute. (#23106) Thanks @jayleekr.
|
- Cron/Gateway: keep `cron.list` and `cron.status` responsive during startup catch-up by avoiding a long-held cron lock while missed jobs execute. (#23106) Thanks @jayleekr.
|
||||||
- Gateway/Config reload: compare array-valued config paths structurally during diffing so unchanged `memory.qmd.paths` and `memory.qmd.scope.rules` no longer trigger false restart-required reloads. (#23185) Thanks @rex05ai.
|
- Gateway/Config reload: compare array-valued config paths structurally during diffing so unchanged `memory.qmd.paths` and `memory.qmd.scope.rules` no longer trigger false restart-required reloads. (#23185) Thanks @rex05ai.
|
||||||
|
|||||||
@@ -370,6 +370,62 @@ describe("Slack native command argument menus", () => {
|
|||||||
harness.postEphemeral.mockClear();
|
harness.postEphemeral.mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("registers options handlers without losing app receiver binding", async () => {
|
||||||
|
const commands = new Map<string, (args: unknown) => Promise<void>>();
|
||||||
|
const actions = new Map<string, (args: unknown) => Promise<void>>();
|
||||||
|
const options = new Map<string, (args: unknown) => Promise<void>>();
|
||||||
|
const postEphemeral = vi.fn().mockResolvedValue({ ok: true });
|
||||||
|
const app = {
|
||||||
|
client: { chat: { postEphemeral } },
|
||||||
|
command: (name: string, handler: (args: unknown) => Promise<void>) => {
|
||||||
|
commands.set(name, handler);
|
||||||
|
},
|
||||||
|
action: (id: string, handler: (args: unknown) => Promise<void>) => {
|
||||||
|
actions.set(id, handler);
|
||||||
|
},
|
||||||
|
options: function (this: unknown, id: string, handler: (args: unknown) => Promise<void>) {
|
||||||
|
expect(this).toBe(app);
|
||||||
|
options.set(id, handler);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const ctx = {
|
||||||
|
cfg: { commands: { native: true, nativeSkills: false } },
|
||||||
|
runtime: {},
|
||||||
|
botToken: "bot-token",
|
||||||
|
botUserId: "bot",
|
||||||
|
teamId: "T1",
|
||||||
|
allowFrom: ["*"],
|
||||||
|
dmEnabled: true,
|
||||||
|
dmPolicy: "open",
|
||||||
|
groupDmEnabled: false,
|
||||||
|
groupDmChannels: [],
|
||||||
|
defaultRequireMention: true,
|
||||||
|
groupPolicy: "open",
|
||||||
|
useAccessGroups: false,
|
||||||
|
channelsConfig: undefined,
|
||||||
|
slashCommand: {
|
||||||
|
enabled: true,
|
||||||
|
name: "openclaw",
|
||||||
|
ephemeral: true,
|
||||||
|
sessionPrefix: "slack:slash",
|
||||||
|
},
|
||||||
|
textLimit: 4000,
|
||||||
|
app,
|
||||||
|
isChannelAllowed: () => true,
|
||||||
|
resolveChannelName: async () => ({ name: "dm", type: "im" }),
|
||||||
|
resolveUserName: async () => ({ name: "Ada" }),
|
||||||
|
} as unknown;
|
||||||
|
const account = {
|
||||||
|
accountId: "acct",
|
||||||
|
config: { commands: { native: true, nativeSkills: false } },
|
||||||
|
} as unknown;
|
||||||
|
|
||||||
|
await registerCommands(ctx, account);
|
||||||
|
expect(commands.size).toBeGreaterThan(0);
|
||||||
|
expect(actions.has("openclaw_cmdarg")).toBe(true);
|
||||||
|
expect(options.has("openclaw_cmdarg")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("shows a button menu when required args are omitted", async () => {
|
it("shows a button menu when required args are omitted", async () => {
|
||||||
const { respond } = await runCommandHandler(usageHandler);
|
const { respond } = await runCommandHandler(usageHandler);
|
||||||
const actions = expectArgMenuLayout(respond);
|
const actions = expectArgMenuLayout(respond);
|
||||||
|
|||||||
@@ -734,21 +734,19 @@ export async function registerSlackMonitorSlashCommands(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const registerArgOptions = () => {
|
const registerArgOptions = () => {
|
||||||
const optionsHandler = (
|
const appWithOptions = ctx.app as unknown as {
|
||||||
ctx.app as unknown as {
|
options?: (
|
||||||
options?: (
|
actionId: string,
|
||||||
actionId: string,
|
handler: (args: {
|
||||||
handler: (args: {
|
ack: (payload: { options: unknown[] }) => Promise<void>;
|
||||||
ack: (payload: { options: unknown[] }) => Promise<void>;
|
body: unknown;
|
||||||
body: unknown;
|
}) => Promise<void>,
|
||||||
}) => Promise<void>,
|
) => void;
|
||||||
) => void;
|
};
|
||||||
}
|
if (typeof appWithOptions.options !== "function") {
|
||||||
).options;
|
|
||||||
if (typeof optionsHandler !== "function") {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
optionsHandler(SLACK_COMMAND_ARG_ACTION_ID, async ({ ack, body }) => {
|
appWithOptions.options(SLACK_COMMAND_ARG_ACTION_ID, async ({ ack, body }) => {
|
||||||
const typedBody = body as {
|
const typedBody = body as {
|
||||||
value?: string;
|
value?: string;
|
||||||
user?: { id?: string };
|
user?: { id?: string };
|
||||||
|
|||||||
Reference in New Issue
Block a user