私がDiscord.jsを嫌う理由

# 経緯
::::note warn
この記事はDiscord.jsへの批判というよりも、「新たなライブラリが必要である」というテーマについて、**理由や観点を整理したい**という意図の元、書かれています。
:::

最近になり、個人的にTypescriptでDiscord.jsを用いてBOTを作り、配布・提供する機会が増えました。

この結果、**不満な点がいくつか出てきた**ので、**DiscordAPIや利便性・運用・学習のしやすさ等の観点**から考え直し改めて整理してみようと思います。

Discord.jsは、なんだかんだもう7-8年近くの付き合いですし、様々な場面で利用されている事も理解しています。
あくまで一意見として読んで頂ければ幸いです。

# 前提情報
### Discord.jsとは
端的に言えば、DiscordAPIを簡単に操作できるようにするための、Node.js製ライブラリ。
DiscordAPIのほぼ全ての機能をカバーしていて、複雑なBOTも開発が可能です。

主な特徴
- Node.jsモジュール
- オブジェクト指向
- 豊富な機能
- 活発なコミュニティ

# 嫌いな理由
## 1. 頻繁なAPI変更と互換性の問題
DiscordAPIはかなり頻繁的に変更がかかる。
この結果ライブラリ自体の破壊的変更が多くなっている。
例えばDiscord.jsのv13 -> v14ではAPIをv10に切り替えた事に伴い根本的な変更が多く導入された。

1. GatewayAPIのイベント名が変更された(`message` -> `messageCreate`)
2. interactionシステムの導入(prefixコマンドから、interactionコマンドへの強制的な変更)
3. Intentsの明示的な指定の必須化

Discordは2015年がリリース年であり、もう10年近く経っているが未だに高頻度で変更がかかる。
最近でもMessageComponentV2がリリースされたばかりなので、今後もかなりの頻度で変更がかかるだろう。

とはいえ、これは**Discord.jsに限った話では無い**のでまぁ除外しても問題はないだろう。
## 2. キャッシュ管理の課題
Discord.jsはある程度自動的にキャッシュをしてくれるのだが、まぁこれが分かりにくいったらありゃしない。

例えばこれが一つのシャードだけで動かしているのならまだ良いのだが、これを複数のシャードにしていた場合に、メモリの使用量が偏ったり増減したりで、まぁまあ面倒くさい。

特に、キャッシュ済みのデータに直接アクセス出来てしまうのも面倒くさい。
```js
const currentChannel = client.channels.cache.get("Snowflake")
```
先述の通り、Discord.jsは自動的にキャッシュされる前提で作られているので、`fetch()`をしなくても`cache.get()`を使えば取れると言えば取れるのだが、逆に言えば**この自動的にキャッシュに入れてくれる上限数やルール**をしっかり知らないパターンが多いので、バグの原因になる可能性が極めて高い。

## 3. InteractionHandlerの不便さ
DiscordAPIはv10辺りからInteractionを物凄く推している。
多分今後もじゃんじゃか増えていくだろう。

Interactionを受信する方法は大まかに2つある。
1. GateWayAPI経由(`client.on("interactionCreate")`)
2. WebHook経由

Discord.jsでは大体がGateWayAPI経由で動作するのだが、CloudFlareでDiscordBotを動かしたくなった場合はWebHook経由が選択される。
ちなみに軽く調べたのだが、Discord.jsからWebHook経由でinteractionを使う方法は見つからなかった。

Interactionを受信したときに動作させるコマンドは出来るだけ元となるコンポーネントとの紐づけを密接にしたいわけだが、ComponentBuilderにはexecute等のハンドラーは一切存在しない。なので自作する必要がある。
これがまぁ初学者にはややこしい上に、Discord.js Guideにはわかりやすく明記されていないのでややこしかったりする。

## 4. CloudFlareで使えない。
DiscordDeveloperPortalではCloudFlare Workerでホストする方法が書かれている。

https://discord.com/developers/docs/tutorials/hosting-on-cloudflare-workers

