diff --git a/src/slack/monitor/events/interactions.test.ts b/src/slack/monitor/events/interactions.test.ts index c4f97acf668..8dc954379c3 100644 --- a/src/slack/monitor/events/interactions.test.ts +++ b/src/slack/monitor/events/interactions.test.ts @@ -519,6 +519,51 @@ describe("registerSlackInteractionEvents", () => { selected_date_time: 1_771_632_300, }, }, + radio_block: { + radio_select: { + type: "radio_buttons", + selected_option: { + text: { type: "plain_text", text: "Blue" }, + value: "blue", + }, + }, + }, + checks_block: { + checks_select: { + type: "checkboxes", + selected_options: [ + { text: { type: "plain_text", text: "A" }, value: "a" }, + { text: { type: "plain_text", text: "B" }, value: "b" }, + ], + }, + }, + number_block: { + number_input: { + type: "number_input", + value: "42.5", + }, + }, + email_block: { + email_input: { + type: "email_text_input", + value: "team@openclaw.ai", + }, + }, + url_block: { + url_input: { + type: "url_text_input", + value: "https://docs.openclaw.ai", + }, + }, + richtext_block: { + richtext_input: { + type: "rich_text_input", + rich_text_value: { + type: "rich_text", + elements: [{ type: "rich_text_section", elements: [] }], + }, + }, + }, }, }, }, @@ -531,11 +576,16 @@ describe("registerSlackInteractionEvents", () => { const payload = JSON.parse(eventText.replace("Slack interaction: ", "")) as { inputs: Array<{ actionId: string; + inputKind?: string; selectedValues?: string[]; selectedLabels?: string[]; selectedDate?: string; selectedTime?: string; selectedDateTime?: number; + inputNumber?: number; + inputEmail?: string; + inputUrl?: string; + richTextValue?: unknown; }>; }; expect(payload.inputs).toEqual( @@ -551,6 +601,39 @@ describe("registerSlackInteractionEvents", () => { expect.objectContaining({ actionId: "date_select", selectedDate: "2026-02-16" }), expect.objectContaining({ actionId: "time_select", selectedTime: "12:45" }), expect.objectContaining({ actionId: "datetime_select", selectedDateTime: 1_771_632_300 }), + expect.objectContaining({ + actionId: "radio_select", + selectedValues: ["blue"], + selectedLabels: ["Blue"], + }), + expect.objectContaining({ + actionId: "checks_select", + selectedValues: ["a", "b"], + selectedLabels: ["A", "B"], + }), + expect.objectContaining({ + actionId: "number_input", + inputKind: "number", + inputNumber: 42.5, + }), + expect.objectContaining({ + actionId: "email_input", + inputKind: "email", + inputEmail: "team@openclaw.ai", + }), + expect.objectContaining({ + actionId: "url_input", + inputKind: "url", + inputUrl: "https://docs.openclaw.ai/", + }), + expect.objectContaining({ + actionId: "richtext_input", + inputKind: "rich_text", + richTextValue: { + type: "rich_text", + elements: [{ type: "rich_text_section", elements: [] }], + }, + }), ]), ); }); diff --git a/src/slack/monitor/events/interactions.ts b/src/slack/monitor/events/interactions.ts index bc0e40c5e80..aa3d8aa2e28 100644 --- a/src/slack/monitor/events/interactions.ts +++ b/src/slack/monitor/events/interactions.ts @@ -23,6 +23,7 @@ type InteractionSummary = { actionId: string; blockId?: string; actionType?: string; + inputKind?: "text" | "number" | "email" | "url" | "rich_text"; value?: string; selectedValues?: string[]; selectedLabels?: string[]; @@ -30,6 +31,10 @@ type InteractionSummary = { selectedTime?: string; selectedDateTime?: number; inputValue?: string; + inputNumber?: number; + inputEmail?: string; + inputUrl?: string; + richTextValue?: unknown; userId?: string; teamId?: string; triggerId?: string; @@ -43,6 +48,7 @@ type ModalInputSummary = { blockId: string; actionId: string; actionType?: string; + inputKind?: "text" | "number" | "email" | "url" | "rich_text"; value?: string; selectedValues?: string[]; selectedLabels?: string[]; @@ -50,6 +56,10 @@ type ModalInputSummary = { selectedTime?: string; selectedDateTime?: number; inputValue?: string; + inputNumber?: number; + inputEmail?: string; + inputUrl?: string; + richTextValue?: unknown; }; function readOptionValues(options: unknown): string[] | undefined { @@ -108,6 +118,7 @@ function summarizeAction( selected_time?: string; selected_date_time?: number; value?: string; + rich_text_value?: unknown; }; const actionType = typed.type; const selectedValues = uniqueNonEmptyStrings([ @@ -124,9 +135,38 @@ function summarizeAction( ...(typed.selected_option?.text?.text ? [typed.selected_option.text.text] : []), ...(readOptionLabels(typed.selected_options) ?? []), ]); + const inputValue = typeof typed.value === "string" ? typed.value : undefined; + const inputNumber = + actionType === "number_input" && inputValue != null ? Number.parseFloat(inputValue) : undefined; + const parsedNumber = Number.isFinite(inputNumber) ? inputNumber : undefined; + const inputEmail = + actionType === "email_text_input" && inputValue?.includes("@") ? inputValue : undefined; + let inputUrl: string | undefined; + if (actionType === "url_text_input" && inputValue) { + try { + // Normalize to a canonical URL string so downstream handlers do not need to reparse. + inputUrl = new URL(inputValue).toString(); + } catch { + inputUrl = undefined; + } + } + const richTextValue = actionType === "rich_text_input" ? typed.rich_text_value : undefined; + const inputKind = + actionType === "number_input" + ? "number" + : actionType === "email_text_input" + ? "email" + : actionType === "url_text_input" + ? "url" + : actionType === "rich_text_input" + ? "rich_text" + : inputValue != null + ? "text" + : undefined; return { actionType, + inputKind, value: typed.value, selectedValues: selectedValues.length > 0 ? selectedValues : undefined, selectedLabels: selectedLabels.length > 0 ? selectedLabels : undefined, @@ -134,7 +174,11 @@ function summarizeAction( selectedTime: typed.selected_time, selectedDateTime: typeof typed.selected_date_time === "number" ? typed.selected_date_time : undefined, - inputValue: typed.value, + inputValue, + inputNumber: parsedNumber, + inputEmail, + inputUrl, + richTextValue, }; }