Discord Message Components V2
Este conteúdo não está disponível em sua língua ainda.
TomoriBot uses Discord Components V2 to render structured interactive messages, control panels, and tool media cards with layout encapsulation. Payloads set MessageFlags.IsComponentsV2 (32768), group elements inside containers and sections, and reference attachments directly within component media fields. For complete wire schema definitions and component types, refer to the Discord Message Components documentation.
Message layout and component limits
Section titled “Message layout and component limits”src/utils/discord/ui/componentsV2Limits.ts enforces Discord structural constraints before REST transmission. Discord rejects oversized or invalid component trees with an HTTP 400 Bad Request error.
The repository enforces these limits:
- Total components: 40 (
DISCORD_MESSAGE_TOTAL_COMPONENTS_MAX), counted recursively across top-level components, container children, and section accessories. - Text Display content: 4,000 Unicode codepoints total across Text Displays.
getDiscordTextLengthcounts codepoints;truncateDiscordTextpreserves whole grapheme clusters while fitting that budget. UTF-16.lengthovercounts supplementary characters, and.slicecan split surrogate pairs. - Action rows: At most 5 buttons (
DISCORD_ACTION_ROW_BUTTONS_MAX) or exactly 1 select menu (DISCORD_ACTION_ROW_SELECTS_MAX). Buttons and selects cannot share an action row. - Sections: 1 to 3 Text Display components (
DISCORD_SECTION_TEXT_DISPLAYS_MINandDISCORD_SECTION_TEXT_DISPLAYS_MAX) with exactly one accessory component (Button or Thumbnail). - String selects: 1 to 25 options (
DISCORD_SELECT_OPTIONS_MINandDISCORD_SELECT_OPTIONS_MAX). Option label, value, and description accept up to 100 characters; placeholder accepts up to 150 characters. - Buttons: Label accepts up to 80 characters (
DISCORD_BUTTON_LABEL_MAX). Interactive custom IDs require 1 to 100 characters (DISCORD_CUSTOM_ID_MAX) and must be unique within the message payload. Link URLs accept up to 512 characters (DISCORD_BUTTON_URL_MAX). - Media descriptions: Alt text accepts up to 1,024 characters (
DISCORD_MEDIA_DESCRIPTION_MAX).
Validation and guarded delivery
Section titled “Validation and guarded delivery”validateComponentsV2MessageLimits in src/utils/discord/ui/componentsV2Limits.ts is a pure validator. It checks payloads without mutating text or clipping collections silently. When a payload exceeds bounds, it returns a structured list of violations with paths, observed values, and error codes (DiscordLimitViolationCode).
Dynamic page budgeting in tests/unit/discord/configPanelTextBudget.test.ts derives text reserves by subtracting measured fixed chrome from the message budget rather than subtracting static constants. This keeps dynamic collections bounded at stored maxima without rotted assumptions.
All panel updates (reply, editReply, update, replace) route through deliverGuardedPanel in src/utils/discord/ui/interactionCore.ts:
- In development and test environments, invalid payloads throw
ComponentsV2LimitErrorto catch payload errors early. - In production,
validateAndFallbackPanelPayloadlogs redacted diagnostics and substitutes a minimal localized error card (buildPanelFallbackPayload). It attempts to repaint even when payload fitting fails after a committed write; Discord transport failure can still prevent delivery. - Failures emit
panel_failuremetrics throughreportPanelFailurefor telemetry tracking.
The producer manifest in tests/unit/discord/componentsV2ProducerManifest.test.ts scans src/ for MessageFlags.IsComponentsV2 usage and asserts that every payload builder is registered and covered by fixture sweeps across all runtime locales.
Interaction lifetime and state ownership
Section titled “Interaction lifetime and state ownership”TomoriBot separates interaction lifetime into two patterns:
- Bounded anchor workflows: Used by ephemeral persona wizards such as
runPersonaPickerWorkflow(...)insrc/utils/discord/ui/personaWorkflow.ts. The workflow maintains a single ephemeral message usingAnchorMessageController. Operations (replace,edit,disableControls,delete) verify that they target the original anchor message ID.replace()clears stale file attachments (such as persona avatar previews) unless explicitly retained. Option sets exceeding 25 choices transition the anchor to localized range buttons (1-25,26-50) instead of opening an invalid modal. - Persistent panels: Used by configuration, moderation, help, and status dashboards (
src/utils/discord/interactions/). Panels use versioned custom IDs (config:v2:*,moderation:v1:*,help:v2:*,status:v1:*). They do not keep in-memory collectors alive. Route handlers own authorization and reload the state relevant to each action before repainting. Outdated version routes resolve to a localized message directing the user to reopen the panel.
Identity-sensitive collection controls, such as personal spotlight and endpoint selectors, bind their custom IDs to an actor, server, and collection fingerprint. Submission checks the current collection so a stale list position cannot mutate a different entity. Mutation routes must recheck permissions and target scope after slow work, then invalidate cached state after database success.
Container conventions and presentation hierarchy
Section titled “Container conventions and presentation hierarchy”Control panels and message cards follow consistent structure:
- Container titles: Use Markdown H3 headings. Persistent panels convey tone through accent bars and keep headings bare; status and notice helpers can add circles through
withStatusCircle. Locale title strings omit leading circles and heading prefixes. See Status circles. - Panel text hierarchy: Standardized across four levels:
- Major headings (
###) for page titles and category counts. - Nested subsection labels for grouped configuration fields.
- Plain explanatory prose (never quoted) for instructions and empty-state guidance.
- Quote rows (
>) for configuration values, semantic status lines, and Discord mention lists (<@id>,<#id>). Mentions omit duplicate raw snowflakes.
- Major headings (
- Dividers and status footers: Separator components (
ComponentType.Separator, divider: true, spacing: 1) separate navigation controls from body content, and precede stale-read warning footers (-# ...). - Notice containers:
buildNoticeContainerconstructs compact cards with an H3 title, Text Display description, separator, muted subtext footer, and optional action buttons (such as an Expand button).
Generated media delivery and reverse discovery
Section titled “Generated media delivery and reverse discovery”Tool outputs for generated images (buildGeneratedImageComponentsV2Payload in src/utils/discord/generatedImageMessage.ts) and videos (buildGeneratedVideoComponentsV2Payload in src/utils/discord/generatedVideoMessage.ts) use Components V2 so timing footers appear below the media. The attachment is uploaded with the request and referenced via attachment://<filename> inside a MediaGallery item, followed by a TextDisplay component with subtext formatting (-# Generated after 4.2 seconds.).
- Mode lock: Once a message is sent with
MessageFlags.IsComponentsV2, Discord forbids mixing legacycontentorembedson subsequent edits.interactionCore.tstracks V2 interaction replies in thecomponentsV2ReplyWeakSetso legacy embed sinks automatically emit V2-safe notice containers instead of triggering 400 Bad Request errors. - Webhook delivery: Webhook executions in discord.js require
withComponents: truewhen sending or editing Components V2 messages. - Reverse discovery: Re-reading images from message context (for vision analysis, image-to-image generation, or inpainting) cannot inspect top-level attachments alone because Components V2 images are referenced inside component trees.
collectImageUrlsFromMessageinsrc/utils/image/imageExtractor.tsdelegates toappendComponentMediaFromMessageinsrc/utils/chat/contextMedia.tsto extract media fromMediaGallery,Thumbnail, andFilecomponents.
Source pointers
Section titled “Source pointers”src/utils/discord/ui/componentsV2Limits.ts: Limit constants andvalidateComponentsV2MessageLimits.src/utils/text/discordTextLimits.ts: Unicode codepoint length calculation and safe truncation.src/utils/discord/ui/interactionCore.ts:deliverGuardedPanel,buildNoticeContainer, andcomponentsV2Replytracking.src/utils/discord/ui/personaWorkflow.ts:runPersonaPickerWorkflowandAnchorMessageController.src/utils/discord/generatedImageMessage.ts: Image Media Gallery card builder.src/utils/discord/generatedVideoMessage.ts: Video Media Gallery card builder.src/utils/image/imageExtractor.ts: Component media extraction for vision and image tools.tests/unit/discord/componentsV2ProducerManifest.test.ts: Producer manifest coverage checks.tests/unit/discord/configPanelTextBudget.test.ts: Text budget validation at stored maxima.