概要
OpenAIのAPIを使用して個人用のアシスタントをDiscord上に作成してみます
事前準備
- Discord Developer PortalからApplicationを作成して、トークンを取得します
- OpenAI APIからシークレットキーを作成します
- .envファイルに以下のように記載します
DISCORD_TOKEN="1で取得したトークン" OPEN_AI_TOKEN="2で取得したシークレットキー"
- 必要なライブラリをインストールします
$ 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
メッセージ履歴の取得
実装
チャット形式での会話にはメッセージ履歴の取得が必要ですので、メッセージの履歴を取得して返信するコードを書いてみます
ここでのポイントは以下の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
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);
});
動作確認
先ほどと同様にコマンドを実行し、メッセージを送信すると文脈に沿った返答を行うことを確認します
まとめ
今回はDiscord上でOpenAIのAPIを利用したアシスタントを作成してみました
今回は利用しませんでしたが、gpt-4-vision-preview
というモデルを呼び出せば画像を渡すことができますし、以前の記事で利用したfunctionCalling
という機能ではGPT経由で任意の関数を呼び出すことができて拡張性は抜群ですので、ぜひ自分専用のアシスタントを作成してみてください