use log::{debug, warn, error}; use serenity::{prelude::*, async_trait}; use serenity::builder::CreateApplicationCommand; use serenity::model::application::interaction::application_command::ApplicationCommandInteraction; use songbird::EventHandler; use crate::bot::commands::audio::{join, leave, add_song, get_songbird, AudioConfigs}; use super::{create_response, edit_response}; pub async fn run(ctx: &Context, command: &ApplicationCommandInteraction) { // Get the track url let track_url = match command.data.options.get(0) { Some(t) => match &t.value { Some(v) => match v.as_str() { Some(s) => s.to_owned(), None => { warn!("Missing track option"); if let Err(why) = create_response(&ctx, &command, format!("Track option is missing")).await { error!("Failed to create response message: {}", why); } return; } } None => { warn!("Missing track option"); if let Err(why) = create_response(&ctx, &command, format!("Track option is missing")).await { error!("Failed to create response message: {}", why); } return; } } None => { warn!("Missing track option"); if let Err(why) = create_response(&ctx, &command, format!("Track option is missing")).await { error!("Failed to create response message: {}", why); } return; } }; // Create the initial response if let Err(why) = create_response(&ctx, &command, format!("Processing command...")).await { error!("Failed to create response message: {}", why); return; } match join(&ctx, &command.guild_id, &command.user).await { Ok(_) => { let guild_id = match command.guild_id { Some(g) => g, None => { if let Err(why) = edit_response(&ctx, &command, "Unable to join voice channel".to_string()).await { error!("Failed to edit response message: {}", why); } return; } }; debug!("Play command executed with track: {:?}", track_url); let manager = get_songbird(ctx).await; if let Some(handler_lock) = manager.get(guild_id) { let is_queue_empty = { let call_handler = handler_lock.lock().await; call_handler.queue().is_empty() }; let audio_config = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected AudioConfigs in TypeMap.").clone() }; let ac = audio_config.read().await; match add_song(handler_lock.clone(), &track_url, is_queue_empty, ac.get(&guild_id)).await { Ok(added_song) => { let track_title = added_song.title.unwrap(); debug!("Added track: {}", track_title); if let Err(why) = edit_response(&ctx, &command, format!("Added track to queue: {}", track_title)).await { error!("Failed to edit response message: {}", why); } let mut handler = handler_lock.lock().await; handler.remove_all_global_events(); handler.add_global_event(songbird::Event::Track(songbird::TrackEvent::End), TrackEndNotifier { guild_id, call: manager }) } Err(why) => { warn!("Failed to add song: {}", why); if let Err(why) = edit_response(&ctx, &command, format!("Failed to add song: {}", why)).await { error!("Failed to edit response message: {}", why); } if let Err(why) = leave(&ctx, &command.guild_id).await { error!("Failed to leave voice channel: {}", why); } return; } }; } }, Err(err) => { warn!("{}", err); if let Err(why) = edit_response(&ctx, &command, format!("{}", err)).await { error!("Failed to edit response message: {}", why); } } } } pub fn register(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand { command.name("play").description("Plays the given track").create_option(|option| { option .name("track") .description("The track to be played") .kind(serenity::model::prelude::command::CommandOptionType::String) .required(true) }) } struct TrackEndNotifier { pub call: std::sync::Arc, pub guild_id: serenity::model::id::GuildId } #[async_trait] impl EventHandler for TrackEndNotifier { async fn act(&self, ctx: &songbird::events::EventContext<'_>) -> Option { if let songbird::EventContext::Track(_track_list) = ctx { if let Some(call) = self.call.get(self.guild_id) { let mut handler = call.lock().await; if handler.queue().is_empty() { debug!("Queue is empty, leaving voice channel"); handler.leave().await.unwrap(); } } } None } }