Discord.jsでBOTを作る

# はじめに
## 環境
OS: Linux (Debian)
Node.js: v18.10.0
npm: v8.19.2
Discord.js: v.14.5.0

## 構築
アップデート
```bash
sudo apt update
sudo apt upgrade
```
Node.js、npmのインストール・更新
```bash
sudo apt-get install node.js npm
```
フォルダの作成・移動
```bash
mkdir bot
cd bot
```
npm初期化
```bash
npm init  
```
実行結果
```bash
See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (bot) bot
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /home/username/bot/package.json:

{
  "name": "bot",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) y
```
Discord.jsインストール,Node.js更新
```bash
npm install discord.js
```
実行結果 
```bash
added 32 packages, and audited 33 packages in 25s

7 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
```
更新
```bash
npm insall n
sudo n latest
```

Discord Developer Portalでの作業

https://discord.com/developers
にアクセス
右上の「New Application」をクリック
![Screenshot 2022-10-19 16.36.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/1061c4be-b29b-0a1f-e413-dccb3a925ca2.png)

BOT名を指定し「Create」をクリック
![Screenshot 2022-10-20 11.57.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/4422d0da-f038-8c09-6b96-9c0c3976d81d.png)

BOTの管理画面
![Screenshot 2022-10-20 11.58.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/697f1497-ff29-0489-0ac4-e3f6d79ff089.png)

「Bot」をクリック
![Screenshot 2022-10-20 11.59.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/d97a13ae-3e64-29c5-004c-e9f23c857b9e.png)

「Add Bot」をクリック
![Screenshot 2022-10-20 12.00.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/3db65f20-b4ec-dc37-9f1d-822f8c6fef56.png)

「Yes, do it!」をクリック
以下のようなエラーが表示された場合BOTの名前が重複しているため変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/92661568-f487-3b2c-101a-e08d8169543d.png)

成功すれば以下のような画面になる
トークンを取得
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/45bf620b-3793-aed2-ad6f-6b0f1d5c109b.png)

「Reset Token」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/1f721296-21aa-1a2f-db1c-d7257a54cd90.png)

「Yes, do it!」をクリック
「Copy」を押してトークンをコピー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/036c71e5-874a-a3c3-4b7e-b04c8aa495fd.png)

### 注意
上の写真ではトークンを隠していませんがトークンはパスワードのようなものなので絶対に他人に教えないでください。万が一外部に流出した場合は「Reset Token」を押してトークンを再生成してください。

##### 追記
*流石に怖い*ので消した

特権インテントの設定
下にスクロールし
「PRESENCE INTENT」「SERVER MEMBERS INTENT」「MESSAGE CONTENT INTENT」を有効化
無効化しているとメッセージをBOT側から認識できなかったり、メンバー関係の関数を使えなくなったりする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/aca0035e-5282-961a-ea0a-605a93aa22d5.png)


BOTを招待
OAuth2
URL Generatorと辿り、「SCOPES」欄の「bot」と「application.commands」(スラッシュコマンド用)にチェックを入れ、付与したい権限を「BOT PERMISSIONS」欄で選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/ebc9d7f3-7841-db19-5224-7cc114c6ed2a.png)
下にスクロールし「GENERATED URL」の「COPY」をクリックしURLをコピーする
そのURLにアクセスすればBOTを招待できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/c06b13cf-833c-98f5-d519-b4561db86fb2.png)


### ファイルの作成
```javascript
const discord = require('discord.js');
const client = new discord.Client({intents: [
    discord.GatewayIntentBits.DirectMessageReactions,
    discord.GatewayIntentBits.DirectMessageTyping,
    discord.GatewayIntentBits.DirectMessages,
    discord.GatewayIntentBits.GuildBans,
    discord.GatewayIntentBits.GuildEmojisAndStickers,
    discord.GatewayIntentBits.GuildIntegrations,
    discord.GatewayIntentBits.GuildInvites,
    discord.GatewayIntentBits.GuildMembers,
    discord.GatewayIntentBits.GuildMessageReactions,
    discord.GatewayIntentBits.GuildMessageTyping,
    discord.GatewayIntentBits.GuildMessages,
    discord.GatewayIntentBits.GuildPresences,
    discord.GatewayIntentBits.GuildScheduledEvents,
    discord.GatewayIntentBits.GuildVoiceStates,
    discord.GatewayIntentBits.GuildWebhooks,
    discord.GatewayIntentBits.Guilds,
    discord.GatewayIntentBits.MessageContent
], partials: [
    discord.Partials.Channel,
    discord.Partials.GuildMember,
    discord.Partials.GuildScheduledEvent,
    discord.Partials.Message,
    discord.Partials.Reaction,
    discord.Partials.ThreadMember,
    discord.Partials.User
]});
client.on('ready', () => {
    console.log(`BOT is now runnnig.`);
});
client.on('messageCreate',async msg => {
     if(msg.author.bot) {
        return;
    }
    if(msg.content === "hello") {
        msg.reply('Hello');
    }
});
process.on('uncaughtException', (err) => {
     console.log(`${err.stack}`);
});
client.login(BOTのTOKEN);
```
以上をBOTディレクトリにindex.jsとして貼る
```
node index.js
```
で実行できる
停止したいときはCtrl + Cを押して停止

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920761/484a6cb7-f99d-06a9-ec84-01e02b83ba86.png)


