diff --git a/src/commands/voice/general_player.rs b/src/commands/voice/general_player.rs index a6c42a0..447e95a 100644 --- a/src/commands/voice/general_player.rs +++ b/src/commands/voice/general_player.rs @@ -22,7 +22,7 @@ pub async fn play(ctx: Context<'_>, #[description = "Source URL: "] url: String, ) -> Result<(), Error> { - let events = vec![TrackEvent::End, TrackEvent::Error]; + let events = vec![TrackEvent::End]; let (manager, guild_id) = match connect(&ctx, channel, events).await { Ok(result) => result, Err(e) => { diff --git a/src/commands/voice/player_common.rs b/src/commands/voice/player_common.rs index fe0cfa8..3911d9d 100644 --- a/src/commands/voice/player_common.rs +++ b/src/commands/voice/player_common.rs @@ -81,7 +81,7 @@ pub async fn play_local(ctx: Context<'_>, #[description = "Filename of local song: "] file_name: String ) -> Result<(), Error> { - let events = vec![TrackEvent::End, TrackEvent::Error]; + let events = vec![TrackEvent::End]; let (manager, guild_id) = match connect(&ctx, channel, events).await { Ok(result) => result, Err(e) => { diff --git a/src/commands/voice/radio/radio_player.rs b/src/commands/voice/radio/radio_player.rs index a48f4db..6c4f36a 100644 --- a/src/commands/voice/radio/radio_player.rs +++ b/src/commands/voice/radio/radio_player.rs @@ -84,7 +84,7 @@ pub async fn play(ctx: Context<'_>, return Ok(()) }; - let events = vec![TrackEvent::End, TrackEvent::Error]; + let events = vec![TrackEvent::End]; let (manager, guild_id) = match connect(&ctx, channel, events).await { Ok(result) => result, Err(e) => { diff --git a/src/commands/voice/voice_utils.rs b/src/commands/voice/voice_utils.rs index 2d70342..31f62b8 100644 --- a/src/commands/voice/voice_utils.rs +++ b/src/commands/voice/voice_utils.rs @@ -1,10 +1,12 @@ use std::sync::Arc; +use std::time::Duration; -use serenity::all::{ChannelId, GuildId}; +use serenity::all::{CacheHttp, ChannelId, CreateMessage, GuildId}; use serenity::async_trait; use songbird::events::{Event, EventContext, EventHandler as VoiceEventHandler}; use songbird::{Songbird, TrackEvent}; +use tokio::time::sleep; use crate::{types::Context, utils::utilities::get_channel_by_name}; @@ -32,18 +34,53 @@ async fn get_voice_channel(ctx: &Context<'_>, name: Option) -> Result TrackEventNotifier { + TrackEventNotifier { + ctx, + guild_id, + channel_id + } + } +} #[async_trait] -impl VoiceEventHandler for TrackErrorNotifier { +impl VoiceEventHandler for TrackEventNotifier { async fn act(&self, ctx: &EventContext<'_>) -> Option { if let EventContext::Track(track_list) = ctx { - for (state, handle) in *track_list { - println!( - "Track {:?} encountered an error: {:?}", - handle.uuid(), - state.playing - ); + for (_, _) in *track_list { + sleep(Duration::from_secs(60)).await; + let manager = songbird::get(&self.ctx) + .await + .expect("Songbird Voice client placed in at initialisation.") + .clone(); + + if let Some(handler_lock) = manager.get(self.guild_id) { + let handler = handler_lock.lock().await; + if !handler.queue().is_empty() { + return None + } + + drop(handler); + drop(handler_lock); + match manager.remove(self.guild_id).await { + Ok(_) => (), + Err(e) => { + dbg!(e); + () + } + } + + let _ = self.channel_id.say(self.ctx.http(), "Disconnected to save bandwidth!").await; + }; } } @@ -51,6 +88,41 @@ impl VoiceEventHandler for TrackErrorNotifier { } } + +struct TrackErrorNotifier { + ctx: serenity::all::Context, + channel_id: ChannelId +} + +impl TrackErrorNotifier { + fn new(ctx: serenity::all::Context, channel_id: ChannelId) -> TrackErrorNotifier { + TrackErrorNotifier { + ctx, + channel_id + } + } +} + +#[async_trait] +impl VoiceEventHandler for TrackErrorNotifier { + async fn act(&self, ctx: &EventContext<'_>) -> Option { + if let EventContext::Track(track_list) = ctx { + for (_, _) in *track_list { + match self.channel_id.send_message( + self.ctx.http(), + CreateMessage::new().content("There was an error when playing a track!") + ).await { + Ok(_) => (), + Err(e) => println!("{}", e) + }; + } + } + + None + } +} + + pub async fn connect(ctx: &Context<'_>, channel: Option, events: Vec) -> Result<(Arc, GuildId), String> { if ctx.guild().is_none() { return Err("This command can be used only in guild".to_string()) @@ -69,8 +141,16 @@ pub async fn connect(ctx: &Context<'_>, channel: Option, events: Vec Option Option<()> { - // The user did not disconnect so we don't need to handle if the bot was left alone // Logic is as follows: // User connected -> voice_state_old is None (we handle this before this function) - // User moved to different VC or disconnected -> we only check old channel for number of users anyway - if voice_state.channel_id.is_some() { - return None + // 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 mut 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 playing any music so we don't need to handle this + // 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; } @@ -55,9 +58,20 @@ pub async fn handle_voice_update(ctx: Context, voice_state_old: VoiceState, voic // 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(5)).await; + sleep(Duration::from_secs(120)).await; - voice_channel = get_channel_info(&ctx, &voice_state_old).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(()) => {