ekuinox logs

DiscordのVCにエロ音声流すerobeamとかいうbotを書いた


パソコン日曜大工です。

エロ音声をDiscordのボイチャに流したくってserenityを使ってbotを書きました。

chobitにある音声作品を再生することができます。

実際に使ってみるとこんな感じです。

再生してみているところ

>>をプレフィックスとして、join, leave, play, np, pauseとか適当によくあるのが一通りがあります。

リンクから追加できます。

もうちょっと細かい使い方はREADME.mdを読んだ方が多少マシかもしれません。 多分使えるはずですが、私がまだまだ弄るので落ちてたりしてるかも知れません。@remokusuに何かあったら言ってください。

実装の話

できたものはこちらです。

ekuinox/erobeam - GitHub

Discord側の話

serenityなので、Rust実装です。僕が知らないだけかも知れないですが、serenity自体にサクっと音声を流す方法はないっぽいです。

今回はsongbirdという別のクレートをserenityに連携させて音声を流しています。

songbirdのリポジトリにserenity向けのexampleがいくつかあるので、今回はそれを参考に実装しました。

songbirdがffmpegの外部コマンド呼び出しをラップして、そこから音声を流してくれているようです。

let source = songbird::ffmpeg(path).await?;
let mut handler = handler.lock().await;
handler.play_source(source);

くらいのコードで音声を再生することができます。

builtin-queueフィーチャーを有効にしておけばキューも利用できます。

handler.enqueue_source(source);

songbirdはaudiopusというクレートに依存しているようで、これはOpus Codecというライブラリがないとビルドできません。

私の開発環境はWindowsだったのですが、Windows向けの64bitのライブラリが配布されているわけではなさそうだったので諦めてdevcontainerを使って環境を作りました。

chobit側の話

本当はDLsiteをメインにしたかったのですが、DLsiteのスクレイピングに苦戦したので諦めてchobitにとりあえず対応することにしました。

scraperというライブラリをDOMの解析に使用しています。

let selector = Selector::parse("div.player-box").unwrap();
let iter = doc.select(&selector);
let div = iter.next().unwrap();

こんな具合でセレクターを使って要素を引っ張ってこれます。

let title = div.value().attr("data-album-title").unwrap();

属性はこんな具合に拾うことができます。

#[tokio::test]
async fn test_from_document() {
    let product = VoiceProduct::from_url("https://chobit.cc/g1y57")
        .await
        .unwrap();
    assert_eq!(
        product.title,
        "【ブルーアーカイブ】ノノミASMR~ほのかな体温を感じる距離~\u{3000}体験版".to_string()
    );
    assert_eq!(product.voice_sample_tracks, vec![
        VoiceSampleTrack { title: "01.『タイトルコール』".into(), media_url: "https://file.chobit.cc/contents/2201/9msjbsuadj408040g80k40wsg/9msjbsuadj408040g80k40wsg_001.m4a".parse().unwrap(), playtime: "00:17".into() },
        VoiceSampleTrack { title: "02.『ちょっとした悪戯です』".into(), media_url: "https://file.chobit.cc/contents/2201/9msjbsuadj408040g80k40wsg/9msjbsuadj408040g80k40wsg_002.m4a".parse().unwrap(), playtime: "09:27".into() },
    ]);
}

あんまり関係ないけど、tokioのtestを初めて使いました。

URLから拾ってきたものは後で通らなくなる気しかしませんが、HTMLを置くのもなんとなく嫌だったので今はこれでいいです。

音声のURLが拾えるので、あとはreqwest::getなり使ってダウンロードしたものを適当なファイルで保存しとけば良いです。

ほか