Update midi.rs

This commit is contained in:
toast 2024-07-25 15:39:15 +10:00
parent d9bb501c1e
commit ce4d27f917
6 changed files with 118 additions and 66 deletions

30
Cargo.lock generated
View File

@ -972,7 +972,7 @@ dependencies = [
[[package]] [[package]]
name = "kon" name = "kon"
version = "0.3.1" version = "0.3.2"
dependencies = [ dependencies = [
"bb8", "bb8",
"bb8-postgres", "bb8-postgres",
@ -1104,13 +1104,14 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.11" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
dependencies = [ dependencies = [
"hermit-abi",
"libc", "libc",
"wasi", "wasi",
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -1155,16 +1156,6 @@ dependencies = [
"autocfg", "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]] [[package]]
name = "object" name = "object"
version = "0.32.2" version = "0.32.2"
@ -2179,28 +2170,27 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.38.1" version = "1.39.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
"libc", "libc",
"mio", "mio",
"num_cpus",
"parking_lot", "parking_lot",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "tokio-macros" name = "tokio-macros"
version = "2.3.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "kon" name = "kon"
version = "0.3.1" version = "0.3.2"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
@ -16,7 +16,7 @@ serde = "1.0.204"
serde_json = "1.0.120" serde_json = "1.0.120"
sysinfo = "0.30.13" sysinfo = "0.30.13"
tokenservice-client = { version = "0.3.2", registry = "gitea" } 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" tokio-postgres = "0.7.11"
uptime_lib = "0.3.1" uptime_lib = "0.3.1"

View File

@ -1,4 +1,4 @@
pub mod midi;
pub mod ping; pub mod ping;
pub mod status; pub mod status;
pub mod midi;
pub mod uptime; pub mod uptime;

View File

@ -1,13 +1,19 @@
use crate::{ use crate::{
Error, Error,
internals::http::HttpClient internals::utils::{
mention_dev,
format_bytes
}
}; };
use regex::Regex; use regex::Regex;
use std::fs::{ use std::{
os::unix::fs::MetadataExt,
fs::{
write, write,
read_to_string, remove_file,
remove_file metadata
}
}; };
use poise::{ use poise::{
CreateReply, CreateReply,
@ -20,57 +26,74 @@ pub async fn midi_to_wav(
ctx: poise::Context<'_, (), Error>, ctx: poise::Context<'_, (), Error>,
#[description = "MIDI file to be converted"] message: poise::serenity_prelude::Message #[description = "MIDI file to be converted"] message: poise::serenity_prelude::Message
) -> Result<(), Error> { ) -> 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?; ctx.defer().await?;
let http = HttpClient::new(); let bytes = match message.attachments[0].download().await {
let resp = http.get(&message.attachments[0].url, "MIDI Conversion").await?; Ok(bytes) => bytes,
let bytes = resp.bytes().await?; 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; let midi_path = &message.attachments[0].filename;
write(midi_path, bytes)?; write(midi_path, bytes)?;
let re = Regex::new(r"(?i)\.mid$").unwrap();
let wav_path = re.replace(&midi_path, ".wav"); 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"; let sf2_path = "/tmp/FluidR3_GM.sf2";
write(sf2_path, alpine_sf2)?; write(sf2_path, include_bytes!("../internals/assets/FluidR3_GM.sf2"))?;
sf2_path
} else {
"/usr/share/sounds/sf2/FluidR3_GM.sf2"
}
} else {
return Err(Error::from("Couldn't read \"/etc/os-release\" file!"))
};
let output = std::process::Command::new("fluidsynth") let output = std::process::Command::new("fluidsynth")
.args(&[ .args(&["-ni", sf2_path, midi_path, "-F", &wav_path])
"-ni", sf2_path, midi_path, "-F", &wav_path
])
.output(); .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 { match output {
Ok(_) => { Ok(_) => {
ctx.send(CreateReply::default() let reply = ctx.send(CreateReply::default()
.attachment(CreateAttachment::path(&*wav_path).await.unwrap()) .attachment(CreateAttachment::path(&*wav_path).await.unwrap())
).await.expect("Reply failed"); ).await;
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(midi_path)?;
remove_file(&*wav_path)?; remove_file(&*wav_path)?;
}
}, },
Err(y) => { Err(y) => {
ctx.send(CreateReply::default() ctx.send(CreateReply::default()
.content("Command didn't execute successfully, check console for more information!") .content("Command didn't execute successfully, check console for more information!")
) ).await.unwrap();
.await.unwrap();
return Err(Error::from(format!( return Err(Error::from(format!("Midi conversion failed: {}", y)))
"Midi conversion failed: {}",
y
)))
} }
} }

View File

@ -1,3 +1,4 @@
use poise::serenity_prelude::UserId;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tokenservice_client::TokenServiceApi; use tokenservice_client::TokenServiceApi;
@ -23,6 +24,25 @@ pub fn concat_message(messages: Vec<String>) -> String {
messages.join("\n") messages.join("\n")
} }
pub fn mention_dev(ctx: poise::Context<'_, (), crate::Error>) -> Option<String> {
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 { pub fn format_duration(secs: u64) -> String {
let days = secs / 86400; let days = secs / 86400;
let hours = (secs % 86400) / 3600; let hours = (secs % 86400) / 3600;
@ -43,3 +63,24 @@ pub fn format_duration(secs: u64) -> String {
formatted_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)
}
}

View File

@ -6,7 +6,10 @@ mod internals;
use crate::{ use crate::{
internals::{ internals::{
utils::token_path, utils::{
token_path,
mention_dev
},
config::BINARY_PROPERTIES config::BINARY_PROPERTIES
}, },
// controllers::database::DatabaseController // controllers::database::DatabaseController
@ -25,7 +28,6 @@ use poise::serenity_prelude::{
ClientBuilder, ClientBuilder,
ChannelId, ChannelId,
Command, Command,
UserId,
GatewayIntents GatewayIntents
}; };
@ -113,12 +115,8 @@ async fn main() {
poise::FrameworkError::Command { error, ctx, .. } => { poise::FrameworkError::Command { error, ctx, .. } => {
println!("PoiseCommandError({}): {}", ctx.command().qualified_name, error); println!("PoiseCommandError({}): {}", ctx.command().qualified_name, error);
ctx.reply(format!( ctx.reply(format!(
"Encountered an error during command execution, ask **{}** to check console for more details!", "Encountered an error during command execution, ask {} to check console for more details!",
UserId::new(BINARY_PROPERTIES.developers[0]) mention_dev(ctx).unwrap_or_default()
.to_user(&ctx.http())
.await.expect("Error getting user")
.nick_in(&ctx.http(), BINARY_PROPERTIES.guild_id)
.await.expect("Error getting nickname")
)).await.expect("Error sending message"); )).await.expect("Error sending message");
}, },
poise::FrameworkError::EventHandler { error, event, .. } => println!("PoiseEventHandlerError({}): {}", event.snake_case_name(), error), poise::FrameworkError::EventHandler { error, event, .. } => println!("PoiseEventHandlerError({}): {}", event.snake_case_name(), error),