diff --git a/src/slack/monitor/events/interactions.test.ts b/src/slack/monitor/events/interactions.test.ts index 94a9bdd43cb..c4f97acf668 100644 --- a/src/slack/monitor/events/interactions.test.ts +++ b/src/slack/monitor/events/interactions.test.ts @@ -165,7 +165,7 @@ describe("registerSlackInteractionEvents", () => { expect(app.client.chat.update).toHaveBeenCalledTimes(1); }); - it("captures select values and skips chat.update for non-button actions", async () => { + it("captures select values and updates action rows for non-button actions", async () => { enqueueSystemEventMock.mockReset(); const { ctx, app, getHandler } = createContext(); registerSlackInteractionEvents({ ctx: ctx as never }); @@ -205,7 +205,19 @@ describe("registerSlackInteractionEvents", () => { expect(payload.actionType).toBe("static_select"); expect(payload.selectedValues).toEqual(["canary"]); expect(payload.selectedLabels).toEqual(["Canary"]); - expect(app.client.chat.update).not.toHaveBeenCalled(); + expect(app.client.chat.update).toHaveBeenCalledTimes(1); + expect(app.client.chat.update).toHaveBeenCalledWith( + expect.objectContaining({ + channel: "C1", + ts: "111.222", + blocks: [ + { + type: "context", + elements: [{ type: "mrkdwn", text: ":white_check_mark: *Canary* selected" }], + }, + ], + }), + ); }); it("falls back to container channel and message timestamps", async () => { @@ -254,6 +266,62 @@ describe("registerSlackInteractionEvents", () => { expect(app.client.chat.update).not.toHaveBeenCalled(); }); + it("summarizes multi-select confirmations in updated message rows", async () => { + enqueueSystemEventMock.mockReset(); + const { ctx, app, getHandler } = createContext(); + registerSlackInteractionEvents({ ctx: ctx as never }); + const handler = getHandler(); + expect(handler).toBeTruthy(); + + const ack = vi.fn().mockResolvedValue(undefined); + await handler!({ + ack, + body: { + user: { id: "U222" }, + channel: { id: "C2" }, + message: { + ts: "333.444", + text: "fallback", + blocks: [ + { + type: "actions", + block_id: "multi_block", + elements: [{ type: "multi_static_select", action_id: "openclaw:multi" }], + }, + ], + }, + }, + action: { + type: "multi_static_select", + action_id: "openclaw:multi", + block_id: "multi_block", + selected_options: [ + { text: { type: "plain_text", text: "Alpha" }, value: "alpha" }, + { text: { type: "plain_text", text: "Beta" }, value: "beta" }, + { text: { type: "plain_text", text: "Gamma" }, value: "gamma" }, + { text: { type: "plain_text", text: "Delta" }, value: "delta" }, + ], + }, + }); + + expect(ack).toHaveBeenCalled(); + expect(app.client.chat.update).toHaveBeenCalledTimes(1); + expect(app.client.chat.update).toHaveBeenCalledWith( + expect.objectContaining({ + channel: "C2", + ts: "333.444", + blocks: [ + { + type: "context", + elements: [ + { type: "mrkdwn", text: ":white_check_mark: *Alpha, Beta, Gamma +1* selected" }, + ], + }, + ], + }), + ); + }); + it("captures expanded selection and temporal payload fields", async () => { enqueueSystemEventMock.mockReset(); const { ctx, getHandler } = createContext(); diff --git a/src/slack/monitor/events/interactions.ts b/src/slack/monitor/events/interactions.ts index ccdda9c331d..bc0e40c5e80 100644 --- a/src/slack/monitor/events/interactions.ts +++ b/src/slack/monitor/events/interactions.ts @@ -147,6 +147,36 @@ function isBulkActionsBlock(block: InteractionMessageBlock): boolean { ); } +function formatInteractionSelectionLabel(params: { + actionId: string; + summary: Omit; + buttonText?: string; +}): string { + if (params.summary.actionType === "button" && params.buttonText?.trim()) { + return params.buttonText.trim(); + } + if (params.summary.selectedLabels?.length) { + if (params.summary.selectedLabels.length <= 3) { + return params.summary.selectedLabels.join(", "); + } + return `${params.summary.selectedLabels.slice(0, 3).join(", ")} +${ + params.summary.selectedLabels.length - 3 + }`; + } + if (params.summary.selectedValues?.length) { + if (params.summary.selectedValues.length <= 3) { + return params.summary.selectedValues.join(", "); + } + return `${params.summary.selectedValues.slice(0, 3).join(", ")} +${ + params.summary.selectedValues.length - 3 + }`; + } + if (params.summary.value?.trim()) { + return params.summary.value.trim(); + } + return params.actionId; +} + function summarizeViewState(values: unknown): ModalInputSummary[] { if (!values || typeof values !== "object") { return []; @@ -274,17 +304,21 @@ export function registerSlackInteractionEvents(params: { ctx: SlackMonitorContex return; } - if (typedAction.type !== "button") { + if (!blockId) { return; } - const buttonText = typedAction.text?.text ?? actionId; + const selectedLabel = formatInteractionSelectionLabel({ + actionId, + summary: actionSummary, + buttonText: typedAction.text?.text, + }); let updatedBlocks = originalBlocks.map((block) => { const typedBlock = block as InteractionMessageBlock; if (typedBlock.type === "actions" && typedBlock.block_id === blockId) { return { type: "context", - elements: [{ type: "mrkdwn", text: `:white_check_mark: *${buttonText}* selected` }], + elements: [{ type: "mrkdwn", text: `:white_check_mark: *${selectedLabel}* selected` }], }; } return block;