diff --git a/Cargo.lock b/Cargo.lock index e38c1b1..8e6a53d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1349,7 +1349,7 @@ dependencies = [ [[package]] name = "lyra" -version = "0.9.1" +version = "0.10.0" dependencies = [ "dotenv", "fancy-regex", diff --git a/Cargo.toml b/Cargo.toml index a393b0a..6d0503c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lyra" -version = "0.9.1" +version = "0.10.0" authors = ["Michał Czyż "] edition = "2021" description = "A featureful Discord bot written in Rust." diff --git a/src/commands.rs b/src/commands.rs index 6af7359..994f1b4 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,4 +1,3 @@ pub mod embeds; -pub mod kashi; pub mod music; pub mod tools; diff --git a/src/commands/kashi.rs b/src/commands/kashi.rs deleted file mode 100644 index a4affc8..0000000 --- a/src/commands/kashi.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod kashi; - -pub use kashi::kashi; diff --git a/src/commands/kashi/kashi.rs b/src/commands/kashi/kashi.rs deleted file mode 100644 index 8b17975..0000000 --- a/src/commands/kashi/kashi.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{Context, Error}; - -/// Kashi integration platform (WIP) -#[poise::command( - prefix_command, - slash_command, - category = "Kashi" -)] -pub async fn kashi( - ctx: Context<'_> -) -> Result<(), Error> { - - let response = format!("Kashi platform is currently under construction!"); - ctx.say(response).await?; - - Ok(()) -} diff --git a/src/commands/music/deafen.rs b/src/commands/music/deafen.rs index e9a224c..cbf84c0 100644 --- a/src/commands/music/deafen.rs +++ b/src/commands/music/deafen.rs @@ -15,7 +15,7 @@ use poise::CreateReply; pub async fn deafen(ctx: Context<'_>) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/music/join.rs b/src/commands/music/join.rs index ad025a4..c0d0725 100644 --- a/src/commands/music/join.rs +++ b/src/commands/music/join.rs @@ -27,7 +27,7 @@ pub async fn join(ctx: Context<'_>) -> Result<(), Error> { } }; - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/music/leave.rs b/src/commands/music/leave.rs index 284483d..390119e 100644 --- a/src/commands/music/leave.rs +++ b/src/commands/music/leave.rs @@ -15,12 +15,12 @@ use poise::CreateReply; pub async fn leave(ctx: Context<'_>) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); - if !manager.get(guild_id).is_some() { + if manager.get(guild_id).is_none() { let msg = "I am not in a voice channel!"; ctx.send(CreateReply::default().embed(error_embed(ctx, msg).await.unwrap())) .await?; diff --git a/src/commands/music/mute.rs b/src/commands/music/mute.rs index a80cb93..30d7968 100644 --- a/src/commands/music/mute.rs +++ b/src/commands/music/mute.rs @@ -15,7 +15,7 @@ use poise::CreateReply; pub async fn mute(ctx: Context<'_>) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/music/pause.rs b/src/commands/music/pause.rs index e5a431a..2b2f1bb 100644 --- a/src/commands/music/pause.rs +++ b/src/commands/music/pause.rs @@ -9,7 +9,7 @@ use poise::CreateReply; pub async fn pause(ctx: Context<'_>) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/music/play.rs b/src/commands/music/play.rs index b2f779b..b3093e5 100644 --- a/src/commands/music/play.rs +++ b/src/commands/music/play.rs @@ -1,23 +1,24 @@ use crate::commands::music::metadata::Metadata; +use crate::commands::music::notifier::TrackErrorNotifier; use crate::{commands::embeds::error_embed, Context, Error}; -use fancy_regex::Regex; use lib_spotify_parser; use poise::serenity_prelude::{ Color, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Timestamp, }; use poise::CreateReply; use regex::Regex as Regex_Classic; +use reqwest::Client; +use serenity::all::GuildId; use songbird::events::TrackEvent; use songbird::input::AuxMetadata; use songbird::input::{Compose, YoutubeDl}; -use songbird::tracks::{TrackHandle, TrackQueue}; +use songbird::tracks::TrackQueue; +use songbird::Call; +use std::collections::VecDeque; use std::process::Command; use std::time::Duration; -use crate::commands::music::notifier::TrackErrorNotifier; -use crate::http::HttpKey; - /// Plays a song; \ /// you can search by query or paste an url; \ /// aliases: play, p, enqueue @@ -31,155 +32,151 @@ pub async fn play( ctx: Context<'_>, #[description = "Provide a query or an url"] #[rest] - mut song: String, + song: String, ) -> Result<(), Error> { - let regex_spotify = Regex::new(r"https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-)+)(?:(?=\?)(?:[?&]foo=(\d*)(?=[&#]|$)|(?![?&]foo=)[^#])+)?(?=#|$)").unwrap(); - let regex_youtube = - Regex_Classic::new(r#""url": "(https://www.youtube.com/watch\?v=[A-Za-z0-9]{11})""#) - .unwrap(); - let regex_youtube_playlist = Regex::new( - r"^((?:https?:)\/\/)?((?:www|m)\.)?((?:youtube\.com)).*(youtu.be\/|list=)([^#&?]*).*", - ) - .unwrap(); - let regex_spotify_playlist = Regex::new(r"https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:(album|playlist)\/|\?uri=spotify:playlist:)((\w|-)+)(?:(?=\?)(?:[?&]foo=(\d*)(?=[&#]|$)|(?![?&]foo=)[^#])+)?(?=#|$)").unwrap(); - - let is_playlist = regex_youtube_playlist.is_match(&song).unwrap() - || regex_spotify_playlist.is_match(&song).unwrap(); - let is_spotify = - regex_spotify.is_match(&song).unwrap() || regex_spotify_playlist.is_match(&song).unwrap(); - let is_query = !song.starts_with("http"); - let guild_id = ctx.guild_id().unwrap(); - let channel_id = ctx - .guild() - .unwrap() - .voice_states - .get(&ctx.author().id) - .and_then(|voice_state| voice_state.channel_id); + let http_client = ctx.data().http_client.clone(); + let manager = songbird::get(ctx.serenity_context()).await.unwrap().clone(); - let connect_to = match channel_id { - Some(channel) => channel, - None => { - let msg = "I am not in a voice channel!"; + let mut rest_playlist: VecDeque = VecDeque::new(); + + if manager.get(guild_id).is_none() { + let channel_id = ctx + .guild() + .unwrap() + .voice_states + .get(&ctx.author().id) + .and_then(|voice_state| voice_state.channel_id); + + let connect_to = match channel_id { + Some(channel) => channel, + None => { + let msg = "I am not in a voice channel!"; + ctx.send(CreateReply::default().embed(error_embed(ctx, msg).await.unwrap())) + .await?; + return Ok(()); + } + }; + + if let Ok(handler_lock) = manager.join(guild_id, connect_to).await { + let mut handler = handler_lock.lock().await; + handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier); + + rest_playlist = handle_play(ctx, song, handler, http_client.clone()) + .await + .unwrap(); + } else { + let msg = "Failed to join the voice channel!"; ctx.send(CreateReply::default().embed(error_embed(ctx, msg).await.unwrap())) .await?; - - return Ok(()); } - }; - - let http_client = { - let data = ctx.serenity_context().data.read().await; - data.get::() - .cloned() - .expect("Guaranteed to exist in the typemap.") - }; - - let manager = songbird::get(&ctx.serenity_context()) - .await - .expect("Songbird Voice placed at init") - .clone(); - - if let Ok(handler_lock) = manager.join(guild_id, connect_to).await { - let mut handler = handler_lock.lock().await; - + } else { + let handler = manager.get(guild_id).unwrap(); + let mut handler = handler.lock().await; handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier); - - if is_playlist && is_spotify { - let tracks: Vec = lib_spotify_parser::retrieve_async_url(&song).await.unwrap(); - - for (index, url) in tracks.clone().iter().enumerate() { - if url.is_empty() { - break; - } - let src = YoutubeDl::new_ytdl_like( - "yt-dlp", - http_client.clone(), - format!("ytsearch:{}", url.to_string()), - ); - let aux_metadata = src.clone().aux_metadata().await.unwrap(); - let track = handler.enqueue_input(src.clone().into()).await; - let _ = track - .typemap() - .write() - .await - .insert::(aux_metadata); - - if index == 0 { - let embed = generate_playlist_embed(ctx, track, tracks.len()).await; - let response = CreateReply::default().embed(embed.unwrap()); - ctx.send(response).await?; - } - } - - return Ok(()); - } - - if is_playlist { - let raw_list = Command::new("yt-dlp") - .args(["-j", "--flat-playlist", &song]) - .output() - .expect("failed to execute process") - .stdout; - - let list = String::from_utf8(raw_list.clone()).expect("Invalid UTF-8"); - - let urls: Vec = regex_youtube - .captures_iter(&list) - .map(|capture| capture[1].to_string()) - .collect(); - - for (index, url) in urls.clone().iter().enumerate() { - if url.is_empty() { - break; - } - let src = YoutubeDl::new_ytdl_like("yt-dlp", http_client.clone(), url.to_string()); - let aux_metadata = src.clone().aux_metadata().await.unwrap(); - let track = handler.enqueue_input(src.clone().into()).await; - let _ = track - .typemap() - .write() - .await - .insert::(aux_metadata); - - if index == 0 { - let embed = generate_playlist_embed(ctx, track, urls.len()).await; - let response = CreateReply::default().embed(embed.unwrap()); - ctx.send(response).await?; - } - } - - return Ok(()); - } - - if is_spotify { - song = format!( - "ytsearch:{}", - lib_spotify_parser::retrieve_async_url(&song) - .await - .unwrap() - .first() - .unwrap() - ); - } - - if is_query { - song = format!("ytsearch:{}", song); - } - - let src = YoutubeDl::new_ytdl_like("yt-dlp", http_client, song); - let embed = generate_embed(ctx, src.clone(), handler.queue()).await; - let response = CreateReply::default().embed(embed.unwrap()); - ctx.send(response).await?; - - let aux_metadata = src.clone().aux_metadata().await.unwrap(); - - let track = handler.enqueue_input(src.clone().into()).await; - let _ = track - .typemap() - .write() + rest_playlist = handle_play(ctx, song, handler, http_client.clone()) .await - .insert::(aux_metadata); + .unwrap(); + } + + handle_playlist(rest_playlist, manager, guild_id, http_client) + .await + .unwrap(); + + Ok(()) +} + +async fn handle_play<'a>( + ctx: Context<'a>, + song: String, + mut handler: tokio::sync::MutexGuard<'a, Call>, + http_client: Client, +) -> Result, Error> { + let mut results = parse_data(song).await; + + let src: YoutubeDl = + YoutubeDl::new_ytdl_like("yt-dlp", http_client, results.pop_front().unwrap()); + + ctx.send( + CreateReply::default().embed( + generate_embed(ctx, src.clone(), handler.queue(), results.clone()) + .await + .unwrap(), + ), + ) + .await?; + + let aux_metadata = src.clone().aux_metadata().await.unwrap(); + + handler + .enqueue_input(src.clone().into()) + .await + .typemap() + .write() + .await + .insert::(aux_metadata); + + Ok(results) +} + +async fn parse_data(data: String) -> VecDeque { + let tracks = lib_spotify_parser::retrieve_async_url(&data) + .await + .unwrap_or(vec![data]) + .iter() + .flat_map(|track| { + if track.contains("?list=") { + let regex_youtube = Regex_Classic::new( + r#""url": "(https://www.youtube.com/watch\?v=[A-Za-z0-9]{11})""#, + ) + .unwrap(); + + let list = Command::new("yt-dlp") + .args(["-j", "--flat-playlist", track]) + .output() + .expect("Failed to execute process") + .stdout; + let list = String::from_utf8(list).unwrap(); + + regex_youtube + .captures_iter(&list) + .map(|capture| capture.get(1).unwrap().as_str().to_string()) + .collect::>() + } else if track.starts_with("http") { + vec![track.clone()] + } else { + vec![format!("ytsearch:{}", track)] + } + }) + .collect(); + + tracks +} + +async fn handle_playlist( + playlist: VecDeque, + manager: std::sync::Arc, + guild_id: GuildId, + http_client: Client, +) -> Result<(), Error> { + for song in playlist { + if manager.get(guild_id).is_some() { + let handler = manager.get(guild_id).unwrap(); + let mut handler = handler.lock().await; + + let src: YoutubeDl = + YoutubeDl::new_ytdl_like("yt-dlp", http_client.clone(), song.clone()); + + let aux_metadata = src.clone().aux_metadata().await.unwrap(); + + handler + .enqueue_input(src.clone().into()) + .await + .typemap() + .write() + .await + .insert::(aux_metadata); + } } Ok(()) @@ -189,6 +186,7 @@ async fn generate_embed( ctx: Context<'_>, src: YoutubeDl, queue: &TrackQueue, + results: VecDeque, ) -> Result { let metadata = src.clone().aux_metadata().await.unwrap(); let AuxMetadata { @@ -199,17 +197,21 @@ async fn generate_embed( duration, .. } = metadata; - let timestamp = Timestamp::now(); let duration_minutes = duration.unwrap_or(Duration::new(0, 0)).clone().as_secs() / 60; let duration_seconds = duration.unwrap_or(Duration::new(0, 0)).clone().as_secs() % 60; - let mut description = format!("Song added to queue @ {}", queue.len() + 1); + let mut description = format!("Enqueued @ {}", queue.len() + 1); + let mut tracks = "Tracks enqueued"; - if queue.len() == 0 { - description = format!("Playing now!"); + if results.len() == 1 { + tracks = "Track enqueued"; + } + + if queue.is_empty() { + description = "Playing now!".to_string(); } let embed = CreateEmbed::default() - .author(CreateEmbedAuthor::new("Track enqueued").icon_url(ctx.author().clone().face())) + .author(CreateEmbedAuthor::new(tracks).icon_url(ctx.author().clone().face())) .colour(Color::from_rgb(255, 58, 97)) .title(title.unwrap()) .url(source_url.unwrap()) @@ -226,59 +228,7 @@ async fn generate_embed( ) .field("DJ", ctx.author().name.clone(), true) .description(description) - .timestamp(timestamp) - .footer( - CreateEmbedFooter::new(ctx.cache().current_user().name.to_string()) - .icon_url(ctx.cache().current_user().face()), - ); - - Ok(embed) -} - -async fn generate_playlist_embed( - ctx: Context<'_>, - track: TrackHandle, - queue_length: usize, -) -> Result { - let meta_typemap = track.typemap().read().await; - let metadata = meta_typemap.get::().unwrap(); - let AuxMetadata { - title, - thumbnail, - source_url, - artist, - duration, - .. - } = metadata; - let timestamp = Timestamp::now(); - let duration_minutes = duration.unwrap_or(Duration::new(0, 0)).clone().as_secs() / 60; - let duration_seconds = duration.unwrap_or(Duration::new(0, 0)).clone().as_secs() % 60; - - let description = format!("Enqueued tracks: {}", queue_length - 1); - - let embed = CreateEmbed::default() - .author(CreateEmbedAuthor::new("Playlist enqueued").icon_url(ctx.author().clone().face())) - .colour(Color::from_rgb(255, 58, 97)) - .title(title.as_ref().unwrap()) - .url(source_url.as_ref().unwrap()) - .thumbnail( - thumbnail - .as_ref() - .unwrap_or(&ctx.cache().current_user().face()), - ) - .field( - "Artist", - artist.as_ref().unwrap_or(&"Unknown Artist".to_string()), - true, - ) - .field( - "Duration", - format!("{:02}:{:02}", duration_minutes, duration_seconds), - true, - ) - .field("DJ", ctx.author().name.clone(), true) - .description(description) - .timestamp(timestamp) + .timestamp(Timestamp::now()) .footer( CreateEmbedFooter::new(ctx.cache().current_user().name.to_string()) .icon_url(ctx.cache().current_user().face()), diff --git a/src/commands/music/queue.rs b/src/commands/music/queue.rs index e3ba012..2f5a59f 100644 --- a/src/commands/music/queue.rs +++ b/src/commands/music/queue.rs @@ -16,7 +16,7 @@ const QUEUE_DISPLAY_LENGTH: usize = 10; pub async fn queue(ctx: Context<'_>) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/music/repeat.rs b/src/commands/music/repeat.rs index ee282d2..b6307fd 100644 --- a/src/commands/music/repeat.rs +++ b/src/commands/music/repeat.rs @@ -21,7 +21,7 @@ pub async fn repeat( ) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/music/resume.rs b/src/commands/music/resume.rs index 9d1af58..e2b5020 100644 --- a/src/commands/music/resume.rs +++ b/src/commands/music/resume.rs @@ -9,7 +9,7 @@ use poise::CreateReply; pub async fn resume(ctx: Context<'_>) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/music/seek.rs b/src/commands/music/seek.rs index 1181838..65a30f8 100644 --- a/src/commands/music/seek.rs +++ b/src/commands/music/seek.rs @@ -14,7 +14,7 @@ pub async fn seek( ) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/music/skip.rs b/src/commands/music/skip.rs index bc4f9c6..9540173 100644 --- a/src/commands/music/skip.rs +++ b/src/commands/music/skip.rs @@ -17,7 +17,7 @@ use songbird::{input::AuxMetadata, tracks::TrackHandle}; pub async fn skip(ctx: Context<'_>) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); @@ -30,23 +30,18 @@ pub async fn skip(ctx: Context<'_>) -> Result<(), Error> { let track = track_raw.get(1); let queue_length = queue.len() - 1; - let response; + let mut response = CreateReply::default().embed( + embed(ctx, "Skipped!", "The queue is empty!", "") + .await + .unwrap(), + ); - match track { - Some(track) => { - response = CreateReply::default().embed( - generate_embed(ctx, track.clone(), queue_length) - .await - .unwrap(), - ); - } - None => { - response = CreateReply::default().embed( - embed(ctx, "Skipped!", "The queue is empty!", "") - .await - .unwrap(), - ); - } + if let Some(track) = track { + response = CreateReply::default().embed( + generate_embed(ctx, track.clone(), queue_length) + .await + .unwrap(), + ); }; ctx.send(response).await?; diff --git a/src/commands/music/soundboard/effect.rs b/src/commands/music/soundboard/effect.rs index ab354b0..2159198 100644 --- a/src/commands/music/soundboard/effect.rs +++ b/src/commands/music/soundboard/effect.rs @@ -34,7 +34,7 @@ pub async fn effect( .expect("Guaranteed to exist in the typemap.") }; - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird Voice placed at init") .clone(); @@ -85,7 +85,7 @@ async fn generate_embed(ctx: Context<'_>, src: YoutubeDl) -> Result, src: YoutubeDl) -> Result) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/music/volume.rs b/src/commands/music/volume.rs index 327b0d4..69dbfc1 100644 --- a/src/commands/music/volume.rs +++ b/src/commands/music/volume.rs @@ -9,7 +9,7 @@ use poise::CreateReply; pub async fn volume(ctx: Context<'_>, #[description = "Volume"] volume: f32) -> Result<(), Error> { let guild_id = ctx.guild_id().unwrap(); - let manager = songbird::get(&ctx.serenity_context()) + let manager = songbird::get(ctx.serenity_context()) .await .expect("Songbird client placed at init") .clone(); diff --git a/src/commands/tools/ai.rs b/src/commands/tools/ai.rs index bae52a6..f33c870 100644 --- a/src/commands/tools/ai.rs +++ b/src/commands/tools/ai.rs @@ -15,7 +15,7 @@ pub async fn ai( #[rest] prompt: String, ) -> Result<(), Error> { - let iamsorry = vec![ + let iamsorry = &[ "I'm sorry, but as an AI language model, I must follow ethical guidelines, and I cannot engage in harmful, malicious, or offensive behavior.", "I'm sorry, but as an AI language model, I may not always be perfect and can make mistakes or provide inaccurate information. Please verify important details from reliable sources.", "I'm sorry, but as an AI language model, I can't engage in real-time conversations or remember previous interactions with users.", @@ -27,19 +27,17 @@ pub async fn ai( println!("Funny prompts: {}", prompt); - let response; - - let _ = { + let response = { let mut rng = rand::thread_rng(); - response = rng.gen_range(0..iamsorry.len()); + rng.gen_range(0..iamsorry.len()) }; sleep(Duration::from_secs(1)); ctx.send( CreateReply::default().embed( - embed(ctx, "AI Response:", "", &format!("{}", iamsorry[response])) + embed(ctx, "AI Response:", "", iamsorry[response]) .await .unwrap(), ), diff --git a/src/commands/tools/dice.rs b/src/commands/tools/dice.rs index fb81c45..d7bbb24 100644 --- a/src/commands/tools/dice.rs +++ b/src/commands/tools/dice.rs @@ -7,12 +7,9 @@ use crate::{commands::embeds::embed, Context, Error}; /// Rolls a dice #[poise::command(prefix_command, slash_command, category = "Tools")] pub async fn dice(ctx: Context<'_>) -> Result<(), Error> { - let dice; - - let _ = { + let dice = { let mut rng = rand::thread_rng(); - - dice = rng.gen_range(1..7); + rng.gen_range(1..=6) }; ctx.send( diff --git a/src/main.rs b/src/main.rs index 4205b2d..980062a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,6 @@ use tracing::{error, info, warn}; mod commands; mod http; -use crate::commands::kashi; use crate::commands::music; use crate::commands::tools; use crate::http::HttpKey; @@ -17,7 +16,9 @@ use crate::http::HttpKey; type Error = Box; type Context<'a> = poise::Context<'a, Data, Error>; -pub struct Data; +pub struct Data { + pub http_client: HttpClient, +} async fn on_error(error: poise::FrameworkError<'_, Data, Error>) { match error { @@ -45,7 +46,6 @@ async fn main() { let prefix = std::env::var("PREFIX").expect("Environment variable `PREFIX` not found!"); let commands = vec![ - kashi::kashi(), music::deafen(), music::join(), music::leave(), @@ -141,7 +141,9 @@ async fn main() { ) .await?; - Ok(Data {}) + Ok(Data { + http_client: HttpClient::new(), + }) }) }) .options(options)