Denoで動作するDiscord botをHerokuにデプロイする

# 概要
Denoで動作する対話的なDiscord botを作成し、Herokuにデプロイするまでの手順についてまとめます。

以下のスクリーンショットのように、投稿したテキストメッセージの内容に応じてDiscord botがメッセージを送信するものを作成します。
![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99114/74a65947-6c39-51fa-dc0c-e84e9ea8975c.png)



# 準備
## Denoのインストール
[Installation \| Manual \| Deno](https://deno.land/manual/getting_started/installation) に従ってインストールします。

```bash
curl -fsSL https://deno.land/x/install/install.sh | sh
```

```console
$ deno --version
deno 1.19.1 (release, x86_64-unknown-linux-gnu)
v8 9.9.115.7
typescript 4.5.2
```

## Denonのインストール
Denoで作成したプログラムを簡単に実行するために、[denon](https://github.com/denosaurs/denon)をインストールします。
denonは様々な機能を提供していますが、今回はnpm scriptsの代替として利用します。プログラムが複雑になるまではシェルスクリプトで代用する方法でも良いと思います。

```bash
deno install -qAf --unstable https://deno.land/x/denon/denon.ts
```
```console
$ denon --version
[*] [main] v2.5.0
```

## Discord botの設定
1. [Discord Developer Portal](https://discord.com/developers)にアクセスし、**New Application** -> **Name**を入力 -> **Create** でDiscord アプリケーションを作成します。

1. **Settings** -> **Bot** -> **Add Bot** で Botの機能を有効化します。

1. **OAuth2** -> **URL Generator** から認証URLを発行します。
![スクリーンショット 2022-02-27 103249.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99114/8398e61d-73dc-8c1c-8e6e-1fce4f7f08e8.png)

1. 発行した認証URLにアクセスし、Discordサーバにbotを追加します。
![スクリーンショット 2022-02-27 103744.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99114/f4289525-9f06-4313-e99b-6e5a0c9a40f3.png)


## Herokuの設定
[The Heroku CLI \| Heroku Dev Center](https://devcenter.heroku.com/articles/heroku-cli#standalone-installation-with-a-tarball) の手順に従ってインストールします。

```bash
curl https://cli-assets.heroku.com/install.sh | sh
```

```console
$ heroku --version
heroku/7.59.2 linux-x64 node-v12.21.0
```

# Discord botのプログラムを作成する
## ディレクトリ構成
```bash
$ tree
.
├── .envrc
├── .gitignore
├── Procfile
├── scripts.yml
├── src
│   └── main.ts
└── runtime.txt
```

## ソースコード
Discordのライブラリとして[discordeno/discordeno: Discord API library for Deno](https://github.com/discordeno/discordeno)を使用しています。
```typescript:./src/main.ts
import {
  createBot,
  startBot,
  getUser
} from "https://deno.land/x/discordeno@13.0.0-rc22/mod.ts";

const bot = createBot({
  token: Deno.env.get("DISCORD_TOKEN")!,
  intents: ["GuildMessages"],
  botId: BigInt(Deno.env.get("APPLICATION_ID")!),
  events: {
    ready() {
      console.log("Successfully connected to gateway");
    },
    async messageCreate(bot, message) {
      console.log(message.content)
      if (message.channelId === BigInt(Deno.env.get("CHANNEL_ID")!)) {
        if (message.content === "bye") {
          const user = await getUser(bot, message.authorId)
          bot.helpers.sendMessage(
            message.channelId,
            {
              content: `die ${user.username} :-1:`,
            },
          );
        } else if (!message.isBot) {
          bot.helpers.sendMessage(
            message.channelId,
            {
              content: ":middle_finger:",
            },
          );
        }
      }
    },
  },
});

await startBot(bot);
```

## 各種設定ファイル
### 環境変数
```bash:./.envrc
# https://discord.com/developers/applications
# Settings -> Bot -> TOKEN
export DISCORD_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXX.XXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXX
# General Information -> APPLICATION ID
export APPLICATION_ID=000000000000000000

# チャンネルを右クリック -> IDをコピー
export CHANNEL_ID=000000000000000000
```

### Denonの設定ファイル
```yaml:./src/scripts.yml
scripts:
  start:
    cmd: "deno run --allow-env --allow-net ./src/main.ts"
    desc: "run discord bot"
```

### Herokuの設定ファイル
```./Procfile
Worker: deno run --allow-env --allow-net ./src/main.ts
```
```text:./runtime.txt
v1.19.1
```

### .gitignore
```./.gitignore
.envrc
```

# 動作確認
```console
$ denon start
[*] [main] v2.5.0
[*] [daem] watching path(s): **/*.*
[*] [daem] watching extensions: ts,tsx,js,jsx,json
[!] [#0] starting `deno run --allow-env --allow-net ./src/main.ts`
Successfully connected to gateway
hi
:middle_finger:
bye
die gogobike :-1:
```
![スクリーンショット 2022-02-27 102342.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99114/e4291841-29f7-3bad-a259-502975fab8b2.png)


# Herokuにデプロイする
```bash
heroku create --buildpack https://github.com/chibat/heroku-buildpack-deno.git
heroku config:set DISCORD_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXX.XXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXX
heroku config:set APPLICATION_ID=000000000000000000
heroku config:set CHANNEL_ID=000000000000000000

git init
git add -A
git commit -m 'Initial commit'
git push heroku main:main

heroku ps:scale Worker=1:Free
```


# 参考
https://qiita.com/RYOSKATE/items/1c07aff32064e2f8173a