🤖

RustでChatGPTを利用したDiscord Bot作った

2023/03/17に公開

ChatGPTを気軽に使えるようにするべく、ChatGPTを利用したDiscord Botを作りました。
Rust(serenity)で開発して、shuttleへデプロイし、常時稼働させています。

ソースコード

https://github.com/yukyu30/chatgpt-bot-for-discord

Botの大まかな動作

便宜上、今回作成したBotの名前を@chatgpt-botとします。

  1. @chatgpt-botへメンションすると、そのメンションの内容と最新の100件のメッセージを取得します。
  2. それらを指定されている形式に変換して、OpenAIのAPIへPOSTします
  3. APIのレスポンスをBotがメンションをしてきたユーザーに返します。

ここではメンションとしましたが、 スラッシュコマンドでも良いです。

利用したもの

言語としてはRustを用いて、Discord API用のクレートであるserenityを利用しました。
デプロイ先にはshuttleを利用しました。

実装

プロジェクト作成

shuttleではserenityがサポートされているので下記のコマンドでserenity用のプロジェクトが作成ができます。

cargo shuttle init --serenity

詳細なDiscord Botの設定などは省きます。下記を参考にしてください。
https://dev.classmethod.jp/articles/rust-discordbot/

コード

コードは公開しているので、ポイントのみ解説します。

トークンやAPIキーの扱い

Shuttleでは.envではなくSecrets.tomlに環境変数などの機密情報を記載して利用します。
https://docs.shuttle.rs/resources/shuttle-secrets
そのため、ShuttleのBlogにもあるようにasync fn serenityで環境変数を取得した後、gpt_tokenでBot構造体に所有させることにしています。

lib.rs
struct Bot {
    gpt_token: String,
    client: reqwest::Client,
}

...

#[shuttle_service::main]
async fn serenity(
    #[shuttle_secrets::Secrets] secret_store: SecretStore,
) -> shuttle_service::ShuttleSerenity {
    // Secrets.tomlからトークンを取得する
    let discord_token = secret_store
        .get("DISCORD_TOKEN")
        .context("'DISCORD_TOKEN' was not found")?;
    let gpt_token = secret_store
        .get("CHATGPT_TOKEN")
        .context("'CHATGPT_TOKEN' was not found")?;

    let client = get_client(&discord_token, &gpt_token).await;

    Ok(client)
}

pub async fn get_client(discord_token: &str, gpt_token: &str) -> serenity::Client {
    let intents = GatewayIntents::GUILD_MESSAGES
        | GatewayIntents::DIRECT_MESSAGES
        | GatewayIntents::MESSAGE_CONTENT;

    serenity::Client::builder(discord_token, intents)
        .event_handler(Bot {
            gpt_token: gpt_token.to_owned(),
            client: reqwest::Client::new(),
        })
        .await
        .expect("Err creating client")
}

文脈に沿った会話をできるようにする

会話の文脈に沿った会話をできるようにするため、最新100件を取得したAPIへ送信することにしました。
https://github.com/yukyu30/chatgpt-bot-for-discord/blob/45be6e2ec5d5547ebbf7121ad16da393008f7899/src/lib.rs#L53-L61
取得した100件に対して、繰り返して処理を行い、messagesパラメーターの形式に変換しています。
https://github.com/yukyu30/chatgpt-bot-for-discord/blob/fd664c4a459beeb1f75d37a34d89a3376b323231/src/lib.rs#L32-L47

チャンネルで実行すると関係ないメッセージも含まれてしまうので、聞きたい話題ごとにスレッドを立てて、会話することをお勧めします。

botが動いているか分かりやすくする

https://github.com/yukyu30/chatgpt-bot-for-discord/blob/fd664c4a459beeb1f75d37a34d89a3376b323231/src/lib.rs#L63

start_typing〇〇が入力中...と表示することができます。Botが動いているのか、エラーで押しているのかわかりやすくなります。

shuttleでDiscord Botを常時稼働させる

現時点でリリースノートにしか書かれていないのですが、30分アクセスがないとアプリケーションはスリープするようになったため、project作成時に--idle-minutesオプションでスリープしないように設定します。

shuttle project new --idle-minutes 0

https://github.com/shuttle-hq/shuttle/releases/tag/v0.11.2

感想

コードを書くにあたり、ChatGPTにRustを教えてもらいながら実装を進めました。
Rustほぼ初心者でも開発できました。

参考

https://zenn.dev/satory074/articles/satory074_2023030201

Discussion