use std::time::Duration; use serenity::all::{Context, CreateMessage, GuildChannel, VoiceState}; use tokio::time::sleep; use crate::utils::{debug::send_error, voice_state_to_guild_channel}; async fn get_channel_info(ctx: &Context, voice_state: &VoiceState) -> Option { let voice_channel = voice_state_to_guild_channel(&ctx, &voice_state).await?; let Ok(members) = voice_channel.members(&ctx.cache) else { return None }; let mut is_connected = false; let mut users_connected: usize = 0; for member in &members { if member.user.id == ctx.cache.current_user().id { is_connected = true } if ! member.user.bot { users_connected += 1; } } // Check if there is no real user in the voice channel if ! is_connected || users_connected > 0 { return None; } Some(voice_channel) } pub async fn handle_autodisconnect(ctx: Context, voice_state_old: VoiceState, voice_state: VoiceState) -> Option<()> { // Logic is as follows: // User connected -> voice_state_old is None (we handle this before this function) // Bot was moved to different vc -> we check if the new voice_state has the bot's user id // User moved to different VC or disconnected -> we check old channel instead (the bot could be still there) let mut voice_channel: GuildChannel; if voice_state.user_id == ctx.cache.current_user().id { voice_channel = get_channel_info(&ctx, &voice_state).await?; } // Someone disconnected or moved channels else { voice_channel = get_channel_info(&ctx, &voice_state_old).await?; } let manager = songbird::get(&ctx) .await .expect("Songbird Voice client placed in at initialisation.") .clone(); // bot is not connected to any channel via songbird, don't need to do anything if manager.get(voice_channel.guild_id).is_none() { return None; } // There is a problem with this implementation // if bot is left alone and users joins and disconnects while this counts down // this is not a big problem since we want to disconnect anyway sleep(Duration::from_secs(120)).await; if voice_state.user_id == ctx.cache.current_user().id { voice_channel = get_channel_info(&ctx, &voice_state).await?; } else { voice_channel = get_channel_info(&ctx, &voice_state_old).await?; } // bot is not connected to any channel via songbird, don't need to do anything // ? could have been removed while sleeping if the song stopped playing if manager.get(voice_channel.guild_id).is_none() { return None; } match manager.remove(voice_channel.guild_id).await { Ok(()) => { let _ = voice_channel.send_message(ctx.http, CreateMessage::new() .content("Disconnected to save bandwidth") ).await; } Err(e) => { let _ = send_error(ctx.http, e.to_string()).await; } } None } pub async fn handle_voice_update(ctx: Context, voice_state_old: VoiceState, voice_state: VoiceState) -> Option<()> { // if the user was disconnected remove Songbird manager so it won't get stuck // Actually this is not really a bug since we can handle disconnect and therefore saving the song queue for example if voice_state.channel_id.is_none() && voice_state.user_id == ctx.cache.current_user().id { let manager = songbird::get(&ctx) .await .expect("Songbird Voice client placed in at initialisation.") .clone(); let guild_id = voice_state_old.guild_id?; // bot is not connected to any channel via songbird, don't need to do anything if manager.get(guild_id).is_some() { let _ = manager.remove(guild_id).await; return None; } } handle_autodisconnect(ctx, voice_state_old, voice_state).await?; None }