mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 18:00:54 +00:00
fix: cap slack command menu blocks
This commit is contained in:
@@ -51,6 +51,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Slack/interactive replies: drop overlong Block Kit button URLs while preserving valid callback values, so malformed link buttons no longer make Slack reject the whole interactive reply. Thanks @slackapi.
|
- Slack/interactive replies: drop overlong Block Kit button URLs while preserving valid callback values, so malformed link buttons no longer make Slack reject the whole interactive reply. Thanks @slackapi.
|
||||||
- Slack/commands: truncate native command argument-menu confirmation text to Slack's dialog limit, so long plugin arg names no longer make fallback buttons render invalid Block Kit payloads. Thanks @slackapi.
|
- Slack/commands: truncate native command argument-menu confirmation text to Slack's dialog limit, so long plugin arg names no longer make fallback buttons render invalid Block Kit payloads. Thanks @slackapi.
|
||||||
- Slack/exec approvals: cap native approval metadata context to Slack's element and text limits, so large approval details no longer make Slack reject the approval card. Thanks @slackapi.
|
- Slack/exec approvals: cap native approval metadata context to Slack's element and text limits, so large approval details no longer make Slack reject the approval card. Thanks @slackapi.
|
||||||
|
- Slack/commands: cap native command argument-menu fallback rows to Slack's message block limit, so large plugin choice lists no longer make Slack reject the generated menu. Thanks @slackapi.
|
||||||
- Channels/WhatsApp: require Baileys outbound message ids before marking auto-replies delivered, so transcript text and ack reactions no longer make failed group replies look sent. Fixes #49225. Thanks @TinyTb.
|
- Channels/WhatsApp: require Baileys outbound message ids before marking auto-replies delivered, so transcript text and ack reactions no longer make failed group replies look sent. Fixes #49225. Thanks @TinyTb.
|
||||||
- CLI/update: scope packaged Node compile caches by OpenClaw version and install metadata, so global installs no longer reuse stale compiled chunks after package updates. Thanks @pashpashpash.
|
- CLI/update: scope packaged Node compile caches by OpenClaw version and install metadata, so global installs no longer reuse stale compiled chunks after package updates. Thanks @pashpashpash.
|
||||||
- Channels/Voice call: keep pre-auth webhook in-flight limiting active when socket remote address metadata is missing, so slow-body requests from stripped-IP proxy paths still share the fallback bucket. (#74453) Thanks @davidangularme.
|
- Channels/Voice call: keep pre-auth webhook in-flight limiting active when socket remote address metadata is missing, so slow-body requests from stripped-IP proxy paths still share the fallback bucket. (#74453) Thanks @davidangularme.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ vi.mock("./slash-commands.runtime.js", () => {
|
|||||||
const reportExternalCommand = { key: "reportexternal", nativeName: "reportexternal" };
|
const reportExternalCommand = { key: "reportexternal", nativeName: "reportexternal" };
|
||||||
const reportLongCommand = { key: "reportlong", nativeName: "reportlong" };
|
const reportLongCommand = { key: "reportlong", nativeName: "reportlong" };
|
||||||
const reportLongButtonCommand = { key: "reportlongbutton", nativeName: "reportlongbutton" };
|
const reportLongButtonCommand = { key: "reportlongbutton", nativeName: "reportlongbutton" };
|
||||||
|
const reportHugeButtonCommand = { key: "reporthugebutton", nativeName: "reporthugebutton" };
|
||||||
const unsafeConfirmCommand = { key: "unsafeconfirm", nativeName: "unsafeconfirm" };
|
const unsafeConfirmCommand = { key: "unsafeconfirm", nativeName: "unsafeconfirm" };
|
||||||
const longConfirmCommand = { key: "longconfirm", nativeName: "longconfirm" };
|
const longConfirmCommand = { key: "longconfirm", nativeName: "longconfirm" };
|
||||||
const statusAliasCommand = { key: "status", nativeName: "status" };
|
const statusAliasCommand = { key: "status", nativeName: "status" };
|
||||||
@@ -76,6 +77,9 @@ vi.mock("./slash-commands.runtime.js", () => {
|
|||||||
if (normalized === "reportlongbutton") {
|
if (normalized === "reportlongbutton") {
|
||||||
return reportLongButtonCommand;
|
return reportLongButtonCommand;
|
||||||
}
|
}
|
||||||
|
if (normalized === "reporthugebutton") {
|
||||||
|
return reportHugeButtonCommand;
|
||||||
|
}
|
||||||
if (normalized === "unsafeconfirm") {
|
if (normalized === "unsafeconfirm") {
|
||||||
return unsafeConfirmCommand;
|
return unsafeConfirmCommand;
|
||||||
}
|
}
|
||||||
@@ -124,6 +128,12 @@ vi.mock("./slash-commands.runtime.js", () => {
|
|||||||
acceptsArgs: true,
|
acceptsArgs: true,
|
||||||
args: [],
|
args: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "reporthugebutton",
|
||||||
|
description: "ReportHugeButton",
|
||||||
|
acceptsArgs: true,
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "unsafeconfirm",
|
name: "unsafeconfirm",
|
||||||
description: "UnsafeConfirm",
|
description: "UnsafeConfirm",
|
||||||
@@ -168,6 +178,15 @@ vi.mock("./slash-commands.runtime.js", () => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
if (params.command?.key === "reporthugebutton") {
|
||||||
|
return resolvePeriodMenu(
|
||||||
|
params,
|
||||||
|
Array.from({ length: 250 }, (_v, i) => ({
|
||||||
|
value: `${String(i + 1)}-${"x".repeat(170)}`,
|
||||||
|
label: `Long button label ${i + 1}`,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (params.command?.key === "reportcompact") {
|
if (params.command?.key === "reportcompact") {
|
||||||
return resolvePeriodMenu(params, baseReportPeriodChoices);
|
return resolvePeriodMenu(params, baseReportPeriodChoices);
|
||||||
}
|
}
|
||||||
@@ -446,6 +465,7 @@ describe("Slack native command argument menus", () => {
|
|||||||
let reportExternalHandler: (args: unknown) => Promise<void>;
|
let reportExternalHandler: (args: unknown) => Promise<void>;
|
||||||
let reportLongHandler: (args: unknown) => Promise<void>;
|
let reportLongHandler: (args: unknown) => Promise<void>;
|
||||||
let reportLongButtonHandler: (args: unknown) => Promise<void>;
|
let reportLongButtonHandler: (args: unknown) => Promise<void>;
|
||||||
|
let reportHugeButtonHandler: (args: unknown) => Promise<void>;
|
||||||
let unsafeConfirmHandler: (args: unknown) => Promise<void>;
|
let unsafeConfirmHandler: (args: unknown) => Promise<void>;
|
||||||
let longConfirmHandler: (args: unknown) => Promise<void>;
|
let longConfirmHandler: (args: unknown) => Promise<void>;
|
||||||
let agentStatusHandler: (args: unknown) => Promise<void>;
|
let agentStatusHandler: (args: unknown) => Promise<void>;
|
||||||
@@ -465,6 +485,11 @@ describe("Slack native command argument menus", () => {
|
|||||||
"/reportlongbutton",
|
"/reportlongbutton",
|
||||||
"/reportlongbutton",
|
"/reportlongbutton",
|
||||||
);
|
);
|
||||||
|
reportHugeButtonHandler = requireHandler(
|
||||||
|
harness.commands,
|
||||||
|
"/reporthugebutton",
|
||||||
|
"/reporthugebutton",
|
||||||
|
);
|
||||||
unsafeConfirmHandler = requireHandler(harness.commands, "/unsafeconfirm", "/unsafeconfirm");
|
unsafeConfirmHandler = requireHandler(harness.commands, "/unsafeconfirm", "/unsafeconfirm");
|
||||||
longConfirmHandler = requireHandler(harness.commands, "/longconfirm", "/longconfirm");
|
longConfirmHandler = requireHandler(harness.commands, "/longconfirm", "/longconfirm");
|
||||||
agentStatusHandler = requireHandler(harness.commands, "/agentstatus", "/agentstatus");
|
agentStatusHandler = requireHandler(harness.commands, "/agentstatus", "/agentstatus");
|
||||||
@@ -601,6 +626,18 @@ describe("Slack native command argument menus", () => {
|
|||||||
expect(firstElement?.confirm).toBeTruthy();
|
expect(firstElement?.confirm).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("caps large button fallback menus to Slack's block limit", async () => {
|
||||||
|
const { respond } = await runCommandHandler(reportHugeButtonHandler);
|
||||||
|
expect(respond).toHaveBeenCalledTimes(1);
|
||||||
|
const payload = respond.mock.calls[0]?.[0] as {
|
||||||
|
blocks?: Array<{ type: string; elements?: unknown[] }>;
|
||||||
|
};
|
||||||
|
const actionBlocks = (payload.blocks ?? []).filter((block) => block.type === "actions");
|
||||||
|
expect(payload.blocks).toHaveLength(50);
|
||||||
|
expect(actionBlocks).toHaveLength(47);
|
||||||
|
expect(actionBlocks.at(-1)?.elements).toHaveLength(5);
|
||||||
|
});
|
||||||
|
|
||||||
it("shows an overflow menu when choices fit compact range", async () => {
|
it("shows an overflow menu when choices fit compact range", async () => {
|
||||||
const element = await getFirstActionElementFromCommand(reportCompactHandler);
|
const element = await getFirstActionElementFromCommand(reportCompactHandler);
|
||||||
expect(element?.type).toBe("overflow");
|
expect(element?.type).toBe("overflow");
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
normalizeOptionalString,
|
normalizeOptionalString,
|
||||||
} from "openclaw/plugin-sdk/text-runtime";
|
} from "openclaw/plugin-sdk/text-runtime";
|
||||||
import type { ResolvedSlackAccount } from "../accounts.js";
|
import type { ResolvedSlackAccount } from "../accounts.js";
|
||||||
|
import { SLACK_MAX_BLOCKS } from "../blocks-input.js";
|
||||||
import { truncateSlackText } from "../truncate.js";
|
import { truncateSlackText } from "../truncate.js";
|
||||||
import { resolveSlackAllowListMatch, resolveSlackUserAllowed } from "./allow-list.js";
|
import { resolveSlackAllowListMatch, resolveSlackUserAllowed } from "./allow-list.js";
|
||||||
import { resolveSlackEffectiveAllowFrom } from "./auth.js";
|
import { resolveSlackEffectiveAllowFrom } from "./auth.js";
|
||||||
@@ -57,6 +58,8 @@ const SLACK_COMMAND_ARG_SELECT_OPTION_VALUE_MAX = 75;
|
|||||||
const SLACK_COMMAND_ARG_BUTTON_TEXT_MAX = 75;
|
const SLACK_COMMAND_ARG_BUTTON_TEXT_MAX = 75;
|
||||||
const SLACK_COMMAND_ARG_CONFIRM_TEXT_MAX = 300;
|
const SLACK_COMMAND_ARG_CONFIRM_TEXT_MAX = 300;
|
||||||
const SLACK_HEADER_TEXT_MAX = 150;
|
const SLACK_HEADER_TEXT_MAX = 150;
|
||||||
|
const SLACK_COMMAND_ARG_CHROME_BLOCKS = 3;
|
||||||
|
const SLACK_COMMAND_ARG_ACTION_BLOCKS_MAX = SLACK_MAX_BLOCKS - SLACK_COMMAND_ARG_CHROME_BLOCKS;
|
||||||
let slashCommandsRuntimePromise: Promise<typeof import("./slash-commands.runtime.js")> | null =
|
let slashCommandsRuntimePromise: Promise<typeof import("./slash-commands.runtime.js")> | null =
|
||||||
null;
|
null;
|
||||||
let slashDispatchRuntimePromise: Promise<typeof import("./slash-dispatch.runtime.js")> | null =
|
let slashDispatchRuntimePromise: Promise<typeof import("./slash-dispatch.runtime.js")> | null =
|
||||||
@@ -338,6 +341,7 @@ function buildSlackCommandArgMenuBlocks(params: {
|
|||||||
`Select one option to continue /${params.command} (${params.arg})`,
|
`Select one option to continue /${params.command} (${params.arg})`,
|
||||||
3000,
|
3000,
|
||||||
);
|
);
|
||||||
|
const visibleRows = rows.slice(0, SLACK_COMMAND_ARG_ACTION_BLOCKS_MAX);
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
type: "header",
|
type: "header",
|
||||||
@@ -351,7 +355,7 @@ function buildSlackCommandArgMenuBlocks(params: {
|
|||||||
type: "context",
|
type: "context",
|
||||||
elements: [{ type: "mrkdwn", text: contextText }],
|
elements: [{ type: "mrkdwn", text: contextText }],
|
||||||
},
|
},
|
||||||
...rows,
|
...visibleRows,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user