しかし、Discord.jsはNode.jsに依存しているのでCloudFlareでは使えない。
最近はNode.jsを継続的に動かせる無料のPaaSも減ってきているので、できればCloudFlareで動かしたい。

## 5. Builderが嫌い
Discord.js v14辺りからBuilderが使われるようになった。
ただ、これがまぁややこしかったりする。
そもそもGoFのBuilderパターンはパラメータが多いと使いにくい印象がある。
例えば以下はComponentsV2を試している際に作ったコードなのだが、Builderを使って出力されるObjectと比べ、どちらがわかりやすいだろうか？


<details><summary>Builder</summary>

```js
const container = new ContainerBuilder()

container.addMediaGalleryComponents(
  new MediaGalleryBuilder().addItems(
    new MediaGalleryItemBuilder().setURL(
      'https://media.discordapp.net/attachments/697138785317814292/1364347504702914602/docs-header.png?ex=680d4ba1&is=680bfa21&hm=a6f8164f0dccca3849522f3bbc658068f27430d580ddf7048f738de618244926&=&format=webp&quality=lossless',
    ),
  ),
)

container.addTextDisplayComponents(
  new TextDisplayBuilder().setContent(
    "## Introducing New Components for Messages!\nWe're bringing new components to messages that you can use in your apps. They allow you to have full control over the layout of your messages.\n\nOur previous components system, while functional, had limitations:\n- Content, attachments, embeds, and components had to follow fixed positioning rules\n- Visual styling options were limited\n\nOur new component system addresses these challenges with fully composable components that can be arranged and laid out in any order, allowing for a more flexible and visually appealing design. Check out the [changelog](https://discord.com/developers/docs/change-log) for more details.",
  ),
)

container.addMediaGalleryComponents(
  new MediaGalleryBuilder().addItems(
    new MediaGalleryItemBuilder().setURL(
      'https://media.discordapp.net/attachments/697138785317814292/1364347505642569850/components-hero.png?ex=680d4ba1&is=680bfa21&hm=a0b8137fc1227d4795b279447f484d9850cb610c58cd72d0f4951f199f0150b5&=&format=webp&quality=lossless&width=1872&height=519',
    ),
  ),
)

container.addSectionComponents(
  new SectionBuilder()
    .addTextDisplayComponents(
      new TextDisplayBuilder().setContent('A brief overview of components:'),
    )
    .setButtonAccessory(
      new ButtonBuilder()
        .setLabel('Overview')
        .setURL('https://discord.com/developers/docs/components/overview')
        .setStyle(ButtonStyle.Link),
    ),
  new SectionBuilder()
    .addTextDisplayComponents(
      new TextDisplayBuilder().setContent('A brief overview of components:'),
    )
    .setButtonAccessory(
      new ButtonBuilder()
        .setLabel('Reference')
        .setURL(
          'https://discord.com/developers/docs/components/reference#what-is-a-component-component-types',
        )
        .setStyle(ButtonStyle.Link),
    ),
  new SectionBuilder()
    .addTextDisplayComponents(
      new TextDisplayBuilder().setContent(
        'Get started with message components:',
      ),
    )
    .setButtonAccessory(
      new ButtonBuilder()
        .setLabel('Guide')
        .setURL(
          'https://discord.com/developers/docs/components/using-message-components',
        )
        .setStyle(ButtonStyle.Link),
    ),
)

container.addSeparatorComponents(
  new SeparatorBuilder()
    .setDivider(true)
    .setSpacing(SeparatorSpacingSize.Small),
)

container.addTextDisplayComponents(
  new TextDisplayBuilder().setContent(
    '-# This message was composed using components, check out the request:',
  ),
)
```
</details>

<details><summary>Object</summary>

