ChatGPT APIを使ったDiscord返答ボットの作成【初心者向け】

この記事では質問をしたら以下のように自動で返答をしてくれるDiscordのBotを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/c8320228-c95b-c1d3-9365-dfd17e6246d9.png)
# 概要
3月1日、遂にOpenAIが「ChatGPT」のAPIを提供開始ました。
これは言語モデル、gpt-3.5-turboを搭載しており、価格は＄0.002/1000tokensとなっています。以前の言語モデル、Davinciと比較すると、1000トークンあたりの価格が1/10となっていてよりリーズナブルになっています。
トークンの計算は、英語の場合1単語あたり1トークンですが、日本語の場合は複雑になっています。
日本語のトークンの数が知りたい場合、[OpenAIのプレイグラウンド](https://platform.openai.com/playground/)で確認することができます。
例えば、「This is a pen」だと4トークン、「これはペンです」だと8トークンとなっています。
## 対象読者
- JavaScriptをある程度理解している人
- VScodeをあらかじめインストールしている人
- これからDiscord botを開発する人
## 開発環境
MacOS Ventura(v13.2.1)
VScode(v1.76)
Node.js(v18.14.0)
npm(v9.3.1)
# 環境の作成
- 任意の場所にコードを入れるファイルを作成します
私の場合、書類フォルダの中に"Discord_gptchat"という名前のフォルダを作成しました。(名前はなんでも可)
- VScodeを開き、作ったフォルダを読み込みます。
- package.jsonの作成
以下のコマンドをターミナルで実行します。
```
npm init
```
色々とオプションを聞かれるので次のようにしてください。("は入力しない)
```
{
  "name": "bot",
  "version": "1.0.0",
  "description": "",",
  "main": "bot/index.js,",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": ",
  "license": "ISC"
}


Is this OK? (yes) y
```


- Discoed.js,dotenv,openaiをインストール
以下のコマンドをターミナルで実行します
```
npm install discord.js dotenv openai
```
discord.jsはDiscordのライブラリ
dotenvは.envを読み込めるようになるモジュール
openaiはOpenAIのライブラリ
となっています。
完了するとこのようなファイル構成になっています。
![pakeg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/ff970a68-5a32-adcf-9cbf-9e39f1a6f58b.png)
botというフォルダを一番外のフォルダ内に作成した後、index.jsというファイルをbotの中に作成します。
![bit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/30cb3b1f-0231-bc9f-5322-50e3c9cb5ded.png)
そして、下記のコマンドで.envファイルを作成します。
```
touch .env
```
最終的には以下のようなファイル構造になります。
![saisixyu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/9f239f9e-31da-4240-27ac-5979ec767afb.png)
# 各種トークンの取得
## discordのトークン取得
- [Discordの開発者ポータル](https://discord.com/developers/applications)にアクセスし、Discordのアカウントでログインします。
- New Applicationを選択ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/cc8521ac-f09e-1041-1bad-267dd5b54ae2.png)
- 任意の名前を入力し、チェックマークをクリックしCreateボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/9bddc4d0-c747-7501-3612-a5883e5fc5d5.png)
- 画面左側のメニューバーでBotを選択した後、Add botをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/4e2e04f1-06c4-8f7d-0850-2e576a464d01.png)
- するとこのような画面になります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/5eecea07-bf00-6860-3e51-ed907296cda3.png)
- 下にスクロールし、以下のように設定し、セーブしてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/fa944161-dd9b-ed6c-d347-9a6346dc1f6c.png)
- 最後に一番上まで戻り、View Tokenを押しトークンを取得してください。(後で使用するのでどこかにメモしておいてください)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/5eecea07-bf00-6860-3e51-ed907296cda3.png)
# OpenAIのAPIキー取得
以下のサイトにアクセスし、アカウントが既にある場合はログイン、無い場合は新規作成をし、secret keyを取得してください。

