Discord.jsでComponents v2を使う方法！

# 概要
先日、Discordより新しい埋め込みメッセージの表示方法が公開されました。そして、discord.jsでは日本時間昨日(2025年04月27日)これに対応した新バージョンv14.19.0が公開されました！では、実際に触りながらどのような物か見ていきましょう。

# Components v2ってそもそも何？
Components v2とは、その名の通り第２世代のComponentsです。え？Componentsって何かって？具体的に言えば、「埋め込み(embed)」、「ボタン(button)」、「ドロップダウンメニュー(selectMenu)」、「モーダル(modal)」などの事です！これらのうち、主にEmbedの中身が大きく変わりました。

# 使う上での注意事項は？
まず、従来のEmbedは今まで通り使えます。それに加えてこのComponents v2が使えるようになったイメージです。そのため、送信したメッセージがComponents v2を利用したものであることを明示する必要があります。そのため、例えばメッセージが送られたチャンネルにBOTからメッセージを送信する場合は次のようにflagsを設定する必要があります。
```js
message.channel.send({
  components: [components],
  flags: MessageFlags.IsComponentsV2
});
```
なお、`interaction.reply()`などにおいて、`IsComponentsV2`と`Ephemeral`など複数のflagを設定する場合は、配列を使って次のように設定する必要があります。
```js
interaction.reply({
  components: [components],
  flags: [MessageFlags.IsComponentsV2, MessageFlags.Ephemeral],
});
```

# Components v2の使い方
基本的な構造は、Embedと同じです。１つずつ見てきましょう。なお、SelectMenuやEmbed外の通常のボタン、modalなどは従来通りのため、表記を省略します。
## containerについて
containerは、従来のEmbedで言う所のEmbedに対応するので、この中に文字や画像などを追加していくイメージです。このcontainerには、AccentColorと言ってEmbedのcolorのようなものが設定できます。また、container全体にスポイラーを設定することも出来ます。
## Sectionについて
sectionは、containerの中の子要素(この後説明するText DisplayやThumbnailなど)たちをまとめた塊と考えれば分かりやすいと思います。一部の要素はsectionの中でないと設定できないものもあるため、注意してください。
## Text Displayについて
Text Displayは、従来のEmbedで言う所のEmbed内のtitle, description, field, footerなどに相当します。原則markdown形式で記述する事によって、titleとdescriptionの違いを考える必要はなくなりました。
## Accessoryについて
container内に設定するthumbnailや文章の右側に1つだけ設定できるbuttonは、Accessoryと呼ばれます。これらは、セクションの中に設定でき、1セクションあたりどちらか片方のみ設定できます。
## Media Galleryについて
Media Galleryは、その名の通り画像などのメディアを一覧表示できるものであり、最大10個のメディアを一覧表示することが出来ます。
## Separatorについて
Separatorとは、その名の通り仕切り線です。要素同士の仕切りとして使用することが出来ます。設定として「divider」と「spacing」が設定できます。「divider」は、仕切り線の描画を行わずに空白のみを描画する設定です。「spacing」は、仕切り線の前後の余白の大きさをデフォルトの大きさか小さい余白かを選択することが出来ます。
## Fileについて
中身としては、従来のメッセージに添付できたファイルをEmbed内に添付できるイメージです。
## ActionRowについて
container内にはActionRowも設定することが出来、その中には従来通りbuttonやselectMenuが設定できます。

