Discord.jsを使って開発したDiscordボットをHerokuで動かす方法

Discord.js を使用したプログラム（ボット）を Heroku で動作させる方法について書かれた記事です。

コマンドラインでの操作や、Git への知識が鈍い場合「[ボットをHerokuで動かす方法の初心者向け補足](https://scrapbox.io/discordjs-japan/ボットをHerokuで動かす方法の初心者向け補足)」も参考にしながら読んでください。

## 無償枠の終了

Herokuから2022年11月27日をもって無償枠が終了されることが発表されました。
無償枠を使用している方で、引き続きHerokuを使用するには有償プランに変更する必要があります。

## 必要なもの

- Node.js
    - [ダウンロード](https://nodejs.org/ja/)
    - Discord.js v13 は現在 Node.js v16.6.0 以上を必要としており、ローカルでボットを動かすことが無くても安定版ではなく、**最新版**をダウンロードしておくことをオススメします
- Git
    - [ダウンロード](https://git-scm.com/downloads)
    - 初めて使う場合は、[初期設定](https://scrapbox.io/discordjs-japan/ボットをHerokuで動かす方法の初心者向け補足#5f061d974daa920000817ff8)を済ませておいてください
- Heroku CLI
    - 私は`npm`のグローバルインストールを使用してインストールしましたが、公式では他のインストール方法を勧めているようですので、[こちら](https://devcenter.heroku.com/articles/heroku-cli#download-and-install)からインストーラーをダウンロードしてインストールすることをオススメします
- Herokuのアカウント
    - まだアカウントを持っていない場合は[こちら](https://signup.heroku.com/jp)で作成できます
    - ログインは[こちら](https://id.heroku.com/login)

これから作業することはほぼ全て**コマンド**を使用して行うので、ターミナル（PowerShellなど）を開いておいてください。

## Heroku CLI を使用して Heroku へログインする

`Heroku CLI`を使用するには、Herokuへのログインが必要になります。

1. `heroku login` をターミナルに打ち込んで、実行
2. `heroku: Press any key to open up the browser to login or q to exit:` というメッセージがコンソールに表示されたら、`Q`以外のキーを押す
3. ブラウザが開かれるので、`Log in` と書かれたボタンを押す[^1]

コンソールに、`Logging in... done` またはブラウザで開かれたページに `Logged In` と表示されていれば、完了です。

## アプリケーションを作成する

Heroku へのログインも完了したので、アプリケーションを作成しましょう。

1. `heroku apps:create -b heroku/nodejs`[^2] または `heroku apps:create アプリケーションの名前 -b heroku/nodejs` をターミナルに打ち込んで、実行[^3]

![アプリケーションの作成に成功したときにコンソールに表示されるメッセージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248810/98f6e68e-dcac-18ec-8bc8-e49637db6d9e.png)


作成に成功すると、`https://<アプリケーションの名前>.herokuapp.com` というアプリケーションに割り当てられるドメインと `https://git.heroku.com/<アプリケーションの名前>.git` というアプリケーションのリポジトリのURLがコンソールに表示されるので、`https://git.heroku.com/<アプリケーションの名前>.git` をどこかにメモしておいてください。

## ボットのプログラムを書いて、デプロイしよう

ボットのプログラムを書いて、Herokuへデプロイしてみましょう。

### リポジトリをクローンする

ターミナルに[アプリケーションを作成する](#アプリケーションを作成する)でメモしたものを `git clone 「https://git.heroku.com/<アプリケーションの名前>.git` というように打ち込んで、実行するだけです。

そして、クローンしたリポジトリに移動しときましょう。

```bash
> git clone https://git.heroku.com/<アプリケーションの名前>.git
> cd アプリケーションの名前
```

### クローンしたリポジトリにコードを追加する

#### package.json を作成する

`npm init -y` を実行することで、プロントに項目を入力する作業をスキップして `package.json` を作成します。

##### Node.js のバージョンを v16.x に指定する

Discord.js v13 からは Node.js v16.6.0 以上を最低でも必要としているので、Heroku に Node.js v16.6.0 以上を使うことを伝えるために`package.json`に少し変更を加える必要があります。

```diff_json:package.json
{
  "name": "my-discord-bot",
  "version": "1.0.0",
  "description": "Bot for Discord",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "Example <xxx@example.com>",
+ "license": "ISC",
+ "engines": {
+   "node": "16.x"
+ }
}
```

これで Heroku 側が自動で Node.js v16 の最新を使用するようになり、Discord.js の最低要件を満たすので問題なく動作します。

#### 必要な依存関係をインストールする

Discord.js が必要になるので、`npm install` を使用して、`discord.js` をインストールしましょう。

```bash
npm i discord.js
```

#### node_modules を Git の管理下から除外する

Heroku はデプロイ時に自動的に `npm install` を実行して、必要な依存をインストールしてくれるのと、`node_modules` をバージョン管理するととんでもないことになってしまうので、管理下から除外します。

`.gitignore` ファイルを作成して、`node_modules/` と書き込んでおきましょう。

```plaintext:.gitignore
node_modules/
```

#### ボットのプログラムを作成する

ここでは `index.js` という名前のJSファイルを作成して、その中にプログラムを書きます。

Herokuで動作すればそれでいいので、discord.js の公式サイトにあるサンプルコードをv13用に変更して貼り付けておきました。

```js:index.js
const Discord = require('discord.js')
const client = new Discord.Client({
  intents: ['GUILDS', 'GUILD_MESSAGES']
})

client.on('ready', client => {
  console.log(`Logged in as ${client.user.tag}!`)
})

client.on('messageCreate', message => {
  if (message.content === 'ping') {
    message.reply('Pong!')
      .catch(console.error)
  }
})

client.login()
  .catch(console.error)
```

**この時、`client.login` の引数に直接ボットのトークンを書き込まないでください。**

#### Procfile を作成する

`Procfile` というファイルを作成して、中に `worker: node ./index.js` と書きましょう。

```plaintext:Procfile
worker: node ./index.js
```

この時 `Procfile` は**UTF-8のBOM無し**で保存するようにしてください。

### ここまでの変更をコミットする

```bash
> git add .
> git commit -m "make it is"
```

### ボットのトークンを環境変数に登録する

ボットのトークンを環境変数に登録しておきましょう。

ターミナルで `heroku config:set -a アプリケーションの名前 DISCORD_TOKEN=<Discordボットのトークン>` を打ち込んで、実行しましょう。

`DISCORD_TOKEN`がキーとなるわけですが、Discord.jsは`Client#login`が実行された際に、引数に何もなければ`DISCORD_TOKEN`に登録されているトークンを使用しようとします。

### デプロイ

ターミナルで `git push` を実行すれば、すぐにデプロイが開始されます。

### Web Dyno から Worker Dyno へ変更

Web Dyno はしばらくするとスリープ状態になってしまうので、Worker Dyno に切り替えてスリープしないようにします。

`heroku scale -a アプリケーションの名前 web=0:Free worker=1:Free` で `Web Dyno` を**OFF**に、 `Worker Dyno` を **ON** にできます。

## 終わり
ログを確認するとうまく動作していることが確認できます。

```bash:cmd
heroku logs -a アプリケーションの名前
```

![ログ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248810/71f32197-fe19-2b59-554c-2634de77d1ee.png)
![Ping Pong](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248810/beffe6ae-46d4-e14f-4f19-5c0f822ef98f.png)

## スリープについて

~~スリープすることはありません。ただクレジットカードを登録していないと**月550時間**しか動かせません。
登録すると**450時間**追加され、合計で**月1000時間**動かせるので余裕で24時間永遠に動かせます。（Heroku の変更がない限り）~~

## 一時的に停止する方法

**Web Dyno から Worker Dyno へ変更**の部分で使用した`heroku ps:scale`というコマンドを使用し、**Worker Dyno**のスケールを`0`にすることでアプリケーションを削除せずとも一時的に停止することが可能です。

```sh
heroku ps:scale -a アプリケーションの名前 worker=0
```

再開させる場合は上記の`worker=0`を`worker=1`に変更して実行してください。

[^1]: もしブラウザが開かなかったらコンソールに表示されているURLをコピーして、自分でURLをブラウザで開いてください

[^2]: `heroku apps:create` を実行すると、適当な名前がアプリケーション名として割り当てられます

[^3]: アプリケーションの名前は先頭がアルファベット、末尾がアルファベットまたは数字である必要があり、小文字、数字、ダッシュのみを含めることができます