diff --git a/src/commands/voice/spotify/player.rs b/src/commands/voice/spotify/player.rs index 9b3b9d5..5c00980 100644 --- a/src/commands/voice/spotify/player.rs +++ b/src/commands/voice/spotify/player.rs @@ -1,13 +1,18 @@ -use poise; +use std::vec; + +use poise::{self, CreateReply}; +use serenity::all::{CacheHttp, Guild, PartialGuild, UserId}; use serenity::{all::ChannelId, async_trait}; use songbird::events::{Event, EventContext, EventHandler as VoiceEventHandler}; +use songbird::input::{File, Input}; +use songbird::tracks::Track; use songbird::TrackEvent; use crate::util::debug::send_error; +use crate::util::poise_context_extension::ContextExt; +use crate::util::utilities::get_local_songs; use crate::{types::{Context, Error}, util::utilities::get_channel_by_name}; -use serenity::all::User; - #[poise::command( slash_command, description_localized("en-US", "Play song") @@ -21,24 +26,28 @@ pub async fn play(ctx: Context<'_>, Ok(()) } - - -#[poise::command( - slash_command, - description_localized("en-US", "Headpat all your friends!") -)] -pub async fn headpat(ctx: Context<'_>, - #[description = "Who is the lucky one?"] - user: User -) -> Result<(), Error> { - let _title = "HEADPATS!"; - let _desc = format!("{} headpats {}", ctx.author(), user); - // send_with_embed(ctx, "headpat", &title, &desc).await?; - ctx.reply("Done!").await?; - Ok(()) +/// Returns either voice channel to which the user is currently connected to or the one passed via name +async fn get_voice_channel(ctx: &Context<'_>, name: Option) -> Result { + if name.is_none() || name.as_ref().is_some_and(|n| n.is_empty()) { + match ctx.guild().and_then(|guild| + guild.voice_states.get(&ctx.author().id).and_then(|voice_state| + voice_state.channel_id + ) + ) { + Some(c) => Ok(c), + None => Err("You must be in a voice channel or specify explicit voice channel by name".to_string()) + } + } + else { + match ctx.guild().and_then(|guild| + get_channel_by_name(guild, name.unwrap()) + ) { + Some(c) => Ok(c), + None => Err("Channel with this name does not exist".to_string()) + } + } } - #[poise::command( slash_command, description_localized("en-US", "Connect to channel") @@ -50,73 +59,37 @@ pub async fn connect(ctx: Context<'_>, ) -> Result<(), Error> { if ctx.guild().is_none() { - ctx.reply("Can't use this outside of guild").await?; - return Ok(()) + ctx.reply_ephemeral("Can't use this outside of guild").await?; + return Ok(()); } - let channel: ChannelId; - if name.is_none() || name.clone().unwrap() == "" { - // ERROR: <> - // !### This will work if you comment out one of the replies ###! - // let Some(guild) = ctx.guild() else { - // ctx.reply("You must be in a voice channel or specify explicit voice channel by name").await?; - // return Ok(()) - // }; - - // let Some(voice_channel) = guild.voice_states.get(&ctx.author().id).and_then(|voice_state| voice_state.channel_id) else { - // ctx.reply("You must be in a voice channel or specify explicit voice channel by name").await?; - // return Ok(()) - // }; - - // channel = voice_channel; - // !### ###! - - // This one liner works only if you comment out the reply - // channel = match ctx.guild().unwrap().voice_states.get(&ctx.author().id).and_then(|voice_state| voice_state.channel_id) { - // Some(channel) => channel, - // None => { - // // ERROR: <> - // // Will work if you comment this out - // ctx.reply("You must be in a voice channel or specify explicit voice channel by name").await?; - // return Ok(()) - // } - // } - - // This one works - // Ugly one liner since I don't know how to do this another way yet - // TODO fix please - let Some(voice_channel) = ctx.guild().unwrap().voice_states.get(&ctx.author().id).and_then(|voice_state| voice_state.channel_id) else { - ctx.reply("You must be in a voice channel or specify explicit voice channel by name").await?; + let voice_channel = match get_voice_channel(&ctx, name).await { + Ok(c) => c, + Err(e) => { + ctx.reply_ephemeral(e.as_str()).await?; return Ok(()) - }; - - channel = voice_channel; - } - else { - channel = match get_channel_by_name(ctx.guild().unwrap(), name.unwrap()) { - Some(channel) => channel, - None => { - ctx.reply("Channel with this name does not exist").await?; - return Ok(()); - } - }; - } + } + }; let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird Voice client placed in at initialisation.") .clone(); - if let Ok(handler_lock) = manager.join(ctx.guild_id().unwrap(), channel).await { + let Some(guild_id) = ctx.guild_id() else { + ctx.reply_ephemeral("Guild id not found").await?; + return Ok(()) + }; + + if let Ok(handler_lock) = manager.join(guild_id, voice_channel).await { let mut handler = handler_lock.lock().await; handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier); } - ctx.reply("Done!").await?; + ctx.reply_ephemeral("Done!").await?; Ok(()) } - struct TrackErrorNotifier; #[async_trait] @@ -152,7 +125,7 @@ async fn autocomplete_channel( slash_command, description_localized("en-US", "Disconnect from voice channel") )] -async fn disconnect( +pub async fn disconnect( ctx: Context<'_> ) -> Result<(), Error> { @@ -184,3 +157,55 @@ async fn disconnect( } } } + +async fn autocomplete_song( + _ctx: Context<'_>, + partial: &str, +) -> Vec { + + match get_local_songs(partial) { + Ok(names) => names, + Err(_) => vec![] + } +} + +#[poise::command( + slash_command, + description_localized("en-US", "Connect to channel") +)] +pub async fn play_local(ctx: Context<'_>, + #[autocomplete = "autocomplete_song"] + #[description = "Voice channel name: "] + file_name: String +) -> Result<(), Error> { + + if ctx.guild().is_none() { + ctx.reply_ephemeral("Can't use this outside of guild").await?; + return Ok(()); + } + + let manager = songbird::get(ctx.serenity_context()) + .await + .expect("Songbird Voice client placed in at initialisation.") + .clone(); + + let Some(guild_id) = ctx.guild_id() else { + ctx.reply_ephemeral("Guild id not found").await?; + return Ok(()) + }; + + if let Some(handler_lock) = manager.get(guild_id) { + let mut handler = handler_lock.lock().await; + + let input_file = File::new(format!("/home/emil/Music/{file_name}")); + let input = Input::Lazy(Box::new(input_file)); + let _ = handler.play_input(input); + } + else { + ctx.reply_ephemeral("Not in a voice channel").await?; + return Ok(()) + } + + ctx.reply_ephemeral("Done!").await?; + Ok(()) +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e4d139f..3eedf7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,7 +85,6 @@ async fn main() -> anyhow::Result<()> { commands::say(), commands::hug(), commands::player::play_local(), - commands::player::connect(), commands::player::disconnect(), ], prefix_options: poise::PrefixFrameworkOptions {