# 書き方
書き方の例を以下のように示します。ご自由にご利用ください。
```js
const {
  ContainerBuilder,
  TextDisplayBuilder,
  MessageFlags,
  SectionBuilder,
  ThumbnailBuilder,
  MediaGalleryBuilder,
  MediaGalleryItemBuilder,
  FileBuilder,
  SeparatorBuilder,
  SeparatorSpacingSize,
  ButtonBuilder,
  ButtonStyle,
  ActionRowBuilder,
  AttachmentBuilder,
} = require("discord.js");

client.on("messageCreate", async (message) => {
  let components = new ContainerBuilder()
    .addSectionComponents(
      new SectionBuilder()
        .addTextDisplayComponents([
          new TextDisplayBuilder({
            content: "# Components V2のテストです",
          }),
          new TextDisplayBuilder({
            content: `これは、Discordの新しいメッセージ表示方法です。TextDisplayは、markdown形式で記述します。
        - 箇条書きも\n- 可能です。`,
          }),
          new TextDisplayBuilder({
            content: `-# もちろん、文字を小さくすることも…`,
          }),
        ])
        .setThumbnailAccessory(
          new ThumbnailBuilder({
            media: {
              url: "attachment://logo.png",
            },
          })
        )
    )
    .addMediaGalleryComponents(
      new MediaGalleryBuilder().addItems(
        new MediaGalleryItemBuilder({ media: { url: "attachment://1.png" } }),
        new MediaGalleryItemBuilder({ media: { url: "attachment://2.png" } }),
        new MediaGalleryItemBuilder({ media: { url: "attachment://3.png" } }),
        new MediaGalleryItemBuilder({ media: { url: "attachment://4.png" } }),
        new MediaGalleryItemBuilder({ media: { url: "attachment://5.png" } })
      )
    )
    .addSeparatorComponents(
      new SeparatorBuilder()
        // 仕切り線を表示するか(デフォルト: true)
        .setDivider(true)
        // 仕切り線の前後の余白の大きさを設定
        .setSpacing(SeparatorSpacingSize.Large)
    )
    .addTextDisplayComponents(
      new TextDisplayBuilder({
        content: "## 添付ファイルを途中に載せる事も出来ます！",
      })
    )
    .addFileComponents(
      new FileBuilder({ file: { url: "attachment://discord.svg" } })
    )
    .addSeparatorComponents(
      new SeparatorBuilder()
        // 仕切り線を表示するか(デフォルト: true)
        .setDivider(true)
        // 仕切り線の前後の余白の大きさを設定
        .setSpacing(SeparatorSpacingSize.Large)
    )

    .addSectionComponents(
      new SectionBuilder()
        .addTextDisplayComponents(
          new TextDisplayBuilder({
            content: "## どのボタンが好き？",
          })
        )
        .setButtonAccessory(
          // ここは、行内にボタンを配置するものなので、ボタンは１つのみ設定可能
          new ButtonBuilder()
            .setCustomId("button1")
            .setLabel("了承！")
            .setStyle(ButtonStyle.Primary)
        )
      /*
          // ここで、thumbnailを設定することも出来るが、設定した場合はbuttonAccessoryは無効になる。
          .setThumbnailAccessory(
            new ThumbnailBuilder({
              media: {
                url: "attachment://logo.png",
              },
            })
          )
          */
    )

    .addActionRowComponents(
      // 1メッセージ無いのbuttonのカスタムIDは一意であるひつようがあるので、先ほどの「button1」は使えない。
      new ActionRowBuilder().addComponents(
        new ButtonBuilder()
          .setCustomId("button2")
          .setLabel("緑！")
          .setStyle(ButtonStyle.Success),
        new ButtonBuilder()
          .setCustomId("button3")
          .setLabel("赤！")
          .setStyle(ButtonStyle.Danger)
      )
    )

    // ↓旧embedのcolorの部分
    .setAccentColor(0xff0000)
    // ↓埋め込みに対してスポイラー
    .setSpoiler(false);

  let attachments = [
    new AttachmentBuilder().setFile("images/logo.png").setName("logo.png"),
    new AttachmentBuilder().setFile("images/1.png").setName("1.png"),
    new AttachmentBuilder().setFile("images/2.png").setName("2.png"),
    new AttachmentBuilder().setFile("images/3.png").setName("3.png"),
    new AttachmentBuilder().setFile("images/4.png").setName("4.png"),
    new AttachmentBuilder().setFile("images/5.png").setName("5.png"),
    new AttachmentBuilder()
      .setFile("images/discord-mark-white.svg")
      .setName("discord.svg"),
  ];

  message.channel.send({
    components: [components],
    files: attachments,
    flags: MessageFlags.IsComponentsV2,
  });
});
```

このコードを使うと、このようなメッセージが送信されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4072455/61cddf07-0867-4ed9-bbdf-e0a61452a6b5.png)

# 追記(2025/05/04)
１メッセージあたりのcomponent数の制限が緩和されました。詳細は、[**こちらのページ**](https://discord.com/developers/docs/change-log#raised-component-limits)(Discord公式,英語)をご覧ください。