【忙しい人向け】爆速で ChatGPT の Discord Bot を作ってみた

# 概要

ChatGPT の Discord Bot を試しに作ってみたら数時間で完成したので、記事にしてみました。
以下のようにBotにメンションすると返事をしてくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/0b0c9429-e61b-25ea-081d-2d70305f5032.png)

:::note warn
**注意事項**
- お金が掛かります。
- 筆者はDiscord Botを作るのが初めてです。
- 普段 Python を書かないのでコードも雑です。
- 本記事は忙しい人向けです。時間がある人は他の記事も参考にしましょう。
:::

# 1. とりあえず Discord Bot を用意する

最速で Discord の Bot が作れる方法を発見しました。
以下の記事に従って、Discord Bot を用意します。

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

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

**注意点がいくつかあります！**

- BOTの追加設定が必要
    - 1つ目の記事に従いBotを作成する際に、追加で設定が必要です。
    - DEVELOPER PORTAL の Bot の設定ページから `MESSAGE CONTENT INTENT` をオンにする必要があります。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/a94d461c-398e-05b8-e32f-1dcbb294fbd4.png)
- 課金が必要
    - 2つ目の記事の冒頭にも書いてありますが、Herokuの無料プランが廃止されたため、課金が必要です。
    - 筆者は一番安いプランの `Eco dynos` に登録しました。月5ドルです。

記事に従い `/ping` に対して `pong` が返ってきたら、次へ進みましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/e8ed7531-2b74-a677-d40a-d8f516e9b118.png)

# 2. OpenAI のアカウントを作成する

以下からアカウントを作成します。

https://platform.openai.com/signup

特に詰まるポイントは無かったので説明は省きますが、困ったら「openai アカウント 作成」とかでググりましょう。

アカウントが作成できたら、以下から API key を作成します。

https://platform.openai.com/account/api-keys

以下から API key が表示されていればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/067caddb-5086-6b54-5127-10274002322f.png)

作成した API key は手順1で登録した Bot のトークンと同様に、Herokuに `OPENAI_API_KEY` という名前で登録しておきましょう。

アカウント作成時に無料クレジットが付与されるので、そのクレジットを消費しきるまでは無料です。
`Usage` から、使用量が確認できるので、定期的に確認しておきましょう。

クレジットを消費しきると止まると思うので、使い続けたい場合は課金しましょう。
1000トークンあたり0.002ドルなので、安いと思います。

# 3. Bot に対してメンションが来たら ChatGPT で返事をするようにする

ここからが本番です。手順1で作成したリポジトリの `discordbot.py` を編集していきます。

その前に、`requirements.txt` に pythonのパッケージ `openai` を追加します。

```diff_python:requirements.txt
  discord.py[voice]
+ openai
```

これで、デプロイ時に以下の OpenAI のパッケージがインストールされます。

https://github.com/openai/openai-python

最後に、OpenAI のパッケージを利用して、以下のように `discordbot.py` を編集します。

```python
import discord
import traceback
from discord.ext import commands
from os import getenv
import openai

intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(command_prefix='/', intents=intents)

messages = [
    {"role": "system", "content": "You are a helpful assistant. The AI assistant's name is AI Qiitan."},
    {"role": "user", "content": "こんにちは。あなたは誰ですか？"},
    {"role": "assistant", "content": "私は AI アシスタントの AI Qiitan です。なにかお手伝いできることはありますか？"}
]

@bot.event
async def on_command_error(ctx, error):
    orig_error = getattr(error, "original", error)
    error_msg = ''.join(traceback.TracebackException.from_exception(orig_error).format())
    await ctx.send(error_msg)

@bot.event
async def on_message(message):
    if message.author == bot.user:
        return
    if bot.user.id in [member.id for member in message.mentions]:
        print(message.content)
        print(message.content.split('>')[1].lstrip())
        messages.append({"role": "user", "content": message.content.split('>')[1].lstrip()})

        openai_api_key = getenv('OPENAI_API_KEY')
        openai.api_key = openai_api_key

        completion = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=messages
        )

        print(completion.choices[0].message.content)
        await message.channel.send(completion.choices[0].message.content)

token = getenv('DISCORD_BOT_TOKEN')
bot.run(token)
```

これで完成です！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/0b0c9429-e61b-25ea-081d-2d70305f5032.png)


# おまけ

上記の `discordbot.py` を少し解説します。

その前に、今回使用しているパッケージのリファレンスを紹介します。

## `discord.py`

以下が `discord.py` 日本語のドキュメントです。困ったらここを読みましょう。

https://discordpy.readthedocs.io/ja/latest/index.html

一番参考にしたのは、APIリファレンスのページです。
例として、下で出てくる `on_message` の引数 `Message` がどんなクラスで、何を持っているかを参照しました。

https://discordpy.readthedocs.io/ja/latest/api.html#message

## `openai-python`

リポジトリは以下です。

https://github.com/openai/openai-python

公式ドキュメントは英語ですが、何となく雰囲気で理解できます。

https://platform.openai.com/docs/api-reference/chat/create

他には、今回使用している `openai.ChatCompletion` でググって出てきた例を参考にすれば実装できます。

## `discordbot.py` 解説

以下のように `on_message` で、Discordのメッセージを処理します。

```py
@bot.event
async def on_message(message):
```

以下で、メッセージのメンションがある場合のみ、処理を行うようにしています。

```py
if bot.user.id in [member.id for member in message.mentions]:
```

`message.content` でメッセージを文字列で取得できますが、メンションが含まれる場合以下のようになっています。

```
<@123456789123456789> こんにちは
```

なので、以下のようにメンションより後ろの部分を取得しています。

```py
message.content.split('>')[1].lstrip()
```

今回使用している API の`openai.ChatCompletion` は、3つのロール(`system`, `user`, `assistant`)による会話に続く次のメッセージが返ってきます。今回は以下のように設定してみました。

```
system    : あなたはアシスタントです。なまえは AI Qiitan です。
user      : あなたは誰ですか？
assistant : 私は AI アシスタントの AI Qiitan です。
user      : (ここに　Discord から取得したメッセージを入れる)
assistant : (ここを ChatGPT が生成する)
```

これを python で書くと以下のようになります。

```py
messages = [
    {"role": "system", "content": "You are a helpful assistant. The AI assistant's name is AI Qiitan."},
    {"role": "user", "content": "こんにちは。あなたは誰ですか？"},
    {"role": "assistant", "content": "私は AI アシスタントの AI Qiitan です。なにかお手伝いできることはありますか？"}
]

messages.append({"role": "user", "content": message.content.split('>')[1].lstrip()})
```

そして、以下で ChatGPTが返したメッセージが取得できるので、これを Discord に投げています。

```py
completion.choices[0].message.content
```

# あとがき

きっかけは社内の Slack の ChatGPT API を使った bot でした。

https://qiita.com/tomoasleep/items/030e42b5e3853c609cf9

社内でとても盛り上がっていたので、普段入り浸っているDiscordサーバーに導入したら盛り上がるのでは？と思い、作ってみました。

爆速で雑に作ったのでまだまだ改善の余地がありますが、こんな状態でもサーバーのメンバーが様々な質問をBotに投げかけており、めちゃくちゃ盛り上がりました。