From f1dbbec32df58ded8d840e02f54ffcc9c64e821a Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Fri, 6 Jun 2025 18:50:10 +0100 Subject: [PATCH 01/27] feat: update teh base rust version --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8cca5be..0837c1f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.80" +channel = "1.87" From e449204863632a2b6668ebfcd0638a5cf9cc544c Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Fri, 6 Jun 2025 23:59:00 +0100 Subject: [PATCH 02/27] feat: need some new inputs to get this to build --- flake.nix | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 0a37869..2f8ba95 100644 --- a/flake.nix +++ b/flake.nix @@ -27,6 +27,8 @@ desc = "Skynet Discord Bot"; buildInputs = with pkgs; [ openssl + glib + gdk-pixbuf pkg-config rustfmt ]; @@ -37,6 +39,10 @@ pname = "${package_name}"; src = ./.; buildInputs = buildInputs; + postInstall = '' + mkdir $out/config + cp .server-icons.toml $out/config + ''; }; # Run `nix build .#fmt` to run tests fmt = naersk'.buildPackage { @@ -63,9 +69,9 @@ # `nix develop` devShell = pkgs.mkShell { - nativeBuildInputs = with pkgs; [rustup rustPlatform.bindgenHook pkg-config openssl]; + nativeBuildInputs = with pkgs; [rustup rustPlatform.bindgenHook]; # libraries here - buildInputs = [ ]; + buildInputs = buildInputs; RUSTC_VERSION = overrides.toolchain.channel; # https://github.com/rust-lang/rust-bindgen#environment-variables shellHook = '' From fcfcfb8409ca17158db8caf93b04939b4de53752 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 00:09:22 +0100 Subject: [PATCH 03/27] feat: added libraries needed to run the new feature --- Cargo.lock | 599 ++++++++++++++++++++++++++++++++++++++++++++++------- Cargo.toml | 6 +- 2 files changed, 528 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47a42f9..f21a0da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -17,6 +17,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aead" version = "0.3.2" @@ -110,6 +116,15 @@ version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + [[package]] name = "arrayvec" version = "0.7.6" @@ -243,8 +258,8 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -260,8 +275,8 @@ version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -307,6 +322,15 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + [[package]] name = "base64" version = "0.13.1" @@ -383,6 +407,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytemuck" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" + [[package]] name = "byteorder" version = "1.5.0" @@ -410,6 +440,16 @@ dependencies = [ "shlex", ] +[[package]] +name = "cfg-expr" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2b34126159980f92da2a08bdec0694fd80fb5eb9e48aff25d20a0d8dfa710d" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -691,8 +731,8 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -763,6 +803,27 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "euclid" +version = "0.19.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "596b99621b9477e7a5f94d2d8dd13a9c5c302ac358b822c67a42b6f1054450e1" +dependencies = [ + "euclid_macros", + "num-traits", +] + +[[package]] +name = "euclid_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdcb84c18ea5037a1c5a23039b4ff29403abce2e0d6b1daa11cf0bde2b30be15" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -815,6 +876,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e" + [[package]] name = "flume" version = "0.9.2" @@ -965,8 +1032,8 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -1009,6 +1076,31 @@ dependencies = [ "byteorder", ] +[[package]] +name = "gdk-pixbuf" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd242894c084f4beed508a56952750bce3e96e85eb68fdc153637daa163e10c" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b34f3b580c988bd217e9543a2de59823fafae369d1a055555e5f95a8b130b96" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1069,6 +1161,80 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +[[package]] +name = "gio" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a5c3829f5794cb15120db87707b2ec03720edff7ad09eb7b711b532e3fe747" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "pin-project-lite", + "smallvec", +] + +[[package]] +name = "gio-sys" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys 0.59.0", +] + +[[package]] +name = "glib" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c501c495842c2b23cdacead803a5a343ca2a5d7a7ddaff14cc5f6cf22cfb92c2" +dependencies = [ + "bitflags 2.6.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe6dc9ce29887c4b3b74d78d5ba473db160a258ae7ed883d23632ac7fed7bc9" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2 1.0.92", + "quote 1.0.37", + "syn 2.0.89", +] + +[[package]] +name = "glib-sys" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215" +dependencies = [ + "libc", + "system-deps", +] + [[package]] name = "gloo-timers" version = "0.3.0" @@ -1081,6 +1247,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gobject-sys" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "h2" version = "0.3.26" @@ -1581,21 +1758,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.0.3" @@ -1718,7 +1885,7 @@ dependencies = [ "futures-util", "hostname", "httpdate", - "idna 1.0.3", + "idna", "mime", "native-tls", "nom", @@ -1735,6 +1902,18 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libflate" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9135df43b1f5d0e333385cb6e7897ecd1a43d7d11b91ac003f4d2c2d2401fdd" +dependencies = [ + "adler32", + "crc32fast", + "rle-decode-fast", + "take_mut", +] + [[package]] name = "libm" version = "0.2.8" @@ -1805,6 +1984,17 @@ dependencies = [ "value-bag", ] +[[package]] +name = "lyon_geom" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb9bf1f1d43be9a9cc2343a7a096dc113cc25337a13e8f99721b01d1d548b60" +dependencies = [ + "arrayvec 0.4.12", + "euclid", + "num-traits", +] + [[package]] name = "maud" version = "0.27.0" @@ -1821,9 +2011,9 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7261b00f3952f617899bc012e3dbd56e4f0110a038175929fa5d18e5a19913ca" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.92", "proc-macro2-diagnostics", - "quote", + "quote 1.0.37", "syn 2.0.89", ] @@ -1903,6 +2093,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "nom" version = "7.1.3" @@ -2008,8 +2204,8 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -2075,6 +2271,24 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.7" @@ -2090,8 +2304,8 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -2186,12 +2400,30 @@ dependencies = [ "zerocopy 0.7.35", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -2207,8 +2439,8 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", "version_check", ] @@ -2222,13 +2454,22 @@ dependencies = [ "cc", ] +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.92", ] [[package]] @@ -2339,6 +2580,12 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rctree" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0b3901505c2faa2390e27188078852eb3ed0dd9176e2153f403cdcdd18e0e7" + [[package]] name = "redox_syscall" version = "0.5.4" @@ -2435,6 +2682,26 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "resvg" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef03b3fc408e1d4107b554ef7717a5933aa9f423e7154af626dd3ae557318d4" +dependencies = [ + "log", + "rgb", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.8" @@ -2450,6 +2717,21 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + +[[package]] +name = "roxmltree" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330d8f80a274bc3cb608908ee345970e7e24b96907f1ad69615a498bec57871c" +dependencies = [ + "xmlparser", +] + [[package]] name = "rsa" version = "0.9.6" @@ -2685,8 +2967,8 @@ version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -2713,6 +2995,15 @@ dependencies = [ "thiserror 1.0.63", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2731,7 +3022,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d72ec4323681bf9a3cabe40fd080abc2435859b502a1b5aa9bf693f125bfa76" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "async-trait", "base64 0.22.1", "bitflags 2.6.0", @@ -2831,21 +3122,36 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simplecss" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135685097a85a64067df36e28a243e94a94f76d829087ce0be34eeb014260c0e" + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" + [[package]] name = "skynet_discord_bot" version = "0.1.0" dependencies = [ "chrono", "dotenvy", + "gdk-pixbuf", "lettre", "maud", "rand 0.9.0", + "resvg", "serde", "serde_json", "serenity", "sqlx", "surf", "tokio", + "toml", "wolves_oxidised", ] @@ -2968,8 +3274,8 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "sqlx-core", "sqlx-macros-core", "syn 2.0.89", @@ -2986,8 +3292,8 @@ dependencies = [ "heck", "hex", "once_cell", - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "serde", "serde_json", "sha2 0.10.8", @@ -3151,8 +3457,8 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "serde", "serde_derive", "syn 1.0.109", @@ -3165,8 +3471,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" dependencies = [ "base-x", - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "serde", "serde_derive", "serde_json", @@ -3220,14 +3526,48 @@ dependencies = [ "web-sys", ] +[[package]] +name = "svgdom" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffe3c59d84b307fc361bbf0baff2aa6f6d7c946fdd4f1fbbcbb7efcd04b0334" +dependencies = [ + "log", + "roxmltree", + "simplecss", + "slab", + "svgtypes", +] + +[[package]] +name = "svgtypes" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444c882c28925ae0585df228a90f9951569588646ceca4753560de93cdd02258" +dependencies = [ + "float-cmp", + "phf", +] + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid", +] + [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "unicode-ident", ] @@ -3237,8 +3577,8 @@ version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "unicode-ident", ] @@ -3263,8 +3603,8 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -3310,6 +3650,31 @@ dependencies = [ "libc", ] +[[package]] +name = "system-deps" +version = "7.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + +[[package]] +name = "target-lexicon" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" + [[package]] name = "tempfile" version = "3.12.0" @@ -3347,8 +3712,8 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -3358,8 +3723,8 @@ version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -3426,8 +3791,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "standback", "syn 1.0.109", ] @@ -3481,8 +3846,8 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -3581,6 +3946,47 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower-service" version = "0.3.3" @@ -3605,8 +4011,8 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -3704,6 +4110,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "universal-hash" version = "0.4.0" @@ -3722,16 +4134,30 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", "serde", ] +[[package]] +name = "usvg" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0286ce4fda429767b5fc6968c8b541a5fc21f0afd67bbe94d4b7e7177d1f77cb" +dependencies = [ + "base64 0.10.1", + "libflate", + "log", + "lyon_geom", + "rctree", + "svgdom", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -3762,6 +4188,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.5" @@ -3830,8 +4262,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", "wasm-bindgen-shared", ] @@ -3854,7 +4286,7 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ - "quote", + "quote 1.0.37", "wasm-bindgen-macro-support", ] @@ -3864,8 +4296,8 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -4144,6 +4576,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -4186,6 +4627,12 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "xmlparser" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecec95f00fb0ff019153e64ea520f87d1409769db3e8f4db3ea588638a3e1cee" + [[package]] name = "yoke" version = "0.7.5" @@ -4204,8 +4651,8 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", "synstructure", ] @@ -4235,8 +4682,8 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -4246,8 +4693,8 @@ version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] @@ -4266,8 +4713,8 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", "synstructure", ] @@ -4295,7 +4742,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.92", + "quote 1.0.37", "syn 2.0.89", ] diff --git a/Cargo.toml b/Cargo.toml index d999267..968e6bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,4 +45,8 @@ chrono = "0.4" lettre = "0.11" maud = "0.27" -serde = "1.0" \ No newline at end of file +toml = "0.8.23" +serde = "1.0" + +gdk-pixbuf = "0.20.10" +resvg = "=0.6.1" \ No newline at end of file From 725bfa41ccc6d827e9f6ea3d0f9f5dd451a91a26 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 00:10:52 +0100 Subject: [PATCH 04/27] feat: initial tests of new function to handle changing the logo in discord server --- .server-icons.toml | 23 ++++ src/bin/update_server-icon.rs | 193 ++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 .server-icons.toml create mode 100644 src/bin/update_server-icon.rs diff --git a/.server-icons.toml b/.server-icons.toml new file mode 100644 index 0000000..587f63b --- /dev/null +++ b/.server-icons.toml @@ -0,0 +1,23 @@ +# this file controls the + +[source] +repo = "https://forgejo.skynet.ie/Computer_Society/open-goverance" +directory = "Resources/Logo_Variants" + +[[festivals]] +name = "pride" +all_year = true +start = { day = 1, month = 6, year = 0} +end = { day = 30, month = 6, year = 0} + +[[festivals]] +name = "christmas" +all_year = false +start = { day = 1, month = 12, year = 0} +end = { day = 31, month = 12, year = 0} + +[[festivals]] +name = "halloween" +all_year = false +start = { day = 1, month = 12, year = 0} +end = { day = 31, month = 12, year = 0} \ No newline at end of file diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs new file mode 100644 index 0000000..229bdbf --- /dev/null +++ b/src/bin/update_server-icon.rs @@ -0,0 +1,193 @@ +use serenity::{ + async_trait, + client::{Context, EventHandler}, + model::gateway::{GatewayIntents, Ready}, + Client, +}; +use skynet_discord_bot::common::database::{db_init, DataBase}; +use skynet_discord_bot::{get_config, Config}; +use std::{fs, process, sync::Arc}; +use std::fs::File; +use std::io::Read; +use std::process::Command; +use chrono::{Datelike, Utc}; +use gdk_pixbuf::PixbufLoader; +use gdk_pixbuf::prelude::PixbufLoaderExt; +use resvg::usvg; +use serde::Deserialize; +use serenity::all::GuildId; +use tokio::sync::RwLock; + +#[tokio::main] +async fn main() { + let config = get_config(); + let db = match db_init(&config).await { + Ok(x) => x, + Err(_) => return, + }; + + // Intents are a bitflag, bitwise operations can be used to dictate which intents to use + let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS; + // Build our client. + let mut client = Client::builder(&config.discord_token, intents) + .event_handler(Handler {}) + .await + .expect("Error creating client"); + + { + let mut data = client.data.write().await; + + data.insert::(Arc::new(RwLock::new(config))); + data.insert::(Arc::new(RwLock::new(db))); + } + + if let Err(why) = client.start().await { + println!("Client error: {:?}", why); + } +} + +struct Handler; +#[async_trait] +impl EventHandler for Handler { + async fn ready(&self, ctx: Context, ready: Ready) { + let ctx = Arc::new(ctx); + println!("{} is connected!", ready.user.name); + + + // pull in the open governance repo + // u[date committee server + update_icon_main(Arc::clone(&ctx)).await; + + // finish up + process::exit(0); + } +} + +async fn update_icon_main(ctx: Arc) { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + + + let config_global = config_lock.read().await; + let config_toml = get_config_icons(); + let server = GuildId::new(689189992417067052); + + + // clone repo into local folder + clone_repo(&config_global, &config_toml); + + // see if there is a current festival + let festival_data = get_festival(&config_toml); + + // get a list of all the graphics files + get_logos(&config_global, &config_toml); +} + +fn get_festival(config_toml: &ConfigToml)-> (Option, Vec){ + let today = Utc::now(); + let day = today.day(); + let month = today.month(); + let year = today.year(); + + let mut festival_current = None; + let mut festival_not = vec![]; + + for festival in &config_toml.festivals { + if (day >= festival.start.day && day <= festival.end.day) + && (month >= festival.start.month && month <= festival.end.month ) + && (year >= festival.start.year && year <= festival.end.year) { + festival_current = Some(festival.name.to_owned()); + } else if !festival.all_year { + festival_not.push(festival.name.to_owned()); + } + } + + (festival_current, festival_not) +} + +#[derive(Deserialize)] +struct ConfigToml { + source: ConfigTomlSource, + festivals: Vec, +} +#[derive(Deserialize)] +struct ConfigTomlSource { + repo: String, + directory: String, +} + +#[derive(Deserialize)] +struct ConfigTomlFestivals { + name: String, + all_year: bool, + start: ConfigTomlFestivalsTime, + end: ConfigTomlFestivalsTime, +} +#[derive(Deserialize)] +struct ConfigTomlFestivalsTime { + day: u32, + month: u32, + year:i32 +} + +fn get_config_icons() -> ConfigToml { + let toml_raw = include_str!("../../.server-icons.toml"); + let config: ConfigToml = toml::from_str(toml_raw).unwrap(); + config +} +fn clone_repo(config: &Config, config_toml: &ConfigToml){ + let url = &config_toml.source.repo; + let folder = format!("{}/open-governance", &config.home); + + Command::new("git") + .arg("clone") + .arg(url) + .arg(&folder) + .output() + .expect("failed to execute process"); + + Command::new("git") + .arg("pull") + .arg("origin") + .arg("main") + .current_dir(&folder) + .output() + .expect("failed to execute process"); +} + +fn convert_svg_to_png(original: &str, out: &str){ + let mut f = File::open(original).unwrap(); + let mut buffer = Vec::new(); + // read the whole file + f.read_to_end(&mut buffer).unwrap(); + let loader = PixbufLoader::with_mime_type("image/svg+xml") + .expect("error loader"); + loader.write(&buffer).expect("TODO: panic message"); + loader.close().expect("TODO: panic message"); + let pixbuf = loader.pixbuf().expect("no pixbuf"); + + let (width, height) = (pixbuf.width(), pixbuf.height()); + println!("size: {}x{}", width, height); + let bytes: Vec = + pixbuf.save_to_bufferv("png", &[]).expect("must not error"); + fs::write(out, &bytes).expect("TODO: panic message"); +} + +fn get_logos(config: &Config, config_toml: &ConfigToml){ + let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); + + let paths = fs::read_dir(folder).unwrap(); + for path in paths { + let tmp = path.unwrap(); + + println!("Name: {}", &tmp.path().display()); + } +} \ No newline at end of file From 0034bd34d6d8a053544e64f35dcca7957c4ea19b Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 01:05:08 +0100 Subject: [PATCH 05/27] feat: code borrowed from https://github.com/MCorange99/svg2colored-png/tree/main in order to convert from svg to png --- Cargo.lock | 823 +++++++++++++++++++++++++--------- Cargo.toml | 11 +- src/bin/update_server-icon.rs | 39 +- src/common/mod.rs | 2 + src/common/renderer.rs | 213 +++++++++ 5 files changed, 867 insertions(+), 221 deletions(-) create mode 100644 src/common/renderer.rs diff --git a/Cargo.lock b/Cargo.lock index f21a0da..1f38aa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,12 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - [[package]] name = "aead" version = "0.3.2" @@ -110,6 +104,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.93" @@ -117,13 +161,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" [[package]] -name = "arrayvec" -version = "0.4.12" +name = "arrayref" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -258,8 +299,8 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -275,8 +316,8 @@ version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -322,15 +363,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" -[[package]] -name = "base64" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] - [[package]] name = "base64" version = "0.13.1" @@ -489,6 +521,95 @@ dependencies = [ "generic-array", ] +[[package]] +name = "clap" +version = "4.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "color-eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -677,6 +798,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +[[package]] +name = "data-url" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" + [[package]] name = "der" version = "0.7.9" @@ -731,8 +858,8 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -803,27 +930,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "euclid" -version = "0.19.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "596b99621b9477e7a5f94d2d8dd13a9c5c302ac358b822c67a42b6f1054450e1" -dependencies = [ - "euclid_macros", - "num-traits", -] - -[[package]] -name = "euclid_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdcb84c18ea5037a1c5a23039b4ff29403abce2e0d6b1daa11cf0bde2b30be15" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -851,6 +957,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -866,6 +982,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "flate2" version = "1.0.33" @@ -878,9 +1003,9 @@ dependencies = [ [[package]] name = "float-cmp" -version = "0.5.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" [[package]] name = "flume" @@ -916,6 +1041,27 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff20bef7942a72af07104346154a70a70b089c572e454b41bef6eb6cb10e9c06" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "ttf-parser", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -1032,8 +1178,8 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -1155,6 +1301,16 @@ dependencies = [ "polyval", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.31.0" @@ -1220,8 +1376,8 @@ checksum = "ebe6dc9ce29887c4b3b74d78d5ba473db160a258ae7ed883d23632ac7fed7bc9" dependencies = [ "heck", "proc-macro-crate", - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -1758,8 +1914,8 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -1784,6 +1940,18 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "imagesize" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72ad49b554c1728b1e83254a1b1565aea4161e28dabbfa171fc15fe62299caf" + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "2.5.0" @@ -1815,6 +1983,12 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "isahc" version = "0.9.14" @@ -1844,6 +2018,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.70" @@ -1853,6 +2033,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kurbo" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "kurbo" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +dependencies = [ + "arrayvec", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -1902,18 +2100,6 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" -[[package]] -name = "libflate" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9135df43b1f5d0e333385cb6e7897ecd1a43d7d11b91ac003f4d2c2d2401fdd" -dependencies = [ - "adler32", - "crc32fast", - "rle-decode-fast", - "take_mut", -] - [[package]] name = "libm" version = "0.2.8" @@ -1984,17 +2170,6 @@ dependencies = [ "value-bag", ] -[[package]] -name = "lyon_geom" -version = "0.12.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb9bf1f1d43be9a9cc2343a7a096dc113cc25337a13e8f99721b01d1d548b60" -dependencies = [ - "arrayvec 0.4.12", - "euclid", - "num-traits", -] - [[package]] name = "maud" version = "0.27.0" @@ -2011,9 +2186,9 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7261b00f3952f617899bc012e3dbd56e4f0110a038175929fa5d18e5a19913ca" dependencies = [ - "proc-macro2 1.0.92", + "proc-macro2", "proc-macro2-diagnostics", - "quote 1.0.37", + "quote", "syn 2.0.89", ] @@ -2033,6 +2208,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + [[package]] name = "mime" version = "0.3.17" @@ -2062,6 +2246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -2093,12 +2278,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "nom" version = "7.1.3" @@ -2162,6 +2341,15 @@ dependencies = [ "libm", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "object" version = "0.36.4" @@ -2177,6 +2365,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -2204,8 +2398,8 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -2227,6 +2421,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "owo-colors" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec" + [[package]] name = "parking" version = "2.2.1" @@ -2272,22 +2472,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "phf" -version = "0.7.24" +name = "pico-args" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_shared" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -dependencies = [ - "siphasher", -] +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" @@ -2304,8 +2492,8 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -2359,6 +2547,19 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "3.7.4" @@ -2415,15 +2616,6 @@ version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid", -] - [[package]] name = "proc-macro2" version = "1.0.92" @@ -2439,8 +2631,8 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", "version_check", ] @@ -2454,22 +2646,13 @@ dependencies = [ "cc", ] -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 1.0.92", + "proc-macro2", ] [[package]] @@ -2582,9 +2765,9 @@ dependencies = [ [[package]] name = "rctree" -version = "0.2.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0b3901505c2faa2390e27188078852eb3ed0dd9176e2153f403cdcdd18e0e7" +checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" [[package]] name = "redox_syscall" @@ -2684,13 +2867,21 @@ dependencies = [ [[package]] name = "resvg" -version = "0.6.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef03b3fc408e1d4107b554ef7717a5933aa9f423e7154af626dd3ae557318d4" +checksum = "76888219c0881e22b0ceab06fddcfe83163cd81642bd60c7842387f9c968a72e" dependencies = [ + "gif", + "jpeg-decoder", "log", + "pico-args", + "png", "rgb", + "svgfilters", + "svgtypes 0.10.0", + "tiny-skia", "usvg", + "usvg-text-layout", ] [[package]] @@ -2718,20 +2909,33 @@ dependencies = [ ] [[package]] -name = "rle-decode-fast" -version = "1.0.3" +name = "rosvgtree" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" +checksum = "bdc23d1ace03d6b8153c7d16f0708cd80b61ee8e80304954803354e67e40d150" +dependencies = [ + "log", + "roxmltree 0.18.1", + "simplecss", + "siphasher", + "svgtypes 0.9.0", +] [[package]] name = "roxmltree" -version = "0.6.1" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330d8f80a274bc3cb608908ee345970e7e24b96907f1ad69615a498bec57871c" +checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" dependencies = [ "xmlparser", ] +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rsa" version = "0.9.6" @@ -2864,6 +3068,22 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustybuzz" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162bdf42e261bee271b3957691018634488084ef577dddeb6420a9684cab2a6a" +dependencies = [ + "bitflags 1.3.2", + "bytemuck", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-general-category", + "unicode-script", +] + [[package]] name = "ryu" version = "1.0.18" @@ -2967,8 +3187,8 @@ version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -3022,7 +3242,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d72ec4323681bf9a3cabe40fd080abc2435859b502a1b5aa9bf693f125bfa76" dependencies = [ - "arrayvec 0.7.6", + "arrayvec", "async-trait", "base64 0.22.1", "bitflags 2.6.0", @@ -3097,6 +3317,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -3123,35 +3352,64 @@ dependencies = [ ] [[package]] -name = "simplecss" -version = "0.1.0" +name = "simd-adler32" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135685097a85a64067df36e28a243e94a94f76d829087ce0be34eeb014260c0e" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simple_logger" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" +dependencies = [ + "colored", + "log", + "time 0.3.36", + "windows-sys 0.48.0", +] + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] [[package]] name = "siphasher" -version = "0.2.3" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "skynet_discord_bot" version = "0.1.0" dependencies = [ "chrono", + "clap", + "color-eyre", "dotenvy", + "eyre", "gdk-pixbuf", "lettre", + "log", "maud", "rand 0.9.0", "resvg", "serde", "serde_json", "serenity", + "simple_logger", "sqlx", "surf", + "tiny-skia", "tokio", "toml", + "usvg", + "usvg-text-layout", "wolves_oxidised", ] @@ -3274,8 +3532,8 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "sqlx-core", "sqlx-macros-core", "syn 2.0.89", @@ -3292,8 +3550,8 @@ dependencies = [ "heck", "hex", "once_cell", - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "serde", "serde_json", "sha2 0.10.8", @@ -3457,8 +3715,8 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "serde", "serde_derive", "syn 1.0.109", @@ -3471,8 +3729,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" dependencies = [ "base-x", - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "serde", "serde_derive", "serde_json", @@ -3486,6 +3744,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + [[package]] name = "stringprep" version = "0.1.5" @@ -3497,6 +3764,12 @@ dependencies = [ "unicode-properties", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -3527,37 +3800,33 @@ dependencies = [ ] [[package]] -name = "svgdom" -version = "0.16.1" +name = "svgfilters" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ffe3c59d84b307fc361bbf0baff2aa6f6d7c946fdd4f1fbbcbb7efcd04b0334" +checksum = "639abcebc15fdc2df179f37d6f5463d660c1c79cd552c12343a4600827a04bce" dependencies = [ - "log", - "roxmltree", - "simplecss", - "slab", - "svgtypes", + "float-cmp", + "rgb", ] [[package]] name = "svgtypes" -version = "0.4.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444c882c28925ae0585df228a90f9951569588646ceca4753560de93cdd02258" +checksum = "c9ee29c1407a5b18ccfe5f6ac82ac11bab3b14407e09c209a6c1a32098b19734" dependencies = [ - "float-cmp", - "phf", + "kurbo 0.8.3", + "siphasher", ] [[package]] -name = "syn" -version = "0.15.44" +name = "svgtypes" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +checksum = "98ffacedcdcf1da6579c907279b4f3c5492fbce99fbbf227f5ed270a589c2765" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid", + "kurbo 0.9.5", + "siphasher", ] [[package]] @@ -3566,8 +3835,8 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "unicode-ident", ] @@ -3577,8 +3846,8 @@ version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "unicode-ident", ] @@ -3603,8 +3872,8 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -3663,12 +3932,6 @@ dependencies = [ "version-compare", ] -[[package]] -name = "take_mut" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" - [[package]] name = "target-lexicon" version = "0.13.2" @@ -3712,8 +3975,8 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -3723,11 +3986,21 @@ version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.2.27" @@ -3751,7 +4024,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde", "time-core", @@ -3791,12 +4066,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "standback", "syn 1.0.109", ] +[[package]] +name = "tiny-skia" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinystr" version = "0.7.6" @@ -3846,8 +4146,8 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -4011,8 +4311,8 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -4023,6 +4323,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", ] [[package]] @@ -4035,12 +4346,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" + [[package]] name = "tungstenite" version = "0.21.0" @@ -4089,6 +4417,24 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +[[package]] +name = "unicode-bidi-mirroring" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" + +[[package]] +name = "unicode-ccc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" + +[[package]] +name = "unicode-general-category" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" + [[package]] name = "unicode-ident" version = "1.0.13" @@ -4111,10 +4457,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" [[package]] -name = "unicode-xid" +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unicode-vo" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "universal-hash" @@ -4146,16 +4498,35 @@ dependencies = [ [[package]] name = "usvg" -version = "0.6.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0286ce4fda429767b5fc6968c8b541a5fc21f0afd67bbe94d4b7e7177d1f77cb" +checksum = "63b6bb4e62619d9f68aa2d8a823fea2bff302340a1f2d45c264d5b0be170832e" dependencies = [ - "base64 0.10.1", - "libflate", + "base64 0.21.7", + "data-url", + "flate2", + "imagesize", + "kurbo 0.9.5", "log", - "lyon_geom", "rctree", - "svgdom", + "rosvgtree", + "strict-num", +] + +[[package]] +name = "usvg-text-layout" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195386e01bc35f860db024de275a76e7a31afdf975d18beb6d0e44764118b4db" +dependencies = [ + "fontdb", + "kurbo 0.9.5", + "log", + "rustybuzz", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "usvg", ] [[package]] @@ -4176,6 +4547,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "value-bag" version = "1.10.0" @@ -4262,8 +4645,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", "wasm-bindgen-shared", ] @@ -4286,7 +4669,7 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ - "quote 1.0.37", + "quote", "wasm-bindgen-macro-support", ] @@ -4296,8 +4679,8 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -4347,6 +4730,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + [[package]] name = "whoami" version = "1.5.2" @@ -4629,9 +5018,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "xmlparser" -version = "0.9.0" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecec95f00fb0ff019153e64ea520f87d1409769db3e8f4db3ea588638a3e1cee" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "yoke" @@ -4651,8 +5040,8 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", "synstructure", ] @@ -4682,8 +5071,8 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -4693,8 +5082,8 @@ version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76331675d372f91bf8d17e13afbd5fe639200b73d01f0fc748bb059f9cca2db7" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] @@ -4713,8 +5102,8 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", "synstructure", ] @@ -4742,7 +5131,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ - "proc-macro2 1.0.92", - "quote 1.0.37", + "proc-macro2", + "quote", "syn 2.0.89", ] diff --git a/Cargo.toml b/Cargo.toml index 968e6bb..deeeca5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,4 +49,13 @@ toml = "0.8.23" serde = "1.0" gdk-pixbuf = "0.20.10" -resvg = "=0.6.1" \ No newline at end of file +clap = { version = "4.1.4", features = ["derive"] } + +eyre = "0.6.8" +color-eyre = "0.6.2" +usvg-text-layout = "0.29.0" +usvg = "0.29.0" +resvg = "0.29.0" +tiny-skia = "0.8.3" +log = "0.4.20" +simple_logger = "4.2.0" \ No newline at end of file diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index 229bdbf..c262ef2 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -7,16 +7,19 @@ use serenity::{ use skynet_discord_bot::common::database::{db_init, DataBase}; use skynet_discord_bot::{get_config, Config}; use std::{fs, process, sync::Arc}; +use std::ffi::OsStr; use std::fs::File; use std::io::Read; +use std::path::PathBuf; use std::process::Command; use chrono::{Datelike, Utc}; -use gdk_pixbuf::PixbufLoader; +use gdk_pixbuf::{Pixbuf, PixbufFormat, PixbufLoader}; use gdk_pixbuf::prelude::PixbufLoaderExt; use resvg::usvg; use serde::Deserialize; use serenity::all::GuildId; use tokio::sync::RwLock; +use skynet_discord_bot::common::renderer::{Args, Renderer}; #[tokio::main] async fn main() { @@ -163,7 +166,7 @@ fn clone_repo(config: &Config, config_toml: &ConfigToml){ .expect("failed to execute process"); } -fn convert_svg_to_png(original: &str, out: &str){ +fn convert_svg_to_png(original: &PathBuf, out: &PathBuf){ let mut f = File::open(original).unwrap(); let mut buffer = Vec::new(); // read the whole file @@ -183,11 +186,41 @@ fn convert_svg_to_png(original: &str, out: &str){ fn get_logos(config: &Config, config_toml: &ConfigToml){ let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); - + let folder_path = PathBuf::from(&folder); let paths = fs::read_dir(folder).unwrap(); + + let args = Args{ + input: folder_path.clone(), + output: folder_path.clone(), + colors: String::from(""), + width: 1024, + height: 1024, + }; + let mut r = Renderer::new(&args).unwrap(); + for path in paths { let tmp = path.unwrap(); + let mut path_local = tmp.path().to_owned(); + match tmp.path().extension() { + None => {} + Some(ext) => { + if ext == "svg" { + let mut path_new = path_local.clone(); + path_new.set_extension("png"); + + // check if exists + match r.render(&path_local, &args) { + Ok(_) => log::info!("Successfully rendered all colors of {path_local:?}"), + Err(e) => { + log::error!("Failed to render {path_local:?}: {}", e) + } + } + path_local = path_new; + } + } + }; + println!("Name: {}", &tmp.path().display()); } } \ No newline at end of file diff --git a/src/common/mod.rs b/src/common/mod.rs index 38f457a..7b93f40 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -2,3 +2,5 @@ pub mod database; pub mod minecraft; pub mod set_roles; pub mod wolves; + +pub mod renderer; \ No newline at end of file diff --git a/src/common/renderer.rs b/src/common/renderer.rs new file mode 100644 index 0000000..61d45ea --- /dev/null +++ b/src/common/renderer.rs @@ -0,0 +1,213 @@ +use std::path::{Path, PathBuf}; + +use clap::builder::OsStr; +use clap::Parser; +use color_eyre::{Result, eyre::bail}; +use usvg_text_layout::TreeTextToPath; + +#[derive(Parser, Debug, Clone)] +#[command(name = "svg2colored-png")] +#[command(author = "MCorange ")] +#[command(version = env!("CARGO_PKG_VERSION"))] +#[command(about = "Converts svgs to multiple png's that differ in color", long_about = "Made by MCorange ")] +pub struct Args { + /// Input folder with the SVG's + #[arg(long, short)] + pub input: PathBuf, + + /// Output folder where the PNG's will be placed + #[arg[long, short]] + pub output: PathBuf, + + /// Comma seperated colors that will be used in HEX Eg. 000000,ffffff + /// Can be like an object: black:000000,white:ffffff + #[arg[long, short, default_value_t = String::from("0d6efd,6c757d,198754,0dcaf0,ffc107,dc3545,f8f9fa,212529,ffffff,000000")]] + pub colors: String, + + /// Width of the generated PNG's + #[arg(long, default_value_t = 1024)] + pub width: u32, + + /// Height of the generated PNG's + #[arg(long, default_value_t = 1024)] + pub height: u32 + +} + +#[derive(Debug, Clone)] +enum ColorType { + Array(Vec), + Object(Vec<(String, String)>), + None +} + + +#[derive(Debug, Clone)] +pub struct Renderer { + fontdb: usvg_text_layout::fontdb::Database, + colors: ColorType, + size: (u32, u32), + pub count: u64, +} + +impl Renderer { + pub fn new(args: &Args) -> Result { + let mut db = usvg_text_layout::fontdb::Database::new(); + db.load_system_fonts(); + + let mut this = Self { + fontdb: db, + colors: ColorType::None, + size: (args.width, args.height), + count: 0, + }; + + let colors = if args.colors.contains(':') { + //? object + let obj = args.colors.split(',').map(|s| { + let s = s.split(':').collect::>(); + + if s.len() < 2 { + log::error!("Invalid color object, try checking help"); + return None; + } + + Some((s[0].to_string(), s[1].to_string())) + }).collect::>>(); + + let mut colors = Vec::new(); + + for c in obj { + if let Some(c) = c { + std::fs::create_dir_all(args.output.join(&c.0))?; + + colors.push(c); + } + } + + ColorType::Object(colors) + + } else { + //? list + // let colors = args.colors.split(",").map(|s| { + // s.to_string() + // }) + // .collect::>(); + + let mut colors = Vec::new(); + + for color in args.colors.split(',') { + std::fs::create_dir_all(args.output.join(&color))?; + colors.push(color.to_string()) + } + + + ColorType::Array(colors) + }; + + this.colors = colors; + Ok(this) + } + + + pub fn render(&mut self, fi: &Path, args: &Args) -> Result<()> { + match fi.extension() { + Some(e) if e.to_str() == Some("svg") => {}, + Some(_) | + None => { + log::warn!("Filer {:?} is not of type SVG", fi); + // util::logger::warning(format!("File '{}' is not of SVG type", fi.clone().to_str().unwrap())); + bail!("Failed to render"); + } + }; + + match self.colors.clone() { + ColorType::Array(c) => { + for color in c { + log::info!("Rendering the color {color:?}"); + let fo = self.get_out_file(fi, &color, &args); + self.render_one(fi, &fo, &color)?; + + } + }, + ColorType::Object(c) => { + for o in c { + log::info!("Rendering the color {:?}", o); + let fo = self.get_out_file(fi, &o.0, &args); + self.render_one(fi, &fo, &o.1)?; + + } + }, + ColorType::None => unreachable!(), + } + + Ok(()) + } + + fn render_one(&mut self, fi: &Path, fo: &Path, color: &String) -> Result<()>{ + + if fo.exists() { + log::warn!("File {fo:?} exists, skipping"); + return Ok(()); + } + + let svg = self.set_color(&self.get_svg_data(fi)?, &color); + + let mut opt = usvg::Options::default(); + // Get file's absolute directory. + opt.resources_dir = std::fs::canonicalize(fi.clone()) + .ok() + .and_then(|p| p.parent().map(|p| p.to_path_buf())); + + let mut tree = match usvg::Tree::from_data(svg.as_bytes(), &opt) { + Ok(v) => v, + Err(_) => { + log::error!("Failed to parse {fi:?}"); + bail!(""); + } + }; + + tree.convert_text(&self.fontdb); + + let mut pixmap = tiny_skia::Pixmap::new(self.size.0, self.size.1).unwrap(); + + log::info!("Rendering {fo:?}"); + + //? maybe handle this and possibly throw error if its none + let _ = resvg::render( + &tree, + usvg::FitTo::Size(self.size.0, self.size.1), + tiny_skia::Transform::default(), + pixmap.as_mut(), + ); + + + pixmap.save_png(fo)?; + self.count += 1; + Ok(()) + } + + + #[inline] + fn get_out_file(&mut self, fi: &Path, sub_folder: &String, args: &Args) -> PathBuf { + let mut fo: std::path::PathBuf = args.output.clone(); + // fo.push(sub_folder); + fo.push(fi.file_name().unwrap_or(&OsStr::from("default")).to_str().unwrap_or("default").replace(".svg", "")); + fo.set_extension("png"); + fo + } + + fn set_color(&self, svg: &String, color: &String) -> String { + svg.replace("fill=\"currentColor\"", &format!("fill=\"#{}\"", color)) + } + + fn get_svg_data(&self, fi: &Path) -> Result{ + match std::fs::read_to_string(fi) { + Ok(d) => Ok(d), + Err(_) => { + log::error!("File {fi:?} does not exist"); + bail!("File {fi:?} does not exist"); + } + } + } +} \ No newline at end of file From b4f68357046edda17649133dc135e60dcb9b32d5 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 19:55:36 +0100 Subject: [PATCH 06/27] feat: got the logos, and converted them if needs be --- src/bin/update_server-icon.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index c262ef2..65875bc 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -7,7 +7,7 @@ use serenity::{ use skynet_discord_bot::common::database::{db_init, DataBase}; use skynet_discord_bot::{get_config, Config}; use std::{fs, process, sync::Arc}; -use std::ffi::OsStr; +use std::ffi::{OsStr, OsString}; use std::fs::File; use std::io::Read; use std::path::PathBuf; @@ -91,7 +91,7 @@ async fn update_icon_main(ctx: Arc) { let festival_data = get_festival(&config_toml); // get a list of all the graphics files - get_logos(&config_global, &config_toml); + let logos = get_logos(&config_global, &config_toml); } fn get_festival(config_toml: &ConfigToml)-> (Option, Vec){ @@ -184,7 +184,7 @@ fn convert_svg_to_png(original: &PathBuf, out: &PathBuf){ fs::write(out, &bytes).expect("TODO: panic message"); } -fn get_logos(config: &Config, config_toml: &ConfigToml){ +fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec<(OsString, PathBuf)> { let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); let folder_path = PathBuf::from(&folder); let paths = fs::read_dir(folder).unwrap(); @@ -198,10 +198,14 @@ fn get_logos(config: &Config, config_toml: &ConfigToml){ }; let mut r = Renderer::new(&args).unwrap(); - for path in paths { - let tmp = path.unwrap(); - - let mut path_local = tmp.path().to_owned(); + let mut logos = vec![]; + + for tmp in paths.flatten() { + let path_local = tmp.path().to_owned(); + let path_local2 = tmp.path().to_owned(); + let temp2 = path_local2.file_name().unwrap(); + let mut file_path = tmp.path(); + match tmp.path().extension() { None => {} Some(ext) => { @@ -216,11 +220,15 @@ fn get_logos(config: &Config, config_toml: &ConfigToml){ log::error!("Failed to render {path_local:?}: {}", e) } } - path_local = path_new; + file_path = path_new; } } }; + logos.push((temp2.to_owned(), file_path.to_owned())); + println!("Name: {}", &tmp.path().display()); } + + logos } \ No newline at end of file From a7423959dcb0e7a53b21a8c4744372153a3fe81c Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 21:24:59 +0100 Subject: [PATCH 07/27] fix: use a struct for clarity --- src/bin/update_server-icon.rs | 36 ++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index 65875bc..ec09845 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -94,26 +94,33 @@ async fn update_icon_main(ctx: Arc) { let logos = get_logos(&config_global, &config_toml); } -fn get_festival(config_toml: &ConfigToml)-> (Option, Vec){ +struct FestivalData{ + current: Option, + exclusions: Vec, +} + +fn get_festival(config_toml: &ConfigToml)-> FestivalData { let today = Utc::now(); let day = today.day(); let month = today.month(); let year = today.year(); - let mut festival_current = None; - let mut festival_not = vec![]; + let mut result = FestivalData { + current: None, + exclusions: vec![], + }; for festival in &config_toml.festivals { if (day >= festival.start.day && day <= festival.end.day) && (month >= festival.start.month && month <= festival.end.month ) && (year >= festival.start.year && year <= festival.end.year) { - festival_current = Some(festival.name.to_owned()); + result.current = Some(festival.name.to_owned()); } else if !festival.all_year { - festival_not.push(festival.name.to_owned()); + result.exclusions.push(festival.name.to_owned()); } } - (festival_current, festival_not) + result } #[derive(Deserialize)] @@ -184,7 +191,11 @@ fn convert_svg_to_png(original: &PathBuf, out: &PathBuf){ fs::write(out, &bytes).expect("TODO: panic message"); } -fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec<(OsString, PathBuf)> { +struct LogoData { + name: OsString, + path: PathBuf, +} +fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); let folder_path = PathBuf::from(&folder); let paths = fs::read_dir(folder).unwrap(); @@ -203,8 +214,8 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec<(OsString, PathB for tmp in paths.flatten() { let path_local = tmp.path().to_owned(); let path_local2 = tmp.path().to_owned(); - let temp2 = path_local2.file_name().unwrap(); - let mut file_path = tmp.path(); + let name = path_local2.file_name().unwrap().to_owned(); + let mut path = tmp.path(); match tmp.path().extension() { None => {} @@ -220,12 +231,15 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec<(OsString, PathB log::error!("Failed to render {path_local:?}: {}", e) } } - file_path = path_new; + path = path_new; } } }; - logos.push((temp2.to_owned(), file_path.to_owned())); + logos.push(LogoData{ + name, + path, + }); println!("Name: {}", &tmp.path().display()); } From ffd6e40d0bc74785c79fb6b48109ea7d32de9540 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 22:29:00 +0100 Subject: [PATCH 08/27] fix: was being too strict in matching the year --- src/bin/update_server-icon.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index ec09845..c2a14b2 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -111,10 +111,13 @@ fn get_festival(config_toml: &ConfigToml)-> FestivalData { }; for festival in &config_toml.festivals { - if (day >= festival.start.day && day <= festival.end.day) - && (month >= festival.start.month && month <= festival.end.month ) - && (year >= festival.start.year && year <= festival.end.year) { - result.current = Some(festival.name.to_owned()); + if (day >= festival.start.day && day <= festival.end.day) && + (month >= festival.start.month && month <= festival.end.month ) { + if festival.start.year == 0 || festival.end.year == 0 { + result.current = Some(festival.name.to_owned()); + } else if (year >= festival.start.year && year <= festival.end.year) { + result.current = Some(festival.name.to_owned()); + } } else if !festival.all_year { result.exclusions.push(festival.name.to_owned()); } From 537fdfd40c0a54f740a0f22e1922a247a57b0074 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 22:44:47 +0100 Subject: [PATCH 09/27] feat: put the converted files into a subfolder --- src/bin/update_server-icon.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index c2a14b2..ac13ec7 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -201,11 +201,13 @@ struct LogoData { fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); let folder_path = PathBuf::from(&folder); + let mut folder_output = folder_path.clone(); + folder_output.push("converted"); let paths = fs::read_dir(folder).unwrap(); let args = Args{ input: folder_path.clone(), - output: folder_path.clone(), + output: folder_output, colors: String::from(""), width: 1024, height: 1024, @@ -219,6 +221,10 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { let path_local2 = tmp.path().to_owned(); let name = path_local2.file_name().unwrap().to_owned(); let mut path = tmp.path(); + + if path.is_dir() { + continue; + } match tmp.path().extension() { None => {} @@ -226,6 +232,11 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { if ext == "svg" { let mut path_new = path_local.clone(); path_new.set_extension("png"); + let filename_tmp = path_new.clone(); + let filename = filename_tmp.file_name().unwrap_or_default(); + path_new.pop(); + path_new.push("converted"); + path_new.push(filename); // check if exists match r.render(&path_local, &args) { From acdfe4b423438105baa4cc0d83dfab61680e7221 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 23:02:16 +0100 Subject: [PATCH 10/27] fix: only convert if it hasnt already been converted --- src/bin/update_server-icon.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index ac13ec7..e3440b9 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -239,10 +239,13 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { path_new.push(filename); // check if exists - match r.render(&path_local, &args) { - Ok(_) => log::info!("Successfully rendered all colors of {path_local:?}"), - Err(e) => { - log::error!("Failed to render {path_local:?}: {}", e) + if !path_new.exists() { + // convert if it hasnt been converted already + match r.render(&path_local, &args) { + Ok(_) => log::info!("Successfully rendered all colors of {path_local:?}"), + Err(e) => { + log::error!("Failed to render {path_local:?}: {}", e) + } } } path = path_new; From 1ff993d236654ef806f0a799b76a999a18982869 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 23:04:57 +0100 Subject: [PATCH 11/27] feat: only need to keep whatever ones are in teh current season (if at all) --- src/bin/update_server-icon.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index e3440b9..cce07f9 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -92,6 +92,9 @@ async fn update_icon_main(ctx: Arc) { // get a list of all the graphics files let logos = get_logos(&config_global, &config_toml); + + // filter them so only the current season (if any) are active + let logos_filtered = logos_filter(&festival_data, logos); } struct FestivalData{ @@ -262,4 +265,34 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { } logos +} + +fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec{ + let mut filtered: Vec = vec![]; + + for logo in existing { + let name_lowercase0 = logo.name.to_ascii_lowercase(); + let name_lowercase = name_lowercase0.to_str().unwrap_or_default(); + + // if its a current festival filter based on it + if let Some(x) = &festival_data.current { + if name_lowercase.contains(x) { + filtered.push(logo); + } + } else { + // else filter using the excluded ones + let mut excluded = false; + for festival in &festival_data.exclusions { + if name_lowercase.contains(festival) { + excluded = true; + } + } + + if !excluded { + filtered.push(logo); + } + } + } + + filtered } \ No newline at end of file From 4f96c9087f9ad812fae5d02b739d6d2ad06403d6 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 7 Jun 2025 23:53:24 +0100 Subject: [PATCH 12/27] feat: get a random image --- src/bin/update_server-icon.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index cce07f9..bd2f295 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -15,6 +15,7 @@ use std::process::Command; use chrono::{Datelike, Utc}; use gdk_pixbuf::{Pixbuf, PixbufFormat, PixbufLoader}; use gdk_pixbuf::prelude::PixbufLoaderExt; +use rand::seq::IndexedRandom; use resvg::usvg; use serde::Deserialize; use serenity::all::GuildId; @@ -95,6 +96,9 @@ async fn update_icon_main(ctx: Arc) { // filter them so only the current season (if any) are active let logos_filtered = logos_filter(&festival_data, logos); + + let mut rng = rand::rng(); + let logo_selected = logos_filtered.choose(&mut rng).unwrap(); } struct FestivalData{ From 7bcf30fb3a1c81472e2f64cc473d4bfb61482e75 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 8 Jun 2025 00:18:30 +0100 Subject: [PATCH 13/27] feat: can now set the server icon programmatically --- src/bin/update_server-icon.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index bd2f295..56a6f0b 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -15,10 +15,13 @@ use std::process::Command; use chrono::{Datelike, Utc}; use gdk_pixbuf::{Pixbuf, PixbufFormat, PixbufLoader}; use gdk_pixbuf::prelude::PixbufLoaderExt; +use rand::rngs::SmallRng; +use rand::SeedableRng; use rand::seq::IndexedRandom; use resvg::usvg; use serde::Deserialize; use serenity::all::GuildId; +use serenity::builder::{CreateAttachment, EditGuild}; use tokio::sync::RwLock; use skynet_discord_bot::common::renderer::{Args, Renderer}; @@ -97,8 +100,10 @@ async fn update_icon_main(ctx: Arc) { // filter them so only the current season (if any) are active let logos_filtered = logos_filter(&festival_data, logos); - let mut rng = rand::rng(); + let mut rng = SmallRng::from_os_rng(); let logo_selected = logos_filtered.choose(&mut rng).unwrap(); + + logo_set(&ctx, &server, logo_selected).await; } struct FestivalData{ @@ -299,4 +304,12 @@ fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec, server: &GuildId, logo_selected: &LogoData){ + let icon = CreateAttachment::path(logo_selected.path.to_str().unwrap_or_default()).await.unwrap(); + + // assuming a `guild` has already been bound + let builder = EditGuild::new().icon(Some(&icon)); + server.edit(ctx, builder).await.unwrap(); } \ No newline at end of file From 1555a94656b7ca6aa8519b556b880c50d781808b Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 8 Jun 2025 00:20:38 +0100 Subject: [PATCH 14/27] fix: give a reference where it needed to be --- src/common/renderer.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/renderer.rs b/src/common/renderer.rs index 61d45ea..65af0a4 100644 --- a/src/common/renderer.rs +++ b/src/common/renderer.rs @@ -1,3 +1,6 @@ +// this code is taken from https://github.com/MCorange99/svg2colored-png/tree/main +// I was unable to figure out how to use usvg myself so younked it from here. + use std::path::{Path, PathBuf}; use clap::builder::OsStr; From 51d5904ffdfc71e087e96624b49df0135db68932 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 10 Jun 2025 16:54:13 +0100 Subject: [PATCH 15/27] feat: allow for overlapping festivals --- src/bin/update_server-icon.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index 56a6f0b..d161352 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -107,7 +107,7 @@ async fn update_icon_main(ctx: Arc) { } struct FestivalData{ - current: Option, + current: Vec, exclusions: Vec, } @@ -118,7 +118,7 @@ fn get_festival(config_toml: &ConfigToml)-> FestivalData { let year = today.year(); let mut result = FestivalData { - current: None, + current: vec![], exclusions: vec![], }; @@ -126,9 +126,9 @@ fn get_festival(config_toml: &ConfigToml)-> FestivalData { if (day >= festival.start.day && day <= festival.end.day) && (month >= festival.start.month && month <= festival.end.month ) { if festival.start.year == 0 || festival.end.year == 0 { - result.current = Some(festival.name.to_owned()); + result.current.push(festival.name.to_owned()); } else if (year >= festival.start.year && year <= festival.end.year) { - result.current = Some(festival.name.to_owned()); + result.current.push(festival.name.to_owned()); } } else if !festival.all_year { result.exclusions.push(festival.name.to_owned()); @@ -279,27 +279,27 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec{ let mut filtered: Vec = vec![]; - for logo in existing { + 'outer: for logo in existing { let name_lowercase0 = logo.name.to_ascii_lowercase(); let name_lowercase = name_lowercase0.to_str().unwrap_or_default(); // if its a current festival filter based on it - if let Some(x) = &festival_data.current { - if name_lowercase.contains(x) { + for festival in &festival_data.current { + if name_lowercase.contains(festival) { filtered.push(logo); + continue 'outer; } - } else { - // else filter using the excluded ones - let mut excluded = false; - for festival in &festival_data.exclusions { - if name_lowercase.contains(festival) { - excluded = true; - } + } + // else filter using the excluded ones + let mut excluded = false; + for festival in &festival_data.exclusions { + if name_lowercase.contains(festival) { + excluded = true; } + } - if !excluded { - filtered.push(logo); - } + if !excluded { + filtered.push(logo); } } From 3523dac46e5deed3fb6e680dfd5f0f6113cff58c Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 14 Jun 2025 15:54:07 +0100 Subject: [PATCH 16/27] fix: properly filter icon based on the festival --- src/bin/update_server-icon.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index d161352..a9b7c62 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -283,23 +283,26 @@ fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec Date: Sat, 14 Jun 2025 16:46:04 +0100 Subject: [PATCH 17/27] feat: save the selected image to teh library --- db/migrations/11_server-icons.sql | 8 ++++++ src/bin/update_server-icon.rs | 41 +++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 db/migrations/11_server-icons.sql diff --git a/db/migrations/11_server-icons.sql b/db/migrations/11_server-icons.sql new file mode 100644 index 0000000..37d8b5b --- /dev/null +++ b/db/migrations/11_server-icons.sql @@ -0,0 +1,8 @@ + +CREATE TABLE IF NOT EXISTS server_icons ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + date TEXT NOT NULL +); + +CREATE INDEX IF NOT EXISTS index_name ON server_icons (name); \ No newline at end of file diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index a9b7c62..15db9bd 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -4,8 +4,8 @@ use serenity::{ model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::common::database::{db_init, DataBase}; -use skynet_discord_bot::{get_config, Config}; +use skynet_discord_bot::common::database::{db_init, DataBase, RoleAdder}; +use skynet_discord_bot::{get_config, get_now_iso, Config}; use std::{fs, process, sync::Arc}; use std::ffi::{OsStr, OsString}; use std::fs::File; @@ -22,6 +22,7 @@ use resvg::usvg; use serde::Deserialize; use serenity::all::GuildId; use serenity::builder::{CreateAttachment, EditGuild}; +use sqlx::{Pool, Sqlite}; use tokio::sync::RwLock; use skynet_discord_bot::common::renderer::{Args, Renderer}; @@ -103,7 +104,7 @@ async fn update_icon_main(ctx: Arc) { let mut rng = SmallRng::from_os_rng(); let logo_selected = logos_filtered.choose(&mut rng).unwrap(); - logo_set(&ctx, &server, logo_selected).await; + logo_set(&ctx, &db,&server, logo_selected).await; } struct FestivalData{ @@ -309,10 +310,40 @@ fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec, server: &GuildId, logo_selected: &LogoData){ + +#[derive(Debug, Clone, sqlx::FromRow)] +pub struct ServerIcons { + pub id: i64, + pub name: String, + pub date: String, +} + +async fn logo_set(ctx: &Arc, db: &Pool, server: &GuildId, logo_selected: &LogoData){ + // add to teh database + logo_set_db(db ,logo_selected).await; + let icon = CreateAttachment::path(logo_selected.path.to_str().unwrap_or_default()).await.unwrap(); // assuming a `guild` has already been bound let builder = EditGuild::new().icon(Some(&icon)); server.edit(ctx, builder).await.unwrap(); -} \ No newline at end of file +} + +async fn logo_set_db(db: &Pool, logo_selected: &LogoData){ + let name = logo_selected.name.to_str().unwrap_or_default(); + + sqlx::query_as::<_, ServerIcons>( + " + INSERT OR REPLACE INTO server_icons (name, date) + VALUES (?1, ?2, ?3) + ", + ) + .bind(name) + .bind(get_now_iso(false)) + .fetch_optional(db) + .await; +} + +// fn command(config_toml: &ConfigToml, logo_selected: &LogoData){ +// let web_url = format!("{}/src/branch/main/{}/{}", &config_toml.source.repo, &config_toml.source.directory, &logo_selected.name.to_str().unwrap_or_default()); +// } \ No newline at end of file From 9d50efb75768d83c913ffa9c16952d6e0c083759 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 14 Jun 2025 18:00:40 +0100 Subject: [PATCH 18/27] fmt: cargo+clippy --- Cargo.lock | 311 --------------------------------- Cargo.toml | 8 +- src/bin/update_server-icon.rs | 168 ++++++++---------- src/common/mod.rs | 2 +- src/common/renderer.rs | 316 ++++++++++++++++------------------ 5 files changed, 222 insertions(+), 583 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f38aa1..66e67f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,56 +104,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "0.6.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.59.0", -] - [[package]] name = "anyhow" version = "1.0.93" @@ -472,16 +422,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cfg-expr" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2b34126159980f92da2a08bdec0694fd80fb5eb9e48aff25d20a0d8dfa710d" -dependencies = [ - "smallvec", - "target-lexicon", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -521,46 +461,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "clap" -version = "4.5.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.89", -] - -[[package]] -name = "clap_lex" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" - [[package]] name = "color-eyre" version = "0.6.5" @@ -594,22 +494,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "colored" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" -dependencies = [ - "lazy_static", - "windows-sys 0.59.0", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1222,31 +1106,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "gdk-pixbuf" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd242894c084f4beed508a56952750bce3e96e85eb68fdc153637daa163e10c" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b34f3b580c988bd217e9543a2de59823fafae369d1a055555e5f95a8b130b96" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1317,80 +1176,6 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" -[[package]] -name = "gio" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a5c3829f5794cb15120db87707b2ec03720edff7ad09eb7b711b532e3fe747" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "pin-project-lite", - "smallvec", -] - -[[package]] -name = "gio-sys" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "windows-sys 0.59.0", -] - -[[package]] -name = "glib" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c501c495842c2b23cdacead803a5a343ca2a5d7a7ddaff14cc5f6cf22cfb92c2" -dependencies = [ - "bitflags 2.6.0", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "smallvec", -] - -[[package]] -name = "glib-macros" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe6dc9ce29887c4b3b74d78d5ba473db160a258ae7ed883d23632ac7fed7bc9" -dependencies = [ - "heck", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.89", -] - -[[package]] -name = "glib-sys" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215" -dependencies = [ - "libc", - "system-deps", -] - [[package]] name = "gloo-timers" version = "0.3.0" @@ -1403,17 +1188,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gobject-sys" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - [[package]] name = "h2" version = "0.3.26" @@ -1983,12 +1757,6 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "isahc" version = "0.9.14" @@ -2341,15 +2109,6 @@ dependencies = [ "libm", ] -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - [[package]] name = "object" version = "0.36.4" @@ -2365,12 +2124,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" - [[package]] name = "opaque-debug" version = "0.3.1" @@ -2601,15 +2354,6 @@ dependencies = [ "zerocopy 0.7.35", ] -[[package]] -name = "proc-macro-crate" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" -dependencies = [ - "toml_edit", -] - [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -3357,18 +3101,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "simple_logger" -version = "4.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" -dependencies = [ - "colored", - "log", - "time 0.3.36", - "windows-sys 0.48.0", -] - [[package]] name = "simplecss" version = "0.2.2" @@ -3389,20 +3121,16 @@ name = "skynet_discord_bot" version = "0.1.0" dependencies = [ "chrono", - "clap", "color-eyre", "dotenvy", "eyre", - "gdk-pixbuf", "lettre", - "log", "maud", "rand 0.9.0", "resvg", "serde", "serde_json", "serenity", - "simple_logger", "sqlx", "surf", "tiny-skia", @@ -3764,12 +3492,6 @@ dependencies = [ "unicode-properties", ] -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "subtle" version = "2.6.1" @@ -3919,25 +3641,6 @@ dependencies = [ "libc", ] -[[package]] -name = "system-deps" -version = "7.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" -dependencies = [ - "cfg-expr", - "heck", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "target-lexicon" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" - [[package]] name = "tempfile" version = "3.12.0" @@ -4024,9 +3727,7 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", - "libc", "num-conv", - "num_threads", "powerfmt", "serde", "time-core", @@ -4547,12 +4248,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "valuable" version = "0.1.1" @@ -4571,12 +4266,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version-compare" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" - [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index deeeca5..3dffd41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,14 +48,10 @@ maud = "0.27" toml = "0.8.23" serde = "1.0" -gdk-pixbuf = "0.20.10" -clap = { version = "4.1.4", features = ["derive"] } - +# for image conversion eyre = "0.6.8" color-eyre = "0.6.2" usvg-text-layout = "0.29.0" usvg = "0.29.0" resvg = "0.29.0" -tiny-skia = "0.8.3" -log = "0.4.20" -simple_logger = "4.2.0" \ No newline at end of file +tiny-skia = "0.8.3" \ No newline at end of file diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index 15db9bd..5b42ac1 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -1,30 +1,30 @@ +use chrono::{Datelike, Utc}; +use rand::{rngs::SmallRng, seq::IndexedRandom, SeedableRng}; +use serde::Deserialize; use serenity::{ + all::GuildId, async_trait, + builder::{CreateAttachment, EditGuild}, client::{Context, EventHandler}, model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::common::database::{db_init, DataBase, RoleAdder}; -use skynet_discord_bot::{get_config, get_now_iso, Config}; -use std::{fs, process, sync::Arc}; -use std::ffi::{OsStr, OsString}; -use std::fs::File; -use std::io::Read; -use std::path::PathBuf; -use std::process::Command; -use chrono::{Datelike, Utc}; -use gdk_pixbuf::{Pixbuf, PixbufFormat, PixbufLoader}; -use gdk_pixbuf::prelude::PixbufLoaderExt; -use rand::rngs::SmallRng; -use rand::SeedableRng; -use rand::seq::IndexedRandom; -use resvg::usvg; -use serde::Deserialize; -use serenity::all::GuildId; -use serenity::builder::{CreateAttachment, EditGuild}; +use skynet_discord_bot::{ + common::{ + database::{db_init, DataBase}, + renderer::{Args, Renderer}, + }, + get_config, get_now_iso, Config, +}; use sqlx::{Pool, Sqlite}; +use std::{ + ffi::OsString, + fs, + path::PathBuf, + process::{self, Command}, + sync::Arc, +}; use tokio::sync::RwLock; -use skynet_discord_bot::common::renderer::{Args, Renderer}; #[tokio::main] async fn main() { @@ -61,7 +61,6 @@ impl EventHandler for Handler { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); - // pull in the open governance repo // u[date committee server update_icon_main(Arc::clone(&ctx)).await; @@ -82,19 +81,17 @@ async fn update_icon_main(ctx: Arc) { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() }; - - + let config_global = config_lock.read().await; let config_toml = get_config_icons(); let server = GuildId::new(689189992417067052); - - + // clone repo into local folder clone_repo(&config_global, &config_toml); - + // see if there is a current festival let festival_data = get_festival(&config_toml); - + // get a list of all the graphics files let logos = get_logos(&config_global, &config_toml); @@ -104,31 +101,29 @@ async fn update_icon_main(ctx: Arc) { let mut rng = SmallRng::from_os_rng(); let logo_selected = logos_filtered.choose(&mut rng).unwrap(); - logo_set(&ctx, &db,&server, logo_selected).await; + logo_set(&ctx, &db, &server, logo_selected).await; } -struct FestivalData{ +#[derive(Debug)] +struct FestivalData { current: Vec, exclusions: Vec, } -fn get_festival(config_toml: &ConfigToml)-> FestivalData { +fn get_festival(config_toml: &ConfigToml) -> FestivalData { let today = Utc::now(); let day = today.day(); let month = today.month(); let year = today.year(); - + let mut result = FestivalData { current: vec![], exclusions: vec![], }; - + for festival in &config_toml.festivals { - if (day >= festival.start.day && day <= festival.end.day) && - (month >= festival.start.month && month <= festival.end.month ) { - if festival.start.year == 0 || festival.end.year == 0 { - result.current.push(festival.name.to_owned()); - } else if (year >= festival.start.year && year <= festival.end.year) { + if (day >= festival.start.day && day <= festival.end.day) && (month >= festival.start.month && month <= festival.end.month) { + if festival.start.year == 0 || festival.end.year == 0 || (year >= festival.start.year && year <= festival.end.year) { result.current.push(festival.name.to_owned()); } } else if !festival.all_year { @@ -144,24 +139,24 @@ struct ConfigToml { source: ConfigTomlSource, festivals: Vec, } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] struct ConfigTomlSource { repo: String, directory: String, } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] struct ConfigTomlFestivals { name: String, all_year: bool, start: ConfigTomlFestivalsTime, end: ConfigTomlFestivalsTime, } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] struct ConfigTomlFestivalsTime { day: u32, month: u32, - year:i32 + year: i32, } fn get_config_icons() -> ConfigToml { @@ -169,56 +164,34 @@ fn get_config_icons() -> ConfigToml { let config: ConfigToml = toml::from_str(toml_raw).unwrap(); config } -fn clone_repo(config: &Config, config_toml: &ConfigToml){ +fn clone_repo(config: &Config, config_toml: &ConfigToml) { let url = &config_toml.source.repo; let folder = format!("{}/open-governance", &config.home); - Command::new("git") - .arg("clone") - .arg(url) - .arg(&folder) - .output() - .expect("failed to execute process"); + Command::new("git").arg("clone").arg(url).arg(&folder).output().expect("failed to execute process"); Command::new("git") - .arg("pull") - .arg("origin") - .arg("main") - .current_dir(&folder) - .output() - .expect("failed to execute process"); -} - -fn convert_svg_to_png(original: &PathBuf, out: &PathBuf){ - let mut f = File::open(original).unwrap(); - let mut buffer = Vec::new(); - // read the whole file - f.read_to_end(&mut buffer).unwrap(); - let loader = PixbufLoader::with_mime_type("image/svg+xml") - .expect("error loader"); - loader.write(&buffer).expect("TODO: panic message"); - loader.close().expect("TODO: panic message"); - let pixbuf = loader.pixbuf().expect("no pixbuf"); - - let (width, height) = (pixbuf.width(), pixbuf.height()); - println!("size: {}x{}", width, height); - let bytes: Vec = - pixbuf.save_to_bufferv("png", &[]).expect("must not error"); - fs::write(out, &bytes).expect("TODO: panic message"); + .arg("pull") + .arg("origin") + .arg("main") + .current_dir(&folder) + .output() + .expect("failed to execute process"); } +#[derive(Debug)] struct LogoData { name: OsString, path: PathBuf, } -fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { +fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); let folder_path = PathBuf::from(&folder); let mut folder_output = folder_path.clone(); folder_output.push("converted"); let paths = fs::read_dir(folder).unwrap(); - let args = Args{ + let args = Args { input: folder_path.clone(), output: folder_output, colors: String::from(""), @@ -226,15 +199,15 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { height: 1024, }; let mut r = Renderer::new(&args).unwrap(); - + let mut logos = vec![]; - + for tmp in paths.flatten() { let path_local = tmp.path().to_owned(); let path_local2 = tmp.path().to_owned(); let name = path_local2.file_name().unwrap().to_owned(); let mut path = tmp.path(); - + if path.is_dir() { continue; } @@ -250,14 +223,14 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { path_new.pop(); path_new.push("converted"); path_new.push(filename); - + // check if exists if !path_new.exists() { // convert if it hasnt been converted already match r.render(&path_local, &args) { - Ok(_) => log::info!("Successfully rendered all colors of {path_local:?}"), - Err(e) => { - log::error!("Failed to render {path_local:?}: {}", e) + Ok(_) => {} + Err(_e) => { + dbg!("Failed to render {path_local:?}: {}"); } } } @@ -266,18 +239,18 @@ fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { } }; - logos.push(LogoData{ + logos.push(LogoData { name, path, }); - println!("Name: {}", &tmp.path().display()); + // println!("Name: {}", &tmp.path().display()); } - + logos } -fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec{ +fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec { let mut filtered: Vec = vec![]; 'outer: for logo in existing { @@ -310,7 +283,6 @@ fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec, db: &Pool, server: &GuildId, logo_selected: &LogoData){ +async fn logo_set(ctx: &Arc, db: &Pool, server: &GuildId, logo_selected: &LogoData) { // add to teh database - logo_set_db(db ,logo_selected).await; + logo_set_db(db, logo_selected).await; let icon = CreateAttachment::path(logo_selected.path.to_str().unwrap_or_default()).await.unwrap(); @@ -329,21 +301,27 @@ async fn logo_set(ctx: &Arc, db: &Pool, server: &GuildId, logo server.edit(ctx, builder).await.unwrap(); } -async fn logo_set_db(db: &Pool, logo_selected: &LogoData){ +async fn logo_set_db(db: &Pool, logo_selected: &LogoData) { let name = logo_selected.name.to_str().unwrap_or_default(); - sqlx::query_as::<_, ServerIcons>( + match sqlx::query_as::<_, ServerIcons>( " INSERT OR REPLACE INTO server_icons (name, date) - VALUES (?1, ?2, ?3) + VALUES (?1, ?2) ", ) - .bind(name) - .bind(get_now_iso(false)) - .fetch_optional(db) - .await; + .bind(name) + .bind(get_now_iso(false)) + .fetch_optional(db) + .await + { + Ok(_) => {} + Err(e) => { + dbg!(e); + } + } } // fn command(config_toml: &ConfigToml, logo_selected: &LogoData){ // let web_url = format!("{}/src/branch/main/{}/{}", &config_toml.source.repo, &config_toml.source.directory, &logo_selected.name.to_str().unwrap_or_default()); -// } \ No newline at end of file +// } diff --git a/src/common/mod.rs b/src/common/mod.rs index 7b93f40..b82b7bb 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -3,4 +3,4 @@ pub mod minecraft; pub mod set_roles; pub mod wolves; -pub mod renderer; \ No newline at end of file +pub mod renderer; diff --git a/src/common/renderer.rs b/src/common/renderer.rs index 65af0a4..f99d891 100644 --- a/src/common/renderer.rs +++ b/src/common/renderer.rs @@ -1,216 +1,192 @@ // this code is taken from https://github.com/MCorange99/svg2colored-png/tree/main // I was unable to figure out how to use usvg myself so younked it from here. +use std::ffi::OsStr; use std::path::{Path, PathBuf}; -use clap::builder::OsStr; -use clap::Parser; -use color_eyre::{Result, eyre::bail}; +// use clap::builder::OsStr; +use color_eyre::{eyre::bail, Result}; use usvg_text_layout::TreeTextToPath; -#[derive(Parser, Debug, Clone)] -#[command(name = "svg2colored-png")] -#[command(author = "MCorange ")] -#[command(version = env!("CARGO_PKG_VERSION"))] -#[command(about = "Converts svgs to multiple png's that differ in color", long_about = "Made by MCorange ")] +#[derive(Debug, Clone)] pub struct Args { - /// Input folder with the SVG's - #[arg(long, short)] - pub input: PathBuf, + pub input: PathBuf, - /// Output folder where the PNG's will be placed - #[arg[long, short]] - pub output: PathBuf, + /// Output folder where the PNG's will be placed + pub output: PathBuf, - /// Comma seperated colors that will be used in HEX Eg. 000000,ffffff - /// Can be like an object: black:000000,white:ffffff - #[arg[long, short, default_value_t = String::from("0d6efd,6c757d,198754,0dcaf0,ffc107,dc3545,f8f9fa,212529,ffffff,000000")]] - pub colors: String, + /// Comma seperated colors that will be used in HEX Eg. 000000,ffffff + /// Can be like an object: black:000000,white:ffffff + pub colors: String, - /// Width of the generated PNG's - #[arg(long, default_value_t = 1024)] - pub width: u32, - - /// Height of the generated PNG's - #[arg(long, default_value_t = 1024)] - pub height: u32 + /// Width of the generated PNG's + pub width: u32, + /// Height of the generated PNG's + pub height: u32, } #[derive(Debug, Clone)] enum ColorType { - Array(Vec), - Object(Vec<(String, String)>), - None + Array(Vec), + Object(Vec<(String, String)>), + None, } - #[derive(Debug, Clone)] pub struct Renderer { - fontdb: usvg_text_layout::fontdb::Database, - colors: ColorType, - size: (u32, u32), - pub count: u64, + fontdb: usvg_text_layout::fontdb::Database, + colors: ColorType, + size: (u32, u32), + pub count: u64, } impl Renderer { - pub fn new(args: &Args) -> Result { - let mut db = usvg_text_layout::fontdb::Database::new(); - db.load_system_fonts(); + pub fn new(args: &Args) -> Result { + let mut db = usvg_text_layout::fontdb::Database::new(); + db.load_system_fonts(); - let mut this = Self { - fontdb: db, - colors: ColorType::None, - size: (args.width, args.height), - count: 0, - }; + let mut this = Self { + fontdb: db, + colors: ColorType::None, + size: (args.width, args.height), + count: 0, + }; - let colors = if args.colors.contains(':') { - //? object - let obj = args.colors.split(',').map(|s| { - let s = s.split(':').collect::>(); + let colors = if args.colors.contains(':') { + //? object + let obj = args + .colors + .split(',') + .map(|s| { + let s = s.split(':').collect::>(); - if s.len() < 2 { - log::error!("Invalid color object, try checking help"); - return None; - } + if s.len() < 2 { + dbg!("Invalid color object, try checking help"); + return None; + } - Some((s[0].to_string(), s[1].to_string())) - }).collect::>>(); + Some((s[0].to_string(), s[1].to_string())) + }) + .collect::>>(); - let mut colors = Vec::new(); + let mut colors = Vec::new(); - for c in obj { - if let Some(c) = c { - std::fs::create_dir_all(args.output.join(&c.0))?; + for c in obj.into_iter().flatten() { + std::fs::create_dir_all(args.output.join(&c.0))?; - colors.push(c); - } - } + colors.push(c); + } - ColorType::Object(colors) + ColorType::Object(colors) + } else { + //? list + // let colors = args.colors.split(",").map(|s| { + // s.to_string() + // }) + // .collect::>(); - } else { - //? list - // let colors = args.colors.split(",").map(|s| { - // s.to_string() - // }) - // .collect::>(); + let mut colors = Vec::new(); - let mut colors = Vec::new(); + for color in args.colors.split(',') { + std::fs::create_dir_all(args.output.join(color))?; + colors.push(color.to_string()) + } - for color in args.colors.split(',') { - std::fs::create_dir_all(args.output.join(&color))?; - colors.push(color.to_string()) - } + ColorType::Array(colors) + }; + this.colors = colors; + Ok(this) + } - ColorType::Array(colors) - }; + pub fn render(&mut self, fi: &Path, args: &Args) -> Result<()> { + match fi.extension() { + Some(e) if e.to_str() == Some("svg") => {} + Some(_) | None => { + dbg!("Filer {:?} is not of type SVG", fi); + // util::logger::warning(format!("File '{}' is not of SVG type", fi.clone().to_str().unwrap())); + bail!("Failed to render"); + } + }; - this.colors = colors; - Ok(this) - } - - - pub fn render(&mut self, fi: &Path, args: &Args) -> Result<()> { - match fi.extension() { - Some(e) if e.to_str() == Some("svg") => {}, - Some(_) | - None => { - log::warn!("Filer {:?} is not of type SVG", fi); - // util::logger::warning(format!("File '{}' is not of SVG type", fi.clone().to_str().unwrap())); - bail!("Failed to render"); - } - }; - - match self.colors.clone() { - ColorType::Array(c) => { - for color in c { - log::info!("Rendering the color {color:?}"); - let fo = self.get_out_file(fi, &color, &args); - self.render_one(fi, &fo, &color)?; - - } - }, - ColorType::Object(c) => { - for o in c { - log::info!("Rendering the color {:?}", o); - let fo = self.get_out_file(fi, &o.0, &args); - self.render_one(fi, &fo, &o.1)?; - - } - }, - ColorType::None => unreachable!(), + match self.colors.clone() { + ColorType::Array(c) => { + for color in c { + // log::info!("Rendering the color {color:?}"); + let fo = self.get_out_file(fi, &color, args); + self.render_one(fi, &fo, &color)?; } - - Ok(()) - } - - fn render_one(&mut self, fi: &Path, fo: &Path, color: &String) -> Result<()>{ - - if fo.exists() { - log::warn!("File {fo:?} exists, skipping"); - return Ok(()); + } + ColorType::Object(c) => { + for o in c { + // log::info!("Rendering the color {:?}", o); + let fo = self.get_out_file(fi, &o.0, args); + self.render_one(fi, &fo, &o.1)?; } - - let svg = self.set_color(&self.get_svg_data(fi)?, &color); - - let mut opt = usvg::Options::default(); - // Get file's absolute directory. - opt.resources_dir = std::fs::canonicalize(fi.clone()) - .ok() - .and_then(|p| p.parent().map(|p| p.to_path_buf())); - - let mut tree = match usvg::Tree::from_data(svg.as_bytes(), &opt) { - Ok(v) => v, - Err(_) => { - log::error!("Failed to parse {fi:?}"); - bail!(""); - } - }; - - tree.convert_text(&self.fontdb); - - let mut pixmap = tiny_skia::Pixmap::new(self.size.0, self.size.1).unwrap(); - - log::info!("Rendering {fo:?}"); - - //? maybe handle this and possibly throw error if its none - let _ = resvg::render( - &tree, - usvg::FitTo::Size(self.size.0, self.size.1), - tiny_skia::Transform::default(), - pixmap.as_mut(), - ); - - - pixmap.save_png(fo)?; - self.count += 1; - Ok(()) + } + ColorType::None => unreachable!(), } + Ok(()) + } - #[inline] - fn get_out_file(&mut self, fi: &Path, sub_folder: &String, args: &Args) -> PathBuf { - let mut fo: std::path::PathBuf = args.output.clone(); - // fo.push(sub_folder); - fo.push(fi.file_name().unwrap_or(&OsStr::from("default")).to_str().unwrap_or("default").replace(".svg", "")); - fo.set_extension("png"); - fo + fn render_one(&mut self, fi: &Path, fo: &Path, color: &String) -> Result<()> { + if fo.exists() { + dbg!("File {fo:?} exists, skipping"); + return Ok(()); } - fn set_color(&self, svg: &String, color: &String) -> String { - svg.replace("fill=\"currentColor\"", &format!("fill=\"#{}\"", color)) - } + let svg = self.set_color(&self.get_svg_data(fi)?, color); - fn get_svg_data(&self, fi: &Path) -> Result{ - match std::fs::read_to_string(fi) { - Ok(d) => Ok(d), - Err(_) => { - log::error!("File {fi:?} does not exist"); - bail!("File {fi:?} does not exist"); - } - } + let opt = usvg::Options { + // Get file's absolute directory. + resources_dir: std::fs::canonicalize(fi).ok().and_then(|p| p.parent().map(|p| p.to_path_buf())), + ..Default::default() + }; + + let mut tree = match usvg::Tree::from_data(svg.as_bytes(), &opt) { + Ok(v) => v, + Err(_) => { + dbg!("Failed to parse {fi:?}"); + bail!(""); + } + }; + + tree.convert_text(&self.fontdb); + + let mut pixmap = tiny_skia::Pixmap::new(self.size.0, self.size.1).unwrap(); + + // log::info!("Rendering {fo:?}"); + + //? maybe handle this and possibly throw error if its none + let _ = resvg::render(&tree, usvg::FitTo::Size(self.size.0, self.size.1), tiny_skia::Transform::default(), pixmap.as_mut()); + + pixmap.save_png(fo)?; + self.count += 1; + Ok(()) + } + + #[inline] + fn get_out_file(&mut self, fi: &Path, _sub_folder: &str, args: &Args) -> PathBuf { + let mut fo: std::path::PathBuf = args.output.clone(); + // fo.push(sub_folder); + fo.push(fi.file_name().unwrap_or(OsStr::new("default")).to_str().unwrap_or("default").replace(".svg", "")); + fo.set_extension("png"); + fo + } + + fn set_color(&self, svg: &str, color: &String) -> String { + svg.replace("fill=\"currentColor\"", &format!("fill=\"#{}\"", color)) + } + + fn get_svg_data(&self, fi: &Path) -> Result { + match std::fs::read_to_string(fi) { + Ok(d) => Ok(d), + Err(_) => { + dbg!("File {fi:?} does not exist"); + bail!("File {fi:?} does not exist"); + } } -} \ No newline at end of file + } +} From 6d5ad8e418ceef1e1f87c6829ef13cd57990f343 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 15 Jun 2025 22:07:52 +0100 Subject: [PATCH 19/27] feat: split out the functions so they can be shared with commands --- src/bin/update_server-icon.rs | 293 +++------------------------------- src/common/mod.rs | 1 + src/common/server_icon.rs | 263 ++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 274 deletions(-) create mode 100644 src/common/server_icon.rs diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index 5b42ac1..4d49490 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -1,27 +1,16 @@ -use chrono::{Datelike, Utc}; -use rand::{rngs::SmallRng, seq::IndexedRandom, SeedableRng}; -use serde::Deserialize; use serenity::{ - all::GuildId, async_trait, - builder::{CreateAttachment, EditGuild}, client::{Context, EventHandler}, model::gateway::{GatewayIntents, Ready}, Client, }; +use skynet_discord_bot::common::server_icon; use skynet_discord_bot::{ - common::{ - database::{db_init, DataBase}, - renderer::{Args, Renderer}, - }, - get_config, get_now_iso, Config, + common::database::{db_init, DataBase}, + get_config, Config, }; -use sqlx::{Pool, Sqlite}; use std::{ - ffi::OsString, - fs, - path::PathBuf, - process::{self, Command}, + process::self, sync::Arc, }; use tokio::sync::RwLock; @@ -61,267 +50,23 @@ impl EventHandler for Handler { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); - // pull in the open governance repo - // u[date committee server - update_icon_main(Arc::clone(&ctx)).await; + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + + let config_global = config_lock.read().await; + let config_toml = server_icon::get_config_icons(); + + server_icon::update_icon::update_icon_main(&ctx, &db, &config_global, &config_toml).await; // finish up process::exit(0); } } - -async fn update_icon_main(ctx: Arc) { - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - let db = db_lock.read().await; - - let config_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Config in TypeMap.").clone() - }; - - let config_global = config_lock.read().await; - let config_toml = get_config_icons(); - let server = GuildId::new(689189992417067052); - - // clone repo into local folder - clone_repo(&config_global, &config_toml); - - // see if there is a current festival - let festival_data = get_festival(&config_toml); - - // get a list of all the graphics files - let logos = get_logos(&config_global, &config_toml); - - // filter them so only the current season (if any) are active - let logos_filtered = logos_filter(&festival_data, logos); - - let mut rng = SmallRng::from_os_rng(); - let logo_selected = logos_filtered.choose(&mut rng).unwrap(); - - logo_set(&ctx, &db, &server, logo_selected).await; -} - -#[derive(Debug)] -struct FestivalData { - current: Vec, - exclusions: Vec, -} - -fn get_festival(config_toml: &ConfigToml) -> FestivalData { - let today = Utc::now(); - let day = today.day(); - let month = today.month(); - let year = today.year(); - - let mut result = FestivalData { - current: vec![], - exclusions: vec![], - }; - - for festival in &config_toml.festivals { - if (day >= festival.start.day && day <= festival.end.day) && (month >= festival.start.month && month <= festival.end.month) { - if festival.start.year == 0 || festival.end.year == 0 || (year >= festival.start.year && year <= festival.end.year) { - result.current.push(festival.name.to_owned()); - } - } else if !festival.all_year { - result.exclusions.push(festival.name.to_owned()); - } - } - - result -} - -#[derive(Deserialize)] -struct ConfigToml { - source: ConfigTomlSource, - festivals: Vec, -} -#[derive(Deserialize, Debug)] -struct ConfigTomlSource { - repo: String, - directory: String, -} - -#[derive(Deserialize, Debug)] -struct ConfigTomlFestivals { - name: String, - all_year: bool, - start: ConfigTomlFestivalsTime, - end: ConfigTomlFestivalsTime, -} -#[derive(Deserialize, Debug)] -struct ConfigTomlFestivalsTime { - day: u32, - month: u32, - year: i32, -} - -fn get_config_icons() -> ConfigToml { - let toml_raw = include_str!("../../.server-icons.toml"); - let config: ConfigToml = toml::from_str(toml_raw).unwrap(); - config -} -fn clone_repo(config: &Config, config_toml: &ConfigToml) { - let url = &config_toml.source.repo; - let folder = format!("{}/open-governance", &config.home); - - Command::new("git").arg("clone").arg(url).arg(&folder).output().expect("failed to execute process"); - - Command::new("git") - .arg("pull") - .arg("origin") - .arg("main") - .current_dir(&folder) - .output() - .expect("failed to execute process"); -} - -#[derive(Debug)] -struct LogoData { - name: OsString, - path: PathBuf, -} -fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { - let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); - let folder_path = PathBuf::from(&folder); - let mut folder_output = folder_path.clone(); - folder_output.push("converted"); - let paths = fs::read_dir(folder).unwrap(); - - let args = Args { - input: folder_path.clone(), - output: folder_output, - colors: String::from(""), - width: 1024, - height: 1024, - }; - let mut r = Renderer::new(&args).unwrap(); - - let mut logos = vec![]; - - for tmp in paths.flatten() { - let path_local = tmp.path().to_owned(); - let path_local2 = tmp.path().to_owned(); - let name = path_local2.file_name().unwrap().to_owned(); - let mut path = tmp.path(); - - if path.is_dir() { - continue; - } - - match tmp.path().extension() { - None => {} - Some(ext) => { - if ext == "svg" { - let mut path_new = path_local.clone(); - path_new.set_extension("png"); - let filename_tmp = path_new.clone(); - let filename = filename_tmp.file_name().unwrap_or_default(); - path_new.pop(); - path_new.push("converted"); - path_new.push(filename); - - // check if exists - if !path_new.exists() { - // convert if it hasnt been converted already - match r.render(&path_local, &args) { - Ok(_) => {} - Err(_e) => { - dbg!("Failed to render {path_local:?}: {}"); - } - } - } - path = path_new; - } - } - }; - - logos.push(LogoData { - name, - path, - }); - - // println!("Name: {}", &tmp.path().display()); - } - - logos -} - -fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec { - let mut filtered: Vec = vec![]; - - 'outer: for logo in existing { - let name_lowercase0 = logo.name.to_ascii_lowercase(); - let name_lowercase = name_lowercase0.to_str().unwrap_or_default(); - - if !festival_data.current.is_empty() { - // if its a current festival filter based on it - for festival in &festival_data.current { - if name_lowercase.contains(festival) { - filtered.push(logo); - continue 'outer; - } - } - } else { - // else filter using the excluded ones - let mut excluded = false; - for festival in &festival_data.exclusions { - if name_lowercase.contains(festival) { - excluded = true; - } - } - - if !excluded { - filtered.push(logo); - } - } - } - - filtered -} - -#[derive(Debug, Clone, sqlx::FromRow)] -pub struct ServerIcons { - pub id: i64, - pub name: String, - pub date: String, -} - -async fn logo_set(ctx: &Arc, db: &Pool, server: &GuildId, logo_selected: &LogoData) { - // add to teh database - logo_set_db(db, logo_selected).await; - - let icon = CreateAttachment::path(logo_selected.path.to_str().unwrap_or_default()).await.unwrap(); - - // assuming a `guild` has already been bound - let builder = EditGuild::new().icon(Some(&icon)); - server.edit(ctx, builder).await.unwrap(); -} - -async fn logo_set_db(db: &Pool, logo_selected: &LogoData) { - let name = logo_selected.name.to_str().unwrap_or_default(); - - match sqlx::query_as::<_, ServerIcons>( - " - INSERT OR REPLACE INTO server_icons (name, date) - VALUES (?1, ?2) - ", - ) - .bind(name) - .bind(get_now_iso(false)) - .fetch_optional(db) - .await - { - Ok(_) => {} - Err(e) => { - dbg!(e); - } - } -} - -// fn command(config_toml: &ConfigToml, logo_selected: &LogoData){ -// let web_url = format!("{}/src/branch/main/{}/{}", &config_toml.source.repo, &config_toml.source.directory, &logo_selected.name.to_str().unwrap_or_default()); -// } diff --git a/src/common/mod.rs b/src/common/mod.rs index b82b7bb..9e1745e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -4,3 +4,4 @@ pub mod set_roles; pub mod wolves; pub mod renderer; +pub mod server_icon; diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs new file mode 100644 index 0000000..4a489be --- /dev/null +++ b/src/common/server_icon.rs @@ -0,0 +1,263 @@ +use serde::Deserialize; +use std::{ffi::OsString, path::PathBuf}; + +#[derive(Deserialize)] +pub struct ConfigToml { + pub source: ConfigTomlSource, + pub festivals: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct ConfigTomlSource { + pub repo: String, + pub directory: String, +} + +#[derive(Deserialize, Debug)] +pub struct ConfigTomlFestivals { + pub name: String, + pub all_year: bool, + pub start: ConfigTomlFestivalsTime, + pub end: ConfigTomlFestivalsTime, +} + +#[derive(Deserialize, Debug)] +pub struct ConfigTomlFestivalsTime { + pub day: u32, + pub month: u32, + pub year: i32, +} + +pub fn get_config_icons() -> ConfigToml { + let toml_raw = include_str!("../../.server-icons.toml"); + let config: ConfigToml = toml::from_str(toml_raw).unwrap(); + config +} + +#[derive(Debug)] +pub struct LogoData { + pub name: OsString, + pub path: PathBuf, +} + +#[derive(Debug, Clone, sqlx::FromRow)] +pub struct ServerIcons { + pub id: i64, + pub name: String, + pub date: String, +} + +pub mod update_icon { + use super::*; + use crate::{ + common::renderer::{Args, Renderer}, + get_now_iso, Config, + }; + use chrono::{Datelike, Utc}; + use rand::{rngs::SmallRng, seq::IndexedRandom, SeedableRng}; + use serenity::{ + all::GuildId, + builder::{CreateAttachment, EditGuild}, + client::Context, + }; + use sqlx::{Pool, Sqlite}; + use std::{fs, process::Command}; + + /// Update the server icon, pulling from open governance. + pub async fn update_icon_main(ctx: &Context, db: &Pool, config_global: &Config, config_toml: &ConfigToml) { + let server = GuildId::new(689189992417067052); + + // clone repo into local folder + clone_repo(config_global, config_toml); + + // see if there is a current festival + let festival_data = get_festival(config_toml); + + // get a list of all the graphics files + let logos = get_logos(config_global, config_toml); + + // filter them so only the current season (if any) are active + let logos_filtered = logos_filter(&festival_data, logos); + + let mut rng = SmallRng::from_os_rng(); + let logo_selected = logos_filtered.choose(&mut rng).unwrap(); + + logo_set(ctx, db, &server, logo_selected).await; + } + + #[derive(Debug)] + struct FestivalData { + current: Vec, + exclusions: Vec, + } + + fn get_festival(config_toml: &ConfigToml) -> FestivalData { + let today = Utc::now(); + let day = today.day(); + let month = today.month(); + let year = today.year(); + + let mut result = FestivalData { + current: vec![], + exclusions: vec![], + }; + + for festival in &config_toml.festivals { + if (day >= festival.start.day && day <= festival.end.day) && (month >= festival.start.month && month <= festival.end.month) { + if festival.start.year == 0 || festival.end.year == 0 || (year >= festival.start.year && year <= festival.end.year) { + result.current.push(festival.name.to_owned()); + } + } else if !festival.all_year { + result.exclusions.push(festival.name.to_owned()); + } + } + + result + } + + fn clone_repo(config: &Config, config_toml: &ConfigToml) { + let url = &config_toml.source.repo; + let folder = format!("{}/open-governance", &config.home); + + Command::new("git").arg("clone").arg(url).arg(&folder).output().expect("failed to execute process"); + + Command::new("git") + .arg("pull") + .arg("origin") + .arg("main") + .current_dir(&folder) + .output() + .expect("failed to execute process"); + } + + fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { + let folder = format!("{}/open-governance/{}", &config.home, &config_toml.source.directory); + let folder_path = PathBuf::from(&folder); + let mut folder_output = folder_path.clone(); + folder_output.push("converted"); + let paths = fs::read_dir(folder).unwrap(); + + let args = Args { + input: folder_path.clone(), + output: folder_output, + colors: String::from(""), + width: 1024, + height: 1024, + }; + let mut r = Renderer::new(&args).unwrap(); + + let mut logos = vec![]; + + for tmp in paths.flatten() { + let path_local = tmp.path().to_owned(); + let path_local2 = tmp.path().to_owned(); + let name = path_local2.file_name().unwrap().to_owned(); + let mut path = tmp.path(); + + if path.is_dir() { + continue; + } + + match tmp.path().extension() { + None => {} + Some(ext) => { + if ext == "svg" { + let mut path_new = path_local.clone(); + path_new.set_extension("png"); + let filename_tmp = path_new.clone(); + let filename = filename_tmp.file_name().unwrap_or_default(); + path_new.pop(); + path_new.push("converted"); + path_new.push(filename); + + // check if exists + if !path_new.exists() { + // convert if it hasnt been converted already + match r.render(&path_local, &args) { + Ok(_) => {} + Err(_e) => { + dbg!("Failed to render {path_local:?}: {}"); + } + } + } + path = path_new; + } + } + }; + + logos.push(LogoData { + name, + path, + }); + + // println!("Name: {}", &tmp.path().display()); + } + + logos + } + + fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec { + let mut filtered: Vec = vec![]; + + 'outer: for logo in existing { + let name_lowercase0 = logo.name.to_ascii_lowercase(); + let name_lowercase = name_lowercase0.to_str().unwrap_or_default(); + + if !festival_data.current.is_empty() { + // if its a current festival filter based on it + for festival in &festival_data.current { + if name_lowercase.contains(festival) { + filtered.push(logo); + continue 'outer; + } + } + } else { + // else filter using the excluded ones + let mut excluded = false; + for festival in &festival_data.exclusions { + if name_lowercase.contains(festival) { + excluded = true; + } + } + + if !excluded { + filtered.push(logo); + } + } + } + + filtered + } + + async fn logo_set(ctx: &Context, db: &Pool, server: &GuildId, logo_selected: &LogoData) { + // add to teh database + logo_set_db(db, logo_selected).await; + + let icon = CreateAttachment::path(logo_selected.path.to_str().unwrap_or_default()).await.unwrap(); + + // assuming a `guild` has already been bound + let builder = EditGuild::new().icon(Some(&icon)); + server.edit(ctx, builder).await.unwrap(); + } + + async fn logo_set_db(db: &Pool, logo_selected: &LogoData) { + let name = logo_selected.name.to_str().unwrap_or_default(); + + match sqlx::query_as::<_, ServerIcons>( + " + INSERT OR REPLACE INTO server_icons (name, date) + VALUES (?1, ?2) + ", + ) + .bind(name) + .bind(get_now_iso(false)) + .fetch_optional(db) + .await + { + Ok(_) => {} + Err(e) => { + dbg!(e); + } + } + } +} From 86a3af2a65fc76d899195753cae61858dfbcd3a2 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 00:21:49 +0100 Subject: [PATCH 20/27] feat: pull the config for the festivals locally, using teh imported repo --- .server-icons.toml | 19 +------ src/bin/update_server-icon.rs | 11 ++-- src/common/server_icon.rs | 104 ++++++++++++++++++++++------------ 3 files changed, 74 insertions(+), 60 deletions(-) diff --git a/.server-icons.toml b/.server-icons.toml index 587f63b..eeb302c 100644 --- a/.server-icons.toml +++ b/.server-icons.toml @@ -3,21 +3,4 @@ [source] repo = "https://forgejo.skynet.ie/Computer_Society/open-goverance" directory = "Resources/Logo_Variants" - -[[festivals]] -name = "pride" -all_year = true -start = { day = 1, month = 6, year = 0} -end = { day = 30, month = 6, year = 0} - -[[festivals]] -name = "christmas" -all_year = false -start = { day = 1, month = 12, year = 0} -end = { day = 31, month = 12, year = 0} - -[[festivals]] -name = "halloween" -all_year = false -start = { day = 1, month = 12, year = 0} -end = { day = 31, month = 12, year = 0} \ No newline at end of file +file = "_festivals.toml" \ No newline at end of file diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index 4d49490..ff4cef9 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -4,15 +4,12 @@ use serenity::{ model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::common::server_icon; +use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon}; use skynet_discord_bot::{ common::database::{db_init, DataBase}, get_config, Config, }; -use std::{ - process::self, - sync::Arc, -}; +use std::{process, sync::Arc}; use tokio::sync::RwLock; #[tokio::main] @@ -62,9 +59,9 @@ impl EventHandler for Handler { }; let config_global = config_lock.read().await; - let config_toml = server_icon::get_config_icons(); + let config_toml = get_config_icons::minimal(); - server_icon::update_icon::update_icon_main(&ctx, &db, &config_global, &config_toml).await; + update_icon::update_icon_main(&ctx, &db, &config_global, &config_toml).await; // finish up process::exit(0); diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs index 4a489be..3f88d34 100644 --- a/src/common/server_icon.rs +++ b/src/common/server_icon.rs @@ -1,37 +1,65 @@ use serde::Deserialize; -use std::{ffi::OsString, path::PathBuf}; +use std::{ffi::OsString, fs, path::PathBuf}; -#[derive(Deserialize)] -pub struct ConfigToml { - pub source: ConfigTomlSource, - pub festivals: Vec, -} +pub mod get_config_icons { + use super::*; + use crate::Config; -#[derive(Deserialize, Debug)] -pub struct ConfigTomlSource { - pub repo: String, - pub directory: String, -} + #[derive(Deserialize)] + pub struct ConfigToml { + pub source: ConfigTomlSource, + pub festivals: Vec, + } -#[derive(Deserialize, Debug)] -pub struct ConfigTomlFestivals { - pub name: String, - pub all_year: bool, - pub start: ConfigTomlFestivalsTime, - pub end: ConfigTomlFestivalsTime, -} + #[derive(Deserialize)] + pub struct ConfigTomlLocal { + pub source: ConfigTomlSource, + } + #[derive(Deserialize)] + pub struct ConfigTomlRemote { + pub festivals: Vec, + } -#[derive(Deserialize, Debug)] -pub struct ConfigTomlFestivalsTime { - pub day: u32, - pub month: u32, - pub year: i32, -} + #[derive(Deserialize, Debug)] + pub struct ConfigTomlSource { + pub repo: String, + pub directory: String, + pub file: String, + } -pub fn get_config_icons() -> ConfigToml { - let toml_raw = include_str!("../../.server-icons.toml"); - let config: ConfigToml = toml::from_str(toml_raw).unwrap(); - config + #[derive(Deserialize, Debug)] + pub struct ConfigTomlFestivals { + pub name: String, + pub all_year: bool, + pub start: ConfigTomlFestivalsTime, + pub end: ConfigTomlFestivalsTime, + } + + #[derive(Deserialize, Debug)] + pub struct ConfigTomlFestivalsTime { + pub day: u32, + pub month: u32, + pub year: i32, + } + pub fn minimal() -> ConfigTomlLocal { + let toml_raw_min = include_str!("../../.server-icons.toml"); + let config_min: ConfigTomlLocal = toml::from_str(toml_raw_min).unwrap(); + config_min + } + + // since a copy of the festival file is in the repo we just need to get to it + pub fn full(config: &Config) -> ConfigToml { + let config_source = minimal(); + + let file_path = format!("{}/open-governance/{}/{}", &config.home, &config_source.source.directory, &config_source.source.file); + let contents = fs::read_to_string(file_path).expect("Should have been able to read the file"); + let config_festivals: ConfigTomlRemote = toml::from_str(&contents).unwrap(); + + ConfigToml { + source: config_source.source, + festivals: config_festivals.festivals, + } + } } #[derive(Debug)] @@ -50,7 +78,10 @@ pub struct ServerIcons { pub mod update_icon { use super::*; use crate::{ - common::renderer::{Args, Renderer}, + common::{ + renderer::{Args, Renderer}, + server_icon::get_config_icons::{self, ConfigToml, ConfigTomlLocal}, + }, get_now_iso, Config, }; use chrono::{Datelike, Utc}; @@ -61,20 +92,23 @@ pub mod update_icon { client::Context, }; use sqlx::{Pool, Sqlite}; - use std::{fs, process::Command}; + use std::process::Command; /// Update the server icon, pulling from open governance. - pub async fn update_icon_main(ctx: &Context, db: &Pool, config_global: &Config, config_toml: &ConfigToml) { + pub async fn update_icon_main(ctx: &Context, db: &Pool, config_global: &Config, config_toml_local: &ConfigTomlLocal) { let server = GuildId::new(689189992417067052); // clone repo into local folder - clone_repo(config_global, config_toml); + clone_repo(config_global, config_toml_local); + + // now the repo has been downloaded/updated we can now access the festivals + let config_toml = get_config_icons::full(config_global); // see if there is a current festival - let festival_data = get_festival(config_toml); + let festival_data = get_festival(&config_toml); // get a list of all the graphics files - let logos = get_logos(config_global, config_toml); + let logos = get_logos(config_global, &config_toml); // filter them so only the current season (if any) are active let logos_filtered = logos_filter(&festival_data, logos); @@ -115,7 +149,7 @@ pub mod update_icon { result } - fn clone_repo(config: &Config, config_toml: &ConfigToml) { + fn clone_repo(config: &Config, config_toml: &ConfigTomlLocal) { let url = &config_toml.source.repo; let folder = format!("{}/open-governance", &config.home); From 86f54aec6dbf387b685ebc5f6b327bf7bf328139 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 04:31:21 +0100 Subject: [PATCH 21/27] feat: got the commands mostly working, will need some further fine tuning --- src/commands/committee.rs | 4 + src/commands/mod.rs | 1 + src/commands/server_icon.rs | 208 ++++++++++++++++++++++++++++++++++++ src/main.rs | 51 ++++++++- 4 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 src/commands/server_icon.rs diff --git a/src/commands/committee.rs b/src/commands/committee.rs index 4813c6a..944fbc0 100644 --- a/src/commands/committee.rs +++ b/src/commands/committee.rs @@ -20,4 +20,8 @@ pub fn register() -> CreateCommand { .add_sub_option(CreateCommandOption::new(CommandOptionType::Role, "role_c", "Sum of A and B").required(true)) .add_sub_option(CreateCommandOption::new(CommandOptionType::Boolean, "delete", "Delete this entry.").required(false)), ) + .add_option( + CreateCommandOption::new(CommandOptionType::SubCommandGroup, "icon", "Committee commands for the server icon") + .add_sub_option(CreateCommandOption::new(CommandOptionType::SubCommand, "change", "Change the server icon.")), + ) } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index b513fc2..5815541 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -3,4 +3,5 @@ pub mod committee; pub mod count; pub mod minecraft; pub mod role_adder; +pub mod server_icon; pub mod wolves; diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs new file mode 100644 index 0000000..a6114c5 --- /dev/null +++ b/src/commands/server_icon.rs @@ -0,0 +1,208 @@ +use serenity::all::{CommandInteraction, Context}; +use skynet_discord_bot::{ + common::{ + database::DataBase, + server_icon::{get_config_icons, update_icon::update_icon_main, ServerIcons}, + }, + Config, +}; + +use serenity::all::{CommandOptionType, CreateCommand, CreateCommandOption}; + +// commands that server mods are able to use +pub(crate) mod admin { + use super::*; + + // Moderators can force a icon change + pub(crate) mod change { + use super::*; + + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Databse in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + let config_global = config_lock.read().await; + + let config_toml = get_config_icons::minimal(); + update_icon_main(ctx, &db, &config_global, &config_toml).await; + + "Changed server Icon".to_string() + } + } +} + +// commands for general users +pub(crate) mod user { + use super::*; + use skynet_discord_bot::common::server_icon::get_config_icons::ConfigTomlLocal; + + pub fn register() -> CreateCommand { + CreateCommand::new("icon") + .description("Commands related to the Server Icon") + .add_option( + CreateCommandOption::new(CommandOptionType::SubCommandGroup, "current", "Information on current items.") + .add_sub_option(CreateCommandOption::new(CommandOptionType::SubCommand, "icon", "Information on current icon.")) + .add_sub_option(CreateCommandOption::new(CommandOptionType::SubCommand, "festival", "Information on current festival.")), + ) + .add_option(CreateCommandOption::new(CommandOptionType::SubCommand, "stats", "Some Stats.")) + } + + fn get_logo_url(config_toml: &ConfigTomlLocal, logo_name: &str) -> String { + format!("{}/src/branch/main/{}/{}", &config_toml.source.repo, &config_toml.source.directory, logo_name) + } + + /// Regular users can get teh link to teh current icon + pub(crate) mod current { + use super::*; + + pub(crate) mod icon { + use super::*; + + use sqlx::{Pool, Sqlite}; + + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Databse in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_toml = get_config_icons::minimal(); + + if let Some(logo) = get_current_icon(&db).await { + get_logo_url(&config_toml, &logo.name) + } else { + "Could not find current icon".to_string() + } + } + + async fn get_current_icon(db: &Pool) -> Option { + match sqlx::query_as::<_, ServerIcons>( + " + SELECT * from server_icons ORDER BY id DESC LIMIT 1 + ", + ) + .fetch_one(db) + .await + { + Ok(res) => Some(res), + Err(e) => { + dbg!(e); + None + } + } + } + } + + pub(crate) mod festival { + use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption}; + use skynet_discord_bot::common::server_icon::get_config_icons; + use skynet_discord_bot::Config; + + // use this to return what current festivals are active? + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + let config_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; + let config = config_lock.read().await; + + let config_toml = get_config_icons::full(&config); + + let mut response = vec![]; + + for festival in &config_toml.festivals { + response.push(festival.name.to_owned()); + } + + format!("Festivals active: {}", response.join(", ")) + } + } + } + + /// Get the statistics of the icons + pub(crate) mod stats { + use super::*; + use sqlx::{Pool, Sqlite}; + + pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Databse in TypeMap.").clone() + }; + let db = db_lock.read().await; + + let config_toml = get_config_icons::minimal(); + + let totals = get_totals(&db).await; + + fmt_msg(&config_toml, &totals) + } + + #[derive(Debug, Clone, sqlx::FromRow)] + pub struct CountResult { + pub name: String, + pub times: i64, + } + + async fn get_totals(db: &Pool) -> Vec { + sqlx::query_as::<_, CountResult>( + " + SELECT + DISTINCT name, + COUNT(*) OVER(PARTITION BY name) AS times + FROM server_icons + ", + ) + .fetch_all(db) + .await + .unwrap_or_else(|e| { + dbg!(e); + vec![] + }) + } + + fn fmt_msg(config_toml: &ConfigTomlLocal, totals: &Vec) -> String { + // msg can be a max 2000 chars long + let mut limit = 2000 - 3; + + let mut response = vec![]; + for CountResult { + name, + times, + } in totals + { + let current_leading = if times < &10 { + "00" + } else if times < &100 { + "0" + } else { + "" + }; + + let url = get_logo_url(config_toml, name); + + let line = format!("{}{} <{}>", current_leading, times, url); + + let length = line.len() + 1; + + // +3 is to account for the closing fense + if length < (limit + 3) { + response.push(line); + limit -= length; + } else { + break; + } + } + + response.join("\n") + } + } +} diff --git a/src/main.rs b/src/main.rs index 6d5e849..3b57470 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; -use serenity::all::{ActivityData, Command, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, Interaction}; +use serenity::all::{ + ActivityData, Command, CommandDataOption, CommandDataOptionValue, CommandOptionType, CreateMessage, EditInteractionResponse, GuildId, + GuildMemberUpdateEvent, Interaction, +}; use serenity::model::guild::Member; use serenity::{ async_trait, @@ -129,6 +132,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use } } + // Inter-Committee server match GuildId::new(1220150752656363520) .set_commands(&ctx.http, vec![commands::count::committee::register()]) .await @@ -139,8 +143,16 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use } } + // compsoc Server match GuildId::new(689189992417067052) - .set_commands(&ctx.http, vec![commands::count::servers::register()]) + .set_commands( + &ctx.http, + vec![ + // commands just for the compsoc server + commands::count::servers::register(), + commands::server_icon::user::register(), + ], + ) .await { Ok(_) => {} @@ -175,6 +187,18 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use Some(x) => match x.name.as_str() { "add" => commands::add_server::run(&command, &ctx).await, "roles_adder" => commands::role_adder::edit::run(&command, &ctx).await, + "icon" => match &x.value { + CommandDataOptionValue::SubCommandGroup(y) => match y.first() { + None => "error".to_string(), + Some(z) => match z.name.as_str() { + "change" => commands::server_icon::admin::change::run(&command, &ctx).await, + &_ => format!("not implemented :( count {}", x.name.as_str()), + }, + }, + _ => { + format!("not implemented :( committee {}", x.name.as_str()) + } + }, // "link" => commands::count::servers::run(&command, &ctx).await, &_ => format!("not implemented :( committee {}", x.name.as_str()), }, @@ -191,6 +215,29 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use &_ => format!("not implemented :( count {}", x.name.as_str()), }, }, + + "icon" => match command.data.options.first() { + None => "Invalid Command".to_string(), + Some(x) => match x.name.as_str() { + "current" => { + let result = match &x.value { + CommandDataOptionValue::SubCommandGroup(y) => match y.first() { + None => "error".to_string(), + Some(z) => match z.name.as_str() { + "icon" => commands::server_icon::user::current::icon::run(&command, &ctx).await, + "festival" => commands::server_icon::user::current::festival::run(&command, &ctx).await, + &_ => format!("not implemented :( count {}", x.name.as_str()), + }, + }, + &_ => format!("not implemented :( {}", command.data.name.as_str()), + }; + + result + } + "stats" => commands::server_icon::user::stats::run(&command, &ctx).await, + &_ => format!("not implemented :( count {}", x.name.as_str()), + }, + }, _ => format!("not implemented :( {}", command.data.name.as_str()), }; From 0f4524ea637ef29aba7f2dab964015274d0ff0c9 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 05:19:58 +0100 Subject: [PATCH 22/27] feat: tidied up the command outouts --- db/migrations/11_server-icons.sql | 1 + src/commands/server_icon.rs | 37 ++++++++++++++++++++++--------- src/common/server_icon.rs | 13 ++++++----- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/db/migrations/11_server-icons.sql b/db/migrations/11_server-icons.sql index 37d8b5b..20fb472 100644 --- a/db/migrations/11_server-icons.sql +++ b/db/migrations/11_server-icons.sql @@ -2,6 +2,7 @@ CREATE TABLE IF NOT EXISTS server_icons ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, + path TEXT NOT NULL, date TEXT NOT NULL ); diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs index a6114c5..4ce10ac 100644 --- a/src/commands/server_icon.rs +++ b/src/commands/server_icon.rs @@ -51,7 +51,7 @@ pub(crate) mod user { .add_sub_option(CreateCommandOption::new(CommandOptionType::SubCommand, "icon", "Information on current icon.")) .add_sub_option(CreateCommandOption::new(CommandOptionType::SubCommand, "festival", "Information on current festival.")), ) - .add_option(CreateCommandOption::new(CommandOptionType::SubCommand, "stats", "Some Stats.")) + .add_option(CreateCommandOption::new(CommandOptionType::SubCommand, "stats", "How many times the particular icon has been used")) } fn get_logo_url(config_toml: &ConfigTomlLocal, logo_name: &str) -> String { @@ -64,6 +64,7 @@ pub(crate) mod user { pub(crate) mod icon { use super::*; + use serenity::all::{CreateAttachment, EditInteractionResponse}; use sqlx::{Pool, Sqlite}; @@ -77,7 +78,15 @@ pub(crate) mod user { let config_toml = get_config_icons::minimal(); if let Some(logo) = get_current_icon(&db).await { - get_logo_url(&config_toml, &logo.name) + let attachment = CreateAttachment::path(&logo.path).await.unwrap(); + match command.edit_response(&ctx.http, EditInteractionResponse::new().new_attachment(attachment)).await { + Ok(_) => {} + Err(e) => { + dbg!(e); + } + } + + format!("[{}]({})", &logo.name, get_logo_url(&config_toml, &logo.name)) } else { "Could not find current icon".to_string() } @@ -102,8 +111,8 @@ pub(crate) mod user { } pub(crate) mod festival { - use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption}; - use skynet_discord_bot::common::server_icon::get_config_icons; + use serenity::all::{CommandInteraction, Context}; + use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon::get_festival}; use skynet_discord_bot::Config; // use this to return what current festivals are active? @@ -116,13 +125,13 @@ pub(crate) mod user { let config_toml = get_config_icons::full(&config); - let mut response = vec![]; + let response = get_festival(&config_toml).current; - for festival in &config_toml.festivals { - response.push(festival.name.to_owned()); + if response.is_empty() { + "No festival currently active".to_string() + } else { + format!("Festivals active: {}", response.join(", ")) } - - format!("Festivals active: {}", response.join(", ")) } } } @@ -170,6 +179,10 @@ pub(crate) mod user { } fn fmt_msg(config_toml: &ConfigTomlLocal, totals: &Vec) -> String { + let mut totals_local = totals.clone(); + totals_local.sort_by_key(|x| x.times); + totals_local.reverse(); + // msg can be a max 2000 chars long let mut limit = 2000 - 3; @@ -177,7 +190,7 @@ pub(crate) mod user { for CountResult { name, times, - } in totals + } in &totals_local { let current_leading = if times < &10 { "00" @@ -189,7 +202,9 @@ pub(crate) mod user { let url = get_logo_url(config_toml, name); - let line = format!("{}{} <{}>", current_leading, times, url); + // the `` is so that the numbers will be rendered in monospaced font + // the <> is to suppress the URL embed + let line = format!("``{}{}`` [{}](<{}>)", current_leading, times, name, url); let length = line.len() + 1; diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs index 3f88d34..ab4b991 100644 --- a/src/common/server_icon.rs +++ b/src/common/server_icon.rs @@ -72,6 +72,7 @@ pub struct LogoData { pub struct ServerIcons { pub id: i64, pub name: String, + pub path: String, pub date: String, } @@ -120,12 +121,12 @@ pub mod update_icon { } #[derive(Debug)] - struct FestivalData { - current: Vec, + pub struct FestivalData { + pub current: Vec, exclusions: Vec, } - fn get_festival(config_toml: &ConfigToml) -> FestivalData { + pub fn get_festival(config_toml: &ConfigToml) -> FestivalData { let today = Utc::now(); let day = today.day(); let month = today.month(); @@ -276,15 +277,17 @@ pub mod update_icon { async fn logo_set_db(db: &Pool, logo_selected: &LogoData) { let name = logo_selected.name.to_str().unwrap_or_default(); + let path = logo_selected.path.to_str().unwrap_or_default(); match sqlx::query_as::<_, ServerIcons>( " - INSERT OR REPLACE INTO server_icons (name, date) - VALUES (?1, ?2) + INSERT OR REPLACE INTO server_icons (name, date, path) + VALUES (?1, ?2, ?3) ", ) .bind(name) .bind(get_now_iso(false)) + .bind(path) .fetch_optional(db) .await { From 721c8246acc3a2285e0dbedcef79939a9a349700 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 05:21:05 +0100 Subject: [PATCH 23/27] todo: add a todo where teh mc commands get moved in under committee --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index 3b57470..4d58bdd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -199,6 +199,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use format!("not implemented :( committee {}", x.name.as_str()) } }, + // TODO: move teh minecraft commands in here as a subgroup // "link" => commands::count::servers::run(&command, &ctx).await, &_ => format!("not implemented :( committee {}", x.name.as_str()), }, From b4cadffdb55bed8c0ab979588f64c0eba7b5d6be Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 05:22:30 +0100 Subject: [PATCH 24/27] fmt: for whenever it gets stabised (or we use nightly) this would be really good for cleaning up imports --- .rustfmt.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index b8ae8dd..6aeb30c 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -6,4 +6,5 @@ fn_params_layout = "Compressed" #brace_style = "PreferSameLine" struct_lit_width = 0 tab_spaces = 2 -use_small_heuristics = "Max" \ No newline at end of file +use_small_heuristics = "Max" +#imports_granularity="Crate" \ No newline at end of file From cae383a18663f6232378aa95ac2ef8ff58ee2a12 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 05:26:00 +0100 Subject: [PATCH 25/27] feat: set up the systemd timer for teh binary --- Cargo.toml | 3 +++ flake.nix | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 3dffd41..b179901 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,9 @@ name = "update_committee" [[bin]] name = "update_minecraft" +[[bin]] +name = "update_server-icon" + [dependencies] # discord library serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } diff --git a/flake.nix b/flake.nix index 2f8ba95..b707b60 100644 --- a/flake.nix +++ b/flake.nix @@ -141,6 +141,8 @@ "update_committee" = "*:15:00"; # minecraft stuff is updated at 5am "update_minecraft" = "5:10:00"; + # server icon gets updated daily at midnight + "update_server-icon" = "0:01:00"; }; in { options.services."${package_name}" = { From 652dd6ff1c3f5dcda95e0cb031422544f5818dae Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 05:27:11 +0100 Subject: [PATCH 26/27] ci: add workflow to check for lfs status --- .forgejo/workflows/check_lfs.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .forgejo/workflows/check_lfs.yaml diff --git a/.forgejo/workflows/check_lfs.yaml b/.forgejo/workflows/check_lfs.yaml new file mode 100644 index 0000000..dca575c --- /dev/null +++ b/.forgejo/workflows/check_lfs.yaml @@ -0,0 +1,12 @@ +on: + - pull_request + - push + - workflow_dispatch + + +jobs: + check_lfs: + # nix/docker + runs-on: nix + steps: + - uses: https://github.com/MPLew-is/lfs-check-action@1 \ No newline at end of file From 9134feee4ea57acbc6146d2ecd6c603e77d9d400 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 06:14:53 +0100 Subject: [PATCH 27/27] feat: cleaned up remaining unwraps, and then clippy+fmt --- src/commands/server_icon.rs | 21 +++---- src/common/server_icon.rs | 114 +++++++++++++++++++++++++++++------- src/main.rs | 3 +- 3 files changed, 105 insertions(+), 33 deletions(-) diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs index 4ce10ac..9c970fc 100644 --- a/src/commands/server_icon.rs +++ b/src/commands/server_icon.rs @@ -17,7 +17,7 @@ pub(crate) mod admin { pub(crate) mod change { use super::*; - pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() @@ -78,11 +78,12 @@ pub(crate) mod user { let config_toml = get_config_icons::minimal(); if let Some(logo) = get_current_icon(&db).await { - let attachment = CreateAttachment::path(&logo.path).await.unwrap(); - match command.edit_response(&ctx.http, EditInteractionResponse::new().new_attachment(attachment)).await { - Ok(_) => {} - Err(e) => { - dbg!(e); + if let Ok(attachment) = CreateAttachment::path(&logo.path).await { + match command.edit_response(&ctx.http, EditInteractionResponse::new().new_attachment(attachment)).await { + Ok(_) => {} + Err(e) => { + dbg!(e); + } } } @@ -116,7 +117,7 @@ pub(crate) mod user { use skynet_discord_bot::Config; // use this to return what current festivals are active? - pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { let config_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() @@ -141,7 +142,7 @@ pub(crate) mod user { use super::*; use sqlx::{Pool, Sqlite}; - pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { + pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() @@ -178,8 +179,8 @@ pub(crate) mod user { }) } - fn fmt_msg(config_toml: &ConfigTomlLocal, totals: &Vec) -> String { - let mut totals_local = totals.clone(); + fn fmt_msg(config_toml: &ConfigTomlLocal, totals: &[CountResult]) -> String { + let mut totals_local = totals.to_owned(); totals_local.sort_by_key(|x| x.times); totals_local.reverse(); diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs index ab4b991..167a105 100644 --- a/src/common/server_icon.rs +++ b/src/common/server_icon.rs @@ -43,8 +43,16 @@ pub mod get_config_icons { } pub fn minimal() -> ConfigTomlLocal { let toml_raw_min = include_str!("../../.server-icons.toml"); - let config_min: ConfigTomlLocal = toml::from_str(toml_raw_min).unwrap(); - config_min + toml::from_str::(toml_raw_min).unwrap_or_else(|e| { + dbg!(e); + ConfigTomlLocal { + source: ConfigTomlSource { + repo: "".to_string(), + directory: "".to_string(), + file: "".to_string(), + }, + } + }) } // since a copy of the festival file is in the repo we just need to get to it @@ -52,12 +60,21 @@ pub mod get_config_icons { let config_source = minimal(); let file_path = format!("{}/open-governance/{}/{}", &config.home, &config_source.source.directory, &config_source.source.file); - let contents = fs::read_to_string(file_path).expect("Should have been able to read the file"); - let config_festivals: ConfigTomlRemote = toml::from_str(&contents).unwrap(); + let contents = fs::read_to_string(file_path).unwrap_or_else(|e| { + dbg!(e); + "".to_string() + }); + let festivals = match toml::from_str::(&contents) { + Ok(config_festivals) => config_festivals.festivals, + Err(e) => { + dbg!(e); + vec![] + } + }; ConfigToml { source: config_source.source, - festivals: config_festivals.festivals, + festivals, } } } @@ -154,15 +171,26 @@ pub mod update_icon { let url = &config_toml.source.repo; let folder = format!("{}/open-governance", &config.home); - Command::new("git").arg("clone").arg(url).arg(&folder).output().expect("failed to execute process"); + if let Err(e) = Command::new("git") + // clone the repo, gracefully "fails" + .arg("clone") + .arg(url) + .arg(&folder) + .output() + { + dbg!(e); + } - Command::new("git") + if let Err(e) = Command::new("git") + // Update the repo .arg("pull") .arg("origin") .arg("main") .current_dir(&folder) .output() - .expect("failed to execute process"); + { + dbg!(e); + } } fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { @@ -170,7 +198,13 @@ pub mod update_icon { let folder_path = PathBuf::from(&folder); let mut folder_output = folder_path.clone(); folder_output.push("converted"); - let paths = fs::read_dir(folder).unwrap(); + let paths = match fs::read_dir(folder) { + Ok(x) => x, + Err(e) => { + dbg!(e); + return vec![]; + } + }; let args = Args { input: folder_path.clone(), @@ -179,14 +213,26 @@ pub mod update_icon { width: 1024, height: 1024, }; - let mut r = Renderer::new(&args).unwrap(); + let mut r = match Renderer::new(&args) { + Ok(x) => x, + Err(e) => { + let _ = dbg!(e); + return vec![]; + } + }; let mut logos = vec![]; for tmp in paths.flatten() { let path_local = tmp.path().to_owned(); let path_local2 = tmp.path().to_owned(); - let name = path_local2.file_name().unwrap().to_owned(); + let name = match path_local2.file_name() { + None => { + dbg!(path_local2); + continue; + } + Some(x) => x.to_owned(), + }; let mut path = tmp.path(); if path.is_dir() { @@ -200,7 +246,13 @@ pub mod update_icon { let mut path_new = path_local.clone(); path_new.set_extension("png"); let filename_tmp = path_new.clone(); - let filename = filename_tmp.file_name().unwrap_or_default(); + let filename = match filename_tmp.file_name() { + None => { + dbg!(filename_tmp); + continue; + } + Some(x) => x, + }; path_new.pop(); path_new.push("converted"); path_new.push(filename); @@ -266,18 +318,36 @@ pub mod update_icon { async fn logo_set(ctx: &Context, db: &Pool, server: &GuildId, logo_selected: &LogoData) { // add to teh database - logo_set_db(db, logo_selected).await; + if !logo_set_db(db, logo_selected).await { + // something went wrong + return; + } - let icon = CreateAttachment::path(logo_selected.path.to_str().unwrap_or_default()).await.unwrap(); - - // assuming a `guild` has already been bound - let builder = EditGuild::new().icon(Some(&icon)); - server.edit(ctx, builder).await.unwrap(); + if let Some(logo_path) = logo_selected.path.to_str() { + match CreateAttachment::path(logo_path).await { + Ok(icon) => { + // assuming a `guild` has already been bound + let builder = EditGuild::new().icon(Some(&icon)); + if let Err(e) = server.edit(ctx, builder).await { + dbg!(e); + } + } + Err(e) => { + dbg!(e); + } + } + } } - async fn logo_set_db(db: &Pool, logo_selected: &LogoData) { - let name = logo_selected.name.to_str().unwrap_or_default(); - let path = logo_selected.path.to_str().unwrap_or_default(); + async fn logo_set_db(db: &Pool, logo_selected: &LogoData) -> bool { + let name = match logo_selected.name.to_str() { + None => return false, + Some(x) => x, + }; + let path = match logo_selected.path.to_str() { + None => return false, + Some(x) => x, + }; match sqlx::query_as::<_, ServerIcons>( " @@ -294,7 +364,9 @@ pub mod update_icon { Ok(_) => {} Err(e) => { dbg!(e); + return false; } } + true } } diff --git a/src/main.rs b/src/main.rs index 4d58bdd..ebad7a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,7 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; use serenity::all::{ - ActivityData, Command, CommandDataOption, CommandDataOptionValue, CommandOptionType, CreateMessage, EditInteractionResponse, GuildId, - GuildMemberUpdateEvent, Interaction, + ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, Interaction, }; use serenity::model::guild::Member; use serenity::{