use std::sync::{Arc}; use std::time::Duration; 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, music_queue}; use crate::util::embed::Embed; use crate::util::user_util; use crate::util::user_util::get_vc_id; 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."); } 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) // TODO match .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::() .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 currently_playing = music_queue::get_now_playing(&guild_id); music_queue::add_to_queue(&guild_id, src.clone()); if currently_playing.is_some() { // 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, Some(src.clone())); let handler_lock = match manager.get(*guild_id) { Some(handler) => handler, None => { return Embed::create("", "Error", "Cannot get handler"); }, }; // Start playing let mut handler = handler_lock.lock().await; let _ = 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 unsafe fn play_song(ctx: &Context, guild_id: &GuildId, target: YoutubeDl) { let manager = &songbird::get(ctx) // TODO match .await .expect("Cannot get Songbird.") .clone(); if !user_util::is_self_connected_to_vc(ctx, guild_id).await { println!("Bot is not connected to a VC, cannot play."); return; } music_queue::set_now_playing(&guild_id, Some(target.clone())); let handler_lock = match manager.get(*guild_id) { Some(handler) => handler, None => { return }, }; let mut handler = handler_lock.lock().await; let _ = handler.play_input(target.into()); // TODO: Add event handlers } 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( 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()); } }; 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 { music_queue::delete_queue(guild_id); // Clear queue 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 { 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 }