diff --git a/src/client.rs b/src/client.rs index a81ad0a..51e3eb4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -40,8 +40,13 @@ pub async fn get_client() -> BotResult { let lava_client = LavalinkClient::builder(current_application.id.0) .set_host(env::var("LAVALINK_HOST").unwrap_or("172.0.0.1".to_string())) - .set_password(env::var("LAVALINK_PORT").expect("Missing lavalink port")) .set_password(env::var("LAVALINK_PASSWORD").expect("Missing lavalink password")) + .set_port( + env::var("LAVALINK_PORT") + .ok() + .and_then(|s| s.parse().ok()) + .expect("Missing lavalink port"), + ) .build(LavalinkHandler { data }) .await?; { diff --git a/src/commands/music/join.rs b/src/commands/music/join.rs index 717acac..c643460 100644 --- a/src/commands/music/join.rs +++ b/src/commands/music/join.rs @@ -4,7 +4,7 @@ use serenity::framework::standard::{Args, CommandResult}; use serenity::model::channel::Message; use crate::commands::common::handle_autodelete; -use crate::commands::music::{get_channel_for_author, is_dj}; +use crate::commands::music::{get_channel_for_author, get_music_player_for_guild, is_dj}; use crate::providers::music::player::MusicPlayer; use bot_serenityutils::core::SHORT_TIMEOUT; use bot_serenityutils::ephemeral_message::EphemeralMessage; @@ -34,6 +34,13 @@ async fn join(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { get_channel_for_author(&msg.author.id, &guild) ) }; + if get_music_player_for_guild(ctx, guild.id).await.is_some() { + EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| { + m.content("‼️ I'm already in a Voice Channel") + }) + .await?; + return Ok(()); + } log::debug!("Joining channel {} for guild {}", channel_id, guild.id); MusicPlayer::join(ctx, guild.id, channel_id).await?; EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| { diff --git a/src/commands/music/leave.rs b/src/commands/music/leave.rs index ba601c2..740e67c 100644 --- a/src/commands/music/leave.rs +++ b/src/commands/music/leave.rs @@ -20,8 +20,15 @@ async fn leave(ctx: &Context, msg: &Message) -> CommandResult { let guild = msg.guild(&ctx.cache).await.unwrap(); log::debug!("Leave request received for guild {}", guild.id); + let manager = songbird::get(ctx).await.unwrap(); + if let Some(handler) = manager.get(guild.id) { + let mut handler_lock = handler.lock().await; + let _ = handler_lock.leave().await; + } + let mut data = ctx.data.write().await; let players = data.get_mut::().unwrap(); + match players.remove(&guild.id.0) { None => { EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| { @@ -32,8 +39,10 @@ async fn leave(ctx: &Context, msg: &Message) -> CommandResult { Some(player) => { let mut player = player.lock().await; player.stop().await?; + player.delete_now_playing().await?; } } + manager.remove(guild.id).await?; handle_autodelete(ctx, msg).await?; diff --git a/src/messages/music/now_playing.rs b/src/messages/music/now_playing.rs index 546c895..01b2182 100644 --- a/src/messages/music/now_playing.rs +++ b/src/messages/music/now_playing.rs @@ -9,7 +9,7 @@ use crate::messages::add_ephemeral_handle_to_database; use crate::providers::music::add_youtube_song_to_database; use crate::providers::music::player::MusicPlayer; use crate::providers::music::queue::Song; -use crate::utils::context_data::{DatabaseContainer, Store}; +use crate::utils::context_data::{DatabaseContainer, MusicPlayers, Store}; use crate::utils::error::*; use bot_serenityutils::core::MessageHandle; use bot_serenityutils::error::SerenityUtilsResult; @@ -230,14 +230,19 @@ async fn stop_button_action( if let Some(handler) = handler { let mut handler_lock = handler.lock().await; - handler_lock.remove_all_global_events(); + let _ = handler_lock.leave().await; } if manager.get(guild_id).is_some() { manager.remove(guild_id).await.map_err(BotError::from)?; - let player = get_music_player_for_guild(ctx, guild_id).await.unwrap(); - let mut player = player.lock().await; - player.stop().await?; + let mut data = ctx.data.write().await; + let players = data.get_mut::().unwrap(); + + if let Some(player) = players.remove(&guild_id.0) { + let mut player = player.lock().await; + player.stop().await?; + } + log::debug!("Left the voice channel"); } else { log::debug!("Not in a voice channel"); diff --git a/src/providers/music/player.rs b/src/providers/music/player.rs index a68b5fc..c5474ed 100644 --- a/src/providers/music/player.rs +++ b/src/providers/music/player.rs @@ -6,11 +6,16 @@ use crate::utils::context_data::MusicPlayers; use crate::utils::error::BotResult; use bot_serenityutils::core::MessageHandle; use lavalink_rs::LavalinkClient; -use serenity::client::Context; -use serenity::http::Http; -use serenity::model::id::{ChannelId, GuildId}; +use serenity::prelude::TypeMap; +use serenity::{ + client::Context, + http::Http, + model::id::{ChannelId, GuildId}, +}; +use songbird::Songbird; use std::mem; use std::sync::Arc; +use std::time::Duration; use tokio::sync::{Mutex, RwLock}; pub struct MusicPlayer { @@ -58,6 +63,13 @@ impl MusicPlayer { player }; + wait_for_disconnect( + Arc::clone(&ctx.data), + Arc::clone(&player), + manager, + guild_id, + ); + Ok(player) } @@ -118,6 +130,9 @@ impl MusicPlayer { } }; + if query_information.tracks.len() == 0 { + return Ok(false); + } let track = query_information.tracks[0].clone(); self.client.play(self.guild_id.0, track).start().await?; self.queue.set_current(next); @@ -179,3 +194,48 @@ impl MusicPlayer { self.leave_flag = flag; } } + +/// Stats a tokio coroutine to check for player disconnect conditions +fn wait_for_disconnect( + data: Arc>, + player: Arc>, + manager: Arc, + guild_id: GuildId, +) { + let mut leave_in: i32 = 5; + tokio::spawn(async move { + loop { + tokio::time::sleep(Duration::from_secs(60)).await; + if manager.get(guild_id).is_none() { + return; // leave when there's no connection to handle + } + let mut player_lock = player.lock().await; + + if player_lock.leave_flag { + log::debug!("Waiting to leave"); + + if leave_in <= 0 { + log::debug!("Leaving voice channel"); + + if let Some(handler) = manager.get(guild_id) { + let mut handler_lock = handler.lock().await; + let _ = handler_lock.leave().await; + } + + let _ = manager.remove(guild_id).await; + let mut data = data.write().await; + let players = data.get_mut::().unwrap(); + players.remove(&guild_id.0); + let _ = player_lock.stop().await; + let _ = player_lock.delete_now_playing().await; + log::debug!("Left the voice channel"); + return; + } + leave_in -= 1; + } else { + log::debug!("Resetting leave value"); + leave_in = 5 + } + } + }); +}