Repository restructure
All checks were successful
Build and push Docker image / build (push) Successful in 3m24s
Build and push Docker image / deploy (push) Successful in 1m5s

This commit is contained in:
toast 2024-12-07 01:33:55 +11:00
parent 27857339be
commit 5417dcaa1d
39 changed files with 436 additions and 269 deletions

View File

@ -7,3 +7,5 @@ docker-compose.yml
Dockerfile Dockerfile
renovate.json renovate.json
run.sh run.sh
target/debug/
target/release/

2
.gitattributes vendored
View File

@ -1 +1 @@
src/internals/assets/FluidR3_GM.sf2 filter=lfs diff=lfs merge=lfs -text libs/assets/FluidR3_GM.sf2 filter=lfs diff=lfs merge=lfs -text

128
Cargo.lock generated
View File

@ -207,9 +207,9 @@ dependencies = [
[[package]] [[package]]
name = "cargo_toml" name = "cargo_toml"
version = "0.20.5" version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88da5a13c620b4ca0078845707ea9c3faf11edbc3ffd8497d11d686211cd1ac0" checksum = "5fbd1fe9db3ebf71b89060adaf7b0504c2d6a425cf061313099547e382c2e472"
dependencies = [ dependencies = [
"serde", "serde",
"toml", "toml",
@ -698,7 +698,7 @@ dependencies = [
"fnv", "fnv",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http 1.1.0", "http 1.2.0",
"indexmap", "indexmap",
"slab", "slab",
"tokio", "tokio",
@ -748,9 +748,9 @@ dependencies = [
[[package]] [[package]]
name = "http" name = "http"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -775,7 +775,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [ dependencies = [
"bytes", "bytes",
"http 1.1.0", "http 1.2.0",
] ]
[[package]] [[package]]
@ -786,7 +786,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-util", "futures-util",
"http 1.1.0", "http 1.2.0",
"http-body 1.0.1", "http-body 1.0.1",
"pin-project-lite", "pin-project-lite",
] ]
@ -837,7 +837,7 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"h2 0.4.7", "h2 0.4.7",
"http 1.1.0", "http 1.2.0",
"http-body 1.0.1", "http-body 1.0.1",
"httparse", "httparse",
"itoa", "itoa",
@ -868,13 +868,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [ dependencies = [
"futures-util", "futures-util",
"http 1.1.0", "http 1.2.0",
"hyper 1.5.1", "hyper 1.5.1",
"hyper-util", "hyper-util",
"rustls 0.23.19", "rustls 0.23.19",
"rustls-pki-types", "rustls-pki-types",
"tokio", "tokio",
"tokio-rustls 0.26.0", "tokio-rustls 0.26.1",
"tower-service", "tower-service",
] ]
@ -903,7 +903,7 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"http 1.1.0", "http 1.2.0",
"http-body 1.0.1", "http-body 1.0.1",
"hyper 1.5.1", "hyper 1.5.1",
"pin-project-lite", "pin-project-lite",
@ -1119,6 +1119,15 @@ version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.14" version = "1.0.14"
@ -1137,24 +1146,73 @@ dependencies = [
[[package]] [[package]]
name = "kon" name = "kon"
version = "0.5.1" version = "0.6.0"
dependencies = [ dependencies = [
"bb8", "kon_cmds",
"bb8-redis", "kon_libs",
"cargo_toml", "kon_tasks",
"feed-rs", "kon_tokens",
"once_cell", "poise",
"tokio",
]
[[package]]
name = "kon_cmds"
version = "0.1.0"
dependencies = [
"kon_libs",
"kon_tokens",
"poise", "poise",
"regex", "regex",
"reqwest 0.12.9", "reqwest 0.12.9",
"serde", "serde",
"serde_json", "serde_json",
"sysinfo", "sysinfo",
"tokenservice-client",
"tokio", "tokio",
"uptime_lib", "uptime_lib",
] ]
[[package]]
name = "kon_libs"
version = "0.1.0"
dependencies = [
"cargo_toml",
"poise",
"reqwest 0.12.9",
]
[[package]]
name = "kon_repo"
version = "0.1.0"
dependencies = [
"bb8",
"bb8-redis",
"kon_tokens",
"tokio",
]
[[package]]
name = "kon_tasks"
version = "0.1.0"
dependencies = [
"feed-rs",
"kon_libs",
"kon_repo",
"once_cell",
"poise",
"regex",
"reqwest 0.12.9",
"tokio",
]
[[package]]
name = "kon_tokens"
version = "0.1.0"
dependencies = [
"tokenservice-client",
"tokio",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.167" version = "0.2.167"
@ -1593,15 +1651,16 @@ dependencies = [
[[package]] [[package]]
name = "redis" name = "redis"
version = "0.27.5" version = "0.27.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cccf17a692ce51b86564334614d72dcae1def0fd5ecebc9f02956da74352b5" checksum = "09d8f99a4090c89cc489a94833c901ead69bfbf3877b4867d5482e321ee875bc"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"async-trait", "async-trait",
"bytes", "bytes",
"combine", "combine",
"futures-util", "futures-util",
"itertools",
"itoa", "itoa",
"num-bigint", "num-bigint",
"percent-encoding", "percent-encoding",
@ -1706,7 +1765,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2 0.4.7", "h2 0.4.7",
"http 1.1.0", "http 1.2.0",
"http-body 1.0.1", "http-body 1.0.1",
"http-body-util", "http-body-util",
"hyper 1.5.1", "hyper 1.5.1",
@ -2188,9 +2247,9 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.32.1" version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af" checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46"
dependencies = [ dependencies = [
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
@ -2283,9 +2342,9 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.36" version = "0.3.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
dependencies = [ dependencies = [
"deranged", "deranged",
"itoa", "itoa",
@ -2304,9 +2363,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.18" version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
dependencies = [ dependencies = [
"num-conv", "num-conv",
"time-core", "time-core",
@ -2352,9 +2411,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.41.1" version = "1.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@ -2412,12 +2471,11 @@ dependencies = [
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.0" version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
dependencies = [ dependencies = [
"rustls 0.23.19", "rustls 0.23.19",
"rustls-pki-types",
"tokio", "tokio",
] ]
@ -2439,9 +2497,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.12" version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
@ -2595,7 +2653,7 @@ dependencies = [
"byteorder", "byteorder",
"bytes", "bytes",
"data-encoding", "data-encoding",
"http 1.1.0", "http 1.2.0",
"httparse", "httparse",
"log", "log",
"rand", "rand",

View File

@ -1,12 +1,15 @@
[package] [package]
name = "kon" name = "kon"
version = "0.5.1" version = "0.6.0"
edition = "2024" edition = "2024"
[dependencies] [workspace]
members = ["cmds", "libs", "repo", "tasks", "tokens"]
[workspace.dependencies]
bb8 = "0.8.6" bb8 = "0.8.6"
bb8-redis = "0.17.0" bb8-redis = "0.17.0"
cargo_toml = "0.20.5" cargo_toml = "0.21.0"
feed-rs = "2.2.0" feed-rs = "2.2.0"
once_cell = "1.20.2" once_cell = "1.20.2"
poise = "0.6.1" poise = "0.6.1"
@ -14,16 +17,26 @@ regex = "1.11.1"
reqwest = { version = "0.12.9", features = ["json", "native-tls-vendored"] } reqwest = { version = "0.12.9", features = ["json", "native-tls-vendored"] }
serde = "1.0.215" serde = "1.0.215"
serde_json = "1.0.133" serde_json = "1.0.133"
sysinfo = "0.32.1" sysinfo = "0.33.0"
tokenservice-client = { version = "0.4.1", registry = "gitea" } tokio = { version = "1.42.0", features = ["macros", "signal", "rt-multi-thread"] }
tokio = { version = "1.41.1", features = ["macros", "signal", "rt-multi-thread"] }
uptime_lib = "0.3.1" uptime_lib = "0.3.1"
kon_libs = { path = "libs" }
kon_tokens = { path = "tokens" }
kon_repo = { path = "repo" }
[dependencies]
kon_cmds = { path = "cmds" }
kon_libs = { workspace = true }
kon_tasks = { path = "tasks" }
kon_tokens = { workspace = true }
poise = { workspace = true }
tokio = { workspace = true }
[patch.crates-io] [patch.crates-io]
poise = { git = "https://github.com/serenity-rs/poise", branch = "next" } poise = { git = "https://github.com/serenity-rs/poise", branch = "next" }
[features] [features]
production = [] production = ["kon_libs/production", "kon_tasks/production"]
[[bin]] [[bin]]
name = "kon" name = "kon"

View File

@ -2,7 +2,7 @@ FROM scratch AS base
WORKDIR /builder WORKDIR /builder
COPY . . COPY . .
FROM alpine:3.20 FROM alpine:3.21
LABEL org.opencontainers.image.source="https://git.toast-server.net/toast/Kon" LABEL org.opencontainers.image.source="https://git.toast-server.net/toast/Kon"
RUN apk add --no-cache libgcc fluidsynth RUN apk add --no-cache libgcc fluidsynth
WORKDIR /kon WORKDIR /kon

16
cmds/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "kon_cmds"
version = "0.1.0"
edition = "2024"
[dependencies]
kon_libs = { workspace = true }
kon_tokens = { workspace = true }
poise = { workspace = true }
regex = { workspace = true }
reqwest = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sysinfo = { workspace = true }
tokio = { workspace = true }
uptime_lib = { workspace = true }

42
cmds/src/dispatch.rs Normal file
View File

@ -0,0 +1,42 @@
use kon_libs::{
KonData,
KonError,
KonResult,
PoiseCtx
};
use poise::Command;
mod ilo;
mod midi;
mod status;
mod uptime;
use {
ilo::ilo,
midi::midi_to_wav,
status::status,
uptime::uptime
};
macro_rules! commands {
($($cmd:ident),*) => {
vec![$($cmd()),*]
}
}
pub fn register_cmds() -> Vec<Command<KonData, KonError>> { commands!(deploy, ping, midi_to_wav, status, ilo, uptime) }
/// Deploy the commands globally or in a guild
#[poise::command(prefix_command, owners_only, guild_only)]
pub async fn deploy(ctx: PoiseCtx<'_>) -> KonResult<()> {
poise::builtins::register_application_commands_buttons(ctx).await?;
Ok(())
}
/// Check if the bot is alive
#[poise::command(slash_command)]
pub async fn ping(ctx: PoiseCtx<'_>) -> KonResult<()> {
ctx.reply(format!("Powong! `{:.2?}`", ctx.ping().await)).await?;
Ok(())
}

View File

@ -1,12 +1,9 @@
use crate::{
Error,
internals::{
config::BINARY_PROPERTIES,
utils::token_path
}
};
use { use {
kon_libs::{
BINARY_PROPERTIES,
KonResult
},
kon_tokens::token_path,
poise::{ poise::{
CreateReply, CreateReply,
serenity_prelude::{ serenity_prelude::{
@ -190,11 +187,11 @@ async fn ilo_data(endpoint: RedfishEndpoint) -> Result<Box<dyn std::any::Any + S
interaction_context = "Guild|BotDm|PrivateChannel", interaction_context = "Guild|BotDm|PrivateChannel",
subcommands("temperature", "power", "system") subcommands("temperature", "power", "system")
)] )]
pub async fn ilo(_: super::PoiseCtx<'_>) -> Result<(), Error> { Ok(()) } pub async fn ilo(_: super::PoiseCtx<'_>) -> KonResult<()> { Ok(()) }
/// Retrieve the server's temperature data /// Retrieve the server's temperature data
#[poise::command(slash_command)] #[poise::command(slash_command)]
pub async fn temperature(ctx: super::PoiseCtx<'_>) -> Result<(), Error> { pub async fn temperature(ctx: super::PoiseCtx<'_>) -> KonResult<()> {
ctx.defer().await?; ctx.defer().await?;
let ilo = ilo_data(RedfishEndpoint::Thermal).await.unwrap(); let ilo = ilo_data(RedfishEndpoint::Thermal).await.unwrap();
let data = ilo.downcast_ref::<Chassis>().unwrap(); let data = ilo.downcast_ref::<Chassis>().unwrap();
@ -242,7 +239,7 @@ pub async fn temperature(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
/// Retrieve the server's power data /// Retrieve the server's power data
#[poise::command(slash_command)] #[poise::command(slash_command)]
pub async fn power(ctx: super::PoiseCtx<'_>) -> Result<(), Error> { pub async fn power(ctx: super::PoiseCtx<'_>) -> KonResult<()> {
ctx.defer().await?; ctx.defer().await?;
let ilo = ilo_data(RedfishEndpoint::Power).await.unwrap(); let ilo = ilo_data(RedfishEndpoint::Power).await.unwrap();
let data = ilo.downcast_ref::<Power>().unwrap(); let data = ilo.downcast_ref::<Power>().unwrap();
@ -272,7 +269,7 @@ pub async fn power(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
/// Retrieve the server's system data /// Retrieve the server's system data
#[poise::command(slash_command)] #[poise::command(slash_command)]
pub async fn system(ctx: super::PoiseCtx<'_>) -> Result<(), Error> { pub async fn system(ctx: super::PoiseCtx<'_>) -> KonResult<()> {
ctx.defer().await?; ctx.defer().await?;
let (ilo_sys, ilo_event) = tokio::join!(ilo_data(RedfishEndpoint::System), ilo_data(RedfishEndpoint::EventService)); let (ilo_sys, ilo_event) = tokio::join!(ilo_data(RedfishEndpoint::System), ilo_data(RedfishEndpoint::EventService));

View File

@ -1,12 +1,10 @@
use crate::{ use {
Error, kon_libs::{
internals::utils::{ KonError,
KonResult,
format_bytes, format_bytes,
mention_dev mention_dev
} },
};
use {
poise::{ poise::{
CreateReply, CreateReply,
serenity_prelude::CreateAttachment serenity_prelude::CreateAttachment
@ -31,7 +29,7 @@ use {
pub async fn midi_to_wav( pub async fn midi_to_wav(
ctx: super::PoiseCtx<'_>, ctx: super::PoiseCtx<'_>,
#[description = "MIDI file to be converted"] message: poise::serenity_prelude::Message #[description = "MIDI file to be converted"] message: poise::serenity_prelude::Message
) -> Result<(), Error> { ) -> KonResult<()> {
let re = Regex::new(r"(?i)\.mid$").unwrap(); let re = Regex::new(r"(?i)\.mid$").unwrap();
if !message.embeds.is_empty() || message.attachments.is_empty() || !re.is_match(&message.attachments[0].filename) { if !message.embeds.is_empty() || message.attachments.is_empty() || !re.is_match(&message.attachments[0].filename) {
@ -52,7 +50,7 @@ pub async fn midi_to_wav(
.await .await
.unwrap(); .unwrap();
return Err(Error::from(format!("Failed to download the file: {y}"))) return Err(KonError::from(format!("Failed to download the file: {y}")))
} }
}; };
@ -62,14 +60,14 @@ pub async fn midi_to_wav(
let wav_path = re.replace(midi_path, ".wav"); let wav_path = re.replace(midi_path, ".wav");
let sf2_path = "/tmp/FluidR3_GM.sf2"; let sf2_path = "/tmp/FluidR3_GM.sf2";
write(sf2_path, include_bytes!("../internals/assets/FluidR3_GM.sf2"))?; write(sf2_path, include_bytes!("../../../libs/assets/FluidR3_GM.sf2"))?;
let output = std::process::Command::new("fluidsynth") 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(); .output();
// Just to add an info to console to tell what the bot is doing when MIDI file is downloaded. // 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.command().qualified_name, midi_path); println!("Discord[{}]: Processing MIDI file: \"{midi_path}\"", ctx.command().qualified_name);
match output { match output {
Ok(_) => { Ok(_) => {
@ -102,7 +100,7 @@ pub async fn midi_to_wav(
.await .await
.unwrap(); .unwrap();
return Err(Error::from(format!("Midi conversion failed: {y}"))) return Err(KonError::from(format!("Midi conversion failed: {y}")))
} }
} }

View File

@ -1,13 +1,5 @@
use crate::{
Error,
internals::{
config::BINARY_PROPERTIES,
http::HttpClient,
utils::token_path
}
};
use { use {
kon_tokens::token_path,
poise::{ poise::{
CreateReply, CreateReply,
serenity_prelude::builder::CreateEmbed serenity_prelude::builder::CreateEmbed
@ -17,7 +9,13 @@ use {
tokio::join tokio::join
}; };
async fn pms_serverstatus(url: &str) -> Result<Vec<(String, Vec<Value>)>, Error> { use kon_libs::{
BINARY_PROPERTIES,
HttpClient,
KonResult
};
async fn pms_serverstatus(url: &str) -> KonResult<Vec<(String, Vec<Value>)>> {
let client = HttpClient::new(); let client = HttpClient::new();
let req = client.get(url, "PMS-Status").await?; let req = client.get(url, "PMS-Status").await?;
@ -66,7 +64,7 @@ fn process_pms_statuses(servers: Vec<(String, Vec<Value>)>) -> Vec<(String, Stri
for (title, servers) in server_map { for (title, servers) in server_map {
let servers_str = servers let servers_str = servers
.iter() .iter()
.map(|(name, status)| format!("{}: {}", name, status)) .map(|(name, status)| format!("{name}: {status}"))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n"); .join("\n");
statuses.push((title, servers_str, true)); statuses.push((title, servers_str, true));
@ -76,11 +74,11 @@ fn process_pms_statuses(servers: Vec<(String, Vec<Value>)>) -> Vec<(String, Stri
/// Query the server statuses /// Query the server statuses
#[poise::command(slash_command, subcommands("wg"))] #[poise::command(slash_command, subcommands("wg"))]
pub async fn status(_: super::PoiseCtx<'_>) -> Result<(), Error> { Ok(()) } pub async fn status(_: super::PoiseCtx<'_>) -> KonResult<()> { Ok(()) }
/// Retrieve the server statuses from Wargaming /// Retrieve the server statuses from Wargaming
#[poise::command(slash_command)] #[poise::command(slash_command)]
pub async fn wg(ctx: super::PoiseCtx<'_>) -> Result<(), Error> { pub async fn wg(ctx: super::PoiseCtx<'_>) -> KonResult<()> {
let pms_asia = token_path().await.wg_pms; let pms_asia = token_path().await.wg_pms;
let pms_eu = pms_asia.replace("asia", "eu"); let pms_eu = pms_asia.replace("asia", "eu");
let embed = CreateEmbed::new().color(BINARY_PROPERTIES.embed_color); let embed = CreateEmbed::new().color(BINARY_PROPERTIES.embed_color);

View File

@ -1,14 +1,14 @@
use crate::{ use kon_libs::{
Error,
GIT_COMMIT_BRANCH, GIT_COMMIT_BRANCH,
GIT_COMMIT_HASH, GIT_COMMIT_HASH
internals::utils::{
BOT_VERSION,
format_duration
}
}; };
use { use {
kon_libs::{
BOT_VERSION,
KonResult,
format_duration
},
std::{ std::{
fs::File, fs::File,
io::{ io::{
@ -47,8 +47,8 @@ fn get_os_info() -> String {
/// Retrieve host and bot uptimes /// Retrieve host and bot uptimes
#[poise::command(slash_command)] #[poise::command(slash_command)]
pub async fn uptime(ctx: super::PoiseCtx<'_>) -> Result<(), Error> { pub async fn uptime(ctx: super::PoiseCtx<'_>) -> KonResult<()> {
let _bot = ctx.http().get_current_user().await.unwrap(); let bot = ctx.http().get_current_user().await.unwrap();
let mut sys = System::new_all(); let mut sys = System::new_all();
sys.refresh_all(); sys.refresh_all();
@ -68,7 +68,7 @@ pub async fn uptime(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
} }
let stat_msg = [ let stat_msg = [
format!("**{} {}** `{GIT_COMMIT_HASH}:{GIT_COMMIT_BRANCH}`", _bot.name, BOT_VERSION.as_str()), format!("**{} {}** `{GIT_COMMIT_HASH}:{GIT_COMMIT_BRANCH}`", bot.name, BOT_VERSION.as_str()),
format!(">>> System: `{}`", format_duration(sys_uptime)), format!(">>> System: `{}`", format_duration(sys_uptime)),
format!("Process: `{}`", format_duration(proc_uptime)), format!("Process: `{}`", format_duration(proc_uptime)),
format!("CPU: `{}`", cpu[0].brand()), format!("CPU: `{}`", cpu[0].brand()),

2
cmds/src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
mod dispatch;
pub use dispatch::*;

12
libs/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "kon_libs"
version = "0.1.0"
edition = "2024"
[dependencies]
cargo_toml = { workspace = true }
poise = { workspace = true }
reqwest = { workspace = true }
[features]
production = []

1
libs/src/data.rs Normal file
View File

@ -0,0 +1 @@
pub struct KonData {}

View File

@ -11,6 +11,10 @@ const ERROR_PREFIX: &str = "HTTPClient[Error]:";
pub struct HttpClient(Client); pub struct HttpClient(Client);
impl Default for HttpClient {
fn default() -> Self { Self::new() }
}
impl HttpClient { impl HttpClient {
pub fn new() -> Self { Self(Client::new()) } pub fn new() -> Self { Self(Client::new()) }
@ -24,7 +28,7 @@ impl HttpClient {
.get(url) .get(url)
.header( .header(
reqwest::header::USER_AGENT, reqwest::header::USER_AGENT,
format!("Kon ({}-{}) - {ua}/reqwest", super::utils::BOT_VERSION.as_str(), crate::GIT_COMMIT_HASH) format!("Kon ({}-{}) - {ua}/reqwest", crate::BOT_VERSION.as_str(), crate::GIT_COMMIT_HASH)
) )
.timeout(Duration::from_secs(30)) .timeout(Duration::from_secs(30))
.send() .send()

View File

@ -1,27 +1,39 @@
mod config;
pub use config::BINARY_PROPERTIES;
mod types;
pub use types::*;
mod data;
pub use data::KonData;
mod http;
pub use http::HttpClient;
use { use {
super::tsclient::TSClient, cargo_toml::Manifest,
poise::serenity_prelude::UserId, poise::serenity_prelude::UserId,
std::sync::LazyLock, std::sync::LazyLock
tokenservice_client::TokenServiceApi,
tokio::sync::Mutex
}; };
#[cfg(feature = "production")]
pub static GIT_COMMIT_HASH: &str = env!("GIT_COMMIT_HASH");
pub static GIT_COMMIT_BRANCH: &str = env!("GIT_COMMIT_BRANCH");
#[cfg(not(feature = "production"))]
pub static GIT_COMMIT_HASH: &str = "devel";
pub static BOT_VERSION: LazyLock<String> = LazyLock::new(|| { pub static BOT_VERSION: LazyLock<String> = LazyLock::new(|| {
let cargo_version = cargo_toml::Manifest::from_str(include_str!("../../Cargo.toml")) Manifest::from_str(include_str!("../../Cargo.toml"))
.unwrap() .unwrap()
.package .package
.unwrap() .unwrap()
.version .version
.unwrap(); .unwrap()
format!("v{cargo_version}")
}); });
static TSCLIENT: LazyLock<Mutex<TSClient>> = LazyLock::new(|| Mutex::new(TSClient::new())); pub fn mention_dev(ctx: PoiseCtx<'_>) -> Option<String> {
let devs = BINARY_PROPERTIES.developers.clone();
pub async fn token_path() -> TokenServiceApi { TSCLIENT.lock().await.get().await.unwrap() }
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 app_owners = ctx.framework().options().owners.clone();
let mut mentions = Vec::new(); let mut mentions = Vec::new();
@ -57,7 +69,7 @@ pub fn format_duration(secs: u64) -> String {
} }
pub fn format_bytes(bytes: u64) -> String { pub fn format_bytes(bytes: u64) -> String {
let units = ["B", "KB", "MB", "GB", "TB", "PB"]; let units = ["B", "KB", "MB", "GB"];
let mut value = bytes as f64; let mut value = bytes as f64;
let mut unit = units[0]; let mut unit = units[0];

9
libs/src/types.rs Normal file
View File

@ -0,0 +1,9 @@
use {
super::KonData,
std::error::Error
};
pub type KonError = Box<dyn Error + Send + Sync>;
pub type KonResult<T> = Result<T, KonError>;
pub type PoiseCtx<'a> = poise::Context<'a, KonData, KonError>;
pub type PoiseFwCtx<'a> = poise::FrameworkContext<'a, KonData, KonError>;

10
repo/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "kon_repo"
version = "0.1.0"
edition = "2024"
[dependencies]
bb8 = { workspace = true }
bb8-redis = { workspace = true }
kon_tokens = { workspace = true }
tokio = { workspace = true }

View File

@ -1,4 +1,4 @@
use crate::internals::utils::token_path; use kon_tokens::token_path;
use { use {
bb8_redis::{ bb8_redis::{

2
repo/src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
mod cache;
pub use cache::RedisController;

View File

@ -1,16 +0,0 @@
use crate::Error;
pub mod ilo;
pub mod midi;
pub mod ping;
pub mod status;
pub mod uptime;
type PoiseCtx<'a> = poise::Context<'a, (), Error>;
/// Deploy the commands globally or in a guild
#[poise::command(prefix_command, owners_only, guild_only)]
pub async fn deploy(ctx: PoiseCtx<'_>) -> Result<(), Error> {
poise::builtins::register_application_commands_buttons(ctx).await?;
Ok(())
}

View File

@ -1,8 +0,0 @@
use crate::Error;
/// Check if the bot is alive
#[poise::command(slash_command)]
pub async fn ping(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
ctx.reply(format!("Powong! `{:?}`", ctx.ping().await)).await?;
Ok(())
}

View File

@ -1 +0,0 @@
pub mod cache;

View File

@ -1,5 +0,0 @@
pub mod config;
pub mod http;
pub mod tasks;
pub mod tsclient;
pub mod utils;

View File

@ -1,21 +0,0 @@
use tokenservice_client::{
TokenService,
TokenServiceApi
};
pub struct TSClient(TokenService);
impl TSClient {
pub fn new() -> Self {
let args: Vec<String> = std::env::args().collect();
let service = if args.len() > 1 { &args[1] } else { "kon" };
Self(TokenService::new(service))
}
pub async fn get(&self) -> Result<TokenServiceApi, crate::Error> {
match self.0.connect().await {
Ok(api) => Ok(api),
Err(e) => Err(e)
}
}
}

View File

@ -1,23 +1,23 @@
mod commands;
mod controllers;
mod internals;
// https://cdn.toast-server.net/RustFSHiearchy.png // https://cdn.toast-server.net/RustFSHiearchy.png
// Using the new filesystem hierarchy // Using the new filesystem hierarchy
use crate::internals::{ use {
config::BINARY_PROPERTIES, kon_cmds::register_cmds,
tasks::{ kon_libs::{
BINARY_PROPERTIES,
BOT_VERSION,
GIT_COMMIT_BRANCH,
GIT_COMMIT_HASH,
KonData,
KonResult,
PoiseFwCtx,
mention_dev
},
kon_tasks::{
rss, rss,
run_task run_task
}, },
utils::{ kon_tokens::token_path,
BOT_VERSION,
mention_dev,
token_path
}
};
use {
poise::serenity_prelude::{ poise::serenity_prelude::{
ChannelId, ChannelId,
ClientBuilder, ClientBuilder,
@ -38,20 +38,10 @@ use {
} }
}; };
type Error = Box<dyn std::error::Error + Send + Sync>;
#[cfg(feature = "production")]
pub static GIT_COMMIT_HASH: &str = env!("GIT_COMMIT_HASH");
pub static GIT_COMMIT_BRANCH: &str = env!("GIT_COMMIT_BRANCH");
#[cfg(not(feature = "production"))]
pub static GIT_COMMIT_HASH: &str = "devel";
async fn on_ready( async fn on_ready(
ctx: &Context, ctx: &Context,
ready: &Ready, ready: &Ready
_framework: &poise::Framework<(), Error> ) -> KonResult<KonData> {
) -> Result<(), Error> {
#[cfg(not(feature = "production"))] #[cfg(not(feature = "production"))]
{ {
println!("Event[Ready][Notice]: Detected a non-production environment!"); println!("Event[Ready][Notice]: Detected a non-production environment!");
@ -73,13 +63,13 @@ async fn on_ready(
.send_message(&ctx.http, message.add_embed(ready_embed)) .send_message(&ctx.http, message.add_embed(ready_embed))
.await?; .await?;
Ok(()) Ok(KonData {})
} }
async fn event_processor( async fn event_processor(
framework: poise::FrameworkContext<'_, (), Error>, framework: PoiseFwCtx<'_>,
event: &FullEvent event: &FullEvent
) -> Result<(), Error> { ) -> KonResult<()> {
if let FullEvent::Ready { .. } = event { if let FullEvent::Ready { .. } = event {
let thread_id = format!("{:?}", current().id()); let thread_id = format!("{:?}", current().id());
let thread_num: String = thread_id.chars().filter(|c| c.is_ascii_digit()).collect(); let thread_num: String = thread_id.chars().filter(|c| c.is_ascii_digit()).collect();
@ -102,14 +92,7 @@ async fn main() {
let framework = poise::Framework::builder() let framework = poise::Framework::builder()
.options(poise::FrameworkOptions { .options(poise::FrameworkOptions {
commands: vec![ commands: register_cmds(),
commands::deploy(),
commands::ilo::ilo(),
commands::ping::ping(),
commands::status::status(),
commands::midi::midi_to_wav(),
commands::uptime::uptime(),
],
prefix_options: poise::PrefixFrameworkOptions { prefix_options: poise::PrefixFrameworkOptions {
prefix, prefix,
mention_as_prefix: false, mention_as_prefix: false,
@ -122,7 +105,7 @@ async fn main() {
Box::pin(async move { Box::pin(async move {
let get_guild_name = match ctx.guild() { let get_guild_name = match ctx.guild() {
Some(guild) => guild.name.clone(), Some(guild) => guild.name.clone(),
None => String::from("Direct Message") None => String::from("DM/User-App")
}; };
println!("Discord[{get_guild_name}]: {} ran /{}", ctx.author().name, ctx.command().qualified_name); println!("Discord[{get_guild_name}]: {} ran /{}", ctx.author().name, ctx.command().qualified_name);
}) })
@ -153,7 +136,7 @@ async fn main() {
event_handler: |framework, event| Box::pin(event_processor(framework, event)), event_handler: |framework, event| Box::pin(event_processor(framework, event)),
..Default::default() ..Default::default()
}) })
.setup(|ctx, ready, framework| Box::pin(on_ready(ctx, ready, framework))) .setup(|ctx, ready, _| Box::pin(on_ready(ctx, ready)))
.build(); .build();
let mut client = ClientBuilder::new( let mut client = ClientBuilder::new(

17
tasks/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "kon_tasks"
version = "0.1.0"
edition = "2024"
[dependencies]
feed-rs = { workspace = true }
kon_libs = { workspace = true }
kon_repo = { workspace = true }
once_cell = { workspace = true }
poise = { workspace = true }
regex = { workspace = true }
reqwest = { workspace = true }
tokio = { workspace = true }
[features]
production = ["kon_libs/production"]

View File

@ -3,6 +3,7 @@ mod rss;
pub use rss::rss; pub use rss::rss;
use { use {
kon_libs::KonResult,
poise::serenity_prelude::Context, poise::serenity_prelude::Context,
std::{ std::{
future::Future, future::Future,
@ -38,7 +39,7 @@ pub async fn run_task<F, T>(
task: F task: F
) where ) where
F: Fn(Arc<Context>) -> T + Send + 'static, F: Fn(Arc<Context>) -> T + Send + 'static,
T: Future<Output = Result<(), crate::Error>> + Send + 'static T: Future<Output = KonResult<()>> + Send + 'static
{ {
let ctx_cl = Arc::clone(&ctx); let ctx_cl = Arc::clone(&ctx);

View File

@ -5,23 +5,18 @@ mod github;
mod gportal; mod gportal;
mod rust; mod rust;
use { use super::{
super::{
super::{
config::BINARY_PROPERTIES,
http::HttpClient
},
task_err, task_err,
task_info task_info
},
crate::{
Error,
controllers::cache::RedisController
}
}; };
use { use {
feed_rs::parser::parse, feed_rs::parser::parse,
kon_libs::{
HttpClient,
KonResult
},
kon_repo::RedisController,
once_cell::sync::OnceCell, once_cell::sync::OnceCell,
poise::serenity_prelude::{ poise::serenity_prelude::{
Context, Context,
@ -82,7 +77,7 @@ fn format_html_to_discord(input: String) -> String {
output output
} }
async fn fetch_feed(url: &str) -> Result<Response, Error> { async fn fetch_feed(url: &str) -> KonResult<Response> {
let http = HttpClient::new(); let http = HttpClient::new();
let res = match http.get(url, "RSS-Monitor").await { let res = match http.get(url, "RSS-Monitor").await {
Ok(res) => res, Ok(res) => res,
@ -95,7 +90,7 @@ async fn fetch_feed(url: &str) -> Result<Response, Error> {
async fn save_to_redis( async fn save_to_redis(
key: &str, key: &str,
value: &str value: &str
) -> Result<(), Error> { ) -> KonResult<()> {
let redis = get_redis().await; let redis = get_redis().await;
redis.set(key, value).await.unwrap(); redis.set(key, value).await.unwrap();
if let Err(y) = redis.expire(key, REDIS_EXPIRY_SECS).await { if let Err(y) = redis.expire(key, REDIS_EXPIRY_SECS).await {
@ -148,7 +143,7 @@ impl IncidentColorMap {
} }
} }
pub async fn rss(ctx: Arc<Context>) -> Result<(), Error> { pub async fn rss(ctx: Arc<Context>) -> KonResult<()> {
#[cfg(feature = "production")] #[cfg(feature = "production")]
let mut interval = interval(Duration::from_secs(300)); // Check feeds every 5 mins let mut interval = interval(Duration::from_secs(300)); // Check feeds every 5 mins
#[cfg(not(feature = "production"))] #[cfg(not(feature = "production"))]

View File

@ -1,5 +1,4 @@
use { use super::{
super::{
super::task_err, super::task_err,
REDIS_EXPIRY_SECS, REDIS_EXPIRY_SECS,
fetch_feed, fetch_feed,
@ -7,11 +6,10 @@ use {
get_redis, get_redis,
parse, parse,
save_to_redis save_to_redis
},
crate::Error
}; };
use { use {
kon_libs::KonResult,
poise::serenity_prelude::{ poise::serenity_prelude::{
CreateEmbed, CreateEmbed,
CreateEmbedAuthor, CreateEmbedAuthor,
@ -21,7 +19,7 @@ use {
std::io::Cursor std::io::Cursor
}; };
pub async fn esxi_embed() -> Result<Option<CreateEmbed>, Error> { pub async fn esxi_embed() -> KonResult<Option<CreateEmbed>> {
let redis = get_redis().await; let redis = get_redis().await;
let rkey = "RSS_ESXi"; let rkey = "RSS_ESXi";
let url = "https://esxi-patches.v-front.de/atom/ESXi-7.0.0.xml"; let url = "https://esxi-patches.v-front.de/atom/ESXi-7.0.0.xml";

View File

@ -1,5 +1,4 @@
use { use super::{
super::{
super::task_err, super::task_err,
IncidentColorMap, IncidentColorMap,
REDIS_EXPIRY_SECS, REDIS_EXPIRY_SECS,
@ -10,11 +9,10 @@ use {
parse, parse,
save_to_redis, save_to_redis,
trim_old_content trim_old_content
},
crate::Error
}; };
use { use {
kon_libs::KonResult,
poise::serenity_prelude::{ poise::serenity_prelude::{
CreateEmbed, CreateEmbed,
Timestamp Timestamp
@ -23,7 +21,7 @@ use {
std::io::Cursor std::io::Cursor
}; };
pub async fn github_embed() -> Result<Option<CreateEmbed>, Error> { pub async fn github_embed() -> KonResult<Option<CreateEmbed>> {
let redis = get_redis().await; let redis = get_redis().await;
let rkey = "RSS_GitHub"; let rkey = "RSS_GitHub";
let rkey_content = format!("{}_Content", rkey); let rkey_content = format!("{}_Content", rkey);

View File

@ -1,5 +1,4 @@
use { use super::{
super::{
super::task_err, super::task_err,
IncidentColorMap, IncidentColorMap,
REDIS_EXPIRY_SECS, REDIS_EXPIRY_SECS,
@ -10,11 +9,10 @@ use {
parse, parse,
save_to_redis, save_to_redis,
trim_old_content trim_old_content
},
crate::Error
}; };
use { use {
kon_libs::KonResult,
poise::serenity_prelude::{ poise::serenity_prelude::{
CreateEmbed, CreateEmbed,
Timestamp Timestamp
@ -23,7 +21,7 @@ use {
std::io::Cursor std::io::Cursor
}; };
pub async fn gportal_embed() -> Result<Option<CreateEmbed>, Error> { pub async fn gportal_embed() -> KonResult<Option<CreateEmbed>> {
let redis = get_redis().await; let redis = get_redis().await;
let rkey = "RSS_GPortal"; let rkey = "RSS_GPortal";
let rkey_content = format!("{}_Content", rkey); let rkey_content = format!("{}_Content", rkey);

View File

@ -1,5 +1,9 @@
use super::{ use kon_libs::{
BINARY_PROPERTIES, BINARY_PROPERTIES,
KonResult
};
use super::{
TASK_NAME, TASK_NAME,
esxi::esxi_embed, esxi::esxi_embed,
get_redis, get_redis,
@ -36,7 +40,7 @@ async fn process_embed(
embed: Option<CreateEmbed>, embed: Option<CreateEmbed>,
redis_key: &str, redis_key: &str,
content_key: &str content_key: &str
) -> Result<(), crate::Error> { ) -> KonResult<()> {
if let Some(embed) = embed { if let Some(embed) = embed {
let redis = get_redis().await; let redis = get_redis().await;
let channel = ChannelId::new(BINARY_PROPERTIES.rss_channel); let channel = ChannelId::new(BINARY_PROPERTIES.rss_channel);

View File

@ -1,21 +1,19 @@
use { use super::{
super::{
REDIS_EXPIRY_SECS, REDIS_EXPIRY_SECS,
fetch_feed, fetch_feed,
get_redis, get_redis,
parse, parse,
save_to_redis, save_to_redis,
task_err task_err
},
crate::Error
}; };
use { use {
kon_libs::KonResult,
regex::Regex, regex::Regex,
std::io::Cursor std::io::Cursor
}; };
pub async fn rust_message() -> Result<Option<String>, Error> { pub async fn rust_message() -> KonResult<Option<String>> {
let redis = get_redis().await; let redis = get_redis().await;
let rkey = "RSS_RustBlog"; let rkey = "RSS_RustBlog";
let url = "https://blog.rust-lang.org/feed.xml"; let url = "https://blog.rust-lang.org/feed.xml";

8
tokens/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "kon_tokens"
version = "0.1.0"
edition = "2024"
[dependencies]
tokenservice-client = { version = "0.4.1", registry = "gitea" }
tokio = { workspace = true }

42
tokens/src/lib.rs Normal file
View File

@ -0,0 +1,42 @@
use {
std::{
env::args,
error::Error,
sync::LazyLock
},
tokenservice_client::{
TokenService,
TokenServiceApi
},
tokio::sync::Mutex
};
static TSCLIENT: LazyLock<Mutex<TSClient>> = LazyLock::new(|| Mutex::new(TSClient::default()));
pub struct TSClient(TokenService);
impl Default for TSClient {
fn default() -> Self { Self::new() }
}
impl TSClient {
pub fn new() -> Self {
let args: Vec<String> = args().collect();
let service = if args.len() > 1 { &args[1] } else { "kon" };
Self(TokenService::new(service))
}
pub async fn get(&self) -> Result<TokenServiceApi, Box<dyn Error + Send + Sync>> {
match self.0.connect().await {
Ok(a) => Ok(a),
Err(e) => Err(e)
}
}
}
pub async fn token_path() -> TokenServiceApi {
match TSCLIENT.lock().await.get().await {
Ok(a) => a,
Err(e) => panic!("TSClient[Error]: {e}")
}
}