🧑‍🏫

#59 OpenAIのAPIを使用してアシスタントをDiscord上に作成してみる

2024/09/19に公開

概要

OpenAIのAPIを使用して個人用のアシスタントをDiscord上に作成してみます

事前準備

  1. Discord Developer PortalからApplicationを作成して、トークンを取得します
  2. OpenAI APIからシークレットキーを作成します
  3. .envファイルに以下のように記載します
    DISCORD_TOKEN="1で取得したトークン"
    OPEN_AI_TOKEN="2で取得したシークレットキー"
    
  4. 必要なライブラリをインストールします
    $ npm install discord.js dotenv log4js openai
    

受け取ったメッセージをそのまま返す

実装

受け取ったメッセージをそのまま返すコードを書いてみます

ここでのポイントは以下の3点です

  • client.login()でログインを行います。トークンを引数にとります
  • client.on('messageCreate', async message => {})でメッセージの受信を行います
  • message.reply()でメッセージの返信を行います。引数はいくつかの形式で渡せますが、今回は文字列を渡します
import { Client } from "discord.js";
import 'dotenv/config'

const client = new Client({
    intents: [
        GatewayIntentBits.GuildMessages, //メッセージを取得できる
        GatewayIntentBits.MessageContent, //メッセージの内容を取得できる
        GatewayIntentBits.GuildWebhooks, //Webhook関係を参照・設定できる
        GatewayIntentBits.Guilds, 
    ]
});

client.on('messageCreate', async message => {
    //送信者がbotならスルー
    if (message.author.bot) {
        logger.debug('message.author.bot == true');
        return;
    }

    //受信したメッセージを返信
    message.reply(message.content);
});

//ログイン
client.login(process.env.DISCORD_TOKEN);

動作確認

以下のコマンドを実行し、Discord上でメッセージを送信してみると、画像のように返信することを確認します

$ npx ts-node ./src/main.ts

image.png

メッセージ履歴の取得

実装

チャット形式での会話にはメッセージ履歴の取得が必要ですので、メッセージの履歴を取得して返信するコードを書いてみます

ここでのポイントは以下の1点です

  • channel.messages.fetch()でチャンネルのメッセージ履歴を取得します
client.on('messageCreate', async message => {
    //送信者がbotならスルー
    if (message.author.bot) {
        logger.debug('message.author.bot == true');
        return;
    }
    const channel = message.channel;

    //メッセージを取得して古い順に並び変え
    const messagesCollection = await channel.messages.fetch();
    messagesCollection.sort((messageA, messageB) => messageA.createdTimestamp - messageB.createdTimestamp);

    const messages: string[] = [];
    for (let message of messagesCollection) {
        messages.push(message[1].content);
    }
    logger.debug(message);

    //返信
    message.reply(JSON.stringify(messages));
});

動作確認

先ほどと同様に以下のコマンドを実行し、Discord上でメッセージを送信すると、画像のようにメッセージ履歴の配列を返信してくれることを確認します

$ npx ts-node ./src/main.ts

image.png

OpenAIのAPIを呼び出す

実装

先ほど取得したメッセージ履歴を用いて、OpenAIのAPIを呼び出して会話できるコードを書いてみます

ここでのポイントは以下の4点です

  • message.author.idで送信者のユーザIDを取得します
  • client.user.idで自身のユーザIDを取得します
  • openai.chat.completions.create()でAPIを呼び出しメッセージを生成します
  • ChatCompletion.choices[0].message.contentで生成された文字列を抽出します(通常ではchoicesは0番目を取得します)
import OpenAI from 'openai';
import { ChatCompletionMessageParam } from "openai/resources";
const openai = new OpenAI({
    apiKey: process.env.OPEN_AI_TOKEN,
});

client.on('messageCreate', async message => {
    //送信者がbotならスルー
    if (message.author.bot) {
        logger.debug('message.author.bot == true');
        return;
    }
    const channel = message.channel;

    //メッセージを取得して古い順に並び変え
    const messagesCollection = await channel.messages.fetch();
    messagesCollection.sort((messageA, messageB) => messageA.createdTimestamp - messageB.createdTimestamp);

    //メッセージをOpenAIのAPIに渡せる形に成型する
    const messages: ChatCompletionMessageParam[] = [];
    for (let message of messagesCollection) {
        const role = ((message[1].author.id == client.user?.id) ? "assistant" : "user");
        messages.push(
            {
                role: role,
                content: message[1].content
            }
        );
    }
    logger.debug(message);

    //APIにメッセージを投げて返答を抽出
    const content = (await openai.chat.completions.create({
        messages,
        model: 'gpt-4-turbo-preview'
    })).choices[0].message.content;
    if (!content) {
        return;
    }

    //返信
    message.reply(content);
});

動作確認

先ほどと同様にコマンドを実行し、メッセージを送信すると文脈に沿った返答を行うことを確認します

image.png

まとめ

今回はDiscord上でOpenAIのAPIを利用したアシスタントを作成してみました

今回は利用しませんでしたが、gpt-4-vision-previewというモデルを呼び出せば画像を渡すことができますし、以前の記事で利用したfunctionCallingという機能ではGPT経由で任意の関数を呼び出すことができて拡張性は抜群ですので、ぜひ自分専用のアシスタントを作成してみてください

公式ドキュメント

Discussion