3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rust製Discord botフレームワークの紹介と開発時つまずきポイント

Posted at

はじめに

過去にserenityとsongbirdを使って作ったDiscord botをpoiseを使うようにアップデートした際に、いくつかつまずいた点があったので共有します。

アップデート後のサンプルbotのコードはこちらです。

現時点(2024/9/23)では公式でserenity、songbird、poiseを使用したExampleコードがないので、こちらのコードが参考になれば幸いです。

サンプルbotは主に、ボイスチャンネルに接続している状態でテキストチャンネルにおいて /play {youtube_url}と送信すると、youtube_urlの動画の音声をボイスチャンネルで再生する機能などを実装しています。

image.png

image.png

注意点

紹介しといてアレなんですが、新しく大規模なDiscord botを作ろうとしている方にはpoise、serenity、songbirdを使用するのは個人的にお勧めしません。理由は以下です。

  • poise、serenity、songbirdはまだ開発途上のため破壊的変更が多発する
  • Changelogやドキュメントがあっさりしており、破壊的変更に関する情報が少ない
  • 公式examplesも非推奨機能をガンガン使っており、良い例を見つけるのが難しい

ある程度安定するまで待つほうが得策かもしれません。

serenity、songbird、poiseをざっくり紹介

serenity

Rust製のDiscord bot フレームワークです。
比較的簡単にDiscord botを作ることができます。

songbird

serenityでボイスチャンネルを扱うためのライブラリです。
Youtubeなどにある動画の音声を流したり、音声トラックをキューに追加して操作することができます。

poise

serenityでスラッシュコマンドを扱うためのライブラリです。

スラッシュコマンドをチャンネルに登録することで、コマンドの入力補完、引数のバリデーション、よく使うコマンドの表示などを自動で行ってくれるようになります。

image.png

serenityでは通常のテキストコマンドを扱う際もpoiseを使用することが推奨されています。

poise、serenity、songbird開発時につまずきやすいポイント

Discordのスラッシュコマンドは仕様として3秒間応答がないとコマンドが失敗したとみなされる

Discordのスラッシュコマンドは3秒間応答がないとコマンドが失敗したとみなされます。

Interaction tokens are valid for 15 minutes and can be used to send followup messages but you must send an initial response within 3 seconds of receiving the event. If the 3 second deadline is exceeded, the token will be invalidated.

コマンド失敗とみなされた場合は以下のように「アプリケーションが応答しませんでした」と表示されます。

image.png

今回の実装ではplayコマンドのYoutubeの音声ダウンロードの処理が重く、3秒以上かかる可能性があったため、defer()を使用しました。

play.rs
#[poise::command(slash_command, prefix_command, guild_only)]
pub(crate) async fn play(
    ctx: Context<'_>,
    #[description = "youtube url"] url: String,
) -> Result<(), Error> {
    // ~~~中略~~~

    // スラッシュコマンドは3秒間応答がないとコマンドが失敗したと判断される
    // そのため、時間のかかる動画ダウンロード処理の前に応答し、コマンド失敗を防ぐ
    // https://discord.com/developers/docs/interactions/receiving-and-responding
    ctx.defer().await?;

    if let Some(handler_lock) = manager.get(guild_id) {
        let mut handler = handler_lock.lock().await;

        let http_client = ctx.data().http.clone();
        let src = YoutubeDl::new(http_client, url);

        handler.enqueue_input(src.into()).await;

        ctx.say(format!("キューに追加: position {}", handler.queue().len()))
            .await?;
    } else {
        ctx.say("ボイスチャンネルに入ってないよ").await?;
    }

    Ok(())
}

defer()中は以下のように表示され、コマンドの実行が続行されます。

image.png

Cargo.tomlでSymphoniaで対応する形式を明示する必要がある

songbirdはSymphonia(Rust製マルチメディアライブラリ)を使用しています。

以前はffmpegを使用していましたが、v0.4.0からSymphoniaを使用するようになったみたいです。

デフォルトではsongbirdはSymphoniaに対していかなる形式のコーデックも要求していないため、songbirdを利用するプロジェクトで対応する形式を指定する必要があります。

今回は以下のように基本的な音声形式を指定しました。

Cargo.toml
symphonia = { features = ["aac", "mp3", "isomp4", "alac"], version = "0.5.2" }

依存するreqwestのバージョンをsongbirdと同じバージョンにする必要がある

reqwestを現時点で最新版の0.12.7に指定したところ、以下のようなエラーが発生しました。

error[E0308]: mismatched types
  --> src\commands\play.rs:43:34
   |
43 |         let src = YoutubeDl::new(http_client, url);
   |                   -------------- ^^^^^^^^^^^ expected `Client`, found `reqwest::Client` 
   |                   |
   |                   arguments to this function are incorrect
   |
   = note: `reqwest::Client` and `Client` have similar names, but are actually distinct types

songbirdが依存しているreqwestとバージョンを合わせた(0.11)ところ、エラーが解消します。

Cargo.toml
reqwest = "0.11"

おわりに

serenity、songbird、poiseを使用したDiscord botに関する記事が非常に少なかったので、少しでも開発の助力になれば幸いです。
また、私自身serenity等のフレームワークを使いこなせている自信がないので、サンプルBotのより良い実装等あればコメントかPR/issueで教えていただけると非常に助かります。

参考文献

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?