From b86e8b9daf098167bdccfc21eb98dde2edeadfae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Czy=C5=BC?= Date: Wed, 21 Feb 2024 12:29:30 +0100 Subject: [PATCH] soundboard and some tools --- Cargo.lock | 101 ++++++++++++++++--- Cargo.toml | 3 +- src/commands/music.rs | 2 - src/commands/music/resume.rs | 1 - src/commands/music/seek.rs | 46 ++++++++- src/commands/music/shuffle.rs | 11 -- src/commands/music/soundboard/effect.rs | 114 ++++++++++++++++++++- src/commands/music/soundboard/stream.rs | 129 ++++++++++++++++++++++-- src/commands/music/volume.rs | 39 ++++++- src/commands/tools/dictionary.rs | 68 ++++++++++++- src/commands/tools/owoify.rs | 10 +- src/main.rs | 1 - 12 files changed, 470 insertions(+), 55 deletions(-) delete mode 100644 src/commands/music/shuffle.rs diff --git a/Cargo.lock b/Cargo.lock index 5f18585..eb52bf1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -336,7 +336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "typenum", ] @@ -682,6 +682,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.12" @@ -691,7 +702,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -965,14 +976,15 @@ dependencies = [ [[package]] name = "lyra" -version = "0.7.0" +version = "0.8.0" dependencies = [ "dotenv", "fancy-regex", "json", "openssl", + "owoify", "poise", - "rand", + "rand 0.8.5", "regex", "reqwest", "serde", @@ -1049,7 +1061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -1059,7 +1071,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom", + "getrandom 0.2.12", ] [[package]] @@ -1230,6 +1242,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owoify" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d5373685d13f2b6080231760ae4e1ca37161f129e1eb2393ea9028ffa49339" +dependencies = [ + "rand 0.7.3", + "regex", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1417,6 +1439,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -1424,8 +1459,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -1435,7 +1480,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -1444,7 +1498,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.12", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -1577,7 +1640,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", - "getrandom", + "getrandom 0.2.12", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -2022,7 +2085,7 @@ dependencies = [ "once_cell", "parking_lot", "pin-project", - "rand", + "rand 0.8.5", "reqwest", "ringbuf", "rubato", @@ -2634,7 +2697,7 @@ dependencies = [ "http", "httparse", "log", - "rand", + "rand 0.8.5", "rustls 0.20.9", "sha1", "thiserror", @@ -2655,7 +2718,7 @@ dependencies = [ "http", "httparse", "log", - "rand", + "rand 0.8.5", "rustls 0.21.10", "sha1", "thiserror", @@ -2671,7 +2734,7 @@ checksum = "30be5c7e2b13b4a59e0f93344c070c23404279a318a324eece1f4384ead47d86" dependencies = [ "bitflags 1.3.2", "futures-util", - "rand", + "rand 0.8.5", "rustls 0.20.9", "rustls-native-certs", "serde", @@ -2823,7 +2886,7 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ - "getrandom", + "getrandom 0.2.12", ] [[package]] @@ -2869,6 +2932,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 4532edb..0732ff2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lyra" -version = "0.7.0" +version = "0.8.0" authors = ["Michał Czyż "] edition = "2021" description = "A featureful Discord bot written in Rust." @@ -16,6 +16,7 @@ dotenv = "0.15.0" fancy-regex = "0.13.0" json = "0.12.4" openssl = { version = "0.10.63", features = ["vendored"] } +owoify = "0.1.5" poise = "0.6.1" rand = "0.8.5" regex = "1.10.3" diff --git a/src/commands/music.rs b/src/commands/music.rs index ac554aa..670b1f0 100644 --- a/src/commands/music.rs +++ b/src/commands/music.rs @@ -10,7 +10,6 @@ pub mod queue; pub mod repeat; pub mod resume; pub mod seek; -pub mod shuffle; pub mod skip; pub mod soundboard; pub mod stop; @@ -26,7 +25,6 @@ pub use queue::queue; pub use repeat::repeat; pub use resume::resume; pub use seek::seek; -pub use shuffle::shuffle; pub use skip::skip; pub use stop::stop; pub use volume::volume; diff --git a/src/commands/music/resume.rs b/src/commands/music/resume.rs index 1fd1be0..9d1af58 100644 --- a/src/commands/music/resume.rs +++ b/src/commands/music/resume.rs @@ -19,7 +19,6 @@ pub async fn resume(ctx: Context<'_>) -> Result<(), Error> { let queue = handler.queue(); let _ = queue.resume(); - ctx.say(format!("Song resumed.")).await?; ctx.send( CreateReply::default().embed( embed(ctx, "Resumed!", "Currently paused song is now resumed!", "") diff --git a/src/commands/music/seek.rs b/src/commands/music/seek.rs index edee2c4..1181838 100644 --- a/src/commands/music/seek.rs +++ b/src/commands/music/seek.rs @@ -1,11 +1,51 @@ -use crate::{commands::embeds::embed, Context, Error}; +use std::time::Duration; + +use crate::{ + commands::embeds::{embed, error_embed}, + Context, Error, +}; use poise::CreateReply; /// Seeks a track by provided seconds #[poise::command(prefix_command, slash_command, category = "Music")] -pub async fn seek(ctx: Context<'_>) -> Result<(), Error> { - ctx.send(CreateReply::default().embed(embed(ctx, "", "", "").await.unwrap())) +pub async fn seek( + ctx: Context<'_>, + #[description = "How many seconds shall I seek"] seek: u64, +) -> Result<(), Error> { + let guild_id = ctx.guild_id().unwrap(); + + let manager = songbird::get(&ctx.serenity_context()) + .await + .expect("Songbird client placed at init") + .clone(); + + if let Some(handler_lock) = manager.get(guild_id) { + let handler = handler_lock.lock().await; + let queue = handler.queue(); + + let seek_duration = Duration::from_secs(seek); + + let track = queue.current().unwrap(); + let _ = track.seek(seek_duration); + + ctx.send( + CreateReply::default().embed( + embed( + ctx, + "Track seeked!", + &format!("Track seeked by: {} seconds", seek), + "", + ) + .await + .unwrap(), + ), + ) .await?; + } else { + let msg = "I am not in a voice channel!"; + ctx.send(CreateReply::default().embed(error_embed(ctx, msg).await.unwrap())) + .await?; + } Ok(()) } diff --git a/src/commands/music/shuffle.rs b/src/commands/music/shuffle.rs deleted file mode 100644 index e17028c..0000000 --- a/src/commands/music/shuffle.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::{commands::embeds::embed, Context, Error}; -use poise::CreateReply; - -/// Shuffles the playlist -#[poise::command(prefix_command, slash_command, category = "Music")] -pub async fn shuffle(ctx: Context<'_>) -> Result<(), Error> { - ctx.send(CreateReply::default().embed(embed(ctx, "", "", "").await.unwrap())) - .await?; - - Ok(()) -} diff --git a/src/commands/music/soundboard/effect.rs b/src/commands/music/soundboard/effect.rs index f0c8ff9..0ffdf95 100644 --- a/src/commands/music/soundboard/effect.rs +++ b/src/commands/music/soundboard/effect.rs @@ -1,11 +1,117 @@ -use crate::{commands::embeds::embed, Context, Error}; +use crate::{commands::embeds::error_embed, Context, Error}; + +use poise::serenity_prelude::model::Timestamp; +use poise::serenity_prelude::Colour; +use poise::serenity_prelude::CreateEmbed; use poise::CreateReply; +use serenity::builder::CreateEmbedAuthor; +use serenity::builder::CreateEmbedFooter; +use songbird::events::TrackEvent; +use songbird::input::AuxMetadata; +use songbird::input::{Compose, YoutubeDl}; +use std::time::Duration; + +use crate::commands::music::notifier::TrackErrorNotifier; +use crate::http::HttpKey; /// Plays one of available audio effects #[poise::command(prefix_command, slash_command, category = "Music")] -pub async fn effect(ctx: Context<'_>) -> Result<(), Error> { - ctx.send(CreateReply::default().embed(embed(ctx, "Playing an effect", "", "").await.unwrap())) - .await?; +pub async fn effect( + ctx: Context<'_>, + #[description = "Shall output pause?"] + #[flag] + pause: bool, + #[description = "Provide a query or an url"] + #[rest] + mut song: String, +) -> Result<(), Error> { + let is_query = !song.starts_with("http"); + + let guild_id = ctx.guild_id().unwrap(); + + 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 pause { + if let Some(handler_lock) = manager.get(guild_id) { + let handler = handler_lock.lock().await; + let queue = handler.queue(); + let _ = queue.pause(); + } + } + + if let Some(handler_lock) = manager.get(guild_id) { + let mut handler = handler_lock.lock().await; + + handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier); + + 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()).await; + let response = CreateReply::default().embed(embed.unwrap()); + ctx.send(response).await?; + + let _ = handler.play_input(src.clone().into()); + } else { + let msg = "I am not in a voice channel!"; + ctx.send(CreateReply::default().embed(error_embed(ctx, msg).await.unwrap())) + .await?; + } Ok(()) } + +async fn generate_embed(ctx: Context<'_>, src: YoutubeDl) -> Result { + let metadata = src.clone().aux_metadata().await.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!("Playing now!"); + + let embed = CreateEmbed::default() + .author(CreateEmbedAuthor::new("Playing an effect!").icon_url(ctx.author().clone().face())) + .colour(Colour::from_rgb(255, 58, 97)) + .title(title.unwrap()) + .url(source_url.unwrap()) + .thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face())) + .field( + "Artist", + artist.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) + .footer( + CreateEmbedFooter::new(ctx.cache().current_user().name.to_string()) + .icon_url(ctx.cache().current_user().face()), + ); + + Ok(embed) +} diff --git a/src/commands/music/soundboard/stream.rs b/src/commands/music/soundboard/stream.rs index f97747c..065e783 100644 --- a/src/commands/music/soundboard/stream.rs +++ b/src/commands/music/soundboard/stream.rs @@ -1,11 +1,126 @@ -use crate::{commands::embeds::embed, Context, Error}; -use poise::CreateReply; +use crate::{commands::embeds::error_embed, Context, Error}; -/// Hijacks current audio output and plays selected audio -#[poise::command(prefix_command, slash_command, aliases("override"), category = "Music")] -pub async fn stream(ctx: Context<'_>) -> Result<(), Error> { - ctx.send(CreateReply::default().embed(embed(ctx, "Playing audio", "", "").await.unwrap())) - .await?; +use poise::serenity_prelude::model::Timestamp; +use poise::serenity_prelude::Colour; +use poise::serenity_prelude::CreateEmbed; +use poise::CreateReply; +use serenity::builder::CreateEmbedAuthor; +use serenity::builder::CreateEmbedFooter; +use songbird::events::TrackEvent; +use songbird::input::AuxMetadata; +use songbird::input::{Compose, YoutubeDl}; +use std::time::Duration; + +use crate::commands::music::notifier::TrackErrorNotifier; +use crate::http::HttpKey; + +/// Hijacks output and plays audio; \ +/// search by query or paste an url; \ +/// aliases: stream, override, hijack +#[poise::command( + prefix_command, + slash_command, + aliases("override", "hijack"), + category = "Music" +)] +pub async fn stream( + ctx: Context<'_>, + #[description = "Shall output pause?"] + #[flag] + pause: bool, + #[description = "Provide a query or an url"] + #[rest] + mut song: String, +) -> Result<(), Error> { + let is_query = !song.starts_with("http"); + + let guild_id = ctx.guild_id().unwrap(); + + 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 pause { + if let Some(handler_lock) = manager.get(guild_id) { + let handler = handler_lock.lock().await; + let queue = handler.queue(); + let _ = queue.pause(); + } + } + + if let Some(handler_lock) = manager.get(guild_id) { + let mut handler = handler_lock.lock().await; + + handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier); + + 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()).await; + let response = CreateReply::default().embed(embed.unwrap()); + ctx.send(response).await?; + + let _ = handler.play_input(src.clone().into()); + } else { + let msg = "I am not in a voice channel!"; + ctx.send(CreateReply::default().embed(error_embed(ctx, msg).await.unwrap())) + .await?; + } Ok(()) } + +async fn generate_embed(ctx: Context<'_>, src: YoutubeDl) -> Result { + let metadata = src.clone().aux_metadata().await.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!("Playing now!"); + + let embed = CreateEmbed::default() + .author( + CreateEmbedAuthor::new("Audio output hijacked!").icon_url(ctx.author().clone().face()), + ) + .colour(Colour::from_rgb(255, 58, 97)) + .title(title.unwrap()) + .url(source_url.unwrap()) + .thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face())) + .field( + "Artist", + artist.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) + .footer( + CreateEmbedFooter::new(ctx.cache().current_user().name.to_string()) + .icon_url(ctx.cache().current_user().face()), + ); + + Ok(embed) +} diff --git a/src/commands/music/volume.rs b/src/commands/music/volume.rs index 77dc48a..327b0d4 100644 --- a/src/commands/music/volume.rs +++ b/src/commands/music/volume.rs @@ -1,11 +1,44 @@ -use crate::{commands::embeds::embed, Context, Error}; +use crate::{ + commands::embeds::{embed, error_embed}, + Context, Error, +}; use poise::CreateReply; /// Changes output volume #[poise::command(prefix_command, slash_command, category = "Music")] -pub async fn volume(ctx: Context<'_>) -> Result<(), Error> { - ctx.send(CreateReply::default().embed(embed(ctx, "", "", "").await.unwrap())) +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()) + .await + .expect("Songbird client placed at init") + .clone(); + + if let Some(handler_lock) = manager.get(guild_id) { + let handler = handler_lock.lock().await; + let queue = handler.queue(); + + let track = queue.current().unwrap(); + let _ = track.set_volume(volume / 100.0); + + ctx.send( + CreateReply::default().embed( + embed( + ctx, + "Volume changed", + "", + &format!("Set volume to {}%", volume), + ) + .await + .unwrap(), + ), + ) .await?; + } else { + let msg = "I am not in a voice channel!"; + ctx.send(CreateReply::default().embed(error_embed(ctx, msg).await.unwrap())) + .await?; + } Ok(()) } diff --git a/src/commands/tools/dictionary.rs b/src/commands/tools/dictionary.rs index aab5b04..28a87fc 100644 --- a/src/commands/tools/dictionary.rs +++ b/src/commands/tools/dictionary.rs @@ -1,12 +1,72 @@ use poise::CreateReply; +use serde::{Deserialize, Serialize}; +use url::form_urlencoded; use crate::{commands::embeds::embed, Context, Error}; /// Explains provided query -#[poise::command(prefix_command, slash_command, category = "Tools")] -pub async fn dictionary(ctx: Context<'_>) -> Result<(), Error> { - ctx.send(CreateReply::default().embed(embed(ctx, "", "", "").await.unwrap())) - .await?; +#[poise::command(prefix_command, slash_command, aliases("dict"), category = "Tools")] +pub async fn dictionary( + ctx: Context<'_>, + #[description = "Word you're looking for"] + #[rest] + word: String, +) -> Result<(), Error> { + let data: String = form_urlencoded::byte_serialize(word.as_bytes()).collect(); + + let client = reqwest::Client::new(); + let response = client + .get(format!( + "https://api.dictionaryapi.dev/api/v2/entries/en/{}", + data + )) + .send() + .await + .unwrap(); + + match response.status() { + reqwest::StatusCode::OK => match response.json::>().await { + Ok(parsed) => { + println!("{:?}", parsed); + + ctx.send(CreateReply::default().embed(embed(ctx, "", "", "").await.unwrap())) + .await?; + } + Err(err) => println!("Something is messed up! {:?}", err), + }, + reqwest::StatusCode::UNAUTHORIZED => { + println!("Unauthorized.. Uoops!!"); + } + error => { + println!("Something went wrong: {:?}", error); + } + } Ok(()) } + +#[derive(Serialize, Deserialize, Debug)] +struct Definition { + definition: String, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +struct Meaning { + partOfSpeech: String, + definitions: Vec, +} + +#[allow(non_snake_case)] +#[derive(Serialize, Deserialize, Debug)] +struct Phonetic { + text: Option, + audio: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +struct Word { + word: String, + phonetics: Vec, + meanings: Vec, +} diff --git a/src/commands/tools/owoify.rs b/src/commands/tools/owoify.rs index e676faa..ad623e1 100644 --- a/src/commands/tools/owoify.rs +++ b/src/commands/tools/owoify.rs @@ -1,11 +1,17 @@ +use owoify::OwOifiable; use poise::CreateReply; use crate::{commands::embeds::embed, Context, Error}; /// Owoifies whatever you want uwu #[poise::command(prefix_command, slash_command, category = "Tools")] -pub async fn owoify(ctx: Context<'_>) -> Result<(), Error> { - ctx.send(CreateReply::default().embed(embed(ctx, "", "", "").await.unwrap())) +pub async fn owoify( + ctx: Context<'_>, + #[description = "Text to owoify w-woify OwO"] + #[rest] + text: String, +) -> Result<(), Error> { + ctx.send(CreateReply::default().embed(embed(ctx, "OwO", &text.owoify(), "").await.unwrap())) .await?; Ok(()) diff --git a/src/main.rs b/src/main.rs index df775bf..35c2cf0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,7 +53,6 @@ async fn main() { music::repeat(), music::resume(), music::seek(), - music::shuffle(), music::skip(), music::stop(), music::volume(),