use std::sync::Arc; use log::{debug, warn, error}; use serenity::model::prelude::GuildId; use serenity::{prelude::*, async_trait}; use serenity::builder::CreateApplicationCommand; use serenity::model::application::interaction::application_command::ApplicationCommandInteraction; use siren::ServiceError; use songbird::{EventHandler, Songbird}; use crate::bot::{guilds::QueryGuild, commands::audio::{leave, add_song, get_songbird}}; use super::{create_response, edit_response, join_by_user}; 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; } let manager = get_songbird(ctx).await; match join_by_user(&ctx.cache, manager,&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; match play_track(manager, guild_id, track_url).await { Ok(_) => { if let Err(why) = edit_response(&ctx, &command, "Playing track".to_string()).await { error!("Failed to edit response message: {}", why); } }, Err(err) => { warn!("Failed to play track: {}", err); if let Err(why) = edit_response(&ctx, &command, format!("Failed to play track: {}", err)).await { error!("Failed to edit response message: {}", why); } } }; }, Err(err) => { warn!("{}", err); if let Err(why) = edit_response(&ctx, &command, format!("{}", err)).await { error!("Failed to edit response message: {}", why); } } } } pub async fn play_track(manager: Arc, guild_id: GuildId, track_url: String) -> Result<(), ServiceError> { 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 guild = QueryGuild::get(guild_id.0 as i64)?; match add_song(handler_lock.clone(), &track_url, is_queue_empty, Some(guild.volume as f32)).await { Ok(added_song) => { let track_title = added_song.title.unwrap(); debug!("Added track: {}", track_title); 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(err) => { warn!("Failed to add song: {}", err); if let Err(why) = leave(manager, &Some(guild_id)).await { error!("Failed to leave voice channel: {}", why); } return Err(ServiceError { status: 422, message: err.to_string() }) } } } Ok(()) } 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 } }