mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-21 16:41:56 +00:00
Slack: preserve slash options receiver binding
This commit is contained in:
@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### 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.
|
||||
- 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.
|
||||
|
||||
@@ -370,6 +370,62 @@ describe("Slack native command argument menus", () => {
|
||||
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 () => {
|
||||
const { respond } = await runCommandHandler(usageHandler);
|
||||
const actions = expectArgMenuLayout(respond);
|
||||
|
||||
@@ -734,21 +734,19 @@ export async function registerSlackMonitorSlashCommands(params: {
|
||||
}
|
||||
|
||||
const registerArgOptions = () => {
|
||||
const optionsHandler = (
|
||||
ctx.app as unknown as {
|
||||
options?: (
|
||||
actionId: string,
|
||||
handler: (args: {
|
||||
ack: (payload: { options: unknown[] }) => Promise<void>;
|
||||
body: unknown;
|
||||
}) => Promise<void>,
|
||||
) => void;
|
||||
}
|
||||
).options;
|
||||
if (typeof optionsHandler !== "function") {
|
||||
const appWithOptions = ctx.app as unknown as {
|
||||
options?: (
|
||||
actionId: string,
|
||||
handler: (args: {
|
||||
ack: (payload: { options: unknown[] }) => Promise<void>;
|
||||
body: unknown;
|
||||
}) => Promise<void>,
|
||||
) => void;
|
||||
};
|
||||
if (typeof appWithOptions.options !== "function") {
|
||||
return;
|
||||
}
|
||||
optionsHandler(SLACK_COMMAND_ARG_ACTION_ID, async ({ ack, body }) => {
|
||||
appWithOptions.options(SLACK_COMMAND_ARG_ACTION_ID, async ({ ack, body }) => {
|
||||
const typedBody = body as {
|
||||
value?: string;
|
||||
user?: { id?: string };
|
||||
|
||||
Reference in New Issue
Block a user