diff --git a/src/commands/delete.rs b/src/commands/delete.rs index ad119c1..9d79974 100644 --- a/src/commands/delete.rs +++ b/src/commands/delete.rs @@ -1,10 +1,9 @@ +use crate::state::State; use std::{env, error::Error, num::NonZeroU64, time::Duration}; use tokio::time::sleep; -use tracing::info; +use tracing::debug; use twilight_model::{channel::Message, id::Id}; -use crate::state::State; - pub(crate) async fn delete( msg: Message, state: State, @@ -33,7 +32,7 @@ pub(crate) async fn delete( .await?; state.http.delete_message(msg.channel_id, msg.id).await?; for message in messages { - info!("Delete message: {:?}: {:?}", message.author.name, message); + debug!("Delete message: {:?}: {:?}", message.author.name, message); state .http .delete_message(msg.channel_id, message.id) diff --git a/src/handler.rs b/src/handler.rs index b7cfee3..99eb683 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,14 +1,13 @@ use crate::commands::{delete, join, leave, pause, play, queue, resume, stop}; use crate::state::State; - use futures::Future; use std::error::Error; use std::sync::Arc; use tracing::debug; +use twilight_gateway::Event; use twilight_model::application::interaction::application_command::CommandOptionValue; use twilight_model::application::interaction::{Interaction, InteractionData}; - -use twilight_gateway::Event; +use twilight_model::gateway::payload::incoming::VoiceStateUpdate; #[derive(Debug)] enum InteractionCommand { @@ -32,6 +31,42 @@ fn spawn( }); } +pub(crate) async fn leave_if_alone( + update: VoiceStateUpdate, + state: State, +) -> Result<(), Box> { + let guild_id = update.guild_id.ok_or("Guild ID not found")?; + let user = state + .cache + .current_user() + .ok_or("Cannot get current user")?; + let user_voice_state = state + .cache + .voice_state(user.id, guild_id) + .ok_or("Cannot get voice state")?; + let channel = state + .cache + .channel(user_voice_state.channel_id()) + .ok_or("Cannot get channel")?; + let channel_voice_states = state + .cache + .voice_channel_states(channel.id) + .ok_or("Cannot get voice channel")?; + let count = channel_voice_states.count(); + + // count is 1 if the bot is the only one in the channel + if count == 1 { + // stop playing + if let Some(call_lock) = state.songbird.get(guild_id) { + let call = call_lock.lock().await; + call.queue().stop(); + } + // leave the voice channel + state.songbird.leave(guild_id).await?; + } + Ok(()) +} + pub(crate) struct Handler { state: State, } @@ -47,6 +82,9 @@ impl Handler { spawn(delete(message.0.clone(), Arc::clone(&self.state))); } } + Event::VoiceStateUpdate(update) => { + spawn(leave_if_alone(*update.clone(), Arc::clone(&self.state))) + } _ => {} } diff --git a/src/main.rs b/src/main.rs index 92e9fde..eecbc06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod commands; mod metadata; mod signal; mod state; +use crate::commands::get_chat_commands; use dotenv::dotenv; use futures::StreamExt; use signal::signal_handler; @@ -21,8 +22,6 @@ use twilight_http::Client as HttpClient; use twilight_model::id::Id; use twilight_standby::Standby; -use crate::commands::get_chat_commands; - #[tokio::main] async fn main() -> Result<(), Box> { dotenv().ok(); @@ -89,9 +88,13 @@ async fn main() -> Result<(), Box> { select! { biased; _ = stop_rx.changed() => { - for guild in state.cache.iter().guilds(){ - info!("Leaving guild {:?}", guild.id()); - state.songbird.leave(guild.id()).await?; + for guild in state.cache.iter().guilds() { + if let Some(user) = state.cache.current_user() { + if state.cache.voice_state(user.id, guild.id()).is_some() { + debug!("Leaving guild {:?}", guild.id()); + state.songbird.leave(guild.id()).await?; + } + } } // need to grab next event to properly leave voice channels stream.next().await;