diff --git a/Cargo.lock b/Cargo.lock index 88f3a0f..b42f702 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -972,7 +972,7 @@ dependencies = [ [[package]] name = "kon" -version = "0.3.1" +version = "0.3.2" dependencies = [ "bb8", "bb8-postgres", @@ -1104,13 +1104,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1155,16 +1156,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" version = "0.32.2" @@ -2179,28 +2170,27 @@ dependencies = [ [[package]] name = "tokio" -version = "1.38.1" +version = "1.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" +checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 8d6b107..f4c2583 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kon" -version = "0.3.1" +version = "0.3.2" edition = "2021" [dependencies] @@ -16,7 +16,7 @@ serde = "1.0.204" serde_json = "1.0.120" sysinfo = "0.30.13" tokenservice-client = { version = "0.3.2", registry = "gitea" } -tokio = { version = "1.38.1", features = ["macros", "signal", "rt-multi-thread"] } +tokio = { version = "1.39.1", features = ["macros", "signal", "rt-multi-thread"] } tokio-postgres = "0.7.11" uptime_lib = "0.3.1" diff --git a/src/commands.rs b/src/commands.rs index 8f6a068..ce603c7 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,4 +1,4 @@ +pub mod midi; pub mod ping; pub mod status; -pub mod midi; pub mod uptime; diff --git a/src/commands/midi.rs b/src/commands/midi.rs index 6dd12b5..57f7766 100644 --- a/src/commands/midi.rs +++ b/src/commands/midi.rs @@ -1,13 +1,19 @@ use crate::{ Error, - internals::http::HttpClient + internals::utils::{ + mention_dev, + format_bytes + } }; use regex::Regex; -use std::fs::{ - write, - read_to_string, - remove_file +use std::{ + os::unix::fs::MetadataExt, + fs::{ + write, + remove_file, + metadata + } }; use poise::{ CreateReply, @@ -20,57 +26,74 @@ pub async fn midi_to_wav( ctx: poise::Context<'_, (), Error>, #[description = "MIDI file to be converted"] message: poise::serenity_prelude::Message ) -> Result<(), Error> { + let re = Regex::new(r"(?i)\.mid$").unwrap(); + + if !message.embeds.is_empty() || message.attachments.is_empty() || !re.is_match(&message.attachments[0].filename) { + ctx.reply("That ain't a MIDI file! What are you even doing??").await?; + return Ok(()); + } + ctx.defer().await?; - let http = HttpClient::new(); - let resp = http.get(&message.attachments[0].url, "MIDI Conversion").await?; - let bytes = resp.bytes().await?; + let bytes = match message.attachments[0].download().await { + Ok(bytes) => bytes, + Err(y) => { + ctx.send(CreateReply::default() + .content(format!( + "Download failed, ask {} to check console for more information!", + mention_dev(ctx).unwrap_or_default() + )) + ) + .await.unwrap(); + + return Err(Error::from(format!("Failed to download the file: {}", y))) + } + }; let midi_path = &message.attachments[0].filename; write(midi_path, bytes)?; - let re = Regex::new(r"(?i)\.mid$").unwrap(); - let wav_path = re.replace(&midi_path, ".wav"); - let alpine_sf2 = include_bytes!("../internals/assets/FluidR3_GM.sf2"); - let sf2_path = if let Ok(os_release) = read_to_string("/etc/os-release") { - if os_release.contains("Alpine") { - let sf2_path = "/tmp/FluidR3_GM.sf2"; - write(sf2_path, alpine_sf2)?; - sf2_path - } else { - "/usr/share/sounds/sf2/FluidR3_GM.sf2" - } - } else { - return Err(Error::from("Couldn't read \"/etc/os-release\" file!")) - }; + let sf2_path = "/tmp/FluidR3_GM.sf2"; + write(sf2_path, include_bytes!("../internals/assets/FluidR3_GM.sf2"))?; let output = std::process::Command::new("fluidsynth") - .args(&[ - "-ni", sf2_path, midi_path, "-F", &wav_path - ]) + .args(&["-ni", sf2_path, midi_path, "-F", &wav_path]) .output(); + // Just to add an info to console to tell what the bot is doing when MIDI file is downloaded. + println!("Discord[{}:{}]: Processing MIDI file: \"{}\"", ctx.guild().unwrap().name, ctx.command().qualified_name, midi_path); + match output { Ok(_) => { - ctx.send(CreateReply::default() + let reply = ctx.send(CreateReply::default() .attachment(CreateAttachment::path(&*wav_path).await.unwrap()) - ).await.expect("Reply failed"); + ).await; - remove_file(midi_path)?; - remove_file(&*wav_path)?; + if reply.is_err() { + println!( + "Discord[{}:{}]: Processed file couldn't be uploaded back to Discord channel due to upload limit", + ctx.guild().unwrap().name, ctx.command().qualified_name + ); + + ctx.send(CreateReply::default() + .content(format!( + "Couldn't upload the processed file (`{}`, `{}`) due to upload limit", + &*wav_path, format_bytes(metadata(&*wav_path).unwrap().size()) + )) + ).await.unwrap(); + } else if reply.is_ok() { + remove_file(midi_path)?; + remove_file(&*wav_path)?; + } }, Err(y) => { ctx.send(CreateReply::default() .content("Command didn't execute successfully, check console for more information!") - ) - .await.unwrap(); + ).await.unwrap(); - return Err(Error::from(format!( - "Midi conversion failed: {}", - y - ))) + return Err(Error::from(format!("Midi conversion failed: {}", y))) } } diff --git a/src/internals/utils.rs b/src/internals/utils.rs index 736151a..ff739e9 100644 --- a/src/internals/utils.rs +++ b/src/internals/utils.rs @@ -1,3 +1,4 @@ +use poise::serenity_prelude::UserId; use once_cell::sync::Lazy; use tokio::sync::Mutex; use tokenservice_client::TokenServiceApi; @@ -23,6 +24,25 @@ pub fn concat_message(messages: Vec) -> String { messages.join("\n") } +pub fn mention_dev(ctx: poise::Context<'_, (), crate::Error>) -> Option { + let devs = super::config::BINARY_PROPERTIES.developers.clone(); + let app_owners = ctx.framework().options().owners.clone(); + + let mut mentions = Vec::new(); + + for dev in devs { + if app_owners.contains(&UserId::new(dev)) { + mentions.push(format!("<@{}>", dev)); + } + } + + if mentions.is_empty() { + None + } else { + Some(mentions.join(", ")) + } +} + pub fn format_duration(secs: u64) -> String { let days = secs / 86400; let hours = (secs % 86400) / 3600; @@ -43,3 +63,24 @@ pub fn format_duration(secs: u64) -> String { formatted_string } + +pub fn format_bytes(bytes: u64) -> String { + let units = ["B", "KB", "MB", "GB", "TB", "PB"]; + let mut value = bytes as f64; + let mut unit = units[0]; + + for &u in &units[1..] { + if value < 1024.0 { + break; + } + + value /= 1024.0; + unit = u; + } + + if unit == "B" { + format!("{}{}", value, unit) + } else { + format!("{:.2}{}", value, unit) + } +} diff --git a/src/main.rs b/src/main.rs index cbd954a..17457a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,10 @@ mod internals; use crate::{ internals::{ - utils::token_path, + utils::{ + token_path, + mention_dev + }, config::BINARY_PROPERTIES }, // controllers::database::DatabaseController @@ -25,7 +28,6 @@ use poise::serenity_prelude::{ ClientBuilder, ChannelId, Command, - UserId, GatewayIntents }; @@ -113,12 +115,8 @@ async fn main() { poise::FrameworkError::Command { error, ctx, .. } => { println!("PoiseCommandError({}): {}", ctx.command().qualified_name, error); ctx.reply(format!( - "Encountered an error during command execution, ask **{}** to check console for more details!", - UserId::new(BINARY_PROPERTIES.developers[0]) - .to_user(&ctx.http()) - .await.expect("Error getting user") - .nick_in(&ctx.http(), BINARY_PROPERTIES.guild_id) - .await.expect("Error getting nickname") + "Encountered an error during command execution, ask {} to check console for more details!", + mention_dev(ctx).unwrap_or_default() )).await.expect("Error sending message"); }, poise::FrameworkError::EventHandler { error, event, .. } => println!("PoiseEventHandlerError({}): {}", event.snake_case_name(), error),