From a06299fb6fe7e3e78a59be9dfeeacb586e3ba479 Mon Sep 17 00:00:00 2001 From: moonleay Date: Sat, 9 Mar 2024 00:25:12 +0100 Subject: [PATCH] feat: started to impl queue system --- Cargo.lock | 8 ----- Cargo.toml | 4 +-- src/commands/play.rs | 2 +- src/commands/stop.rs | 2 +- src/main.rs | 26 ++++++++------- src/music/mod.rs | 3 +- src/music/music_events.rs | 22 ++++++++++--- src/music/music_manager.rs | 56 ++++++++++++++++++-------------- src/music/music_queue.rs | 65 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 134 insertions(+), 54 deletions(-) create mode 100644 src/music/music_queue.rs diff --git a/Cargo.lock b/Cargo.lock index 55d56bf..cd4694e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1338,12 +1338,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "queues" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1475abae4f8ad4998590fe3acfe20104f0a5d48fc420c817cd2c09c3f56151f0" - [[package]] name = "quote" version = "1.0.35" @@ -1567,8 +1561,6 @@ dependencies = [ "chrono", "confy", "futures", - "once_cell", - "queues", "reqwest", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 02c692f..394638a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustendo" version = "0.1.0" -authors = ["moonleay "] +authors = ["moonleay ", "migueldamota "] edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -21,5 +21,3 @@ tracing = "0.1.40" tracing-subscriber = "0.3.18" tracing-futures = "0.2.5" futures = "0.3.1" -queues = "1" -once_cell = "1" diff --git a/src/commands/play.rs b/src/commands/play.rs index 1df5511..8fceb13 100644 --- a/src/commands/play.rs +++ b/src/commands/play.rs @@ -5,7 +5,7 @@ use crate::music::music_manager; use crate::util::embed::Embed; -pub async fn run(ctx: &Context, command: &CommandInteraction) -> CreateEmbed { +pub async unsafe fn run(ctx: &Context, command: &CommandInteraction) -> CreateEmbed { let username = command.user.name.as_str(); let options = &command.data.options; diff --git a/src/commands/stop.rs b/src/commands/stop.rs index 2f0600a..beee7ea 100644 --- a/src/commands/stop.rs +++ b/src/commands/stop.rs @@ -3,7 +3,7 @@ use serenity::builder::{CreateCommand, CreateEmbed}; use crate::music::music_manager; use crate::util::embed::Embed; -pub async fn run(ctx: &Context, command: &CommandInteraction) -> CreateEmbed { +pub async unsafe fn run(ctx: &Context, command: &CommandInteraction) -> CreateEmbed { let username = command.user.name.as_str(); let guild_id = match &command.guild_id { diff --git a/src/main.rs b/src/main.rs index 7e36070..090a238 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,20 +29,22 @@ struct Handler; #[async_trait] impl EventHandler for Handler { async fn interaction_create(&self, ctx: Context, interaction: Interaction) { - if let Interaction::Command(command) = interaction { - let _ = &command.defer(&ctx.http()).await.expect("Cannot defer"); + unsafe { + if let Interaction::Command(command) = interaction { + let _ = &command.defer(&ctx.http()).await.expect("Cannot defer"); - let content = Some(match command.data.name.as_str() { - "info" => commands::info::run(&ctx, &command).await, - "play" => commands::play::run(&ctx, &command).await, - "stop" => commands::stop::run(&ctx, &command).await, - _ => respond_with_error(&ctx, &command).await, - }); + let content = Some(match command.data.name.as_str() { + "info" => commands::info::run(&ctx, &command).await, + "play" => commands::play::run(&ctx, &command).await, + "stop" => commands::stop::run(&ctx, &command).await, + _ => respond_with_error(&ctx, &command).await, + }); - if let Some(embed) = content { - let followup = CreateInteractionResponseFollowup::new().embed(embed); - if let Err(why) = command.create_followup(&ctx.http, followup).await { - println!("Cannot followup to slash command: {why}") + if let Some(embed) = content { + let followup = CreateInteractionResponseFollowup::new().embed(embed); + if let Err(why) = command.create_followup(&ctx.http, followup).await { + println!("Cannot followup to slash command: {why}") + } } } } diff --git a/src/music/mod.rs b/src/music/mod.rs index f16ee08..28737b1 100644 --- a/src/music/mod.rs +++ b/src/music/mod.rs @@ -1,2 +1,3 @@ pub mod music_manager; -pub mod music_events; \ No newline at end of file +pub mod music_events; +mod music_queue; \ No newline at end of file diff --git a/src/music/music_events.rs b/src/music/music_events.rs index ca48045..6358fd4 100644 --- a/src/music/music_events.rs +++ b/src/music/music_events.rs @@ -1,9 +1,8 @@ use std::sync::Arc; -use queues::IsQueue; use serenity::all::{ChannelId, GuildId, Http}; use serenity::async_trait; use songbird::{Event, EventContext, EventHandler}; -use crate::music::music_manager; +use crate::music::{music_manager, music_queue}; pub struct TrackEndNotifier { pub guild_id: GuildId, @@ -18,8 +17,10 @@ impl EventHandler for TrackEndNotifier { unsafe { // TODO: Does this need to be unsafe? if let EventContext::Track(track_list) = ctx { println!("The track ended!"); - let queue = music_manager::get_queue(&self.guild_id); - if queue.size() == 0 { + let music_queue = music_queue::get_queue(&self.guild_id); + let q = &music_queue.queue; + if q.len() == 0 { + // No more songs in queue, exit the vc let stopped = match music_manager::stop(&self.cmdctx, &self.guild_id).await { Ok(stopped) => stopped, Err(e) => { @@ -34,6 +35,19 @@ impl EventHandler for TrackEndNotifier { } return None; } + let head = music_queue::get_head(&self.guild_id); + if head.is_none() { + println!("Cannot get head of queue"); + return None; + } + let head = head.unwrap(); + /*let started = match music_manager::play(&self.cmdctx, &self.guild_id, &head).await { + Ok(started) => started, + Err(e) => { + println!("Cannot play: {:?}", e); + return None; + } + }; */ } } diff --git a/src/music/music_manager.rs b/src/music/music_manager.rs index 7781b98..64a2063 100644 --- a/src/music/music_manager.rs +++ b/src/music/music_manager.rs @@ -1,28 +1,16 @@ -use std::collections::{HashMap}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc}; use std::time::Duration; -use once_cell::sync::Lazy; -use queues::Queue; use serenity::all::{Context, CreateEmbed, GuildId, UserId}; use songbird::{Event, TrackEvent}; use songbird::error::JoinError; use songbird::input::{Compose, YoutubeDl}; use crate::HttpKey; -use crate::music::music_events; +use crate::music::{music_events, music_queue}; use crate::util::embed::Embed; use crate::util::user_util; use crate::util::user_util::get_vc_id; -/// pub static mut MUSIC_QUEUE: HashMap> = HashMap::new(); -pub static mut MUSIC_QUEUE: Lazy>>> = Lazy::new(|| { - Mutex::new(HashMap::new()) -}); // TODO: This does not work and this is not the way to do it. This is a placeholder for now. - -pub unsafe fn get_queue(guild_id: &GuildId) -> &Queue { - MUSIC_QUEUE.lock().unwrap().entry(*guild_id).or_insert_with(Queue::new) -} - -pub async fn attempt_to_queue_song(ctx: &Context, guild_id: &GuildId, user_id: &UserId, username: &str, query: &str) -> CreateEmbed { +pub async unsafe fn attempt_to_queue_song(ctx: &Context, guild_id: &GuildId, user_id: &UserId, username: &str, query: &str) -> CreateEmbed { if !user_util::is_user_connected_to_vc(ctx, guild_id, user_id).await { return Embed::create(username, "You are not connected to a VC", "Connect to my VC to control the music."); } @@ -35,7 +23,7 @@ pub async fn attempt_to_queue_song(ctx: &Context, guild_id: &GuildId, user_id: & }; - let manager = &songbird::get(ctx) + let manager = &songbird::get(ctx) // TODO match .await .expect("Cannot get Songbird.") .clone(); @@ -64,6 +52,16 @@ pub async fn attempt_to_queue_song(ctx: &Context, guild_id: &GuildId, user_id: & ); } } + + let currently_playing = music_queue::get_now_playing(&guild_id); + music_queue::add_to_queue(&guild_id, query.to_string()); + if currently_playing != "".to_string() { + // Add to queue + return Embed::create(username, "Added to queue", "The song was added to the queue."); + } + + let query = music_queue::get_head(&guild_id).expect("Cannot get head of queue"); + music_queue::set_now_playing(&guild_id, query.clone()); // Get query let do_search = !query.starts_with("http"); @@ -83,13 +81,13 @@ pub async fn attempt_to_queue_song(ctx: &Context, guild_id: &GuildId, user_id: & let handler_lock = match manager.get(*guild_id) { Some(handler) => handler, None => { - return Embed::create(username, "Error", "Cannot get handler"); + return Embed::create("", "Error", "Cannot get handler"); }, }; // Start playing let mut handler = handler_lock.lock().await; - let track_handle = handler.play_input(src.clone().into()); // TODO: Add event handlers + let _ = handler.play_input(src.clone().into()); // TODO: Add event handlers handler.add_global_event( Event::Track(TrackEvent::End), music_events::TrackEndNotifier { @@ -113,8 +111,11 @@ pub async fn attempt_to_queue_song(ctx: &Context, guild_id: &GuildId, user_id: & .thumbnail(thumbnail) } +pub async unsafe fn play() { + +} -pub async fn attempt_to_stop(ctx: &Context, guild_id: &GuildId, user_id: &UserId, username: &str) -> CreateEmbed { +pub async unsafe fn attempt_to_stop(ctx: &Context, guild_id: &GuildId, user_id: &UserId, username: &str) -> CreateEmbed { if !user_util::is_self_connected_to_vc(ctx, guild_id).await { // Bot is not connectd to vc; no need to dc return Embed::create( @@ -142,18 +143,25 @@ pub async fn attempt_to_stop(ctx: &Context, guild_id: &GuildId, user_id: &UserId return Embed::create(username, "There was an error", "Tell moonleay to check the logs.".to_string()); } }; - - if !stopped { - return Embed::create(username, "Can't stop, what ain't running.", "I am not connected. I cant stop doing something, when I'm not doing it".to_string()); + + return if !stopped { + Embed::create(username, "Can't stop, what ain't running.", "I am not connected. I cant stop doing something, when I'm not doing it".to_string()) } else { - return Embed::create( + music_queue::delete_queue(guild_id); // Clear queue + + Embed::create( username, "I stopped and left", "Just like your girlfriend.", - ); + ) } } +/// pub async unsafe fn play_song(ctx: &Context, guild_id: &GuildId, query: String) -> Result { + +/// } + + // Make the bot leave the voice channel. Returns Ok(true) if bot was connected, returns Ok(false) if bot was not connected. Returns Err if something went wrong. pub async fn stop(ctx: &Context, guild_id: &GuildId) -> Result { let manager = songbird::get(ctx) diff --git a/src/music/music_queue.rs b/src/music/music_queue.rs new file mode 100644 index 0000000..182a8ee --- /dev/null +++ b/src/music/music_queue.rs @@ -0,0 +1,65 @@ +use serenity::all::GuildId; + +pub struct MusicQueue { // God this sucks. This needs to be reprogrammed properly. + guild_id: GuildId, + pub queue: Vec, + pub now_playing: String, +} + +static mut MUSIC_QUEUE: Vec = Vec::new(); + +pub unsafe fn get_queue(guild_id: &GuildId) -> &MusicQueue { + let queue = MUSIC_QUEUE.iter().find(|q| q.guild_id == *guild_id); + match queue { + Some(queue) => queue, + None => { + let new_queue = MusicQueue { + guild_id: *guild_id, + queue: Vec::new(), + now_playing: "".to_string(), + }; + MUSIC_QUEUE.push(new_queue); + MUSIC_QUEUE.iter().find(|q| q.guild_id == *guild_id).expect("Cannot get queue") + } + } +} + +unsafe fn update_queue(guild_id: &GuildId, queue: Vec, now_playing: String) { + let index = MUSIC_QUEUE.iter().position(|q| q.guild_id == *guild_id).expect("Cannot get index"); + MUSIC_QUEUE[index] = MusicQueue{ + guild_id: *guild_id, + queue, + now_playing, + }; +} + +pub unsafe fn delete_queue(guild_id: &GuildId) { + let index = MUSIC_QUEUE.iter().position(|q| q.guild_id == *guild_id).expect("Cannot get index"); + MUSIC_QUEUE.remove(index); +} + +pub unsafe fn add_to_queue(guild_id: &GuildId, input: String) { + let queue = get_queue(guild_id); + let mut new_queue = queue.queue.clone(); + new_queue.push(input); + update_queue(guild_id, new_queue, queue.now_playing.clone()); +} + +pub unsafe fn get_head(guild_id: &GuildId) -> Option { + let queue = get_queue(guild_id); + let mut q = queue.queue.clone(); + let result = q.first().map(|s| s.clone()); + q.remove(0); + update_queue(guild_id, q, queue.now_playing.clone()); + result +} + +pub unsafe fn set_now_playing(guild_id: &GuildId, now_playing: String) { + let queue = get_queue(guild_id); + update_queue(guild_id, queue.queue.clone(), now_playing); +} + +pub unsafe fn get_now_playing(guild_id: &GuildId) -> String { + let queue = get_queue(guild_id); + queue.now_playing.clone() +} \ No newline at end of file