mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-25 00:03:03 +00:00
125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
import {
|
|
presentationToInteractiveReply,
|
|
reduceInteractiveReply,
|
|
} from "openclaw/plugin-sdk/interactive-runtime";
|
|
import type {
|
|
InteractiveButtonStyle,
|
|
InteractiveReply,
|
|
MessagePresentation,
|
|
} from "openclaw/plugin-sdk/interactive-runtime";
|
|
import type {
|
|
DiscordComponentButtonSpec,
|
|
DiscordComponentButtonStyle,
|
|
DiscordComponentMessageSpec,
|
|
} from "./components.types.js";
|
|
|
|
function resolveDiscordInteractiveButtonStyle(
|
|
style?: InteractiveButtonStyle,
|
|
): DiscordComponentButtonStyle | undefined {
|
|
return style ?? "secondary";
|
|
}
|
|
|
|
const DISCORD_INTERACTIVE_BUTTON_ROW_SIZE = 5;
|
|
|
|
export function buildDiscordInteractiveComponents(
|
|
interactive?: InteractiveReply,
|
|
): DiscordComponentMessageSpec | undefined {
|
|
const blocks = reduceInteractiveReply(
|
|
interactive,
|
|
[] as NonNullable<DiscordComponentMessageSpec["blocks"]>,
|
|
(state, block) => {
|
|
if (block.type === "text") {
|
|
const text = block.text.trim();
|
|
if (text) {
|
|
state.push({ type: "text", text });
|
|
}
|
|
return state;
|
|
}
|
|
if (block.type === "buttons") {
|
|
if (block.buttons.length === 0) {
|
|
return state;
|
|
}
|
|
for (
|
|
let index = 0;
|
|
index < block.buttons.length;
|
|
index += DISCORD_INTERACTIVE_BUTTON_ROW_SIZE
|
|
) {
|
|
state.push({
|
|
type: "actions",
|
|
buttons: block.buttons
|
|
.slice(index, index + DISCORD_INTERACTIVE_BUTTON_ROW_SIZE)
|
|
.map((button) => {
|
|
const spec: DiscordComponentButtonSpec = {
|
|
label: button.label,
|
|
style: button.url ? "link" : resolveDiscordInteractiveButtonStyle(button.style),
|
|
};
|
|
if (button.value) {
|
|
spec.callbackData = button.value;
|
|
}
|
|
if (button.url) {
|
|
spec.url = button.url;
|
|
}
|
|
return spec;
|
|
}),
|
|
});
|
|
}
|
|
return state;
|
|
}
|
|
if (block.type === "select" && block.options.length > 0) {
|
|
state.push({
|
|
type: "actions",
|
|
select: {
|
|
type: "string",
|
|
placeholder: block.placeholder,
|
|
options: block.options.map((option) => ({
|
|
label: option.label,
|
|
value: option.value,
|
|
})),
|
|
},
|
|
});
|
|
}
|
|
return state;
|
|
},
|
|
);
|
|
return blocks.length > 0 ? { blocks } : undefined;
|
|
}
|
|
|
|
export function buildDiscordPresentationComponents(
|
|
presentation?: MessagePresentation,
|
|
): DiscordComponentMessageSpec | undefined {
|
|
if (!presentation) {
|
|
return undefined;
|
|
}
|
|
const spec: DiscordComponentMessageSpec = { blocks: [] };
|
|
if (presentation.title) {
|
|
spec.blocks?.push({ type: "text", text: presentation.title });
|
|
}
|
|
for (const block of presentation.blocks) {
|
|
if (block.type === "text" || block.type === "context") {
|
|
const text = block.text.trim();
|
|
if (text) {
|
|
spec.blocks?.push({
|
|
type: "text",
|
|
text: block.type === "context" ? `-# ${text}` : text,
|
|
});
|
|
}
|
|
continue;
|
|
}
|
|
if (block.type === "divider") {
|
|
spec.blocks?.push({ type: "separator" });
|
|
continue;
|
|
}
|
|
}
|
|
const interactiveSpec = buildDiscordInteractiveComponents(
|
|
presentationToInteractiveReply({
|
|
blocks: presentation.blocks.filter(
|
|
(block) => block.type === "buttons" || block.type === "select",
|
|
),
|
|
}),
|
|
);
|
|
if (interactiveSpec?.blocks?.length) {
|
|
spec.blocks?.push(...interactiveSpec.blocks);
|
|
}
|
|
return spec.blocks?.length ? spec : undefined;
|
|
}
|