Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
423
extensions/discord/src/components.builders.ts
Normal file
423
extensions/discord/src/components.builders.ts
Normal file
@@ -0,0 +1,423 @@
|
||||
// Discord plugin module implements components.builders behavior.
|
||||
import crypto from "node:crypto";
|
||||
import { ButtonStyle, MessageFlags } from "discord-api-types/v10";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { buildDiscordComponentCustomId as buildDiscordComponentCustomIdImpl } from "./component-custom-id.js";
|
||||
import { mapButtonStyle, normalizeModalFieldName } from "./components.parse.js";
|
||||
import type {
|
||||
DiscordComponentBuildResult,
|
||||
DiscordComponentButtonSpec,
|
||||
DiscordComponentEntry,
|
||||
DiscordComponentMessageSpec,
|
||||
DiscordComponentSelectSpec,
|
||||
DiscordComponentSelectType,
|
||||
DiscordModalEntry,
|
||||
} from "./components.types.js";
|
||||
import {
|
||||
Button,
|
||||
ChannelSelectMenu,
|
||||
Container,
|
||||
File,
|
||||
LinkButton,
|
||||
MediaGallery,
|
||||
MentionableSelectMenu,
|
||||
RoleSelectMenu,
|
||||
Row,
|
||||
Section,
|
||||
Separator,
|
||||
StringSelectMenu,
|
||||
TextDisplay,
|
||||
Thumbnail,
|
||||
UserSelectMenu,
|
||||
type TopLevelComponents,
|
||||
} from "./internal/discord.js";
|
||||
|
||||
function createShortId(prefix: string) {
|
||||
return `${prefix}${crypto.randomBytes(6).toString("base64url")}`;
|
||||
}
|
||||
|
||||
function buildTextDisplays(text?: string, texts?: string[]): TextDisplay[] {
|
||||
if (texts && texts.length > 0) {
|
||||
return texts.map((entry) => new TextDisplay(entry));
|
||||
}
|
||||
if (text) {
|
||||
return [new TextDisplay(text)];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function createButtonComponent(params: {
|
||||
spec: DiscordComponentButtonSpec;
|
||||
componentId?: string;
|
||||
modalId?: string;
|
||||
}): { component: Button | LinkButton; entry?: DiscordComponentEntry } {
|
||||
const style = mapButtonStyle(params.spec.style);
|
||||
const isLink = style === ButtonStyle.Link || Boolean(params.spec.url);
|
||||
if (isLink) {
|
||||
if (!params.spec.url) {
|
||||
throw new Error("Link buttons require a url");
|
||||
}
|
||||
const linkUrl = params.spec.url;
|
||||
class DynamicLinkButton extends LinkButton {
|
||||
label = params.spec.label;
|
||||
url = linkUrl;
|
||||
override disabled = params.spec.disabled ?? false;
|
||||
}
|
||||
return { component: new DynamicLinkButton() };
|
||||
}
|
||||
const componentId = params.componentId ?? createShortId("btn_");
|
||||
const internalCustomId =
|
||||
typeof params.spec.internalCustomId === "string" && params.spec.internalCustomId.trim()
|
||||
? params.spec.internalCustomId.trim()
|
||||
: undefined;
|
||||
const customId =
|
||||
internalCustomId ??
|
||||
buildDiscordComponentCustomIdImpl({
|
||||
componentId,
|
||||
modalId: params.modalId,
|
||||
});
|
||||
class DynamicButton extends Button {
|
||||
label = params.spec.label;
|
||||
customId = customId;
|
||||
override style = style;
|
||||
override emoji = params.spec.emoji;
|
||||
override disabled = params.spec.disabled ?? false;
|
||||
}
|
||||
if (internalCustomId) {
|
||||
return {
|
||||
component: new DynamicButton(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
component: new DynamicButton(),
|
||||
entry: {
|
||||
id: componentId,
|
||||
kind: params.modalId ? "modal-trigger" : "button",
|
||||
label: params.spec.label,
|
||||
...(params.spec.callbackData !== undefined ? { callbackData: params.spec.callbackData } : {}),
|
||||
...(params.spec.callbackDataKind !== undefined
|
||||
? { callbackDataKind: params.spec.callbackDataKind }
|
||||
: {}),
|
||||
...(params.modalId !== undefined ? { modalId: params.modalId } : {}),
|
||||
...(params.spec.reusable !== undefined ? { reusable: params.spec.reusable } : {}),
|
||||
...(params.spec.allowedUsers !== undefined ? { allowedUsers: params.spec.allowedUsers } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createSelectComponent(params: {
|
||||
spec: DiscordComponentSelectSpec;
|
||||
componentId?: string;
|
||||
}): {
|
||||
component:
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu;
|
||||
entry: DiscordComponentEntry;
|
||||
} {
|
||||
const type = normalizeLowercaseStringOrEmpty(
|
||||
params.spec.type ?? "string",
|
||||
) as DiscordComponentSelectType;
|
||||
const componentId = params.componentId ?? createShortId("sel_");
|
||||
const customId = buildDiscordComponentCustomIdImpl({ componentId });
|
||||
const createEntry = (
|
||||
selectType: DiscordComponentSelectType,
|
||||
label: string,
|
||||
options?: DiscordComponentEntry["options"],
|
||||
): DiscordComponentEntry => ({
|
||||
id: componentId,
|
||||
kind: "select",
|
||||
label,
|
||||
...(params.spec.callbackData !== undefined ? { callbackData: params.spec.callbackData } : {}),
|
||||
...(params.spec.callbackDataKind !== undefined
|
||||
? { callbackDataKind: params.spec.callbackDataKind }
|
||||
: {}),
|
||||
selectType,
|
||||
...(options ? { options } : {}),
|
||||
...(params.spec.allowedUsers !== undefined ? { allowedUsers: params.spec.allowedUsers } : {}),
|
||||
});
|
||||
|
||||
if (type === "string") {
|
||||
const options = params.spec.options ?? [];
|
||||
if (options.length === 0) {
|
||||
throw new Error("String select menus require options");
|
||||
}
|
||||
class DynamicStringSelect extends StringSelectMenu {
|
||||
customId = customId;
|
||||
override options = options;
|
||||
override minValues = params.spec.minValues;
|
||||
override maxValues = params.spec.maxValues;
|
||||
override placeholder = params.spec.placeholder;
|
||||
override disabled = false;
|
||||
}
|
||||
return {
|
||||
component: new DynamicStringSelect(),
|
||||
entry: createEntry(
|
||||
"string",
|
||||
params.spec.placeholder ?? "select",
|
||||
options.map((option) => ({ value: option.value, label: option.label })),
|
||||
),
|
||||
};
|
||||
}
|
||||
if (type === "user") {
|
||||
class DynamicUserSelect extends UserSelectMenu {
|
||||
customId = customId;
|
||||
override minValues = params.spec.minValues;
|
||||
override maxValues = params.spec.maxValues;
|
||||
override placeholder = params.spec.placeholder;
|
||||
override disabled = false;
|
||||
}
|
||||
return {
|
||||
component: new DynamicUserSelect(),
|
||||
entry: createEntry("user", params.spec.placeholder ?? "user select"),
|
||||
};
|
||||
}
|
||||
if (type === "role") {
|
||||
class DynamicRoleSelect extends RoleSelectMenu {
|
||||
customId = customId;
|
||||
override minValues = params.spec.minValues;
|
||||
override maxValues = params.spec.maxValues;
|
||||
override placeholder = params.spec.placeholder;
|
||||
override disabled = false;
|
||||
}
|
||||
return {
|
||||
component: new DynamicRoleSelect(),
|
||||
entry: createEntry("role", params.spec.placeholder ?? "role select"),
|
||||
};
|
||||
}
|
||||
if (type === "mentionable") {
|
||||
class DynamicMentionableSelect extends MentionableSelectMenu {
|
||||
customId = customId;
|
||||
override minValues = params.spec.minValues;
|
||||
override maxValues = params.spec.maxValues;
|
||||
override placeholder = params.spec.placeholder;
|
||||
override disabled = false;
|
||||
}
|
||||
return {
|
||||
component: new DynamicMentionableSelect(),
|
||||
entry: createEntry("mentionable", params.spec.placeholder ?? "mentionable select"),
|
||||
};
|
||||
}
|
||||
class DynamicChannelSelect extends ChannelSelectMenu {
|
||||
customId = customId;
|
||||
override minValues = params.spec.minValues;
|
||||
override maxValues = params.spec.maxValues;
|
||||
override placeholder = params.spec.placeholder;
|
||||
override disabled = false;
|
||||
}
|
||||
return {
|
||||
component: new DynamicChannelSelect(),
|
||||
entry: createEntry("channel", params.spec.placeholder ?? "channel select"),
|
||||
};
|
||||
}
|
||||
|
||||
function isSelectComponent(
|
||||
component: unknown,
|
||||
): component is
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu {
|
||||
return (
|
||||
component instanceof StringSelectMenu ||
|
||||
component instanceof UserSelectMenu ||
|
||||
component instanceof RoleSelectMenu ||
|
||||
component instanceof MentionableSelectMenu ||
|
||||
component instanceof ChannelSelectMenu
|
||||
);
|
||||
}
|
||||
|
||||
export function buildDiscordComponentMessage(params: {
|
||||
spec: DiscordComponentMessageSpec;
|
||||
fallbackText?: string;
|
||||
sessionKey?: string;
|
||||
agentId?: string;
|
||||
accountId?: string;
|
||||
}): DiscordComponentBuildResult {
|
||||
const entries: DiscordComponentEntry[] = [];
|
||||
const consumptionGroupId = createShortId("grp_");
|
||||
const modals: DiscordModalEntry[] = [];
|
||||
const components: TopLevelComponents[] = [];
|
||||
const containerChildren: Array<
|
||||
| Row<
|
||||
| Button
|
||||
| LinkButton
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu
|
||||
>
|
||||
| TextDisplay
|
||||
| Section
|
||||
| MediaGallery
|
||||
| Separator
|
||||
| File
|
||||
> = [];
|
||||
|
||||
const addEntry = (entry: DiscordComponentEntry) => {
|
||||
const reusable = entry.reusable ?? params.spec.reusable;
|
||||
entries.push({
|
||||
...entry,
|
||||
...(params.sessionKey !== undefined ? { sessionKey: params.sessionKey } : {}),
|
||||
...(params.agentId !== undefined ? { agentId: params.agentId } : {}),
|
||||
...(params.accountId !== undefined ? { accountId: params.accountId } : {}),
|
||||
...(reusable !== undefined ? { reusable } : {}),
|
||||
consumptionGroupId,
|
||||
});
|
||||
};
|
||||
|
||||
const text = params.spec.text ?? params.fallbackText;
|
||||
if (text) {
|
||||
containerChildren.push(new TextDisplay(text));
|
||||
}
|
||||
|
||||
for (const block of params.spec.blocks ?? []) {
|
||||
if (block.type === "text") {
|
||||
containerChildren.push(new TextDisplay(block.text));
|
||||
continue;
|
||||
}
|
||||
if (block.type === "section") {
|
||||
const displays = buildTextDisplays(block.text, block.texts);
|
||||
if (displays.length > 3) {
|
||||
throw new Error("Section blocks support up to 3 text displays");
|
||||
}
|
||||
let accessory: Thumbnail | Button | LinkButton | undefined;
|
||||
if (block.accessory?.type === "thumbnail") {
|
||||
accessory = new Thumbnail(block.accessory.url);
|
||||
} else if (block.accessory?.type === "button") {
|
||||
const { component, entry } = createButtonComponent({ spec: block.accessory.button });
|
||||
accessory = component;
|
||||
if (entry) {
|
||||
addEntry(entry);
|
||||
}
|
||||
}
|
||||
containerChildren.push(new Section(displays, accessory));
|
||||
continue;
|
||||
}
|
||||
if (block.type === "separator") {
|
||||
containerChildren.push(new Separator({ spacing: block.spacing, divider: block.divider }));
|
||||
continue;
|
||||
}
|
||||
if (block.type === "media-gallery") {
|
||||
containerChildren.push(new MediaGallery(block.items));
|
||||
continue;
|
||||
}
|
||||
if (block.type === "file") {
|
||||
containerChildren.push(new File(block.file, block.spoiler));
|
||||
continue;
|
||||
}
|
||||
if (block.type === "actions") {
|
||||
const rowComponents: Array<
|
||||
| Button
|
||||
| LinkButton
|
||||
| StringSelectMenu
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu
|
||||
> = [];
|
||||
if (block.buttons) {
|
||||
if (block.buttons.length > 5) {
|
||||
throw new Error("Action rows support up to 5 buttons");
|
||||
}
|
||||
for (const button of block.buttons) {
|
||||
const { component, entry } = createButtonComponent({ spec: button });
|
||||
rowComponents.push(component);
|
||||
if (entry) {
|
||||
addEntry(entry);
|
||||
}
|
||||
}
|
||||
} else if (block.select) {
|
||||
const { component, entry } = createSelectComponent({ spec: block.select });
|
||||
rowComponents.push(component);
|
||||
addEntry(entry);
|
||||
}
|
||||
containerChildren.push(new Row(rowComponents));
|
||||
}
|
||||
}
|
||||
|
||||
if (params.spec.modal) {
|
||||
const modalId = createShortId("mdl_");
|
||||
const fields = params.spec.modal.fields.map((field, index) => ({
|
||||
id: createShortId("fld_"),
|
||||
name: normalizeModalFieldName(field.name, index),
|
||||
label: field.label,
|
||||
type: field.type,
|
||||
...(field.description !== undefined ? { description: field.description } : {}),
|
||||
...(field.placeholder !== undefined ? { placeholder: field.placeholder } : {}),
|
||||
...(field.required !== undefined ? { required: field.required } : {}),
|
||||
...(field.options !== undefined ? { options: field.options } : {}),
|
||||
...(field.minValues !== undefined ? { minValues: field.minValues } : {}),
|
||||
...(field.maxValues !== undefined ? { maxValues: field.maxValues } : {}),
|
||||
...(field.minLength !== undefined ? { minLength: field.minLength } : {}),
|
||||
...(field.maxLength !== undefined ? { maxLength: field.maxLength } : {}),
|
||||
...(field.style !== undefined ? { style: field.style } : {}),
|
||||
}));
|
||||
modals.push({
|
||||
id: modalId,
|
||||
title: params.spec.modal.title,
|
||||
fields,
|
||||
...(params.spec.modal.callbackData !== undefined
|
||||
? { callbackData: params.spec.modal.callbackData }
|
||||
: {}),
|
||||
...(params.sessionKey !== undefined ? { sessionKey: params.sessionKey } : {}),
|
||||
...(params.agentId !== undefined ? { agentId: params.agentId } : {}),
|
||||
...(params.accountId !== undefined ? { accountId: params.accountId } : {}),
|
||||
...(params.spec.reusable !== undefined ? { reusable: params.spec.reusable } : {}),
|
||||
...(params.spec.modal.allowedUsers !== undefined
|
||||
? { allowedUsers: params.spec.modal.allowedUsers }
|
||||
: {}),
|
||||
});
|
||||
|
||||
const triggerSpec: DiscordComponentButtonSpec = {
|
||||
label: params.spec.modal.triggerLabel ?? "Open form",
|
||||
style: params.spec.modal.triggerStyle ?? "primary",
|
||||
allowedUsers: params.spec.modal.allowedUsers,
|
||||
};
|
||||
|
||||
const { component, entry } = createButtonComponent({
|
||||
spec: triggerSpec,
|
||||
modalId,
|
||||
});
|
||||
|
||||
if (entry) {
|
||||
addEntry(entry);
|
||||
}
|
||||
|
||||
const lastChild = containerChildren.at(-1);
|
||||
if (lastChild instanceof Row) {
|
||||
const row = lastChild;
|
||||
const hasSelect = row.components.some((entryLocal) => isSelectComponent(entryLocal));
|
||||
if (row.components.length < 5 && !hasSelect) {
|
||||
row.addComponent(component as Button);
|
||||
} else {
|
||||
containerChildren.push(new Row([component as Button]));
|
||||
}
|
||||
} else {
|
||||
containerChildren.push(new Row([component as Button]));
|
||||
}
|
||||
}
|
||||
|
||||
if (containerChildren.length === 0) {
|
||||
throw new Error("components must include at least one block, text, or modal trigger");
|
||||
}
|
||||
|
||||
const container = new Container(containerChildren, params.spec.container);
|
||||
components.push(container);
|
||||
const consumptionGroupEntryIds = entries.map((entry) => entry.id);
|
||||
for (const entry of entries) {
|
||||
entry.consumptionGroupEntryIds = consumptionGroupEntryIds;
|
||||
}
|
||||
return { components, entries, modals };
|
||||
}
|
||||
|
||||
export function buildDiscordComponentMessageFlags(
|
||||
components: TopLevelComponents[],
|
||||
): number | undefined {
|
||||
const hasV2 = components.some((component) => component.isV2);
|
||||
return hasV2 ? MessageFlags.IsComponentsV2 : undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user