123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- 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<HashMap<GuildId, Arc<Mutex<AudioState>>>> = Mutex::new(HashMap::new());
- }
- async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<Mutex<AudioState>>> {
- 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(())
- }
|