mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(slack): preserve time colons in interactive labels (#99877)
* fix(slack): preserve time colons in button labels * fix(slack): preserve clock labels in interactive replies --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
co-authored by
Peter Steinberger
parent
79d2e568a0
commit
bd2740fedc
@@ -94,4 +94,101 @@ describe("compileSlackInteractiveReplies", () => {
|
||||
);
|
||||
expect(result.interactive).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps time-style colons in Slack button labels", () => {
|
||||
const result = compileSlackInteractiveReplies({
|
||||
text: "[[slack_buttons: Fr 10.07. 9:00:slot_fr_0900, Mo 13.07. 10:45:slot_mo_1045, Today 11:30:ticket:123, Mon 14:30-16:00:slot_range]]",
|
||||
});
|
||||
|
||||
expect(result.interactive).toEqual({
|
||||
blocks: [
|
||||
{
|
||||
type: "buttons",
|
||||
buttons: [
|
||||
{ label: "Fr 10.07. 9:00", value: "slot_fr_0900" },
|
||||
{ label: "Mo 13.07. 10:45", value: "slot_mo_1045" },
|
||||
{ label: "Today 11:30", value: "ticket:123" },
|
||||
{ label: "Mon 14:30-16:00", value: "slot_range" },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps button style suffixes after time-style labels", () => {
|
||||
const result = compileSlackInteractiveReplies({
|
||||
text: "[[slack_buttons: Fr 10.07. 9:00:slot_fr_0900:primary, Later 10:45:slot_later:danger]]",
|
||||
});
|
||||
|
||||
expect(result.interactive).toEqual({
|
||||
blocks: [
|
||||
{
|
||||
type: "buttons",
|
||||
buttons: [
|
||||
{ label: "Fr 10.07. 9:00", value: "slot_fr_0900", style: "primary" },
|
||||
{ label: "Later 10:45", value: "slot_later", style: "danger" },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves colon-containing Slack callback values", () => {
|
||||
const result = compileSlackInteractiveReplies({
|
||||
text: "[[slack_buttons: Model v2:01:open, Step 2:30-day:open, Timed:ticket:9:00:id, Today 11:30:12:34:id, Deny:/approve plugin:approval-123 deny]]",
|
||||
});
|
||||
|
||||
expect(result.interactive).toEqual({
|
||||
blocks: [
|
||||
{
|
||||
type: "buttons",
|
||||
buttons: [
|
||||
{ label: "Model v2", value: "01:open" },
|
||||
{ label: "Step 2", value: "30-day:open" },
|
||||
{ label: "Timed", value: "ticket:9:00:id" },
|
||||
{ label: "Today 11:30", value: "12:34:id" },
|
||||
{ label: "Deny", value: "/approve plugin:approval-123 deny" },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps single-colon button entries unchanged when the value matches a style name", () => {
|
||||
const result = compileSlackInteractiveReplies({
|
||||
text: "[[slack_buttons: Primary:primary, Danger:danger, Fr 10.07. 9:00:primary]]",
|
||||
});
|
||||
|
||||
expect(result.interactive).toEqual({
|
||||
blocks: [
|
||||
{
|
||||
type: "buttons",
|
||||
buttons: [
|
||||
{ label: "Primary", value: "primary" },
|
||||
{ label: "Danger", value: "danger" },
|
||||
{ label: "Fr 10.07. 9:00", value: "primary" },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps time-style colons in Slack select option labels", () => {
|
||||
const result = compileSlackInteractiveReplies({
|
||||
text: "[[slack_select: Pick a time | Fr 10.07. 9:00:slot_fr_0900, Mo 13.07. 10:45:slot_mo_1045]]",
|
||||
});
|
||||
|
||||
expect(result.interactive).toEqual({
|
||||
blocks: [
|
||||
{
|
||||
type: "select",
|
||||
placeholder: "Pick a time",
|
||||
options: [
|
||||
{ label: "Fr 10.07. 9:00", value: "slot_fr_0900" },
|
||||
{ label: "Mo 13.07. 10:45", value: "slot_mo_1045" },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,12 +21,36 @@ type SlackChoice = {
|
||||
style?: "primary" | "secondary" | "success" | "danger";
|
||||
};
|
||||
|
||||
function resolveChoiceDelimiter(value: string): number {
|
||||
const firstDelimiter = value.indexOf(":");
|
||||
if (firstDelimiter === -1) {
|
||||
return -1;
|
||||
}
|
||||
const prefix = value.slice(0, firstDelimiter);
|
||||
const suffix = value.slice(firstDelimiter + 1);
|
||||
const startsWithClockLabel = /\b(?:[01]?\d|2[0-3])$/.test(prefix) && /^[0-5]\d/.test(suffix);
|
||||
if (!startsWithClockLabel) {
|
||||
return firstDelimiter;
|
||||
}
|
||||
|
||||
// Legacy syntax makes label and callback colons ambiguous. Recognize only a
|
||||
// complete clock or dash-separated range followed by the real separator/end.
|
||||
const clockEnd = firstDelimiter + 3;
|
||||
const labelSuffix =
|
||||
value.slice(clockEnd).match(/^(?:\s*[-–—]\s*(?:[01]?\d|2[0-3]):[0-5]\d)*\s*/)?.[0] ?? "";
|
||||
const delimiter = clockEnd + labelSuffix.length;
|
||||
if (value[delimiter] === ":") {
|
||||
return delimiter;
|
||||
}
|
||||
return delimiter === value.length ? -1 : firstDelimiter;
|
||||
}
|
||||
|
||||
function parseChoice(raw: string, options?: { allowStyle?: boolean }): SlackChoice | null {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
const delimiter = trimmed.indexOf(":");
|
||||
const delimiter = resolveChoiceDelimiter(trimmed);
|
||||
if (delimiter === -1) {
|
||||
return {
|
||||
label: trimmed,
|
||||
|
||||
Reference in New Issue
Block a user