Mensagens Components V2
Aprenda cada peça visual e combine containers, seções, mídia, action rows e selects em mensagens legíveis.
Mensagem básica com container#
O container cria um bloco visual e pode receber cor de destaque, texto e divisores.
import { ContainerBuilder, MessageBuilder, SeparatorBuilder, TextDisplayBuilder} from '@ninenity/componentbuilder'export const statusCard = new MessageBuilder() .addComponents( new ContainerBuilder() .setAccentColor(0xa571f4) .addTextDisplayComponents( new TextDisplayBuilder().setContent('## Status do serviço'), new TextDisplayBuilder().setContent('Todos os sistemas estão operacionais.') ) .addSeparatorComponents( new SeparatorBuilder().setDivider(true).setSpacing('small') ) .addTextDisplayComponents( new TextDisplayBuilder().setContent('- API: online- Bot: online- Banco: online') ) ) .template()O conteúdo do TextDisplayBuilder aceita markdown suportado pelo Discord. Prefira blocos curtos e uma hierarquia clara em vez de uma única parede de texto.
Enviar pelo Core#
Converta o documento e combine as flags de Components V2 e resposta efêmera quando necessário.
import { MessageFlags, SlashCommandBuilder } from '@ninenity/core'import { toDiscordMessagePayload } from '@ninenity/componentbuilder'import { statusCard } from '../Interactions/StatusCard'import Client from '../../Shared/Settings/Client'Client.slash({ data: new SlashCommandBuilder() .setName('status') .setDescription('Mostra o estado dos serviços.'), execute: async interaction => { await interaction.reply({ ...toDiscordMessagePayload(statusCard.document()), flags: MessageFlags.IsComponentsV2 | MessageFlags.Ephemeral }) }})Seção com thumbnail ou botão#
Uma SectionBuilder combina texto com exatamente um acessório lateral.
const withThumbnail = new SectionBuilder() .addTextDisplayComponents( new TextDisplayBuilder().setContent( '### Builder StudioCrie e exporte interfaces Components V2.' ) ) .setThumbnailAccessory( new ThumbnailBuilder() .setURL('https://cdn.example.com/builder.png') .setDescription('Logo do Builder Studio') )const withButton = new SectionBuilder() .setContent('### DocumentaçãoVeja todos os exemplos do componente.') .setButtonAccessory( new ButtonBuilder() .setStyle(ButtonStyle.Link) .setLabel('Abrir docs') .setURL('https://ninenity.com/doc') )const template = new MessageBuilder() .addComponents( new ContainerBuilder().addSectionComponents(withThumbnail, withButton) ) .template()Botões de link não possuem customId nem callback. Botões de ação precisam de customId, estilo diferente de Link e podem usar .setCallback().
Galeria e arquivo#
Use galeria para mídia visual e arquivo quando o anexo faz parte da composição.
const gallery = new MediaGalleryBuilder() .addItems( item => item .setURL('https://cdn.example.com/dashboard.png') .setDescription('Dashboard do projeto'), item => item .setURL('https://cdn.example.com/components.png') .setDescription('Componentes no Discord') )const template = new MessageBuilder() .addComponents( new ContainerBuilder() .addTextDisplayComponents( new TextDisplayBuilder().setContent('## Visão do projeto') ) .addMediaGalleryComponents(gallery) .addFileComponents( new FileBuilder().setURL('attachment://relatorio.pdf') ) ) .template()- A galeria aceita no máximo 10 itens.
- Use
setSpoiler(true)em uma imagem ou arquivo que não deve aparecer imediatamente. - A URL
attachment://nome.extprecisa corresponder a um arquivo realmente enviado no payload. - Sempre forneça descrição útil para imagens importantes.
Linha de botões#
Uma action row aceita até cinco botões. Use estilos para significado, não apenas decoração.
const actions = new ActionRowBuilder<MessageActionRowComponentBuilder>() .addComponents( new ButtonBuilder() .setCustomId('project:approve') .setLabel('Aprovar') .setStyle(ButtonStyle.Success), new ButtonBuilder() .setCustomId('project:edit') .setLabel('Editar') .setStyle(ButtonStyle.Secondary), new ButtonBuilder() .setCustomId('project:delete') .setLabel('Excluir') .setStyle(ButtonStyle.Danger), new ButtonBuilder() .setLabel('Ver no HUB') .setStyle(ButtonStyle.Link) .setURL('https://hub.ninenity.com') )const message = new MessageBuilder() .addComponents(new ContainerBuilder().addActionRowComponents(actions))| Estilo | Use para |
|---|---|
Primary | A ação principal daquela etapa |
Secondary | Ações neutras, navegação e alternativas |
Success | Confirmar, concluir ou ativar |
Danger | Excluir, revogar ou outra ação destrutiva |
Link | Abrir URL; não recebe callback |
Menus de seleção#
String select define opções; selects de usuário, cargo, canal e menção usam os seletores nativos do Discord.
const projectSelect = new StringSelectMenuBuilder() .setCustomId('project:environment') .setPlaceholder('Selecione um ambiente') .setMinValues(1) .setMaxValues(1) .addOptions( option => option .setLabel('Produção') .setValue('production') .setDescription('Ambiente público'), option => option .setLabel('Desenvolvimento') .setValue('development') .setDescription('Ambiente de testes') )const row = new ActionRowBuilder<MessageActionRowComponentBuilder>() .addComponents(projectSelect)const userRow = new ActionRowBuilder<MessageActionRowComponentBuilder>() .addComponents( new UserSelectMenuBuilder() .setCustomId('team:members') .setPlaceholder('Selecione até 3 membros') .setMinValues(1) .setMaxValues(3) )const roleRow = new ActionRowBuilder<MessageActionRowComponentBuilder>() .addComponents( new RoleSelectMenuBuilder() .setCustomId('team:role') .setPlaceholder('Selecione o cargo da equipe') )Variáveis de template#
Tokens deixam o mesmo layout renderizar dados diferentes sem reconstruir toda a árvore.
const memberTemplate = new MessageBuilder() .addComponents( new ContainerBuilder().addTextDisplayComponents( new TextDisplayBuilder().setContent( '## Olá, ${user.name}!Seu plano atual é **${account.plan}**.' ) ) ) .template()const rendered = memberTemplate.render({ user: { name: interaction.user.displayName }, account: { plan: 'Pro' }})await interaction.reply({ ...toDiscordMessagePayload(rendered.document()), flags: MessageFlags.IsComponentsV2})