Merge branch 'fix/music-queue-improvements'
This commit is contained in:
commit
ed89386ed9
8 changed files with 86 additions and 75 deletions
|
@ -6,7 +6,7 @@ use crate::util::embed::Embed;
|
||||||
pub async fn run(_ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
|
pub async fn run(_ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
|
||||||
let username = command.user.name.as_str();
|
let username = command.user.name.as_str();
|
||||||
|
|
||||||
Embed::create_success_response(username, "Botendo v7", "developed by [moonleay](https://moonleay.net)\n\nCheck out the repository: https://git.moonleay.net/DiscordBots/Rustendo")
|
Embed::create_success_response(username, "Botendo v7", "developed by [moonleay](https://moonleay.net)\n\nCheck out the repository: https://git.moonleay.net/DiscordBots/Rustendo")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register() -> CreateCommand {
|
pub fn register() -> CreateCommand {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
pub mod info;
|
pub mod info;
|
||||||
pub mod play;
|
pub mod play;
|
||||||
|
pub mod skip;
|
||||||
pub mod stop;
|
pub mod stop;
|
||||||
pub mod skip;
|
|
|
@ -6,7 +6,7 @@ use serenity::model::application::CommandOptionType;
|
||||||
|
|
||||||
use crate::util::embed::Embed;
|
use crate::util::embed::Embed;
|
||||||
|
|
||||||
pub async unsafe fn run(ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
|
||||||
let username = command.user.name.as_str();
|
let username = command.user.name.as_str();
|
||||||
let options = &command.data.options;
|
let options = &command.data.options;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::util::embed::Embed;
|
||||||
use serenity::all::{CommandInteraction, Context};
|
use serenity::all::{CommandInteraction, Context};
|
||||||
use serenity::builder::{CreateCommand, CreateEmbed};
|
use serenity::builder::{CreateCommand, CreateEmbed};
|
||||||
|
|
||||||
pub async unsafe fn run(ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
|
pub async fn run(ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
|
||||||
let username = command.user.name.as_str();
|
let username = command.user.name.as_str();
|
||||||
|
|
||||||
let guild_id = match &command.guild_id {
|
let guild_id = match &command.guild_id {
|
||||||
|
|
44
src/main.rs
44
src/main.rs
|
@ -3,7 +3,7 @@ mod music;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use serenity::all::{
|
use serenity::all::{
|
||||||
CommandInteraction, CreateInteractionResponseFollowup, GuildId, OnlineStatus, VoiceState,
|
CommandInteraction, CreateInteractionResponseFollowup, OnlineStatus, VoiceState,
|
||||||
};
|
};
|
||||||
use serenity::async_trait;
|
use serenity::async_trait;
|
||||||
use serenity::builder::CreateEmbed;
|
use serenity::builder::CreateEmbed;
|
||||||
|
@ -30,42 +30,13 @@ impl TypeMapKey for HttpKey {
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
use crate::music::music_queue::MusicQueue;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref HASHMAP: Mutex<HashMap<GuildId, MusicQueue>> = Mutex::new(HashMap::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_queue(guild_id: &GuildId) -> MusicQueue {
|
|
||||||
let mut queues = HASHMAP.lock().await;
|
|
||||||
|
|
||||||
match queues.get(&guild_id) {
|
|
||||||
Some(music_queue) => music_queue.clone(),
|
|
||||||
None => {
|
|
||||||
let q = MusicQueue {
|
|
||||||
guild_id: *guild_id,
|
|
||||||
queue: Vec::new(),
|
|
||||||
now_playing: None,
|
|
||||||
};
|
|
||||||
queues.insert(*guild_id, q.clone());
|
|
||||||
q
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn set_queue(guild_id: &GuildId, queue: MusicQueue) {
|
|
||||||
HASHMAP.lock().await.insert(*guild_id, queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Handler;
|
struct Handler;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EventHandler for Handler {
|
impl EventHandler for Handler {
|
||||||
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
||||||
unsafe {
|
if let Interaction::Command(command) = interaction {
|
||||||
if let Interaction::Command(command) = interaction {
|
let _ = &command.defer(&ctx.http()).await.expect("Cannot defer");
|
||||||
let _ = &command.defer(&ctx.http()).await.expect("Cannot defer");
|
|
||||||
|
|
||||||
let content = Some(match command.data.name.as_str() {
|
let content = Some(match command.data.name.as_str() {
|
||||||
"info" => commands::info::run(&ctx, &command).await,
|
"info" => commands::info::run(&ctx, &command).await,
|
||||||
|
@ -75,11 +46,10 @@ impl EventHandler for Handler {
|
||||||
_ => respond_with_error(&ctx, &command).await,
|
_ => respond_with_error(&ctx, &command).await,
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(embed) = content {
|
if let Some(embed) = content {
|
||||||
let followup = CreateInteractionResponseFollowup::new().embed(embed);
|
let followup = CreateInteractionResponseFollowup::new().embed(embed);
|
||||||
if let Err(why) = command.create_followup(&ctx.http, followup).await {
|
if let Err(why) = command.create_followup(&ctx.http, followup).await {
|
||||||
println!("Cannot followup to slash command: {why}")
|
println!("Cannot followup to slash command: {why}")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::music::{music_manager, music_queue};
|
use crate::music::{music_manager, music_queue};
|
||||||
|
|
||||||
use serenity::all::{ChannelId, GuildId, Http};
|
use serenity::all::{ChannelId, GuildId, Http};
|
||||||
use serenity::async_trait;
|
use serenity::async_trait;
|
||||||
use songbird::input::Compose;
|
use songbird::input::Compose;
|
||||||
|
@ -18,9 +19,8 @@ impl EventHandler for TrackEndNotifier {
|
||||||
// TODO: Does this need to be unsafe?
|
// TODO: Does this need to be unsafe?
|
||||||
if let EventContext::Track(..) = ctx {
|
if let EventContext::Track(..) = ctx {
|
||||||
println!("The track ended!");
|
println!("The track ended!");
|
||||||
let music_queue = crate::get_queue(&self.guild_id).await;
|
|
||||||
let q = &music_queue.queue;
|
if music_queue::is_empty(&self.guild_id).await {
|
||||||
if q.is_empty() {
|
|
||||||
// No more songs in queue, exit the vc
|
// No more songs in queue, exit the vc
|
||||||
let stopped = match music_manager::leave(&self.cmdctx, &self.guild_id).await {
|
let stopped = match music_manager::leave(&self.cmdctx, &self.guild_id).await {
|
||||||
Ok(stopped) => stopped,
|
Ok(stopped) => stopped,
|
||||||
|
@ -37,12 +37,13 @@ impl EventHandler for TrackEndNotifier {
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let head = music_queue::get_head(&self.guild_id).await;
|
let mut head = match music_queue::get_head(&self.guild_id).await {
|
||||||
if head.is_none() {
|
Some(head) => head,
|
||||||
println!("Cannot get head of queue");
|
None => {
|
||||||
return None;
|
println!("Cannot get head of queue: {:?}", e);
|
||||||
}
|
return None;
|
||||||
let mut head = head.unwrap();
|
}
|
||||||
|
};
|
||||||
println!("Now playing: {}", head.aux_metadata().await.unwrap().title.unwrap());
|
println!("Now playing: {}", head.aux_metadata().await.unwrap().title.unwrap());
|
||||||
music_manager::play_song(&self.cmdctx, &self.guild_id, &head).await;
|
music_manager::play_song(&self.cmdctx, &self.guild_id, &head).await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,41 @@
|
||||||
use serenity::all::GuildId;
|
use serenity::all::GuildId;
|
||||||
use songbird::input::YoutubeDl;
|
use songbird::input::YoutubeDl;
|
||||||
|
use tokio::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
type MusicQueueItem = Arc<Mutex<MusicQueue>>;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref HASHMAP: Mutex<HashMap<GuildId, MusicQueueItem>> = Mutex::new(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_music_queue(guild_id: &GuildId) -> MusicQueueItem {
|
||||||
|
let mut queues = HASHMAP.lock().await;
|
||||||
|
|
||||||
|
queues
|
||||||
|
.entry(*guild_id)
|
||||||
|
.or_insert(Arc::new(Mutex::new(MusicQueue {
|
||||||
|
guild_id: *guild_id,
|
||||||
|
queue: Vec::new(),
|
||||||
|
now_playing: None,
|
||||||
|
})))
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn with_music_queue<F, T>(guild_id: &GuildId, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut MusicQueue) -> T,
|
||||||
|
T: Send,
|
||||||
|
{
|
||||||
|
let queue = get_music_queue(guild_id).await;
|
||||||
|
let mut queue = queue.lock().await;
|
||||||
|
|
||||||
|
f(&mut *queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct MusicQueue {
|
pub struct MusicQueue {
|
||||||
// God this sucks. This needs to be reprogrammed properly.
|
// God this sucks. This needs to be reprogrammed properly.
|
||||||
pub guild_id: GuildId,
|
pub guild_id: GuildId,
|
||||||
|
@ -10,35 +44,44 @@ pub struct MusicQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_queue(guild_id: &GuildId) {
|
pub async fn delete_queue(guild_id: &GuildId) {
|
||||||
let mut queue = crate::get_queue(guild_id).await;
|
with_music_queue(guild_id, |queue| {
|
||||||
queue.now_playing = None;
|
queue.now_playing = None;
|
||||||
queue.queue = Vec::new();
|
queue.queue.clear();
|
||||||
crate::set_queue(guild_id, queue).await;
|
})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_to_queue(guild_id: &GuildId, input: YoutubeDl) {
|
pub async fn add_to_queue(guild_id: &GuildId, input: YoutubeDl) {
|
||||||
let mut queue = crate::get_queue(guild_id).await;
|
with_music_queue(guild_id, |queue| {
|
||||||
queue.queue.push(input);
|
queue.queue.push(input);
|
||||||
crate::set_queue(guild_id, queue).await;
|
})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_head(guild_id: &GuildId) -> Option<YoutubeDl> {
|
pub async fn get_head(guild_id: &GuildId) -> Option<YoutubeDl> {
|
||||||
let mut music_queue = crate::get_queue(guild_id).await;
|
with_music_queue(guild_id, |queue| {
|
||||||
if music_queue.queue.is_empty() {
|
if queue.queue.is_empty() {
|
||||||
return None;
|
None
|
||||||
}
|
} else {
|
||||||
let result = music_queue.queue.remove(0);
|
Some(queue.queue.remove(0))
|
||||||
crate::set_queue(guild_id, music_queue).await;
|
}
|
||||||
Some(result)
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_now_playing(guild_id: &GuildId, now_playing: Option<YoutubeDl>) {
|
pub async fn set_now_playing(guild_id: &GuildId, now_playing: Option<YoutubeDl>) {
|
||||||
let mut queue = crate::get_queue(guild_id).await;
|
let queue = get_music_queue(guild_id).await;
|
||||||
|
let mut queue = queue.lock().await;
|
||||||
|
|
||||||
queue.now_playing = now_playing;
|
queue.now_playing = now_playing;
|
||||||
crate::set_queue(guild_id, queue).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_now_playing(guild_id: &GuildId) -> Option<YoutubeDl> {
|
pub async fn get_now_playing(guild_id: &GuildId) -> Option<YoutubeDl> {
|
||||||
let queue = crate::get_queue(guild_id).await;
|
let queue = get_queue(guild_id).await;
|
||||||
queue.now_playing
|
|
||||||
|
queue.now_playing.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn is_empty(guild_id: &GuildId) -> bool {
|
||||||
|
with_music_queue(guild_id, |queue| queue.queue.is_empty()).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub fn request_guild(ctx: &Context, guild_id: &GuildId) -> Guild {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request a guild by id, get it from Discord, not from cache, this is a partial guild
|
/// Request a guild by id, get it from Discord, not from cache, this is a partial guild
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn request_partial_guild(ctx: &Context, guild_id: &GuildId) -> PartialGuild {
|
pub async fn request_partial_guild(ctx: &Context, guild_id: &GuildId) -> PartialGuild {
|
||||||
match ctx.http.get_guild(*guild_id).await {
|
match ctx.http.get_guild(*guild_id).await {
|
||||||
Ok(guild) => guild,
|
Ok(guild) => guild,
|
||||||
|
@ -32,16 +33,12 @@ pub async fn get_vc_id(ctx: &Context, guild_id: &GuildId, user_id: &UserId) -> O
|
||||||
|
|
||||||
/// Check if the bot is connected to a voice channel
|
/// Check if the bot is connected to a voice channel
|
||||||
pub async fn is_self_connected_to_vc(ctx: &Context, guild_id: &GuildId) -> bool {
|
pub async fn is_self_connected_to_vc(ctx: &Context, guild_id: &GuildId) -> bool {
|
||||||
let channel_id = get_self_vc_id(ctx, guild_id);
|
get_self_vc_id(ctx, guild_id).await.is_some()
|
||||||
|
|
||||||
channel_id.await.is_some()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a user is connected to a voice channel
|
/// Check if a user is connected to a voice channel
|
||||||
pub async fn is_user_connected_to_vc(ctx: &Context, guild_id: &GuildId, user_id: &UserId) -> bool {
|
pub async fn is_user_connected_to_vc(ctx: &Context, guild_id: &GuildId, user_id: &UserId) -> bool {
|
||||||
let channel_id = get_vc_id(ctx, guild_id, user_id).await;
|
get_vc_id(ctx, guild_id, user_id).await.is_some()
|
||||||
|
|
||||||
channel_id.is_some()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the voice channel id of the bot
|
/// Get the voice channel id of the bot
|
||||||
|
|
Loading…
Reference in a new issue