feat: bot now plays songs, started to work on queue system

This commit is contained in:
moonleay 2024-03-08 01:18:30 +01:00
parent edc22a91f2
commit a16d8a6b60
Signed by: moonleay
GPG key ID: 82667543CCD715FB
12 changed files with 289 additions and 130 deletions

2
src/music/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod music_manager;
pub mod music_events;

42
src/music/music_events.rs Normal file
View file

@ -0,0 +1,42 @@
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;
pub struct TrackEndNotifier {
pub guild_id: GuildId,
pub channel_id: ChannelId,
pub http: Arc<Http>,
pub cmdctx: Arc<serenity::client::Context>,
}
#[async_trait]
impl EventHandler for TrackEndNotifier {
async fn act(&self, ctx: &EventContext<'_>) -> Option<Event> {
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 stopped = match music_manager::stop(&self.cmdctx, &self.guild_id).await {
Ok(stopped) => stopped,
Err(e) => {
println!("Cannot stop: {:?}", e);
return None;
}
};
if stopped {
println!("Stopped playing successfully.");
} else {
println!("Failed to stop playing.");
}
return None;
}
}
}
None
}
}

173
src/music/music_manager.rs Normal file
View file

@ -0,0 +1,173 @@
use std::collections::{HashMap};
use std::sync::{Arc, Mutex};
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::util::embed::Embed;
use crate::util::user_util;
use crate::util::user_util::get_vc_id;
/// pub static mut MUSIC_QUEUE: HashMap<GuildId, Queue<String>> = HashMap::new();
pub static mut MUSIC_QUEUE: Lazy<Mutex<HashMap<GuildId, Queue<String>>>> = 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<String> {
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 {
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.");
}
let connect_to = match get_vc_id(ctx, &guild_id, &user_id).await {
Some(channel_id) => channel_id,
None => {
return Embed::create(username, "Error", "Cannot get channel id");
}
};
let manager = &songbird::get(ctx)
.await
.expect("Cannot get Songbird.")
.clone();
let self_channel = user_util::get_self_vc_id(ctx, &guild_id).await;
if !user_util::is_self_connected_to_vc(ctx, &guild_id).await {
// self is connected to vc, check if user is in same vc
if self_channel.is_none() { // TODO This could maybe be removed?
// Connect to VC
manager
.join(*guild_id, connect_to)
.await
.expect("Cannot connect>...");
}
} else {
let self_channel = self_channel.expect("Cannot get self channel");
// Check if user is in the same VC as the bot
if self_channel != connect_to {
return Embed::create(
username,
"You are not in my VC.",
"Connect to my VC to control the music.",
);
}
}
// Get query
let do_search = !query.starts_with("http");
let http_client = {
let data = ctx.data.read().await;
data.get::<HttpKey>()
.cloned()
.expect("Guaranteed to exist in the typemap.")
};
// Create source
let mut src = if do_search {
YoutubeDl::new_search(http_client, query.to_string())
} else {
YoutubeDl::new(http_client, query.to_string())
};
let handler_lock = match manager.get(*guild_id) {
Some(handler) => handler,
None => {
return Embed::create(username, "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
handler.add_global_event(
Event::Track(TrackEvent::End),
music_events::TrackEndNotifier {
guild_id: *guild_id,
channel_id: connect_to,
http: Arc::clone(&ctx.http),
cmdctx: Arc::new(ctx.clone()),
},
);
// Get metadata
let metadata = src.aux_metadata().await.expect("Cannot get metadata");
let title = metadata.title.unwrap_or("Unknown title".to_string());
let author = metadata.artist.unwrap_or("Unknown artist".to_string());
let duration = metadata.duration.unwrap_or(Duration::from_millis(0));
let thumbnail = metadata.thumbnail.unwrap_or("https://http.cat/images/404.jpg".to_string());
let link = metadata.source_url.unwrap_or("https://piped.moonleay.net/404".to_string());
Embed::create(username, "Added to queue", format!("{} by {} ({}min {}sec) was added to the queue.\n [[Link]({})]",
title, author, duration.as_secs() / 60, duration.as_secs() % 60, link))
.thumbnail(thumbnail)
}
pub async 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(
username,
"Bot is not connected",
"And therefore there is no need to do anything.",
);
}
let self_channel = user_util::get_self_vc_id(ctx, &guild_id).await.expect("Cannot get self channel");
let connect_to = get_vc_id(ctx, &guild_id, &user_id).await.expect("Cannot get channel id");
// Check if user is in the same VC as the bot
if self_channel != connect_to {
return Embed::create(
username,
"You are not in my VC.",
"Connect to my VC to control the music.",
);
}
let stopped = match stop(ctx, guild_id).await {
Ok(stopped) => stopped,
Err(e) => {
println!("Error while stopping: {:?}", e);
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());
} else {
return Embed::create(
username,
"I stopped and left",
"Just like your girlfriend.",
);
}
}
// 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<bool, JoinError> {
let manager = songbird::get(ctx)
.await
.expect("Cannot get Songbird")
.clone();
let has_handler = manager.get(*guild_id).is_some();
if has_handler {
if let Err(e) = manager.remove(*guild_id).await {
return Err(e); // Failed to remove handler
}
return Ok(true) // Handler removed
}
Ok(false) // No handler, so it's already stopped
}