```js
const messageComponent = {
  type: 17,
  components: [
    {
      type: 12,
      items: [
        {
          media: {
            url: 'https://media.discordapp.net/attachments/697138785317814292/1364347504702914602/docs-header.png?ex=680d4ba1&is=680bfa21&hm=a6f8164f0dccca3849522f3bbc658068f27430d580ddf7048f738de618244926&=&format=webp&quality=lossless',
          },
        },
      ],
    },
    {
      type: 10,
      content:
        "## Introducing New Components for Messages!\nWe're bringing new components to messages that you can use in your apps. They allow you to have full control over the layout of your messages.\n\nOur previous components system, while functional, had limitations:\n- Content, attachments, embeds, and components had to follow fixed positioning rules\n- Visual styling options were limited\n\nOur new component system addresses these challenges with fully composable components that can be arranged and laid out in any order, allowing for a more flexible and visually appealing design. Check out the [changelog](https://discord.com/developers/docs/change-log) for more details.",
    },
    {
      type: 12,
      items: [
        {
          media: {
            url: 'https://media.discordapp.net/attachments/697138785317814292/1364347505642569850/components-hero.png?ex=680d4ba1&is=680bfa21&hm=a0b8137fc1227d4795b279447f484d9850cb610c58cd72d0f4951f199f0150b5&=&format=webp&quality=lossless&width=1872&height=519',
          },
        },
      ],
    },
    {
      type: 9,
      components: [
        {
          type: 10,
          content: 'A brief overview of components:',
        },
      ],
      accessory: {
        type: 2,
        label: 'Overview',
        url: 'https://discord.com/developers/docs/components/overview',
        style: 5,
      },
    },
    {
      type: 9,
      components: [
        {
          type: 10,
          content: 'A brief overview of components:',
        },
      ],
      accessory: {
        type: 2,
        label: 'Reference',
        url: 'https://discord.com/developers/docs/components/reference#what-is-a-component-component-types',
        style: 5,
      },
    },
    {
      type: 9,
      components: [
        {
          type: 10,
          content: 'Get started with message components:',
        },
      ],
      accessory: {
        type: 2,
        label: 'Guide',
        url: 'https://discord.com/developers/docs/components/using-message-components',
        style: 5,
      },
    },
    {
      type: 14,
      divider: true,
      spacing: 1,
    },
    {
      type: 10,
      content:
        '-# This message was composed using components, check out the request:',
    },
  ],
}

```

</details>

正直私はObjectの方がわかりやすい。
なぜならtype部分はEnumを使えばもっとわかりやすく出来るし、型制約をしっかり作ればなんら問題ないからだ。

さらに言えば、Builderがある事によってメッセージに埋め込まれたEmbedを再利用する際にも
Embed->EmbedBuilderに置き換え->内容の変更->再送信
という面倒な手順を踏まなくてはならない。
正直なところ
EmbedJson->自作クラス->EmbedJson->再送信
とした方がまだ汎用性が高かったりする。

## 6. ドキュメントがわかりづらい
Discord.jsは大まかに3つほどドキュメントが存在する。

https://discord.js.org/

https://old.discordjs.dev

https://discordjs.guide/

discord.js.orgとold.discordjs.devは主にAPIリファレンスとなっていて、中身的にはTypeDocというライブラリを使って出力されている。前者と後者の違いはレイアウトの違いが主で、バージョンの差は一切ない。
しかも現行どっちも生きている。
discordjs.guideはライブラリの使い方ガイドのようなページになっている。
もう既にこの時点でちょっと分かりにくい。

ついでに検索機能もうまく動いているとは言いづらい、直感的に検索が出来なかったりする。
(まぁここはDiscordAPIも一緒なんだけど)

試しにちょっと見てもらうと、その不便さが理解できるとおもう。

# まとめ
まぁ他にも`Message<boolean>`のbooleanがisGuildに依存しているのが分かりづらかったり等など言いたいことは様々あるが、ざっくり纏めると。

- 利用者数は多いが、未だにドキュメントの不十分な点がいくつかある
- キャッシュシステムによって型が煩雑になっている
- Builderはクソ
- Nodejsとの依存問題
- interactionへの対応が下手

という具合だと思う。

もちろん、Discordxや他のライブラリを使えという意見もごもっともなのだが、正直安定性等を考えたときにDiscord.jsからの移行は難しい節がある。
だれかこの辺りを解消した新しいライブラリ作ってくれないかなぁ....