読みそというDiscord読み上げBotの仕組みを解説してみる

こんにちは!読みそという読み上げBotを作っているめぐみそです!

今回は、私が制作している読みそがどういう仕組みであるかを解説していきます。
参考にできるとこはどんどんパクっていきましょう!(炎上しそうな発言)

サービス一覧

読みそは複数のサービスが合わさってできています。
各サービスはDockerコンテナで動いています。

現在は、読みそのBotコンテナ、男声女声の声を生成するコンテナ、ずんだもんの声を生成するコンテナ、データベースのコンテナで動いています。

読みそコンテナ

読みそのBot本体はこのコンテナから動いています。

このコンテナで、読みそのコマンド受付、読みそが読み上げるチャットの判定、読み上げ等を行っています。

具体的な動きは後ほど解説します。

OpenJTalkコンテナ

読みその男声、女声の声を担当しているコンテナです。

コンテナイメージは、以下のものを使用させていただいております。

https://hub.docker.com/r/u6kapps/open-jtalk-api/

OpenJTalk環境が整ったコンテナに、httpサーバーとしての機能を持たせ、飛んできたリクエストに応じて声を変更し、wavファイルを生成し、返しています。

パブリックIPに公開すると誰でもリクエストできてしまい危険なので、パスワードをつけるかローカルネットワークにだけ公開する必要があります。
読みその場合はDockerでローカルネットワークを構築して、その範囲内のみでアクセスできるようにしています。

VOICEVOXコンテナ

読みそのずんだもんの音声を担当しているコンテナです。

コンテナイメージは、以下のものを使用させていただいております。

https://hub.docker.com/r/voicevox/voicevox_engine

OpenJTalkと同じように、httpサーバーが存在し、パラメータに応じてwavファイルが返されます。

データベースコンテナ

読みそのサーバー設定やユーザー情報を保存しているデータベースです。

特にこだわりはないので、MySQLを使用しています。(データベースごとの癖をあまりよく理解できていない)

なぜDockerなのか

コンテナ化しておくことで、今後ユーザーが増加した後でも、コンテナを増やすだけで、負荷を分散できるからです。(正確にはnginxとかhaproxy等のロードバランサが必要ですが)

この辺りはこれが正解とかはないと思うので、あくまでめぐみそはこうやってますという感じで。

他にはVMを複数用意する方法もあったり、kubernetesを使用する方法もあります。その場合はクラウドを利用することになりそうです。

読みその細かい仕組み

読みそは、Rustという言語で作られています。

仕組みとしては、様々なコマンドに応じて処理が決まっています。

新規ユーザーの追加

読みそがVCにいる時、読み上げ対象のチャンネルに新しいユーザー(今まで読みそがいるサーバーで会話したことがない人)がチャットすると、データベースに新しいユーザーとして登録します。

データベースに登録されていないと、ボイスの設定変更ができないので、未然に防いでいます。

ボイス変更

サーバー上で読み上げられる声を変更することができます。

ユーザーから入力されるパラメータに応じて、データベースの中身を書き換えます。
具体的には、データベースに存在するユーザー情報のテーブルで、Discord固有のユーザーIDを元にユーザーを特定し、そのユーザーに紐付けされたボイス情報を変更します。

読み上げる時は、この情報を参考にしながら読み上げの処理を行います。

サーバー情報の変更

管理者が読みそのサーバーに関する設定を変える時の処理を解説します。

データベースには、サーバー固有の読みそ設定が格納されるテーブルがあるので、管理者からのコマンドに応じて、データベースの中身を書き換えます。

ただし、管理者専用コマンドなので、全員が使えないような仕組みにする必要があります。

読みそでは、コマンドが実行された時に、実行した人個人、または割り当てられているロールに管理者権限がつけられているか確認しています。

読みそ参加&読み上げ処理

読みそがどこのチャットをどのVCに読み上げるかを判定します。

まず、読みそが参加するためのコマンドが実行された時、実行したユーザーがどのVCにいるかを確認します。
この時、ユーザーがVCにいないか、読みそから見えないVCにいた場合は、VCに参加してもらう旨のメッセージを送って処理を中断します。

ユーザーが見つかったら、ユーザーがいるVCに参加して、読み上げするチャンネルを管理する変数にVCチャンネルとテキストチャンネルのIDを格納します。
読み上げる時は、チャットが送られたチャンネルのIDを確認し、管理用の変数に入っているかどうかを確認します。

読み上げるよう判定されたら、チャットを送ったユーザーのユーザー情報を、データベースから取得します。
取得されたデータに応じて、どこのサーバーのボイスにアクセスするか、どのようなパラメータでアクセスするかを判断します。

レスポンスが来たら、中身のwavファイルをそのままVCに再生します。この時の再生には、ffmpegを使用しています。何故かは分からないですが、ドキュメントにそう書いてありました。

これで、読みその一連の流れは終わりです。これを永遠に続けてくれます。

まとめ

今回は、私が制作、運営している読みその仕組みを解説してみました。

あくまで読みその場合のパターンなので、他の読み上げBot制作者の方は、別の方法を取っていたりしますが、好みや慣れの部分もあったり、みんな同じ方法というのはないと思います。

なので、読みそはこうなんだなーみたいに、1つの参考例のように扱ってもらえれば幸いです。

参考

読みそ:【bot招待可能】Discordチャット読み上げBot、読みそを作ってみた