はじめに
過去にserenityとsongbirdを使って作ったDiscord botをpoiseを使うようにアップデートした際に、いくつかつまずいた点があったので共有します。
アップデート後のサンプルbotのコードはこちらです。
現時点(2024/9/23)では公式でserenity、songbird、poiseを使用したExampleコードがないので、こちらのコードが参考になれば幸いです。
サンプルbotは主に、ボイスチャンネルに接続している状態でテキストチャンネルにおいて /play {youtube_url}
と送信すると、youtube_url
の動画の音声をボイスチャンネルで再生する機能などを実装しています。
注意点
紹介しといてアレなんですが、新しく大規模なDiscord botを作ろうとしている方にはpoise、serenity、songbirdを使用するのは個人的にお勧めしません。理由は以下です。
- poise、serenity、songbirdはまだ開発途上のため破壊的変更が多発する
- Changelogやドキュメントがあっさりしており、破壊的変更に関する情報が少ない
- 公式examplesも非推奨機能をガンガン使っており、良い例を見つけるのが難しい
- 例: serenityの非推奨の構造体StandardFrameworkをsongbirdの公式exampleが使用している
ある程度安定するまで待つほうが得策かもしれません。
serenity、songbird、poiseをざっくり紹介
serenity
Rust製のDiscord bot フレームワークです。
比較的簡単にDiscord botを作ることができます。
songbird
serenityでボイスチャンネルを扱うためのライブラリです。
Youtubeなどにある動画の音声を流したり、音声トラックをキューに追加して操作することができます。
poise
serenityでスラッシュコマンドを扱うためのライブラリです。
スラッシュコマンドをチャンネルに登録することで、コマンドの入力補完、引数のバリデーション、よく使うコマンドの表示などを自動で行ってくれるようになります。
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.
コマンド失敗とみなされた場合は以下のように「アプリケーションが応答しませんでした」と表示されます。
今回の実装ではplay
コマンドのYoutubeの音声ダウンロードの処理が重く、3秒以上かかる可能性があったため、defer()
を使用しました。
#[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()
中は以下のように表示され、コマンドの実行が続行されます。
Cargo.toml
でSymphoniaで対応する形式を明示する必要がある
songbirdはSymphonia(Rust製マルチメディアライブラリ)を使用しています。
以前はffmpegを使用していましたが、v0.4.0
からSymphoniaを使用するようになったみたいです。
デフォルトではsongbirdはSymphoniaに対していかなる形式のコーデックも要求していないため、songbirdを利用するプロジェクトで対応する形式を指定する必要があります。
今回は以下のように基本的な音声形式を指定しました。
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
)ところ、エラーが解消します。
reqwest = "0.11"
おわりに
serenity、songbird、poiseを使用したDiscord botに関する記事が非常に少なかったので、少しでも開発の助力になれば幸いです。
また、私自身serenity等のフレームワークを使いこなせている自信がないので、サンプルBotのより良い実装等あればコメントかPR/issueで教えていただけると非常に助かります。