https://platform.openai.com/account/api-keys
# .envファイルの編集
以下のように書いてください。
```
OPENAI_API_KEY=取得したOpenAIのシークレットキー
BOT_TOKEN=取得したDiscord botのトークン
```
# コードの作成
以下のコードを先ほど作ったindex.jsに保存します。
```javascript
// dotenvを使って、環境変数を読み込む
require("dotenv").config();

// 必要なモジュールを読み込む
var fs = require("fs");
const { Client, GatewayIntentBits } = require("discord.js");
const Discord = require("discord.js");

// Discordクライアントを初期化する
const client = new Discord.Client({
  intents: Object.values(Discord.IntentsBitField.Flags),
});

// OpenAIのAPIキーを設定する
const { Configuration, OpenAIApi } = require("openai");
const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

// Discordクライアントが起動すると、一度だけ呼び出される関数を定義する
client.once("ready", () => {
  console.log(`${client.user.tag} Ready`);
});

// Discordクライアントが起動すると、一度だけ呼び出される関数を定義する
client.on("ready", async () => {
  // コマンドを定義する
  const chat = [
    {
      name: "gpt",
      description: "質問したら答えが返ってきます",
      options: [
        {
          type: 3,
          name: "質問",
          description: "質問したい文を入れてください",
          required: true,
        },
      ],
    },
  ];

  // コマンドを登録する
  await client.application.commands.set(chat);
});

// インタラクション（コマンド）が作成されたときに呼び出される関数を定義する
client.on("interactionCreate", async (interaction) => {
  // インタラクションがコマンドでなければ、何もしない
  if (!interaction.isCommand()) return;

  // インタラクションがどのコマンドかを取得する
  const command = interaction.commandName;

  // gptコマンドが呼び出された場合、OpenAIに質問を送信する
  if (command === "gpt") {
    // 質問を取得する
    const question = interaction.options.getString("質問");
    console.log(question); // 質問がコンソールに出力される

    // interactionの返信を遅延する
    await interaction.deferReply();

    // OpenAIに質問を送信し、回答を取得する
    (async () => {
      try {
        const completion = await openai.createChatCompletion({
          model: "gpt-3.5-turbo",
          messages: [{ role: "user", content: `${question}` }],
        });
        const answer = completion.data.choices[0].text;
        await interaction.editReply(
          `${question}\n>>${completion.data.choices[0].message.content.trim()}\r\n`
        );
      } catch (error) {
        console.error(error);
        await interaction.editReply(`エラーが発生しました: ${error.message}`);
      }
    })();
  }
});

//Discordクライアントにログイン
client.login(process.env.BOT_TOKEN);

```
詳しい説明は省略しますが、/gptというスラッシュコマンドを作成し、コマンドの引数に質問を入力しています。
下の部分でgpt-3-turboのオプションを設定しています。ここを変える事で動作が変わります。詳細は、詳細は[OpenAIのドキュメンテーション](https://platform.openai.com/docs)を参照してください。
```javascript
    // OpenAIに質問を送信し、回答を取得する
    (async () => {
      try {
        const completion = await openai.createChatCompletion({
          model: "gpt-3.5-turbo",
          messages: [{ role: "user", content: `${question}` }],
        });
```
# Discord botの招待
Discordの開発者ポータルを開きOAuth2→URL Generatorを押します。
そして、以下のように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/ef4b817b-227f-02cb-afdd-d8a153907103.png)
一番下にGENERATED URLというものがあるので、それをコピーします。
コピーしたリンクを開くとどのサーバーに追加するかを選択できるので、追加するサーバーを選択しbotを入れます。
# 実行
以下のコマンドをターミナルで打ち込むと、botが起動します。
```
node bot/index.js
```
Discord上で以下のように、/gpt 質問と入力し、送信します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/b3d83abb-e55d-f2ac-9b2d-c3575a1aff4d.png)
すると以下のような返答になりました。
ChatGPTと同じような動作をしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2014168/c8320228-c95b-c1d3-9365-dfd17e6246d9.png)
# おわりに
結構サクッと書いていますが結構情報量が少なくコーディングに時間がかかりました。
Discord.jsは情報が少なかったのですが、ChatGPTを使って質問するのがGoogleで検索するよりも明らかに効率が良いことに驚きました。
このAPIを使えば様々なことに転用できそうですね。
最後まで見ていただきありがとうございました！