Closed22

discordbotをPythonからRust🦀に置き換える

yukyuyukyu

そもそも動作してるBotのソースコード

import discord
from os import getenv

intents = discord.Intents.default()
intents.members = True
client = discord.Client(intents=intents)

@client.event
async def on_message(message):
    # 送信者がbotかの判断
    if message.author.bot:
        return

    if message.content == "/neko":
        await message.channel.send("path/to/cat/image")
    if message.content == "/blog":
        await message.channel.send("url/to/blog")

#VC開始時に通知
@client.event
async def on_voice_state_update(member, before, after):

    # チャンネルへの入室ステータスが変更を検出
    if before.channel != after.channel:
        # 通知メッセージを書き込むテキストチャンネルを指定
        botRoom = client.get_channel(XXXXXXXXX)

        # 最初にだれかが入室した時のみに限定
        if after.channel is not None and len(after.channel.members) == 1:
            bot_msg = await botRoom.send("**[VC開始]** " + member.name + " が話をはじめたよ!\n 参加できるよ:👍\n 無理そう:😭")
            await bot_msg.add_reaction('👍')
            await bot_msg.add_reaction('😭')
            # print(len(after.channel.members))

token = getenv('DISCORD_BOT_TOKEN')
client.run(token)

yukyuyukyu

RustのDiscordbot用ライブラリを使う

https://github.com/serenity-rs/serenity

exampleが動作するようにする

https://github.com/serenity-rs/serenity/tree/current/examples/e01_basic_ping_bot

Client error: Gateway(DisallowedGatewayIntents)

exampleを実行したら

Client error: Gateway(DisallowedGatewayIntents)

とエラーが出てきた

解決方法

同じ現象のissue
https://github.com/serenity-rs/serenity/discussions/1529

DiscordBotの設定を変える必要がある
https://scrapbox.io/discordjs-japan/Developer_PortalからPrivileged_Intentsを有効化する

yukyuyukyu

VCのステータスが変わった時に動作するメソッドをつくる

voice_state_updateでVCの状態が変わったら動作するやつっぽい
https://docs.rs/serenity/0.11.4/serenity/client/trait.EventHandler.html#method.voice_state_update
Available on crate feature cache only.とあるのでcacheを作らないといけないっぽい

今の現状

  • Available on crate feature cache only.とは
  • voice_state_updateの正しい使い方

解決策

main関数内でこうする

let intents = GatewayIntents::GUILD_VOICE_STATES;
yukyuyukyu

ここまでできてること

VCへの入手、退出時にメッセージを飛ばす。

理想的な動き

誰も参加していないVCに誰かが参加した時にメッセージを飛ばす。
(VC参加人数が0人から1人になった時)

理想的な動きの実現のために必要なこと

  • VCの状態を記録すること
  • 0人から1人になった時の判定
yukyuyukyu

discord.pyでは、イベント発火前と後の状態を参照できたが、serenityでは無理っぽい

yukyuyukyu

上記の内容から、serenityの関数や機能でVCの状態は記録できないっぽい。
voice_state_updateのイベントが発火するたびにVCの状態を記録する必要がある。
そして、voice_state_updateは入室、退室どちらでも発火するため、以下のような実装が必要そう

  • voice_state_update発火時、イベント発火させたメンバーの名前がVC状況を記録する配列になかったら、入室とする。そして、配列にユーザー名を保存。
  • voice_state_update発火時、イベントを発火させたメンバーの名前が配列にあったら、退室として、配列から一致するユーザー名を削除
yukyuyukyu

配列だと永続化が難しい。だからと言ってDBを使うのはやりすぎな気がするのjsonファイルとかに書き込むとかで永続化をしたい。

yukyuyukyu

VC: hogeからVC: fugaに移動した場合、この想定だと、退室したことになってしまう。
だから、どのVCを識別する必要がある。
VCのチャンネルIDが変わったのであれば、部屋移動と判定できるそう。

yukyuyukyu

幸い、channel_idが取れるので、これが遷移先のchannel_idを示すのであれば、いけそう。

yukyuyukyu

channel_idの仕様は

  • VC: Aに参加→AのチャンネルID
  • VC AからBに移動→BのチャンネルID
  • VC Bから退室→チャンネルIDはNone
yukyuyukyu

つまり、退室時の判定をわざわざ配列から探すとかやらなくて良さそう。Noneだったら、VCに参加してる人数から1を引く。とかできるね

yukyuyukyu

でも、部屋移動を考えるとそれだと、人数が会わなくなる。
Aへ参加、Bへ移動、退室とすると、+1,+1,-1= +1なので移動では加算してはいけない

yukyuyukyu

本日のまとめ

voice_state_update
チャンネルID、ユーザーIDを見ることで、入室か退室か、移動かなのかわかる。

入室

チャンネルIDは入室先のVC
ユーザーIDは入室したユーザーのID
member_count ++する

移動

チャンネルIDは移動先のチャンネルID
ユーザーは移動したユーザーのID
member_countはそのまま

退出

チャンネルIDはNone
ユーザーIDは退出した人のユーザーID
member_count--

VCの状況を記録する

チャンネルIDが変わった(Noneではない)場合は、ユーザーが別の部屋に移動したことになる。移動と判定するために、前のチャンネルIDとユーザーセットで保持する必要がある。

yukyuyukyu

2022/08/06

連想配列にユーザー,VCのIDを格納して、入室状況を管理する

yukyuyukyu

グローバル変数には厳しい制約があって扱いにくそう。最終的にデータは永続化がしたい。という二点からjsonファイルを読み書きすることにします

yukyuyukyu

voice_state_updateでわかったこと
ミュートの切り替えでもイベントが発火する。音声まわりの状態のアップデートだからそれはそう。

このスクラップは2022/12/29にクローズされました