## コードの解説
```javascript
const discord = require('discord.js');
```
discordを宣言

```javascript
const client = new discord.Client({intents: [
    discord.GatewayIntentBits.DirectMessageReactions,
    discord.GatewayIntentBits.DirectMessageTyping,
    discord.GatewayIntentBits.DirectMessages,
    discord.GatewayIntentBits.GuildBans,
    discord.GatewayIntentBits.GuildEmojisAndStickers,
    discord.GatewayIntentBits.GuildIntegrations,
    discord.GatewayIntentBits.GuildInvites,
    discord.GatewayIntentBits.GuildMembers,
    discord.GatewayIntentBits.GuildMessageReactions,
    discord.GatewayIntentBits.GuildMessageTyping,
    discord.GatewayIntentBits.GuildMessages,
    discord.GatewayIntentBits.GuildPresences,
    discord.GatewayIntentBits.GuildScheduledEvents,
    discord.GatewayIntentBits.GuildVoiceStates,
    discord.GatewayIntentBits.GuildWebhooks,
    discord.GatewayIntentBits.Guilds,
    discord.GatewayIntentBits.MessageContent
], partials: [
    discord.Partials.Channel,
    discord.Partials.GuildMember,
    discord.Partials.GuildScheduledEvent,
    discord.Partials.Message,
    discord.Partials.Reaction,
    discord.Partials.ThreadMember,
    discord.Partials.User
]});
```
Discord APIのGateway Intentsを宣言
宣言漏れがあるとそれ関連の関数が使用できなくなる
ただし余計なものも多いのでキャッシュが重い

```javascript
client.on('ready', () => {
    console.log(`BOT is now runnnig.`);
});
```
先程宣言したclientがreadyイベント（BOT起動時）にconsole.log()でコンソールにログを書き出す

```javascript
client.on('messageCreate',async msg => {
     if(msg.author.bot) {
        return;
    }
    if(msg.content === "hello") {
        msg.reply('Hello');
    }
});
```
clientがmessageCreate（BOTが入っているサーバーでメッセージが送信されたときに発火するイベント）のときメッセージを変数msgとして取得
msgの送信者(author)がbotであるか否かを判断しBOTならばreturnし
それ以外なら、msgのcontent(文章)が「hello」ならば
msgに返信(.reply)している

```javascript
process.on('uncaughtException', (err) => {
     console.log(`${err.stack}`);
});
```
uncaughtException(キャッチされなかった例外)が発生した際にエラーを変数errとして取得し、エラーのstackをconsole.logで出力している

```
client.login('BOTのトークン');
```
clientにTokenでログインしている

## エラー対処
### 1.
```bash
node:internal/modules/cjs/loader:988
  throw err;
  ^

Error: Cannot find module 'discord.js'
Require stack:
- /home/username/bot/index.js
    at Module._resolveFilename (node:internal/modules/cjs/loader:985:15)
    at Module._load (node:internal/modules/cjs/loader:833:27)
    at Module.require (node:internal/modules/cjs/loader:1051:19)
    at require (node:internal/modules/cjs/helpers:103:18)
    at Object.<anonymous> (/home/username/bot/index.js:1:17)
    at Module._compile (node:internal/modules/cjs/loader:1149:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1203:10)
    at Module.load (node:internal/modules/cjs/loader:1027:32)
    at Module._load (node:internal/modules/cjs/loader:868:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ '/home/username/bot/index.js' ]
}

Node.js v18.10.0
```
Discord.jsがインストールできていない
```bash
npm install discord.js
```
でDiscord.jsをインストールする

### 2.
```bash
Error [TokenInvalid]: An invalid token was provided.
    at WebSocketManager.connect (/home/username/bot/node_modules/discord.js/src/client/websocket/WebSocketManager.js:134:26)
    at Client.login (/home/username/bot/node_modules/discord.js/src/client/Client.js:232:21)
    at Object.<anonymous> (/home/username/bot/index.js:45:8)
    at Module._compile (node:internal/modules/cjs/loader:1149:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1203:10)
    at Module.load (node:internal/modules/cjs/loader:1027:32)
    at Module._load (node:internal/modules/cjs/loader:868:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47
```
トークンが間違っている

### 3.
```bash
Error [DisallowedIntents]: Privileged intent provided is not enabled or whitelisted.
    at WebSocketManager.createShards (/home/username/bot/node_modules/discord.js/src/client/websocket/WebSocketManager.js:250:15)
    at async Client.login (/home/username/bot/node_modules/discord.js/src/client/Client.js:232:7)
```
Discord Developer Portalで「PRESENCE INTENT」「SERVER MEMBERS INTENT」「MESSAGE CONTENT INTENT」のいずれかもしくは全てが無効化されている

# 参考
Discord.js Docs: https://discord.js.org
Discord Developer Portal: https://discord.com/developers