use super::audio_state::AudioState; use super::song::Song; use lazy_static::lazy_static; use serenity::{ client::Context, framework::standard::{ macros::{command, group}, Args, CommandResult, }, model::{channel::Message, id::GuildId}, }; use songbird::tracks::TrackCommand; use std::{collections::HashMap, sync::Arc}; use tokio::sync::Mutex; use crate::util::{message_react, send_embed}; #[group] #[commands( join, disconnect, play, skip, pause, resume, change_loop, volume, shuffle, clear, queue )] struct Audio; lazy_static! { static ref AUDIO_STATES: Mutex>>> = Mutex::new(HashMap::new()); } async fn get_audio_state(ctx: &Context, msg: &Message) -> Option>> { let guild = msg.guild(&ctx.cache).await.unwrap(); let guild_id = guild.id; let mut audio_states = AUDIO_STATES.lock().await; let manager = songbird::get(ctx).await.unwrap(); let channel_id = guild .voice_states .get(&msg.author.id) .and_then(|voice_state| voice_state.channel_id); let channel_id = match channel_id { Some(channel_id) => channel_id, None => { send_embed(ctx, msg, "Error: please be in a voice channel").await; return None; } }; if let Some(call_locked) = manager.get(guild_id) { let call = call_locked.lock().await; if call.current_channel().is_none() { drop(call); match manager.remove(guild_id).await { Ok(_) => {} Err(err) => { println!("Error leaving call: {:?}", err); return None; } } audio_states.remove(&guild_id); } } let call_lock = match manager.get(guild_id) { Some(call) => call, None => { audio_states.remove(&guild_id); match manager.join(guild_id, channel_id).await { (call, Ok(_)) => call, (_, Err(err)) => { println!("Error joining call: {:?}", err); return None; } } } }; let mut call = call_lock.lock().await; if call.current_channel() != Some(channel_id.into()) { if let Err(err) = call.join(channel_id.into()).await { println!("Error joining call: {:?}", err); } } match audio_states.get(&guild_id) { Some(state) => { let state = state.clone(); AudioState::set_context(state.clone(), ctx, msg).await; Some(state) } None => { let audio_state = AudioState::new(manager.get(guild_id).unwrap(), ctx, msg); audio_states.insert(guild_id, audio_state.clone()); Some(audio_state) } } } async fn remove_audio_state(ctx: &Context, msg: &Message) -> Result<(), String> { let guild = msg.guild(&ctx.cache).await.unwrap(); let guild_id = guild.id; let mut audio_states = AUDIO_STATES.lock().await; let manager = songbird::get(ctx).await.unwrap(); if let Err(_) = manager.remove(guild_id).await { return Err("Could not leave channel".to_string()); } if audio_states.remove(&guild_id).is_some() { Ok(()) } else { Err("bot is not currently active".to_string()) } } #[command] async fn join(ctx: &Context, msg: &Message) -> CommandResult { let audio_state = get_audio_state(ctx, msg).await; if audio_state.is_some() { message_react(ctx, msg, "🥳").await; } Ok(()) } #[command] #[aliases("leave")] async fn disconnect(ctx: &Context, msg: &Message) -> CommandResult { match remove_audio_state(ctx, msg).await { Ok(()) => message_react(ctx, msg, "👋").await, Err(why) => send_embed(ctx, msg, &format!("Error: {}", why)).await, }; Ok(()) } #[command] async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let query = args.rest().to_string(); message_react(ctx, msg, "🎶").await; let audio_state = get_audio_state(ctx, msg).await; let audio_state = match audio_state { Some(audio_state) => audio_state, None => return Ok(()), }; let song = { let ctx = ctx.clone(); let msg = msg.clone(); tokio::spawn(async move { let song = Song::from_query(msg.author.clone(), query).await; match song { Ok(song) => { message_react(&ctx, &msg, "✅").await; Some(song) } Err(why) => { message_react(&ctx, &msg, "❎").await; send_embed(&ctx, &msg, &format!("Error: {}", why)).await; None } } }) }; AudioState::add_audio(audio_state, song).await; Ok(()) } #[command] async fn skip(ctx: &Context, msg: &Message) -> CommandResult { let audio_state = get_audio_state(ctx, msg).await; let audio_state = match audio_state { Some(audio_state) => audio_state, None => return Ok(()), }; if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Stop).await { send_embed(ctx, msg, &format!("Error: {}", why)).await; } else { message_react(ctx, msg, "↪").await; }; Ok(()) } #[command] async fn pause(ctx: &Context, msg: &Message) -> CommandResult { let audio_state = get_audio_state(ctx, msg).await; let audio_state = match audio_state { Some(audio_state) => audio_state, None => return Ok(()), }; if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Pause).await { send_embed(ctx, msg, &format!("Error: {}", why)).await; } else { message_react(ctx, msg, "⏸").await; }; Ok(()) } #[command] async fn resume(ctx: &Context, msg: &Message) -> CommandResult { let audio_state = get_audio_state(ctx, msg).await; let audio_state = match audio_state { Some(audio_state) => audio_state, None => return Ok(()), }; if let Err(why) = AudioState::send_track_command(audio_state, TrackCommand::Play).await { send_embed(ctx, msg, &format!("Error: {}", why)).await; } else { message_react(ctx, msg, "▶").await; }; Ok(()) } #[command] async fn shuffle(ctx: &Context, msg: &Message) -> CommandResult { let audio_state = get_audio_state(ctx, msg).await; let audio_state = match audio_state { Some(audio_state) => audio_state, None => return Ok(()), }; if let Err(why) = AudioState::shuffle(audio_state).await { send_embed(ctx, msg, &format!("Error: {}", why)).await; } else { message_react(ctx, msg, "🔀").await; }; Ok(()) } #[command] async fn clear(ctx: &Context, msg: &Message) -> CommandResult { let audio_state = get_audio_state(ctx, msg).await; let audio_state = match audio_state { Some(audio_state) => audio_state, None => return Ok(()), }; if let Err(why) = AudioState::clear(audio_state.clone()).await { send_embed(ctx, msg, &format!("Error: {}", why)).await; } else { message_react(ctx, msg, "🗑").await; }; Ok(()) } #[command] #[aliases("loop")] async fn change_loop(ctx: &Context, msg: &Message) -> CommandResult { let audio_state = get_audio_state(ctx, msg).await; let audio_state = match audio_state { Some(audio_state) => audio_state, None => return Ok(()), }; match AudioState::change_looping(audio_state).await { Ok(true) => message_react(ctx, msg, "🔄").await, Ok(false) => message_react(ctx, msg, "➡").await, Err(why) => send_embed(ctx, msg, &format!("Error: {}", why)).await, }; Ok(()) } #[command] #[aliases("vol")] async fn volume(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let audio_state = get_audio_state(ctx, msg).await; let audio_state = match audio_state { Some(audio_state) => audio_state, None => return Ok(()), }; let volume: u8 = args.rest().parse()?; match AudioState::set_volume(audio_state, volume.into()).await { Ok(()) => message_react(ctx, msg, "🎶").await, Err(why) => send_embed(ctx, msg, &format!("Error: {}", why)).await, }; Ok(()) } #[command] async fn queue(ctx: &Context, msg: &Message) -> CommandResult { let audio_state = get_audio_state(ctx, msg).await; let audio_state = match audio_state { Some(audio_state) => audio_state, None => return Ok(()), }; send_embed(ctx, msg, &AudioState::get_string(audio_state).await).await; Ok(()) }