Discord.jsでメンションに反応するbotを作る

#概要
Discordのテキストチャンネルで特定の文字列を含む発言をしたときに反応するbotを作ったので、その手順をなるべく丁寧に書いていきます。

- botの作成、設定などの準備
- botがメンションを受けた判定
- 発言者がボイスチャンネルにいるかどうかの判定
- ボイスチャンネルへの入退室
- ボイスチャンネル内で音楽を再生する
- 発言者へメンションする

について記述しています。

#1. Node.jsのインストール
割愛します

#2. 必要なもののインストール
[discord.js - Introduction to Voice](https://discord.js.org/#/docs/main/stable/topics/voice)と
[Discord.jsによるBotでボイスチャンネルに触れてみる](https://qiita.com/allegrogiken/items/947d920aeb3e8c75e23b)を参考に、必要なライブラリをインストールしていきます。

```shell
# windows環境のみ
npm install --global windows-build-tools

# 共通
npm install discord.js
npm install ffmpeg-binaries
# 下記の2つのうちどちらか
npm install node-opus
npm install opusscript
```

これでもエラーが出るWindows環境の方は[ここ](http://blog.gregzaal.com/how-to-install-ffmpeg-on-windows/)を参考にffmpegをインストールしてみてください。


#3. Botアカウントの作成・サーバへの追加

1. [Discord - My Applications](https://discordapp.com/developers/applications/)にアクセス
2. 「General Information」からNAMEを変更
    - （よくある名前だと `Too many users have this username, please try another.`とbot作成を拒否されます
3. 「Bot」から「Add Bot」をクリックしてBot作成
4. `https://discordapp.com/oauth2/authorize?client_id=クライアントID&permissions=31477768&scope=bot`にアクセスして、導入したいサーバを選択してBotを追加する
    - クライアントIDには「General Information」タブにある「CLIENT ID」欄の数字をコピペしてください
    - permissionsの数字は、botに与えたい権限に応じて変更してください。「Bot」タブの下部にて、どの権限を与えるにはどの数字にすればいいか計算してくれます。
5. 「Bot」の「Token」欄の「Click to Reveal Token」をクリックするとトークンが表示されます。あとで必要なのでコピーしておきましょう。


#4. コーディング
[Discord.js](https://discord.js.org/)や[Discord.jsによるBotでボイスチャンネルに触れてみる](https://qiita.com/allegrogiken/items/947d920aeb3e8c75e23b)を参考に、コーディングしていきます！

今回は、最近巷ではやっている"敗北者"という単語を含む文字列に反応するエースのbotを作ってみます。

反応条件と挙動は、ちょっとややこしいですが

- ボイスチャンネルにいる人がbotに敗北者という単語を含むメンションをしたとき
    - ボイスチャンネルに入り音声を流す
- 上記に合致しない人が敗北者という単語を含む発言をしたとき
    - 発言者にメンションする

という感じにします。

```javascript:App.js
// ログイン処理
const Discord = require("discord.js");
const client = new Discord.Client();
client.on("ready", () => {
  console.log(`Logged in as ${client.user.tag}`);
});

// テキストチャンネルに発言があったときの処理
client.on("message", message => {
  // bot自身の発言は無視
  if (message.author.bot) return;

  //　敗北者という単語が含まれていたときの処理
  if (message.content.includes("敗北者")) {
    if (message.isMemberMentioned(client.user) && message.member.voiceChannel) {
      /* ボイスチャンネルにいる人がbotにメンションをしたときの処理 */
      message.member.voiceChannel
        .join()
        .then(connection => {
          const fileNames = ["haibokusya.m4a", "torikeseyoimanokotoba.m4a"];
          // 複数の音声データのうちいずれかをランダムで選ぶ
          const fileName =
            fileNames[Math.floor(Math.random() * fileNames.length)];

          // 再生 再生終了時にボイスチャンネルから切断
          const dispatcher = connection.playFile(fileName);
          dispatcher.on("end", () => connection.disconnect());
        })
        .catch(console.error);
    } else {
      /* 敗北者という単語が含まれているが、
      メンションでなかったり発言者がボイスチャンネルにいないときの処理 */
      const texts = [
        "ハァ...ハァ...敗北者...?",
        "取り消せよ...!!今の言葉...!!",
      ];

      // メンションする文字列をランダムで選択
      const replyText = texts[Math.floor(Math.random() * texts.length)];
      message.reply(replyText).catch(console.error);
    }
  }
});

const token = "トークンをここにペースト";
client.login(token);
```

音声ファイルは少なくとも`.mp3`や`.m4a`が対応しているようです。

自分が参考にした記事では、音声流してボイスチャンネルから切断した後などに`return;`があったのですが、取り除いても今の所動いているので取ってしまいました。
もし必要でその理由が分かる方いましたら教えていただけると助かります…！

# 5. 実行
ターミナルで実行します

```shell
node App.js
```

botのログインに成功したら

```
Logged in as エース#数字
```
と出ますので、メンションするなりしてみてください。

# 6. 最後に
色々自分好みのネタを仕込むことができて楽しめそうですね
普段使ってるPC上でずっと動かすのもアレなので、Herokuとかで動かしときたいところ…
[ここ](https://qiita.com/sato-ken/items/9dae1f00e863af161a5a)とか参考になりそう？

# 7. 参考
[Discord.js](https://discord.js.org/#/)
[Discord.jsでのBotの作り方!!](https://qiita.com/mouse_484/items/ffa35740f5c5251e1af6)
[Discord.jsで簡単にbotを作成する【基礎編】](https://qiita.com/misskotocoin/items/5d8c503e02093eca1f9b)
[Discord.jsによるBotでボイスチャンネルに触れてみる](https://qiita.com/allegrogiken/items/947d920aeb3e8c75e23b)
[“Error: FFMPEG not found”: Error with simple Discord Bot](https://stackoverflow.com/questions/48647177/error-ffmpeg-not-found-error-with-simple-discord-bot/49272723)