DenoでDiscordのBOTを作ってみた - discordeno

discordenoの記事が１つしか見つからなかったから初めて記事を書いてみる…
# 導入
ある日いつものように身内のDiscordでワイワイゲームをしていた時、VCに入ってきた男がこう言ったのである…

> とも「俺、毎日愛してるって言われたいからBOT作ってくれない？」
> ぼく「......？？」

と、いうことでBOTを作ることになったので、せっかくだからと前々から気になっていたDenoを使用してDiscordのBOTを制作することにしたのです......

(実話)

## 注意
この記事では、Denoのインストール方法やDiscordのBOTの登録方法などは説明しません、調べたらいっぱい出てくるし...

自分が参考にした記事を貼っておくのでそちらを参考にしてください！

https://qiita.com/RYOSKATE/items/1c07aff32064e2f8173a

https://qiita.com/1ntegrale9/items/cb285053f2fa5d0cccdf

# まずは最小構成
```typescript
import { createBot, startBot } from "https://deno.land/x/discordeno@13.0.0-rc18/mod.ts"
import { enableCachePlugin, enableCacheSweepers } from "https://deno.land/x/discordeno_cache_plugin@0.0.21/mod.ts"

const baseBot = createBot({
  token: Deno.env.get("DISCORD_TOKEN")!,
  intents: ["Guilds", "GuildMessages"],
  botId: BigInt(Deno.env.get("BOT_ID")!),
  events: {
    ready() {
      console.log("Successfully connected to gateway")
    },
    messageCreate(bot, message) {
      if(message.content.startsWith("!ping")) {
        bot.helpers.sendMessage(message.channelId, { content: "pong!" })
      }
    }
  }
})

const bot = enableCachePlugin(baseBot)
enableCacheSweepers(bot)

await startBot(bot)
```

とりあえず上のコードで最低限動作するBOTは作ることができます、簡単！

## bigInt型に注意

```typescript
botId: BigInt(Deno.env.get("BOT_ID")!)
```

[公式のGitHub](https://github.com/discordeno/discordeno)に載っている最小構成と違う部分としては、CreateBot()関数内のbotIdが最近のバージョンでbigInt型で定義されているので、BigInt()関数で囲ってあげて型変換を行う必要があります。
他にもメッセージを送る際に必要になるチャンネルIDなどもbigInt型になっているので、その都度変換を行いましょう！

## Cache Plugin

他にもdiscordeno公式のプラグインとして **[Cache Plugin](https://github.com/discordeno/discordeno/tree/main/plugins/cache)** が用意されています。

```typescript
import { enableCachePlugin, enableCacheSweepers } from "https://deno.land/x/discordeno_cache_plugin@0.0.21/mod.ts"

~~~省略~~~

const bot = enableCachePlugin(baseBot)
enableCacheSweepers(bot)
```

discordeno単体ではキャッシュを行わないため、こういった個人で作る規模のBOTではプラグインを導入して自動的にキャッシュを行うようにしましょう。おまじない！！

# よく使いそうな機能

最小構成のコードを見れば大体はわかると思いますが、以下のコードはすべて**createBot()関数**の**events**内に記述するものです。

## 特定の単語が送信されたチャンネルにメッセージを送信する
```typescript
messageCreate(bot, message) {
  if(message.content.includes("こんにちは")) {
    bot.helpers.sendMessage(message.channelId, { content: "やっほ〜" })
  }
}
```
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679443/a14e8655-8f57-8e48-51f3-5bc8addce580.png)

## メンションされたらリアクションを追加する
```typescript
messageCreate(bot, message) {
  if(message.mentionedUserIds.includes(bot.id)) {
    bot.helpers.addReaction(message.channelId, message.id, "🤧")
  }
}
```
![スクリーンショット 2022-06-03 1.07.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679443/b22017bd-257a-178f-b5ad-061819d875f4.png)

## ダイレクトメッセージの送信
```typescript
import { sendDirectMessage } from "https://deno.land/x/discordeno_helpers_plugin@0.0.8/mod.ts"

messageCreate(bot, message) {
  // はろ　が送信されたらDMを送る
  if(message.content.includes("はろ")) {
    sendDirectMessage(bot, message.authorId, "DMだよ〜")
  }
}
```
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679443/dcbbabb6-7027-3e0a-3c87-438f29b4d22c.png)
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679443/32da6e22-eb8d-7408-f490-1d3109358e8c.png)

ダイレクトメッセージを送る場合、discordeno公式のプラグインである **[Helpers Plugin](https://github.com/discordeno/discordeno/tree/main/plugins/helpers)** をimportして **sendDirectMessage** 関数を使用すると簡単にDMを送信することができます。

## リアクションイベントを取得する
```typescript
const baseBot = createBot({
  token: 
  // intentsに GuildMessageReactions を追加してあげる
  intents: ["Guilds", "GuildMessages", "GuildMessageReactions"],
  botId: 
  events: {
    reactionAdd(bot, payload){
      // bot自身が追加したリアクションは早期リターンしてあげる
      if(payload.userId == bot.id) return

      bot.helpers.sendMessage(payload.channelId, { content: payload.emoji.name })
    }
  }
})
```
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679443/90f854a0-46fd-da63-c583-8faab93dcf35.png)

## 上で紹介したものを組み合わせるとこんなこともできる...
```typescript
import { createBot, startBot } from "https://deno.land/x/discordeno@13.0.0-rc18/mod.ts"
import { enableCachePlugin, enableCacheSweepers } from "https://deno.land/x/discordeno_cache_plugin@0.0.21/mod.ts"
import { sendDirectMessage } from "https://deno.land/x/discordeno_helpers_plugin@0.0.8/mod.ts"

let messageId = BigInt(0)

const baseBot = createBot({
  token: Deno.env.get("DISCORD_TOKEN")!,
  intents: ["Guilds", "GuildMessages", "GuildMessageReactions"],
  botId: BigInt(Deno.env.get("BOT_ID")!),
  events: {
    messageCreate(bot, message) {
      // !help が送信されたらヘルプのメッセージを送信し
      // そのメッセージに追加されたリアクションをクリックすることでDMにヘルプの詳細が送られてくる
      if(message.content.startsWith("!help")) {
        (async() => {
          const result = await bot.helpers.sendMessage(message.channelId, { content: "これはヘルプです\n詳細はチェックマークをクリックしてください" })
          bot.helpers.addReaction(result.channelId, result.id, "✅")
          messageId = result.id
        })()
      }
    },
    reactionAdd(bot, payload) {
      if(payload.userId == bot.id) return

      if(payload.messageId == messageId && payload.emoji.name == "✅") {
        sendDirectMessage(bot, payload.userId, "これはヘルプの詳細です")
      }
    }
  }
})

const bot = enableCachePlugin(baseBot)
enableCacheSweepers(bot)

await startBot(bot)
```
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679443/e1c190e4-b0eb-d441-cdf4-fd632c083f96.png)
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679443/1d772481-ebf0-c43e-bab7-28d2d8266fe0.png)

今回紹介した、ready, messageCreate, reactionAdd 以外にもたくさんのイベントがあるので、ぜひソースコードを覗いて触ってみてください！

# さいごに
今回記事で紹介したコードは[GitHubで公開してる](https://github.com/yamayama250/discordeno-tutorial)ので全体を見たい場合は覗いてみてください。

冒頭に出てきた友達は喜んでくれたのでよかったです。
![スクリーンショット](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2679443/d3b498ea-6286-e680-6a7e-76d3672852f1.png)