From f1dbbec32df58ded8d840e02f54ffcc9c64e821a Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Fri, 6 Jun 2025 18:50:10 +0100 Subject: [PATCH 01/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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::{ From f841039c530160dca8ffd916f0af203f268ee4b2 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 20:16:25 +0100 Subject: [PATCH 28/67] fix: was pulling in the wrong env var --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 762dc69..c682364 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,7 +99,7 @@ pub fn get_config() -> Config { config.committee_server = GuildId::new(x); } } - if let Ok(x) = env::var("COMMITTEE_DISCORD") { + if let Ok(x) = env::var("COMMITTEE_ROLE") { if let Ok(x) = x.trim().parse::() { config.committee_role = RoleId::new(x); } From 72226cc59bb4d97a5dab8080050acbcf52a0603d Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 20:20:21 +0100 Subject: [PATCH 29/67] feat: add support for passing teh compsoc server id via env --- src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index c682364..8a7273d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,6 +33,9 @@ pub struct Config { pub committee_server: GuildId, pub committee_role: RoleId, pub committee_category: ChannelId, + + // items pertaining to compsoc only + pub compsoc_server: GuildId, } impl TypeMapKey for Config { type Value = Arc>; @@ -58,6 +61,7 @@ pub fn get_config() -> Config { committee_server: GuildId::new(1), committee_role: RoleId::new(1), committee_category: ChannelId::new(1), + compsoc_server: GuildId::new(1), }; if let Ok(x) = env::var("DATABASE_HOME") { @@ -110,6 +114,12 @@ pub fn get_config() -> Config { } } + if let Ok(x) = env::var("COMPSOC_DISCORD") { + if let Ok(x) = x.trim().parse::() { + config.compsoc_server = GuildId::new(x); + } + } + config } From a6eff75e397a34b6b0b904b3aba6b0324d7c36ef Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 16 Jun 2025 21:50:26 +0100 Subject: [PATCH 30/67] feat: use values from teh env file to dictate the servers --- src/commands/wolves.rs | 12 +++++++++--- src/common/server_icon.rs | 2 +- src/common/set_roles.rs | 18 ++++++------------ src/lib.rs | 10 ++++++---- src/main.rs | 20 +++++++++++--------- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/commands/wolves.rs b/src/commands/wolves.rs index 92c6ec0..d790f9f 100644 --- a/src/commands/wolves.rs +++ b/src/commands/wolves.rs @@ -282,7 +282,7 @@ pub mod link { pub mod verify { use super::*; use crate::commands::wolves::link::{db_pending_clear_expired, get_server_member_discord, get_verify_from_db}; - use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, GuildId, RoleId}; + use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; use serenity::model::user::User; use skynet_discord_bot::common::database::get_server_config; use skynet_discord_bot::common::database::{ServerMembersWolves, Servers}; @@ -429,12 +429,18 @@ pub mod verify { } async fn set_server_roles_committee(db: &Pool, discord: &User, ctx: &Context) { + 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; + if let Some(x) = get_server_member_discord(db, &discord.id).await { // if they are a member of one or more committees, and in teh committee server then give the teh general committee role // they will get teh more specific vanity role later if !get_committees_id(db, x.id_wolves).await.is_empty() { - let server = GuildId::new(1220150752656363520); - let committee_member = RoleId::new(1226602779968274573); + let server = config.committee_server; + let committee_member = config.committee_role; if let Ok(member) = server.member(ctx, &discord.id).await { member.add_roles(&ctx, &[committee_member]).await.unwrap_or_default(); diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs index 167a105..8fcbcee 100644 --- a/src/common/server_icon.rs +++ b/src/common/server_icon.rs @@ -114,7 +114,7 @@ pub mod update_icon { /// Update the server icon, pulling from open governance. pub async fn update_icon_main(ctx: &Context, db: &Pool, config_global: &Config, config_toml_local: &ConfigTomlLocal) { - let server = GuildId::new(689189992417067052); + let server = config_global.compsoc_server; // clone repo into local folder clone_repo(config_global, config_toml_local); diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 0dc7cc5..4e24b1a 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -135,7 +135,7 @@ pub mod committee { use crate::common::wolves::committees::Committees; use crate::Config; use serde::{Deserialize, Serialize}; - use serenity::all::{EditRole, GuildId}; + use serenity::all::EditRole; use serenity::builder::CreateChannel; use serenity::client::Context; use serenity::model::channel::ChannelType; @@ -161,7 +161,7 @@ pub mod committee { }; let config_global = config_lock.read().await; - let server = GuildId::new(1220150752656363520); + let server = config_global.committee_server; // because to use it to update a single user we need to pre-get the members of teh server let mut members = server.members(&ctx, None, None).await.unwrap_or_default(); @@ -172,17 +172,11 @@ pub mod committee { /** This function can take a vec of members (or just one) and gives tehm the appropiate roles on teh committee server */ - pub async fn update_committees(db: &Pool, ctx: &Context, _config: &Config, members: &mut Vec) { - let server = GuildId::new(1220150752656363520); - let committee_member = RoleId::new(1226602779968274573); + pub async fn update_committees(db: &Pool, ctx: &Context, config: &Config, members: &mut Vec) { + let server = config.committee_server; + let committee_member = config.committee_role; let committees = get_committees(db).await; - let categories = [ - ChannelId::new(1226606560973815839), - // C&S Chats 2 - ChannelId::new(1341457244973305927), - // C&S Chats 3 - ChannelId::new(1341457509717639279), - ]; + let categories = config.committee_category.clone(); // information about the server let mut roles_db = HashMap::new(); diff --git a/src/lib.rs b/src/lib.rs index 8a7273d..abf8a2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,7 @@ pub struct Config { // discord server for committee pub committee_server: GuildId, pub committee_role: RoleId, - pub committee_category: ChannelId, + pub committee_category: Vec, // items pertaining to compsoc only pub compsoc_server: GuildId, @@ -60,7 +60,7 @@ pub fn get_config() -> Config { wolves_api: "".to_string(), committee_server: GuildId::new(1), committee_role: RoleId::new(1), - committee_category: ChannelId::new(1), + committee_category: vec![], compsoc_server: GuildId::new(1), }; @@ -109,8 +109,10 @@ pub fn get_config() -> Config { } } if let Ok(x) = env::var("COMMITTEE_CATEGORY") { - if let Ok(x) = x.trim().parse::() { - config.committee_category = ChannelId::new(x); + for part in x.split(",") { + if let Ok(x) = part.trim().parse::() { + config.committee_category.push(ChannelId::new(x)); + } } } diff --git a/src/main.rs b/src/main.rs index ebad7a0..9de027e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,7 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; -use serenity::all::{ - ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, Interaction, -}; +use serenity::all::{ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildMemberUpdateEvent, Interaction}; use serenity::model::guild::Member; use serenity::{ async_trait, @@ -42,7 +40,7 @@ impl EventHandler for Handler { let config_global = config_lock.read().await; // committee server takes priority - let committee_server = GuildId::new(1220150752656363520); + let committee_server = config_global.committee_server; if new_member.guild_id.get() == committee_server.get() { let mut member = vec![new_member.clone()]; update_committees(&db, &ctx, &config_global, &mut member).await; @@ -113,6 +111,12 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use println!("[Main] {} is connected!", ready.user.name); ctx.set_presence(Some(ActivityData::playing("with humanity's fate")), OnlineStatus::Online); + 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; + match Command::set_global_commands( &ctx.http, vec![ @@ -132,10 +136,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 - { + match config.committee_server.set_commands(&ctx.http, vec![commands::count::committee::register()]).await { Ok(_) => {} Err(e) => { println!("{:?}", e) @@ -143,7 +144,8 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use } // compsoc Server - match GuildId::new(689189992417067052) + match config + .compsoc_server .set_commands( &ctx.http, vec![ From 327ff99b693c838476e98cbf599c6ed2ee50f2d9 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 17 Jun 2025 16:21:15 +0100 Subject: [PATCH 31/67] fix: the pipeline got caught on a lint This isnt in the 1.87 rustfmt but its stilla good catch --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index abf8a2d..734fa2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,7 +109,7 @@ pub fn get_config() -> Config { } } if let Ok(x) = env::var("COMMITTEE_CATEGORY") { - for part in x.split(",") { + for part in x.split(',') { if let Ok(x) = part.trim().parse::() { config.committee_category.push(ChannelId::new(x)); } From 3a56d7bba5fa2ca562ab5288f8799339cdc947e3 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 17 Jun 2025 16:21:57 +0100 Subject: [PATCH 32/67] feat: lock the ``nix build`` to using the repo rust version --- flake.lock | 17 +++++++++++++++++ flake.nix | 23 +++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/flake.lock b/flake.lock index 9f7289a..2c4a9bc 100644 --- a/flake.lock +++ b/flake.lock @@ -32,6 +32,22 @@ "type": "indirect" } }, + "nixpkgs-mozilla": { + "flake": false, + "locked": { + "lastModified": 1744624473, + "narHash": "sha256-S6zT/w5SyAkJ//dYdjbrXgm+6Vkd/k7qqUl4WgZ6jjk=", + "owner": "mozilla", + "repo": "nixpkgs-mozilla", + "rev": "2292d4b35aa854e312ad2e95c4bb5c293656f21a", + "type": "github" + }, + "original": { + "owner": "mozilla", + "repo": "nixpkgs-mozilla", + "type": "github" + } + }, "nixpkgs_2": { "locked": { "lastModified": 1722995383, @@ -51,6 +67,7 @@ "inputs": { "naersk": "naersk", "nixpkgs": "nixpkgs_2", + "nixpkgs-mozilla": "nixpkgs-mozilla", "utils": "utils" } }, diff --git a/flake.nix b/flake.nix index b707b60..f7fee94 100644 --- a/flake.nix +++ b/flake.nix @@ -4,6 +4,10 @@ inputs = { nixpkgs.url = "nixpkgs/nixos-unstable"; naersk.url = "github:nix-community/naersk"; + nixpkgs-mozilla = { + url = "github:mozilla/nixpkgs-mozilla"; + flake = false; + }; utils.url = "github:numtide/flake-utils"; }; @@ -17,12 +21,27 @@ nixpkgs, utils, naersk, + nixpkgs-mozilla, }: utils.lib.eachDefaultSystem ( system: let overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml)); - pkgs = (import nixpkgs) {inherit system;}; - naersk' = pkgs.callPackage naersk {}; + pkgs = (import nixpkgs) { + inherit system; + + overlays = [ + (import nixpkgs-mozilla) + ]; + }; + toolchain = (pkgs.rustChannelOf { + rustToolchain = ./rust-toolchain.toml; + sha256 = "sha256-KUm16pHj+cRedf8vxs/Hd2YWxpOrWZ7UOrwhILdSJBU="; + }).rust; + + naersk' = pkgs.callPackage naersk { + cargo = toolchain; + rustc = toolchain; + }; package_name = "skynet_discord_bot"; desc = "Skynet Discord Bot"; buildInputs = with pkgs; [ From 1dc5c105df7ba80643b596d3de025a28871aba2c Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Wed, 18 Jun 2025 03:57:04 +0100 Subject: [PATCH 33/67] fix: needed to add git and git lfs to teh path of the service --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index f7fee94..1b39fbb 100644 --- a/flake.nix +++ b/flake.nix @@ -122,7 +122,7 @@ wantedBy = []; after = ["network-online.target"]; environment = environment_config; - + path = with pkgs; [ git git-lfs ]; serviceConfig = { Type = "oneshot"; User = "${cfg.user}"; From 7403f531eb38a8c1ff0621d0c7f2fb30ecc919fd Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Tue, 24 Jun 2025 00:00:56 +0100 Subject: [PATCH 34/67] feat: the backend is pretty simple, just pull the rep link from teh config_toml and add on the path to the docs. Then its just linking it all up. Closes #37 --- src/commands/wolves.rs | 17 +++++++++++++++++ src/main.rs | 1 + 2 files changed, 18 insertions(+) diff --git a/src/commands/wolves.rs b/src/commands/wolves.rs index d790f9f..d78454b 100644 --- a/src/commands/wolves.rs +++ b/src/commands/wolves.rs @@ -279,6 +279,22 @@ pub mod link { } } +pub mod link_docs { + use super::*; + pub mod users { + use super::*; + use serenity::all::CommandInteraction; + + pub async fn run(_command: &CommandInteraction, _ctx: &Context) -> String { + "https://forgejo.skynet.ie/Skynet/discord-bot/src/branch/main/doc/User.md".to_string() + } + } + + // pub mod committee { + // + // } +} + pub mod verify { use super::*; use crate::commands::wolves::link::{db_pending_clear_expired, get_server_member_discord, get_verify_from_db}; @@ -522,4 +538,5 @@ pub fn register() -> CreateCommand { .add_sub_option(CreateCommandOption::new(CommandOptionType::String, "minecraft_username", "Your Minecraft username").required(true)) .add_sub_option(CreateCommandOption::new(CommandOptionType::Boolean, "bedrock_account", "Is this a Bedrock account?").required(false)), ) + .add_option(CreateCommandOption::new(CommandOptionType::SubCommand, "docs", "Link to where the documentation can be found.")) } diff --git a/src/main.rs b/src/main.rs index 9de027e..238777d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -177,6 +177,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use "verify" => commands::wolves::verify::run(&command, &ctx).await, "unlink" => commands::wolves::unlink::run(&command, &ctx).await, "link_minecraft" => commands::minecraft::user::add::run(&command, &ctx).await, + "docs" => commands::wolves::link_docs::users::run(&command, &ctx).await, // "link" => commands::count::servers::run(&command, &ctx).await, &_ => format!("not implemented :( wolves {}", x.name.as_str()), }, From c4da3e91096741dc827f3a5ac3c184160b8f1e8c Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sat, 5 Jul 2025 15:31:53 +0100 Subject: [PATCH 35/67] fix: only allow image files to be chosen --- src/common/server_icon.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs index 8fcbcee..abd0972 100644 --- a/src/common/server_icon.rs +++ b/src/common/server_icon.rs @@ -286,9 +286,19 @@ pub mod update_icon { fn logos_filter(festival_data: &FestivalData, existing: Vec) -> Vec { let mut filtered: Vec = vec![]; + let allowed_files = vec![".png", ".jpeg", ".gif", ".svg"]; 'outer: for logo in existing { let name_lowercase0 = logo.name.to_ascii_lowercase(); let name_lowercase = name_lowercase0.to_str().unwrap_or_default(); + let mut allowed = false; + for allowed_type in &allowed_files { + if name_lowercase.ends_with(allowed_type) { + allowed = true; + } + } + if !allowed { + continue; + } if !festival_data.current.is_empty() { // if its a current festival filter based on it From 76f8aa2712dcfeb701b3dce16c7d93323afbeb66 Mon Sep 17 00:00:00 2001 From: esy Date: Sun, 6 Jul 2025 18:12:04 +0000 Subject: [PATCH 36/67] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01543e6..1f49dc1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Skynet Discord Bot The Skynet bot is designed to manage users on Discord. It allows users to link their UL Wolves account with Wolves in a GDPR compliant manner. -Skynet (bot) is hosted is hosted by the Computer Society on Skynet (computer cluster). +Skynet (bot) is hosted by the Computer Society on Skynet (computer cluster). ## Documentation We have split up the documentation into different segments depending on who the user is. From 764e8cd620d61ed6ca6d168cd06489e94d40b615 Mon Sep 17 00:00:00 2001 From: Daragh Date: Sun, 6 Jul 2025 23:28:51 +0000 Subject: [PATCH 37/67] Fix LFS on discord bot (#39) Reviewed-on: https://forgejo.skynet.ie/Skynet/discord-bot/pulls/39 Thanks @esy Co-authored-by: Daragh Co-committed-by: Daragh --- flake.nix | 1 + src/common/server_icon.rs | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/flake.nix b/flake.nix index 1b39fbb..4494ace 100644 --- a/flake.nix +++ b/flake.nix @@ -221,6 +221,7 @@ after = ["network-online.target"]; wants = []; environment = environment_config; + path = with pkgs; [ git git-lfs ]; serviceConfig = { User = "${cfg.user}"; diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs index abd0972..d99f265 100644 --- a/src/common/server_icon.rs +++ b/src/common/server_icon.rs @@ -191,6 +191,28 @@ pub mod update_icon { { dbg!(e); } + + if let Err(e) = Command::new("git") + // Install LFS for the repo + .arg("lfs") + .arg("install") + .current_dir(&folder) + .output() + { + dbg!(e); + } + + if let Err(e) = Command::new("git") + // clone the repo, gracefully "fails" + .arg("lfs") + .arg("pull") + .arg("origin") + .arg("main") + .current_dir(&folder) + .output() + { + dbg!(e); + } } fn get_logos(config: &Config, config_toml: &ConfigToml) -> Vec { From b8ffd421844414b44cc66d7ef671c206072027da Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 7 Jul 2025 21:29:37 +0100 Subject: [PATCH 38/67] feat: the bot wasnt using any caching, this should make many operations far faster now --- src/main.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 238777d..2770db7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,16 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; -use serenity::all::{ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildMemberUpdateEvent, Interaction}; -use serenity::model::guild::Member; +use serenity::all::{ + ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, Interaction, +}; use serenity::{ async_trait, client::{Context, EventHandler}, + gateway::ChunkGuildFilter, model::{ gateway::{GatewayIntents, Ready}, + guild::Member, user::OnlineStatus, }, Client, @@ -24,6 +27,14 @@ struct Handler; #[async_trait] impl EventHandler for Handler { + // this caches members of all servers teh bot is in + async fn cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(500), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + // handles previously linked accounts joining the server async fn guild_member_addition(&self, ctx: Context, new_member: Member) { let db_lock = { @@ -281,6 +292,7 @@ async fn main() { // Build our client. let mut client = Client::builder(&config.discord_token, intents) .event_handler(Handler {}) + .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); From 3abbb8d4854a41754bda907271ef1504fbbfe53b Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 7 Jul 2025 22:18:04 +0100 Subject: [PATCH 39/67] feat: added script to clean up the committee server if it got flooded with extra channels --- Cargo.toml | 3 + src/bin/cleanup_committee.rs | 129 +++++++++++++++++++++++++++++++++++ src/common/set_roles.rs | 6 +- 3 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 src/bin/cleanup_committee.rs diff --git a/Cargo.toml b/Cargo.toml index b179901..bca450c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ name = "update_minecraft" [[bin]] name = "update_server-icon" +[[bin]] +name = "cleanup_committee" + [dependencies] # discord library serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs new file mode 100644 index 0000000..7d2ea5a --- /dev/null +++ b/src/bin/cleanup_committee.rs @@ -0,0 +1,129 @@ +use serenity::all::{ChunkGuildFilter, GuildId}; +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::common::set_roles::committee::{db_roles_get}; +use skynet_discord_bot::{get_config, Config}; +use sqlx::{Pool, Sqlite}; +use std::{process, sync::Arc}; +use tokio::sync::RwLock; + +/// Cleanup teh Committee server +/// +/// This removes any invalid roles/channels which ay have been set up accidentally +/// DO NOT run this locally unless you have a fresh copy of teh live database handy. +#[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 {}) + .cache_settings(serenity::cache::Settings::default()) + .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 cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(500), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + + async fn ready(&self, ctx: Context, ready: Ready) { + let ctx = Arc::new(ctx); + println!("{} is connected!", ready.user.name); + + 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 = config_lock.read().await; + + cleanup(&db, &ctx, &config).await; + // finish up + process::exit(0); + } +} + +async fn cleanup(db: &Pool, ctx: &Context, config: &Config) { + let server = config.committee_server; + let committees = db_roles_get(db).await; + + if let Ok(channels) = server.channels(ctx).await { + for (id, channel) in &channels { + let name = &channel.name; + let committee_tmp = committees.iter().filter(|x| &x.name_channel == name).collect::>(); + let committee = match committee_tmp.first() { + // if there are no committees which match then this is not a channelw e care about + None => { + continue; + } + Some(x) => x, + }; + + // if the id of the channel does not match then remove it + if id != &committee.id_channel { + println!("Deleting Channel - ID: {} Name: {}", id, &channel.name); + if let Err(e) = channel.delete(ctx).await { + dbg!(e); + } + } + } + } + + if let Ok(mut roles) = server.roles(ctx).await { + for (id, role) in &mut roles { + let name = &role.name; + let committee_tmp = committees.iter().filter(|x| &x.name_role == name).collect::>(); + let committee = match committee_tmp.first() { + // if there are no committees which match then this is not a channelw e care about + None => { + continue; + } + Some(x) => x, + }; + + // if the id of the role does not match then remove it + if id != &committee.id_role { + println!("Deleting Role - ID: {} Name: {}", id, &role.name); + if let Err(e) = role.delete(ctx).await { + dbg!(e); + } + } + } + } +} \ No newline at end of file diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 4e24b1a..0012539 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -400,10 +400,10 @@ pub mod committee { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct CommitteeRoles { id_wolves: i64, - id_role: RoleId, - id_channel: ChannelId, + pub id_role: RoleId, + pub id_channel: ChannelId, pub name_role: String, - name_channel: String, + pub name_channel: String, pub count: i64, } From e901f3ed74120cb533b6eff7a873a8cd07776efd Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 7 Jul 2025 22:22:25 +0100 Subject: [PATCH 40/67] feat: add caching to everything, should make all member interacts faster --- src/bin/cleanup_committee.rs | 6 +++--- src/bin/update_committee.rs | 9 +++++++++ src/bin/update_data.rs | 9 +++++++++ src/bin/update_server-icon.rs | 9 +++++++++ src/bin/update_users.rs | 9 +++++++++ src/main.rs | 2 +- 6 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs index 7d2ea5a..a2c3772 100644 --- a/src/bin/cleanup_committee.rs +++ b/src/bin/cleanup_committee.rs @@ -7,7 +7,7 @@ use serenity::{ }; use skynet_discord_bot::common::database::{db_init, DataBase}; -use skynet_discord_bot::common::set_roles::committee::{db_roles_get}; +use skynet_discord_bot::common::set_roles::committee::db_roles_get; use skynet_discord_bot::{get_config, Config}; use sqlx::{Pool, Sqlite}; use std::{process, sync::Arc}; @@ -51,7 +51,7 @@ struct Handler; impl EventHandler for Handler { async fn cache_ready(&self, ctx: Context, guilds: Vec) { for guild in guilds { - ctx.shard.chunk_guild(guild, Some(500), false, ChunkGuildFilter::None, None); + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); } println!("Cache built successfully!"); } @@ -126,4 +126,4 @@ async fn cleanup(db: &Pool, ctx: &Context, config: &Config) { } } } -} \ No newline at end of file +} diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs index d2026c0..2eef977 100644 --- a/src/bin/update_committee.rs +++ b/src/bin/update_committee.rs @@ -1,3 +1,4 @@ +use serenity::all::{ChunkGuildFilter, GuildId}; use serenity::{ async_trait, client::{Context, EventHandler}, @@ -23,6 +24,7 @@ async fn main() { // Build our client. let mut client = Client::builder(&config.discord_token, intents) .event_handler(Handler {}) + .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); @@ -41,6 +43,13 @@ async fn main() { struct Handler; #[async_trait] impl EventHandler for Handler { + async fn cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + async fn ready(&self, ctx: Context, ready: Ready) { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index 2d36892..d184ccd 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -1,3 +1,4 @@ +use serenity::all::{ChunkGuildFilter, GuildId}; use serenity::{ async_trait, client::{Context, EventHandler}, @@ -27,6 +28,7 @@ async fn main() { // Build our client. let mut client = Client::builder(&config.discord_token, intents) .event_handler(Handler {}) + .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); @@ -45,6 +47,13 @@ async fn main() { struct Handler; #[async_trait] impl EventHandler for Handler { + async fn cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + async fn ready(&self, ctx: Context, ready: Ready) { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index ff4cef9..2c093f9 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -1,3 +1,4 @@ +use serenity::all::{ChunkGuildFilter, GuildId}; use serenity::{ async_trait, client::{Context, EventHandler}, @@ -25,6 +26,7 @@ async fn main() { // Build our client. let mut client = Client::builder(&config.discord_token, intents) .event_handler(Handler {}) + .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); @@ -43,6 +45,13 @@ async fn main() { struct Handler; #[async_trait] impl EventHandler for Handler { + async fn cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + async fn ready(&self, ctx: Context, ready: Ready) { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index 3617d14..27dc1d6 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -1,3 +1,4 @@ +use serenity::all::{ChunkGuildFilter, GuildId}; use serenity::{ async_trait, client::{Context, EventHandler}, @@ -23,6 +24,7 @@ async fn main() { // Build our client. let mut client = Client::builder(&config.discord_token, intents) .event_handler(Handler {}) + .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); @@ -41,6 +43,13 @@ async fn main() { struct Handler; #[async_trait] impl EventHandler for Handler { + async fn cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + async fn ready(&self, ctx: Context, ready: Ready) { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); diff --git a/src/main.rs b/src/main.rs index 2770db7..b09fb7a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,7 @@ impl EventHandler for Handler { // this caches members of all servers teh bot is in async fn cache_ready(&self, ctx: Context, guilds: Vec) { for guild in guilds { - ctx.shard.chunk_guild(guild, Some(500), false, ChunkGuildFilter::None, None); + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); } println!("Cache built successfully!"); } From 2b2dfc253147a0ed5f4456db5c20b421deae71c9 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 20:32:43 +0100 Subject: [PATCH 41/67] feat: cleaned up array that was used to count members/changes to a struct --- src/common/set_roles.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 0012539..5c7e29c 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -5,6 +5,13 @@ pub mod normal { use serenity::model::id::{GuildId, RoleId, UserId}; use sqlx::{Pool, Sqlite}; + struct RolesChange { + total: i32, + new: i32, + current_add: i32, + current_rem: i32, + } + pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { let db_lock = { let data_read = ctx.data.read().await; @@ -20,7 +27,12 @@ pub mod normal { .. } = server; - let mut roles_set = [0, 0, 0]; + let mut roles_set = RolesChange { + total: 0, + new: 0, + current_add: 0, + current_rem: 0, + }; let mut members = vec![]; for member in get_server_member_bulk(&db, server).await { @@ -38,17 +50,19 @@ pub mod normal { } if members.contains(&member.user.id) { + roles_set.total += 1; + let mut roles = vec![]; if let Some(role) = &role_past { if !member.roles.contains(role) { - roles_set[0] += 1; + roles_set.new += 1; roles.push(role.to_owned()); } } if !member.roles.contains(role_current) { - roles_set[1] += 1; + roles_set.current_add += 1; roles.push(role_current.to_owned()); } @@ -65,7 +79,7 @@ pub mod normal { } if member.roles.contains(role_current) { - roles_set[2] += 1; + roles_set.current_rem += 1; // if theya re not a current member and have the role then remove it if let Err(e) = member.remove_role(ctx, role_current).await { println!("{:?}", e); @@ -83,7 +97,14 @@ pub mod normal { set_server_numbers(&db, server, members_all as i64, members.len() as i64).await; // small bit of logging to note changes over time - println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.get(), roles_set[0], roles_set[1], roles_set[2]); + println!( + "{:?} Total: {} Changes: New: +{}, Current: +{}/-{}", + server.get(), + roles_set.total, + roles_set.new, + roles_set.current_add, + roles_set.current_rem + ); } pub async fn get_server_member_bulk(db: &Pool, server: &GuildId) -> Vec { From 04aa0e63d4fb0422c2762799eab3d5e3c145abc4 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 20:37:28 +0100 Subject: [PATCH 42/67] feat: setup update users to be every 5 min while using the cache --- src/main.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index b09fb7a..5dab67f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,8 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; use serenity::all::{ - ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, Interaction, + ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, GuildMembersChunkEvent, + Interaction, }; use serenity::{ async_trait, @@ -15,15 +16,20 @@ use serenity::{ }, Client, }; -use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_member, DataBase}; +use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_config_bulk, get_server_member, DataBase}; use skynet_discord_bot::common::set_roles::committee::update_committees; +use skynet_discord_bot::common::set_roles::normal; use skynet_discord_bot::common::wolves::committees::Committees; use skynet_discord_bot::{get_config, Config}; use sqlx::{Pool, Sqlite}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use std::time::Duration; use tokio::sync::RwLock; -struct Handler; +struct Handler { + is_loop_running: AtomicBool, +} #[async_trait] impl EventHandler for Handler { @@ -35,6 +41,41 @@ impl EventHandler for Handler { println!("Cache built successfully!"); } + async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { + if (chunk.chunk_index + 1) == chunk.chunk_count { + // from https://github.com/serenity-rs/serenity/blob/18349f7bba43acad4261103eb38fe01d93f382df/examples/e13_parallel_loops/src/main.rs#L48 + let ctx = Arc::new(ctx); + + if !self.is_loop_running.load(Ordering::Relaxed) { + // We have to clone the Arc, as it gets moved into the new thread. + + { + // this is to update member roles every 5 min + let ctx1 = Arc::clone(&ctx); + tokio::spawn(async move { + let db_lock = { + let data_read = ctx1.data.read().await; + data_read.get::().expect("Expected Database in TypeMap.").clone() + }; + let db = db_lock.read().await; + + loop { + println!("User update - Start"); + for server_config in get_server_config_bulk(&db).await { + normal::update_server(&ctx, &server_config, &[], &[]).await; + } + println!("User update - End"); + tokio::time::sleep(Duration::from_secs(60 * 5)).await; + } + }); + } + + // Now that the loop is running, we set the bool to true + self.is_loop_running.swap(true, Ordering::Relaxed); + } + } + } + // handles previously linked accounts joining the server async fn guild_member_addition(&self, ctx: Context, new_member: Member) { let db_lock = { @@ -291,7 +332,9 @@ async fn main() { 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 {}) + .event_handler(Handler { + is_loop_running: AtomicBool::new(false), + }) .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); From 3dd81a5c5412dda8090f3d3651a236628ee5a964 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 20:40:15 +0100 Subject: [PATCH 43/67] feat: remove the update_users service --- Cargo.toml | 5 +-- flake.nix | 2 -- src/bin/update_users.rs | 76 ----------------------------------------- 3 files changed, 1 insertion(+), 82 deletions(-) delete mode 100644 src/bin/update_users.rs diff --git a/Cargo.toml b/Cargo.toml index bca450c..f2bc696 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,6 @@ edition = "2021" [[bin]] name = "update_data" -[[bin]] -name = "update_users" - [[bin]] name = "update_committee" @@ -38,7 +35,7 @@ surf = "2.3" dotenvy = "0.15" # For sqlite -sqlx = { version = "0.8", features = [ "runtime-tokio", "sqlite", "migrate" ] } +sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite", "migrate"] } serde_json = { version = "1.0", features = ["raw_value"] } # create random strings diff --git a/flake.nix b/flake.nix index 4494ace..04e062b 100644 --- a/flake.nix +++ b/flake.nix @@ -154,8 +154,6 @@ scripts = { # every 20 min "update_data" = "*:0,10,20,30,40,50"; - # groups are updated every hour, offset from teh ldap - "update_users" = "*:05:00"; # Committee server has its own timer "update_committee" = "*:15:00"; # minecraft stuff is updated at 5am diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs deleted file mode 100644 index 27dc1d6..0000000 --- a/src/bin/update_users.rs +++ /dev/null @@ -1,76 +0,0 @@ -use serenity::all::{ChunkGuildFilter, GuildId}; -use serenity::{ - async_trait, - client::{Context, EventHandler}, - model::gateway::{GatewayIntents, Ready}, - Client, -}; -use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase}; -use skynet_discord_bot::common::set_roles::normal; -use skynet_discord_bot::{get_config, Config}; -use std::{process, sync::Arc}; -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 {}) - .cache_settings(serenity::cache::Settings::default()) - .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 cache_ready(&self, ctx: Context, guilds: Vec) { - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache built successfully!"); - } - - async fn ready(&self, ctx: Context, ready: Ready) { - let ctx = Arc::new(ctx); - println!("{} is connected!", ready.user.name); - - // this goes into each server and sets roles for each wolves member - check_bulk(Arc::clone(&ctx)).await; - - // finish up - process::exit(0); - } -} - -async fn check_bulk(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; - - for server_config in get_server_config_bulk(&db).await { - normal::update_server(&ctx, &server_config, &[], &[]).await; - } -} From a8bed0bacc72295eb193f1351bb6cb33a730ccf6 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 20:42:32 +0100 Subject: [PATCH 44/67] fix: was calling the wrong ctx --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5dab67f..08699e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,10 +51,10 @@ impl EventHandler for Handler { { // this is to update member roles every 5 min - let ctx1 = Arc::clone(&ctx); + let ctx_task = Arc::clone(&ctx); tokio::spawn(async move { let db_lock = { - let data_read = ctx1.data.read().await; + let data_read = ctx_task.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() }; let db = db_lock.read().await; @@ -62,7 +62,7 @@ impl EventHandler for Handler { loop { println!("User update - Start"); for server_config in get_server_config_bulk(&db).await { - normal::update_server(&ctx, &server_config, &[], &[]).await; + normal::update_server(&ctx_task, &server_config, &[], &[]).await; } println!("User update - End"); tokio::time::sleep(Duration::from_secs(60 * 5)).await; From 43ef787d59f1315a8537e318b6ae2c88ca3d3f29 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 22:25:31 +0100 Subject: [PATCH 45/67] fix: no need to pass in teh full object, a reference will do --- src/common/set_roles.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 5c7e29c..c51c52e 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -168,7 +168,7 @@ pub mod committee { use std::collections::HashMap; use std::sync::Arc; - pub async fn check_committee(ctx: Arc) { + pub async fn check_committee(ctx: &Context) { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() From 1729ec0a54d1656139f403b7a9c50131dedd5d6e Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 22:26:51 +0100 Subject: [PATCH 46/67] feat: drastically speed up the committee server script, it no longer tries to remove the committee role from those who dont have it. --- src/common/set_roles.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index c51c52e..fbbc828 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -329,15 +329,11 @@ pub mod committee { None => { vec![] } - Some(x) => { - let mut tmp = x.to_owned(); - if !tmp.is_empty() { - tmp.push(committee_member); - } - tmp - } + Some(x) => x.to_owned(), }; + let on_committee = !roles_required.is_empty(); + let mut roles_rem = vec![]; let mut roles_add = vec![]; // get a list of all the roles to remove from someone @@ -346,14 +342,25 @@ pub mod committee { for role in &roles_current { roles_current_id.push(role.id.to_owned()); if !roles_required.contains(&role.id) { - roles_rem.push(role.id.to_owned()); + if role.id == committee_member { + if !on_committee { + roles_rem.push(role.id.to_owned()); + } + } } } - if !roles_required.is_empty() { + let has_committee_role = roles_current_id.contains(&committee_member); + + if on_committee && !has_committee_role { // if there are committee roles then give the general purporse role roles_add.push(committee_member); + } + if !on_committee && has_committee_role { + roles_rem.push(committee_member); + } + if !roles_required.is_empty() { if let Some(x) = roles_db.get_mut(&0) { x.count += 1; } @@ -372,8 +379,6 @@ pub mod committee { if !roles_add.is_empty() { // these roles are flavor roles, only there to make folks mentionable member.add_roles(&ctx, &roles_add).await.unwrap_or_default(); - } else { - member.remove_roles(&ctx, &[committee_member]).await.unwrap_or_default(); } } From 5815cde38b1216f4b286e19617ae324fe15ebadd Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 22:28:59 +0100 Subject: [PATCH 47/67] fmt: better wording for logs --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 08699e4..76ef059 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,11 +60,11 @@ impl EventHandler for Handler { let db = db_lock.read().await; loop { - println!("User update - Start"); + println!("Update - Users - Start"); for server_config in get_server_config_bulk(&db).await { normal::update_server(&ctx_task, &server_config, &[], &[]).await; } - println!("User update - End"); + println!("Update - Users - End"); tokio::time::sleep(Duration::from_secs(60 * 5)).await; } }); From 96eb81293b498b71f55824d47e8f7f72314cb03b Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 22:32:55 +0100 Subject: [PATCH 48/67] feat: got the committee update running smoothly (using cache and far far faster due to fixing some logic issues) --- src/main.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 76ef059..ffbd10d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use serenity::{ }; use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_config_bulk, get_server_member, DataBase}; use skynet_discord_bot::common::set_roles::committee::update_committees; -use skynet_discord_bot::common::set_roles::normal; +use skynet_discord_bot::common::set_roles::{committee, normal}; use skynet_discord_bot::common::wolves::committees::Committees; use skynet_discord_bot::{get_config, Config}; use sqlx::{Pool, Sqlite}; @@ -70,6 +70,19 @@ impl EventHandler for Handler { }); } + { + // this is to update committee roles every 5 min + let ctx_task = Arc::clone(&ctx); + tokio::spawn(async move { + loop { + println!("Update - Committee - Start"); + committee::check_committee(&ctx_task).await; + println!("Update - Committee - End"); + tokio::time::sleep(Duration::from_secs(60 * 5)).await; + } + }); + } + // Now that the loop is running, we set the bool to true self.is_loop_running.swap(true, Ordering::Relaxed); } From eb8821674080a465f275f9e8fcf03fa18a40f776 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 22:33:56 +0100 Subject: [PATCH 49/67] feat: removed the cronjob for updating the committee server --- Cargo.toml | 3 -- flake.nix | 2 -- src/bin/update_committee.rs | 63 ------------------------------------- 3 files changed, 68 deletions(-) delete mode 100644 src/bin/update_committee.rs diff --git a/Cargo.toml b/Cargo.toml index f2bc696..deb6676 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,6 @@ edition = "2021" [[bin]] name = "update_data" -[[bin]] -name = "update_committee" - [[bin]] name = "update_minecraft" diff --git a/flake.nix b/flake.nix index 04e062b..ea52e09 100644 --- a/flake.nix +++ b/flake.nix @@ -154,8 +154,6 @@ scripts = { # every 20 min "update_data" = "*:0,10,20,30,40,50"; - # Committee server has its own timer - "update_committee" = "*:15:00"; # minecraft stuff is updated at 5am "update_minecraft" = "5:10:00"; # server icon gets updated daily at midnight diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs deleted file mode 100644 index 2eef977..0000000 --- a/src/bin/update_committee.rs +++ /dev/null @@ -1,63 +0,0 @@ -use serenity::all::{ChunkGuildFilter, GuildId}; -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::common::set_roles::committee; -use skynet_discord_bot::{get_config, Config}; -use std::{process, sync::Arc}; -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 {}) - .cache_settings(serenity::cache::Settings::default()) - .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 cache_ready(&self, ctx: Context, guilds: Vec) { - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache built successfully!"); - } - - async fn ready(&self, ctx: Context, ready: Ready) { - let ctx = Arc::new(ctx); - println!("{} is connected!", ready.user.name); - - // u[date committee server - committee::check_committee(Arc::clone(&ctx)).await; - - // finish up - process::exit(0); - } -} From 13eb2307543c7d117661a8affedae04f175084f4 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 22:48:04 +0100 Subject: [PATCH 50/67] fix: updating the wolves (user data) should not trigger a server update (directly). That should always be triggered separately. This was a holdover from a time when updating teh users was expensive (timewise) --- src/common/wolves.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 6f73842..e199ad2 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -49,7 +49,6 @@ async fn add_users_wolves(db: &Pool, user: &WolvesResultUserMin) { */ pub mod cns { use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers}; - use crate::common::set_roles::normal::update_server; use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; use crate::Config; use serenity::client::Context; @@ -96,7 +95,6 @@ pub mod cns { let existing = existing_tmp.iter().map(|data| (data.id_wolves, data)).collect::>(); // list of users that need to be updated for this server - let mut user_to_update = vec![]; let mut server_name_tmp = None; for user in wolves.get_members(wolves_api).await { // dbg!(&user.committee); @@ -115,10 +113,6 @@ pub mod cns { add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await; if old.expiry != user.expiry { add_users_server_members(&db, server, &user).await; - - if let Some(discord_id) = old.discord { - user_to_update.push(discord_id); - } } } } @@ -129,9 +123,6 @@ pub mod cns { set_server_member(&db, server, cs_id).await; } } - if !user_to_update.is_empty() { - update_server(ctx, &server_config, &[], &user_to_update).await; - } } } From 227db8a74130d104aff9e767fb155f72913e06a8 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 22:54:34 +0100 Subject: [PATCH 51/67] feat: moved the update data to the main thread --- Cargo.toml | 4 --- flake.nix | 2 -- src/bin/update_data.rs | 70 ------------------------------------------ src/main.rs | 22 ++++++++++++- 4 files changed, 21 insertions(+), 77 deletions(-) delete mode 100644 src/bin/update_data.rs diff --git a/Cargo.toml b/Cargo.toml index deb6676..ef48675 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,6 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[[bin]] -name = "update_data" - [[bin]] name = "update_minecraft" diff --git a/flake.nix b/flake.nix index ea52e09..31d6434 100644 --- a/flake.nix +++ b/flake.nix @@ -152,8 +152,6 @@ # modify these scripts = { - # every 20 min - "update_data" = "*:0,10,20,30,40,50"; # minecraft stuff is updated at 5am "update_minecraft" = "5:10:00"; # server icon gets updated daily at midnight diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs deleted file mode 100644 index d184ccd..0000000 --- a/src/bin/update_data.rs +++ /dev/null @@ -1,70 +0,0 @@ -use serenity::all::{ChunkGuildFilter, GuildId}; -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::common::wolves::cns::get_wolves; -use skynet_discord_bot::common::wolves::committees::get_cns; -use skynet_discord_bot::{get_config, Config}; -use std::{process, sync::Arc}; -use tokio::sync::RwLock; - -#[tokio::main] -async fn main() { - let config = get_config(); - let db = match db_init(&config).await { - Ok(x) => x, - Err(e) => { - dbg!(e); - 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 {}) - .cache_settings(serenity::cache::Settings::default()) - .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 cache_ready(&self, ctx: Context, guilds: Vec) { - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache built successfully!"); - } - - async fn ready(&self, ctx: Context, ready: Ready) { - let ctx = Arc::new(ctx); - println!("{} is connected!", ready.user.name); - - // get the data for each individual club/soc - get_wolves(&ctx).await; - - // get teh data for the clubs/socs committees - get_cns(&ctx).await; - - // finish up - process::exit(0); - } -} diff --git a/src/main.rs b/src/main.rs index ffbd10d..df08f6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,8 @@ use serenity::{ use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_config_bulk, get_server_member, DataBase}; use skynet_discord_bot::common::set_roles::committee::update_committees; use skynet_discord_bot::common::set_roles::{committee, normal}; -use skynet_discord_bot::common::wolves::committees::Committees; +use skynet_discord_bot::common::wolves::cns::get_wolves; +use skynet_discord_bot::common::wolves::committees::{get_cns, Committees}; use skynet_discord_bot::{get_config, Config}; use sqlx::{Pool, Sqlite}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -49,6 +50,25 @@ impl EventHandler for Handler { if !self.is_loop_running.load(Ordering::Relaxed) { // We have to clone the Arc, as it gets moved into the new thread. + { + // This updates all the data, wolves user data and the committees + let ctx_task = Arc::clone(&ctx); + tokio::spawn(async move { + loop { + println!("Update - Data - Start"); + + // get the data for each individual club/soc + get_wolves(&ctx_task).await; + + // get teh data for the clubs/socs committees + get_cns(&ctx_task).await; + + println!("Update - Data - End"); + tokio::time::sleep(Duration::from_secs(60 * 5)).await; + } + }); + } + { // this is to update member roles every 5 min let ctx_task = Arc::clone(&ctx); From feff293043b4f2f587e9aef6475decdc5d3e87cb Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 23:12:02 +0100 Subject: [PATCH 52/67] feat: moved the update server icon to the main thread --- Cargo.toml | 3 -- flake.nix | 3 +- src/bin/update_server-icon.rs | 78 ----------------------------------- src/commands/server_icon.rs | 2 +- src/main.rs | 40 +++++++++++++++++- 5 files changed, 40 insertions(+), 86 deletions(-) delete mode 100644 src/bin/update_server-icon.rs diff --git a/Cargo.toml b/Cargo.toml index ef48675..79ad1d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,6 @@ edition = "2021" [[bin]] name = "update_minecraft" -[[bin]] -name = "update_server-icon" - [[bin]] name = "cleanup_committee" diff --git a/flake.nix b/flake.nix index 31d6434..2dee869 100644 --- a/flake.nix +++ b/flake.nix @@ -153,9 +153,8 @@ # modify these scripts = { # minecraft stuff is updated at 5am + # this service does not depend on teh discord cache "update_minecraft" = "5:10:00"; - # server icon gets updated daily at midnight - "update_server-icon" = "0:01:00"; }; in { options.services."${package_name}" = { diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs deleted file mode 100644 index 2c093f9..0000000 --- a/src/bin/update_server-icon.rs +++ /dev/null @@ -1,78 +0,0 @@ -use serenity::all::{ChunkGuildFilter, GuildId}; -use serenity::{ - async_trait, - client::{Context, EventHandler}, - model::gateway::{GatewayIntents, Ready}, - Client, -}; -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, sync::Arc}; -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 {}) - .cache_settings(serenity::cache::Settings::default()) - .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 cache_ready(&self, ctx: Context, guilds: Vec) { - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache built successfully!"); - } - - async fn ready(&self, ctx: Context, ready: Ready) { - let ctx = Arc::new(ctx); - println!("{} is connected!", ready.user.name); - - 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::minimal(); - - update_icon::update_icon_main(&ctx, &db, &config_global, &config_toml).await; - - // finish up - process::exit(0); - } -} diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs index 9c970fc..59d755f 100644 --- a/src/commands/server_icon.rs +++ b/src/commands/server_icon.rs @@ -93,7 +93,7 @@ pub(crate) mod user { } } - async fn get_current_icon(db: &Pool) -> Option { + pub async fn get_current_icon(db: &Pool) -> Option { match sqlx::query_as::<_, ServerIcons>( " SELECT * from server_icons ORDER BY id DESC LIMIT 1 diff --git a/src/main.rs b/src/main.rs index df08f6b..6e6ad8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,11 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; +use crate::commands::server_icon::user::current::icon::get_current_icon; +use chrono::{Days, SecondsFormat, TimeDelta, Utc}; use serenity::all::{ - ActivityData, Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, GuildMembersChunkEvent, - Interaction, + ActivityData, Command, CommandDataOptionValue, CreateAttachment, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, + GuildMembersChunkEvent, Interaction, }; use serenity::{ async_trait, @@ -17,6 +19,7 @@ use serenity::{ Client, }; use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_config_bulk, get_server_member, DataBase}; +use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon}; use skynet_discord_bot::common::set_roles::committee::update_committees; use skynet_discord_bot::common::set_roles::{committee, normal}; use skynet_discord_bot::common::wolves::cns::get_wolves; @@ -103,6 +106,39 @@ impl EventHandler for Handler { }); } + { + // this updates teh server icon once a day + let ctx_task = Arc::clone(&ctx); + tokio::spawn(async move { + let db_lock = { + let data_read = ctx_task.data.read().await; + data_read.get::().expect("Expected Database 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(); + + loop { + println!("Update - Logo - Start"); + // even though this task will run every 5 min it will only actually change the icon when its a day old + if let Some(logo) = get_current_icon(&db).await { + let now = Utc::now(); + let yesterday = now.checked_sub_days(Days::new(1)).unwrap_or_default(); + if logo.date < yesterday.to_rfc3339_opts(SecondsFormat::Millis, true) { + update_icon::update_icon_main(&ctx, &db, &config_global, &config_toml).await; + } + } + println!("Update - Logo - End"); + tokio::time::sleep(Duration::from_secs(60 * 5)).await; + } + }); + } + // Now that the loop is running, we set the bool to true self.is_loop_running.swap(true, Ordering::Relaxed); } From 1af7f28a453bda331c203e862a71258690860490 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 23:40:34 +0100 Subject: [PATCH 53/67] feat: restore teh original services, giving up the logging and control was too much --- Cargo.toml | 9 ++++ flake.nix | 8 ++++ src/bin/update_committee.rs | 63 ++++++++++++++++++++++++++++ src/bin/update_data.rs | 70 +++++++++++++++++++++++++++++++ src/bin/update_server-icon.rs | 78 +++++++++++++++++++++++++++++++++++ src/bin/update_users.rs | 76 ++++++++++++++++++++++++++++++++++ 6 files changed, 304 insertions(+) create mode 100644 src/bin/update_committee.rs create mode 100644 src/bin/update_data.rs create mode 100644 src/bin/update_server-icon.rs create mode 100644 src/bin/update_users.rs diff --git a/Cargo.toml b/Cargo.toml index 79ad1d0..832eb09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,18 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "update_data" + +[[bin]] +name = "update_committee" + [[bin]] name = "update_minecraft" +[[bin]] +name = "update_server-icon" + [[bin]] name = "cleanup_committee" diff --git a/flake.nix b/flake.nix index 2dee869..81f4490 100644 --- a/flake.nix +++ b/flake.nix @@ -152,9 +152,17 @@ # modify these scripts = { + # every 10 min + "update_data" = "*:0,10,20,30,40,50"; + # groups are updated every hour, offset from teh ldap + "update_users" = "*:5,15,25,35,45,55"; + # Committee server has its own timer + "update_committee" = "*:5,15,25,35,45,55"; # minecraft stuff is updated at 5am # this service does not depend on teh discord cache "update_minecraft" = "5:10:00"; + # server icon gets updated daily at midnight + "update_server-icon" = "0:01:00"; }; in { options.services."${package_name}" = { diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs new file mode 100644 index 0000000..b348d18 --- /dev/null +++ b/src/bin/update_committee.rs @@ -0,0 +1,63 @@ +use serenity::all::{ChunkGuildFilter, GuildId}; +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::common::set_roles::committee; +use skynet_discord_bot::{get_config, Config}; +use std::{process, sync::Arc}; +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 {}) + .cache_settings(serenity::cache::Settings::default()) + .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 cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + + async fn ready(&self, ctx: Context, ready: Ready) { + let ctx = Arc::new(ctx); + println!("{} is connected!", ready.user.name); + + // u[date committee server + committee::check_committee(&ctx).await; + + // finish up + process::exit(0); + } +} diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs new file mode 100644 index 0000000..d184ccd --- /dev/null +++ b/src/bin/update_data.rs @@ -0,0 +1,70 @@ +use serenity::all::{ChunkGuildFilter, GuildId}; +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::common::wolves::cns::get_wolves; +use skynet_discord_bot::common::wolves::committees::get_cns; +use skynet_discord_bot::{get_config, Config}; +use std::{process, sync::Arc}; +use tokio::sync::RwLock; + +#[tokio::main] +async fn main() { + let config = get_config(); + let db = match db_init(&config).await { + Ok(x) => x, + Err(e) => { + dbg!(e); + 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 {}) + .cache_settings(serenity::cache::Settings::default()) + .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 cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + + async fn ready(&self, ctx: Context, ready: Ready) { + let ctx = Arc::new(ctx); + println!("{} is connected!", ready.user.name); + + // get the data for each individual club/soc + get_wolves(&ctx).await; + + // get teh data for the clubs/socs committees + get_cns(&ctx).await; + + // finish up + process::exit(0); + } +} diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs new file mode 100644 index 0000000..2c093f9 --- /dev/null +++ b/src/bin/update_server-icon.rs @@ -0,0 +1,78 @@ +use serenity::all::{ChunkGuildFilter, GuildId}; +use serenity::{ + async_trait, + client::{Context, EventHandler}, + model::gateway::{GatewayIntents, Ready}, + Client, +}; +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, sync::Arc}; +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 {}) + .cache_settings(serenity::cache::Settings::default()) + .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 cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + + async fn ready(&self, ctx: Context, ready: Ready) { + let ctx = Arc::new(ctx); + println!("{} is connected!", ready.user.name); + + 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::minimal(); + + update_icon::update_icon_main(&ctx, &db, &config_global, &config_toml).await; + + // finish up + process::exit(0); + } +} diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs new file mode 100644 index 0000000..27dc1d6 --- /dev/null +++ b/src/bin/update_users.rs @@ -0,0 +1,76 @@ +use serenity::all::{ChunkGuildFilter, GuildId}; +use serenity::{ + async_trait, + client::{Context, EventHandler}, + model::gateway::{GatewayIntents, Ready}, + Client, +}; +use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase}; +use skynet_discord_bot::common::set_roles::normal; +use skynet_discord_bot::{get_config, Config}; +use std::{process, sync::Arc}; +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 {}) + .cache_settings(serenity::cache::Settings::default()) + .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 cache_ready(&self, ctx: Context, guilds: Vec) { + for guild in guilds { + ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); + } + println!("Cache built successfully!"); + } + + async fn ready(&self, ctx: Context, ready: Ready) { + let ctx = Arc::new(ctx); + println!("{} is connected!", ready.user.name); + + // this goes into each server and sets roles for each wolves member + check_bulk(Arc::clone(&ctx)).await; + + // finish up + process::exit(0); + } +} + +async fn check_bulk(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; + + for server_config in get_server_config_bulk(&db).await { + normal::update_server(&ctx, &server_config, &[], &[]).await; + } +} From bd9d0cd43f12ce062ceab1811848ede632d4e4c3 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 23:44:13 +0100 Subject: [PATCH 54/67] fix: these do not need to use teh cache --- src/bin/update_data.rs | 7 ------- src/bin/update_server-icon.rs | 7 ------- 2 files changed, 14 deletions(-) diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index d184ccd..e67720e 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -47,13 +47,6 @@ async fn main() { struct Handler; #[async_trait] impl EventHandler for Handler { - async fn cache_ready(&self, ctx: Context, guilds: Vec) { - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache built successfully!"); - } - async fn ready(&self, ctx: Context, ready: Ready) { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index 2c093f9..c59634e 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -45,13 +45,6 @@ async fn main() { struct Handler; #[async_trait] impl EventHandler for Handler { - async fn cache_ready(&self, ctx: Context, guilds: Vec) { - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache built successfully!"); - } - async fn ready(&self, ctx: Context, ready: Ready) { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); From 6d08312f48dca12d4b06d104499ef3633fa21924 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 23:46:30 +0100 Subject: [PATCH 55/67] fix: these were not using teh cache to access teh member/role data *was executing before teh cache was built (``cache_ready`` is only when teh cache has been init, not when it is populated) --- src/bin/cleanup_committee.rs | 35 +++++++++++++++++------------------ src/bin/update_committee.rs | 17 ++++++++--------- src/bin/update_users.rs | 19 +++++++++---------- 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs index a2c3772..cc68536 100644 --- a/src/bin/cleanup_committee.rs +++ b/src/bin/cleanup_committee.rs @@ -1,4 +1,4 @@ -use serenity::all::{ChunkGuildFilter, GuildId}; +use serenity::all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}; use serenity::{ async_trait, client::{Context, EventHandler}, @@ -56,26 +56,25 @@ impl EventHandler for Handler { println!("Cache built successfully!"); } - async fn ready(&self, ctx: Context, ready: Ready) { - let ctx = Arc::new(ctx); - println!("{} is connected!", ready.user.name); + async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { + if (chunk.chunk_index + 1) == chunk.chunk_count { + let db_lock = { + let data_read = ctx.data.read().await; + data_read.get::().expect("Expected Config in TypeMap.").clone() + }; - 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 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 = config_lock.read().await; - 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; - - cleanup(&db, &ctx, &config).await; - // finish up - process::exit(0); + cleanup(&db, &ctx, &config).await; + // finish up + process::exit(0); + } } } diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs index b348d18..e3e7277 100644 --- a/src/bin/update_committee.rs +++ b/src/bin/update_committee.rs @@ -1,4 +1,4 @@ -use serenity::all::{ChunkGuildFilter, GuildId}; +use serenity::all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}; use serenity::{ async_trait, client::{Context, EventHandler}, @@ -50,14 +50,13 @@ impl EventHandler for Handler { println!("Cache built successfully!"); } - async fn ready(&self, ctx: Context, ready: Ready) { - let ctx = Arc::new(ctx); - println!("{} is connected!", ready.user.name); + async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { + if (chunk.chunk_index + 1) == chunk.chunk_count { + // u[date committee server + committee::check_committee(&ctx).await; - // u[date committee server - committee::check_committee(&ctx).await; - - // finish up - process::exit(0); + // finish up + process::exit(0); + } } } diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index 27dc1d6..6ba99b0 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -1,4 +1,4 @@ -use serenity::all::{ChunkGuildFilter, GuildId}; +use serenity::all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}; use serenity::{ async_trait, client::{Context, EventHandler}, @@ -50,19 +50,18 @@ impl EventHandler for Handler { println!("Cache built successfully!"); } - async fn ready(&self, ctx: Context, ready: Ready) { - let ctx = Arc::new(ctx); - println!("{} is connected!", ready.user.name); + async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { + if (chunk.chunk_index + 1) == chunk.chunk_count { + // this goes into each server and sets roles for each wolves member + check_bulk(&ctx).await; - // this goes into each server and sets roles for each wolves member - check_bulk(Arc::clone(&ctx)).await; - - // finish up - process::exit(0); + // finish up + process::exit(0); + } } } -async fn check_bulk(ctx: Arc) { +async fn check_bulk(ctx: &Context) { let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() From 57d4947edfdcb005faaa98d45d7973dbe1870557 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Sun, 20 Jul 2025 23:48:05 +0100 Subject: [PATCH 56/67] fix: no longer needint to wait until the cache in teh main program is filled --- src/main.rs | 100 ---------------------------------------------------- 1 file changed, 100 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6e6ad8a..e76bd34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,106 +45,6 @@ impl EventHandler for Handler { println!("Cache built successfully!"); } - async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { - if (chunk.chunk_index + 1) == chunk.chunk_count { - // from https://github.com/serenity-rs/serenity/blob/18349f7bba43acad4261103eb38fe01d93f382df/examples/e13_parallel_loops/src/main.rs#L48 - let ctx = Arc::new(ctx); - - if !self.is_loop_running.load(Ordering::Relaxed) { - // We have to clone the Arc, as it gets moved into the new thread. - - { - // This updates all the data, wolves user data and the committees - let ctx_task = Arc::clone(&ctx); - tokio::spawn(async move { - loop { - println!("Update - Data - Start"); - - // get the data for each individual club/soc - get_wolves(&ctx_task).await; - - // get teh data for the clubs/socs committees - get_cns(&ctx_task).await; - - println!("Update - Data - End"); - tokio::time::sleep(Duration::from_secs(60 * 5)).await; - } - }); - } - - { - // this is to update member roles every 5 min - let ctx_task = Arc::clone(&ctx); - tokio::spawn(async move { - let db_lock = { - let data_read = ctx_task.data.read().await; - data_read.get::().expect("Expected Database in TypeMap.").clone() - }; - let db = db_lock.read().await; - - loop { - println!("Update - Users - Start"); - for server_config in get_server_config_bulk(&db).await { - normal::update_server(&ctx_task, &server_config, &[], &[]).await; - } - println!("Update - Users - End"); - tokio::time::sleep(Duration::from_secs(60 * 5)).await; - } - }); - } - - { - // this is to update committee roles every 5 min - let ctx_task = Arc::clone(&ctx); - tokio::spawn(async move { - loop { - println!("Update - Committee - Start"); - committee::check_committee(&ctx_task).await; - println!("Update - Committee - End"); - tokio::time::sleep(Duration::from_secs(60 * 5)).await; - } - }); - } - - { - // this updates teh server icon once a day - let ctx_task = Arc::clone(&ctx); - tokio::spawn(async move { - let db_lock = { - let data_read = ctx_task.data.read().await; - data_read.get::().expect("Expected Database 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(); - - loop { - println!("Update - Logo - Start"); - // even though this task will run every 5 min it will only actually change the icon when its a day old - if let Some(logo) = get_current_icon(&db).await { - let now = Utc::now(); - let yesterday = now.checked_sub_days(Days::new(1)).unwrap_or_default(); - if logo.date < yesterday.to_rfc3339_opts(SecondsFormat::Millis, true) { - update_icon::update_icon_main(&ctx, &db, &config_global, &config_toml).await; - } - } - println!("Update - Logo - End"); - tokio::time::sleep(Duration::from_secs(60 * 5)).await; - } - }); - } - - // Now that the loop is running, we set the bool to true - self.is_loop_running.swap(true, Ordering::Relaxed); - } - } - } - // handles previously linked accounts joining the server async fn guild_member_addition(&self, ctx: Context, new_member: Member) { let db_lock = { From 9d409e3692c8be39d7d98cccc18cd60f8bf8fa51 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 21 Jul 2025 00:38:59 +0100 Subject: [PATCH 57/67] fmt: clippy and nightly fmt --- .rustfmt.toml | 2 +- src/bin/cleanup_committee.rs | 15 ++++++---- src/bin/update_committee.rs | 14 +++++---- src/bin/update_data.rs | 12 ++++---- src/bin/update_minecraft.rs | 10 +++++-- src/bin/update_server-icon.rs | 7 +++-- src/bin/update_users.rs | 16 ++++++---- src/commands/add_server.rs | 14 +++++---- src/commands/count.rs | 13 ++++---- src/commands/minecraft.rs | 56 +++++++++++++++++++++-------------- src/commands/role_adder.rs | 3 +- src/commands/server_icon.rs | 6 ++-- src/commands/wolves.rs | 28 +++++++++++------- src/common/database.rs | 19 +++++++----- src/common/minecraft.rs | 9 ++---- src/common/renderer.rs | 6 ++-- src/common/set_roles.rs | 49 ++++++++++++++++-------------- src/common/wolves.rs | 16 +++++----- src/lib.rs | 6 ++-- src/main.rs | 36 +++++++++------------- 20 files changed, 194 insertions(+), 143 deletions(-) diff --git a/.rustfmt.toml b/.rustfmt.toml index 6aeb30c..2b0831a 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -7,4 +7,4 @@ fn_params_layout = "Compressed" struct_lit_width = 0 tab_spaces = 2 use_small_heuristics = "Max" -#imports_granularity="Crate" \ No newline at end of file +imports_granularity = "Crate" \ No newline at end of file diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs index cc68536..76f0526 100644 --- a/src/bin/cleanup_committee.rs +++ b/src/bin/cleanup_committee.rs @@ -1,14 +1,17 @@ -use serenity::all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}; use serenity::{ + all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}, async_trait, client::{Context, EventHandler}, - model::gateway::{GatewayIntents, Ready}, + model::gateway::GatewayIntents, Client, }; -use skynet_discord_bot::common::database::{db_init, DataBase}; - -use skynet_discord_bot::common::set_roles::committee::db_roles_get; -use skynet_discord_bot::{get_config, Config}; +use skynet_discord_bot::{ + common::{ + database::{db_init, DataBase}, + set_roles::committee::db_roles_get, + }, + get_config, Config, +}; use sqlx::{Pool, Sqlite}; use std::{process, sync::Arc}; use tokio::sync::RwLock; diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs index e3e7277..ff263f6 100644 --- a/src/bin/update_committee.rs +++ b/src/bin/update_committee.rs @@ -1,13 +1,17 @@ -use serenity::all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}; use serenity::{ + all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}, async_trait, client::{Context, EventHandler}, - model::gateway::{GatewayIntents, Ready}, + model::gateway::GatewayIntents, Client, }; -use skynet_discord_bot::common::database::{db_init, DataBase}; -use skynet_discord_bot::common::set_roles::committee; -use skynet_discord_bot::{get_config, Config}; +use skynet_discord_bot::{ + common::{ + database::{db_init, DataBase}, + set_roles::committee, + }, + get_config, Config, +}; use std::{process, sync::Arc}; use tokio::sync::RwLock; diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index e67720e..902920e 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -1,14 +1,16 @@ -use serenity::all::{ChunkGuildFilter, GuildId}; 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::common::wolves::cns::get_wolves; -use skynet_discord_bot::common::wolves::committees::get_cns; -use skynet_discord_bot::{get_config, Config}; +use skynet_discord_bot::{ + common::{ + database::{db_init, DataBase}, + wolves::{cns::get_wolves, committees::get_cns}, + }, + get_config, Config, +}; use std::{process, sync::Arc}; use tokio::sync::RwLock; diff --git a/src/bin/update_minecraft.rs b/src/bin/update_minecraft.rs index f5d3634..f7c24e0 100644 --- a/src/bin/update_minecraft.rs +++ b/src/bin/update_minecraft.rs @@ -1,6 +1,10 @@ -use skynet_discord_bot::common::database::db_init; -use skynet_discord_bot::common::minecraft::{get_minecraft_config, update_server, whitelist_wipe}; -use skynet_discord_bot::get_config; +use skynet_discord_bot::{ + common::{ + database::db_init, + minecraft::{get_minecraft_config, update_server, whitelist_wipe}, + }, + get_config, +}; use std::collections::HashSet; #[tokio::main] diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index c59634e..ba9de80 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -1,13 +1,14 @@ -use serenity::all::{ChunkGuildFilter, GuildId}; use serenity::{ async_trait, client::{Context, EventHandler}, model::gateway::{GatewayIntents, Ready}, Client, }; -use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon}; use skynet_discord_bot::{ - common::database::{db_init, DataBase}, + common::{ + database::{db_init, DataBase}, + server_icon::{get_config_icons, update_icon}, + }, get_config, Config, }; use std::{process, sync::Arc}; diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index 6ba99b0..c146a25 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -1,13 +1,17 @@ -use serenity::all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}; use serenity::{ + all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent}, async_trait, client::{Context, EventHandler}, - model::gateway::{GatewayIntents, Ready}, + model::gateway::GatewayIntents, Client, }; -use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase}; -use skynet_discord_bot::common::set_roles::normal; -use skynet_discord_bot::{get_config, Config}; +use skynet_discord_bot::{ + common::{ + database::{db_init, get_server_config_bulk, DataBase}, + set_roles::normal, + }, + get_config, Config, +}; use std::{process, sync::Arc}; use tokio::sync::RwLock; @@ -70,6 +74,6 @@ async fn check_bulk(ctx: &Context) { let db = db_lock.read().await; for server_config in get_server_config_bulk(&db).await { - normal::update_server(&ctx, &server_config, &[], &[]).await; + normal::update_server(ctx, &server_config, &[], &[]).await; } } diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 387bd9e..63f3d49 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -1,8 +1,12 @@ -use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; -use serenity::client::Context; -use skynet_discord_bot::common::database::{get_server_config, DataBase, Servers}; -use skynet_discord_bot::common::set_roles::normal::update_server; -use skynet_discord_bot::common::wolves::cns::get_wolves; +use serenity::{ + all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, + client::Context, +}; +use skynet_discord_bot::common::{ + database::{get_server_config, DataBase, Servers}, + set_roles::normal::update_server, + wolves::cns::get_wolves, +}; use sqlx::{Error, Pool, Sqlite}; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { diff --git a/src/commands/count.rs b/src/commands/count.rs index 678c9e0..2bed533 100644 --- a/src/commands/count.rs +++ b/src/commands/count.rs @@ -5,8 +5,7 @@ pub mod committee { use serenity::all::{ CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption, }; - use skynet_discord_bot::common::database::DataBase; - use skynet_discord_bot::common::set_roles::committee::db_roles_get; + use skynet_discord_bot::common::{database::DataBase, set_roles::committee::db_roles_get}; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { let sub_options = if let Some(CommandDataOption { @@ -85,9 +84,13 @@ pub mod servers { // get the list of all the current clubs/socs use serde::{Deserialize, Serialize}; use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption}; - use skynet_discord_bot::common::database::{get_server_config_bulk, DataBase}; - use skynet_discord_bot::common::set_roles::committee::get_committees; - use skynet_discord_bot::get_now_iso; + use skynet_discord_bot::{ + common::{ + database::{get_server_config_bulk, DataBase}, + set_roles::committee::get_committees, + }, + get_now_iso, + }; use sqlx::{Pool, Sqlite}; use std::collections::HashMap; diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index ca395c6..f3a050d 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -9,11 +9,17 @@ pub(crate) mod user { use super::*; use crate::commands::wolves::link::get_server_member_discord; use serde::{Deserialize, Serialize}; - use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; - use serenity::model::id::UserId; - use skynet_discord_bot::common::database::Wolves; - use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft}; - use skynet_discord_bot::Config; + use serenity::{ + all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, + model::id::UserId, + }; + use skynet_discord_bot::{ + common::{ + database::Wolves, + minecraft::{whitelist_update, Minecraft}, + }, + Config, + }; use sqlx::Error; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { @@ -185,14 +191,17 @@ pub(crate) mod server { use super::*; pub(crate) mod add { - use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}; - use serenity::model::id::GuildId; + use serenity::{ + all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}, + model::id::GuildId, + }; use sqlx::Error; // this is to managfe the server side of commands related to minecraft use super::*; - use skynet_discord_bot::common::minecraft::update_server; - use skynet_discord_bot::common::minecraft::Minecraft; - use skynet_discord_bot::Config; + use skynet_discord_bot::{ + common::minecraft::{update_server, Minecraft}, + Config, + }; pub fn register() -> CreateCommand { CreateCommand::new("minecraft_add") @@ -260,12 +269,14 @@ pub(crate) mod server { } pub(crate) mod list { - use serenity::all::CommandInteraction; - use serenity::builder::CreateCommand; - use serenity::client::Context; - use skynet_discord_bot::common::database::DataBase; - use skynet_discord_bot::common::minecraft::{get_minecraft_config_server, server_information}; - use skynet_discord_bot::Config; + use serenity::{all::CommandInteraction, builder::CreateCommand, client::Context}; + use skynet_discord_bot::{ + common::{ + database::DataBase, + minecraft::{get_minecraft_config_server, server_information}, + }, + Config, + }; pub fn register() -> CreateCommand { CreateCommand::new("minecraft_list") @@ -320,12 +331,13 @@ pub(crate) mod server { } pub(crate) mod delete { - use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}; - use serenity::builder::CreateCommand; - use serenity::client::Context; - use serenity::model::id::GuildId; - use skynet_discord_bot::common::database::DataBase; - use skynet_discord_bot::common::minecraft::Minecraft; + use serenity::{ + all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}, + builder::CreateCommand, + client::Context, + model::id::GuildId, + }; + use skynet_discord_bot::common::{database::DataBase, minecraft::Minecraft}; use sqlx::{Error, Pool, Sqlite}; pub fn register() -> CreateCommand { diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs index f58803b..20928ba 100644 --- a/src/commands/role_adder.rs +++ b/src/commands/role_adder.rs @@ -142,8 +142,7 @@ pub mod edit { pub mod list {} pub mod tools { - use serenity::client::Context; - use serenity::model::guild::Member; + use serenity::{client::Context, model::guild::Member}; use skynet_discord_bot::common::database::RoleAdder; use sqlx::{Pool, Sqlite}; diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs index 59d755f..2989c42 100644 --- a/src/commands/server_icon.rs +++ b/src/commands/server_icon.rs @@ -113,8 +113,10 @@ pub(crate) mod user { pub(crate) mod festival { 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 skynet_discord_bot::{ + common::server_icon::{get_config_icons, update_icon::get_festival}, + Config, + }; // use this to return what current festivals are active? pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { diff --git a/src/commands/wolves.rs b/src/commands/wolves.rs index d78454b..f137622 100644 --- a/src/commands/wolves.rs +++ b/src/commands/wolves.rs @@ -4,11 +4,16 @@ use lettre::{ Message, SmtpTransport, Transport, }; use maud::html; -use serenity::all::CommandOptionType; -use serenity::builder::CreateCommandOption; -use serenity::{builder::CreateCommand, client::Context, model::id::UserId}; -use skynet_discord_bot::common::database::{DataBase, Wolves, WolvesVerify}; -use skynet_discord_bot::{get_now_iso, random_string, Config}; +use serenity::{ + all::CommandOptionType, + builder::{CreateCommand, CreateCommandOption}, + client::Context, + model::id::UserId, +}; +use skynet_discord_bot::{ + common::database::{DataBase, Wolves, WolvesVerify}, + get_now_iso, random_string, Config, +}; use sqlx::{Pool, Sqlite}; pub mod link { @@ -298,11 +303,14 @@ pub mod link_docs { pub mod verify { use super::*; use crate::commands::wolves::link::{db_pending_clear_expired, get_server_member_discord, get_verify_from_db}; - use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; - use serenity::model::user::User; - use skynet_discord_bot::common::database::get_server_config; - use skynet_discord_bot::common::database::{ServerMembersWolves, Servers}; - use skynet_discord_bot::common::wolves::committees::Committees; + use serenity::{ + all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, + model::user::User, + }; + use skynet_discord_bot::common::{ + database::{get_server_config, ServerMembersWolves, Servers}, + wolves::committees::Committees, + }; use sqlx::Error; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { diff --git a/src/common/database.rs b/src/common/database.rs index 0663799..190baf2 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -1,12 +1,17 @@ use crate::Config; use serde::{Deserialize, Serialize}; -use serenity::model::guild; -use serenity::model::id::{ChannelId, GuildId, RoleId, UserId}; -use serenity::prelude::TypeMapKey; -use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}; -use sqlx::{Error, FromRow, Pool, Row, Sqlite}; -use std::str::FromStr; -use std::sync::Arc; +use serenity::{ + model::{ + guild, + id::{ChannelId, GuildId, RoleId, UserId}, + }, + prelude::TypeMapKey, +}; +use sqlx::{ + sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow}, + Error, FromRow, Pool, Row, Sqlite, +}; +use std::{str::FromStr, sync::Arc}; use tokio::sync::RwLock; pub struct DataBase; diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index 4faa8ed..b7362e6 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -1,10 +1,7 @@ -use crate::common::set_roles::normal::get_server_member_bulk; -use crate::Config; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use crate::{common::set_roles::normal::get_server_member_bulk, Config}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serenity::model::id::GuildId; -use sqlx::sqlite::SqliteRow; -use sqlx::{Error, FromRow, Pool, Row, Sqlite}; +use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Minecraft { diff --git a/src/common/renderer.rs b/src/common/renderer.rs index f99d891..e105c16 100644 --- a/src/common/renderer.rs +++ b/src/common/renderer.rs @@ -1,8 +1,10 @@ // 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 std::{ + ffi::OsStr, + path::{Path, PathBuf}, +}; // use clap::builder::OsStr; use color_eyre::{eyre::bail, Result}; diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index fbbc828..3a894ee 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -1,8 +1,12 @@ pub mod normal { - use crate::common::database::{DataBase, ServerMembersWolves, Servers, Wolves}; - use crate::get_now_iso; - use serenity::client::Context; - use serenity::model::id::{GuildId, RoleId, UserId}; + use crate::{ + common::database::{DataBase, ServerMembersWolves, Servers, Wolves}, + get_now_iso, + }; + use serenity::{ + client::Context, + model::id::{GuildId, RoleId, UserId}, + }; use sqlx::{Pool, Sqlite}; struct RolesChange { @@ -152,21 +156,22 @@ pub mod normal { // for updating committee members pub mod committee { - use crate::common::database::{get_channel_from_row, get_role_from_row, DataBase, Wolves}; - use crate::common::wolves::committees::Committees; - use crate::Config; + use crate::{ + common::{ + database::{get_channel_from_row, get_role_from_row, DataBase, Wolves}, + wolves::committees::Committees, + }, + Config, + }; use serde::{Deserialize, Serialize}; - use serenity::all::EditRole; - use serenity::builder::CreateChannel; - use serenity::client::Context; - use serenity::model::channel::ChannelType; - use serenity::model::guild::Member; - use serenity::model::id::ChannelId; - use serenity::model::prelude::RoleId; - use sqlx::sqlite::SqliteRow; - use sqlx::{Error, FromRow, Pool, Row, Sqlite}; + use serenity::{ + all::EditRole, + builder::CreateChannel, + client::Context, + model::{channel::ChannelType, guild::Member, id::ChannelId, prelude::RoleId}, + }; + use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite}; use std::collections::HashMap; - use std::sync::Arc; pub async fn check_committee(ctx: &Context) { let db_lock = { @@ -187,7 +192,7 @@ pub mod committee { // because to use it to update a single user we need to pre-get the members of teh server let mut members = server.members(&ctx, None, None).await.unwrap_or_default(); - update_committees(&db, &ctx, &config_global, &mut members).await; + update_committees(&db, ctx, &config_global, &mut members).await; } /** @@ -342,11 +347,11 @@ pub mod committee { for role in &roles_current { roles_current_id.push(role.id.to_owned()); if !roles_required.contains(&role.id) { - if role.id == committee_member { - if !on_committee { - roles_rem.push(role.id.to_owned()); - } + if role.id == committee_member && on_committee { + continue; } + + roles_rem.push(role.id.to_owned()); } } diff --git a/src/common/wolves.rs b/src/common/wolves.rs index e199ad2..7ee71df 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -48,11 +48,14 @@ async fn add_users_wolves(db: &Pool, user: &WolvesResultUserMin) { This is getting data for Clubs and Socs */ pub mod cns { - use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers}; - use crate::common::wolves::{add_users_wolves, WolvesResultUserMin}; - use crate::Config; - use serenity::client::Context; - use serenity::model::id::GuildId; + use crate::{ + common::{ + database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers}, + wolves::{add_users_wolves, WolvesResultUserMin}, + }, + Config, + }; + use serenity::{client::Context, model::id::GuildId}; use sqlx::{Pool, Sqlite}; use std::collections::BTreeMap; @@ -191,8 +194,7 @@ pub mod cns { Get and store the data on C&S committees */ pub mod committees { - use crate::common::database::DataBase; - use crate::Config; + use crate::{common::database::DataBase, Config}; use serenity::client::Context; use sqlx::{Pool, Sqlite}; diff --git a/src/lib.rs b/src/lib.rs index 734fa2c..75c9d78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,10 @@ pub mod common; use chrono::{Datelike, SecondsFormat, Utc}; use dotenvy::dotenv; use rand::{distr::Alphanumeric, rng, Rng}; -use serenity::model::id::{ChannelId, GuildId, RoleId}; -use serenity::prelude::TypeMapKey; +use serenity::{ + model::id::{ChannelId, GuildId, RoleId}, + prelude::TypeMapKey, +}; use std::{env, sync::Arc}; use tokio::sync::RwLock; diff --git a/src/main.rs b/src/main.rs index e76bd34..c0bddb3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,39 +1,33 @@ pub mod commands; use crate::commands::role_adder::tools::on_role_change; -use crate::commands::server_icon::user::current::icon::get_current_icon; -use chrono::{Days, SecondsFormat, TimeDelta, Utc}; -use serenity::all::{ - ActivityData, Command, CommandDataOptionValue, CreateAttachment, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, - GuildMembersChunkEvent, Interaction, -}; use serenity::{ + all::{Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, Interaction}, async_trait, client::{Context, EventHandler}, - gateway::ChunkGuildFilter, + gateway::{ActivityData, ChunkGuildFilter}, model::{ + event::GuildMemberUpdateEvent, gateway::{GatewayIntents, Ready}, guild::Member, + id::GuildId, user::OnlineStatus, }, Client, }; -use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_config_bulk, get_server_member, DataBase}; -use skynet_discord_bot::common::server_icon::{get_config_icons, update_icon}; -use skynet_discord_bot::common::set_roles::committee::update_committees; -use skynet_discord_bot::common::set_roles::{committee, normal}; -use skynet_discord_bot::common::wolves::cns::get_wolves; -use skynet_discord_bot::common::wolves::committees::{get_cns, Committees}; -use skynet_discord_bot::{get_config, Config}; +use skynet_discord_bot::{ + common::{ + database::{db_init, get_server_config, get_server_member, DataBase}, + set_roles::committee::update_committees, + wolves::committees::Committees, + }, + get_config, Config, +}; use sqlx::{Pool, Sqlite}; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use std::time::Duration; use tokio::sync::RwLock; -struct Handler { - is_loop_running: AtomicBool, -} +struct Handler; #[async_trait] impl EventHandler for Handler { @@ -301,9 +295,7 @@ async fn main() { 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 { - is_loop_running: AtomicBool::new(false), - }) + .event_handler(Handler) .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); From d0726169ee106d2da49f3960a82c86ebb481290b Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 21 Jul 2025 00:50:20 +0100 Subject: [PATCH 58/67] clippy: changes from nightly clippy all of this is embeding teh var into teh format macro --- src/bin/cleanup_committee.rs | 2 +- src/bin/update_committee.rs | 2 +- src/bin/update_data.rs | 2 +- src/bin/update_server-icon.rs | 2 +- src/bin/update_users.rs | 2 +- src/commands/add_server.rs | 4 ++-- src/commands/count.rs | 4 ++-- src/commands/minecraft.rs | 8 ++++---- src/commands/role_adder.rs | 12 ++++++------ src/commands/server_icon.rs | 2 +- src/commands/wolves.rs | 10 +++++----- src/common/database.rs | 2 +- src/common/minecraft.rs | 2 +- src/common/renderer.rs | 2 +- src/common/set_roles.rs | 14 +++++++------- src/common/wolves.rs | 12 ++++++------ src/main.rs | 12 ++++++------ 17 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs index 76f0526..c6485a7 100644 --- a/src/bin/cleanup_committee.rs +++ b/src/bin/cleanup_committee.rs @@ -45,7 +45,7 @@ async fn main() { } if let Err(why) = client.start().await { - println!("Client error: {:?}", why); + println!("Client error: {why:?}"); } } diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs index ff263f6..b2792e0 100644 --- a/src/bin/update_committee.rs +++ b/src/bin/update_committee.rs @@ -40,7 +40,7 @@ async fn main() { } if let Err(why) = client.start().await { - println!("Client error: {:?}", why); + println!("Client error: {why:?}"); } } diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index 902920e..8f73ce9 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -42,7 +42,7 @@ async fn main() { } if let Err(why) = client.start().await { - println!("Client error: {:?}", why); + println!("Client error: {why:?}"); } } diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index ba9de80..56957da 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -39,7 +39,7 @@ async fn main() { } if let Err(why) = client.start().await { - println!("Client error: {:?}", why); + println!("Client error: {why:?}"); } } diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index c146a25..6ac9e00 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -40,7 +40,7 @@ async fn main() { } if let Err(why) = client.start().await { - println!("Client error: {:?}", why); + println!("Client error: {why:?}"); } } diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 63f3d49..0c7dd2f 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -76,8 +76,8 @@ pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { match add_server(&db, ctx, &server_data).await { Ok(_) => {} Err(e) => { - println!("{:?}", e); - return format!("Failure to insert into Servers {:?}", server_data); + println!("{e:?}"); + return format!("Failure to insert into Servers {server_data:?}"); } } diff --git a/src/commands/count.rs b/src/commands/count.rs index 2bed533..8a64ee7 100644 --- a/src/commands/count.rs +++ b/src/commands/count.rs @@ -52,7 +52,7 @@ pub mod committee { for (count, name) in cs { let leading = if count < 10 { " " } else { "" }; - let line = format!("{}{} {}", leading, count, name); + let line = format!("{leading}{count} {name}"); let length = line.len() + 1; @@ -146,7 +146,7 @@ pub mod servers { "" }; - let line = format!("{}{} {}{} {}", current_leading, current, past_leading, past, name); + let line = format!("{current_leading}{current} {past_leading}{past} {name}"); let length = line.len() + 1; diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index f3a050d..a4d1b1b 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -75,14 +75,14 @@ pub(crate) mod user { Ok(_) => {} Err(e) => { dbg!("{:?}", e); - return format!("Failure to minecraft username {:?}", username); + return format!("Failure to minecraft username {username:?}"); } } username_mc = username.to_string(); } else { match get_minecraft_bedrock(username, &config.minecraft_mcprofile).await { None => { - return format!("No UID found for {:?}", username); + return format!("No UID found for {username:?}"); } Some(x) => { match add_minecraft_bedrock(&db, &command.user.id, &x.floodgateuid).await { @@ -238,7 +238,7 @@ pub(crate) mod server { match add_server(&db, &g_id, &server_minecraft).await { Ok(_) => {} Err(e) => { - println!("{:?}", e); + println!("{e:?}"); return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft); } } @@ -375,7 +375,7 @@ pub(crate) mod server { match server_remove(&db, &g_id, &server_minecraft).await { Ok(_) => {} Err(e) => { - println!("{:?}", e); + println!("{e:?}"); return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft); } } diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs index 20928ba..e60ac83 100644 --- a/src/commands/role_adder.rs +++ b/src/commands/role_adder.rs @@ -79,8 +79,8 @@ pub mod edit { match add_server(&db, &server_data, delete).await { Ok(_) => {} Err(e) => { - println!("{:?}", e); - return format!("Failure to insert into Servers {:?}", server_data); + println!("{e:?}"); + return format!("Failure to insert into Servers {server_data:?}"); } } @@ -101,9 +101,9 @@ pub mod edit { } if delete { - format!("Removed {} + {} = {}", role_a_name, role_b_name, role_c_name) + format!("Removed {role_a_name} + {role_b_name} = {role_c_name}") } else { - format!("Added {} + {} = {}", role_a_name, role_b_name, role_c_name) + format!("Added {role_a_name} + {role_b_name} = {role_c_name}") } } @@ -179,13 +179,13 @@ pub mod tools { if !roles_add.is_empty() { if let Err(e) = new_data.add_roles(&ctx, &roles_add).await { - println!("{:?}", e); + println!("{e:?}"); } } if !roles_remove.is_empty() { if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await { - println!("{:?}", e); + println!("{e:?}"); } } } diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs index 2989c42..0cba1b1 100644 --- a/src/commands/server_icon.rs +++ b/src/commands/server_icon.rs @@ -207,7 +207,7 @@ pub(crate) mod user { // 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 line = format!("``{current_leading}{times}`` [{name}](<{url}>)"); let length = line.len() + 1; diff --git a/src/commands/wolves.rs b/src/commands/wolves.rs index f137622..dc40ad0 100644 --- a/src/commands/wolves.rs +++ b/src/commands/wolves.rs @@ -115,7 +115,7 @@ pub mod link { } } - format!("Verification email sent to {}, it may take up to 15 min for it to arrive. If it takes longer check the Junk folder.", email) + format!("Verification email sent to {email}, it may take up to 15 min for it to arrive. If it takes longer check the Junk folder.") } pub async fn get_server_member_discord(db: &Pool, user: &UserId) -> Option { @@ -365,12 +365,12 @@ pub mod verify { "Discord username linked to Wolves".to_string() } Err(e) => { - println!("{:?}", e); + println!("{e:?}"); "Failed to save, please try /link_wolves again".to_string() } }; } - Err(e) => println!("{:?}", e), + Err(e) => println!("{e:?}"), } "Failed to verify".to_string() @@ -427,7 +427,7 @@ pub mod verify { } if let Err(e) = member.add_roles(&ctx, &roles).await { - println!("{:?}", e); + println!("{e:?}"); } } } @@ -443,7 +443,7 @@ pub mod verify { WHERE committee LIKE ?1 "#, ) - .bind(format!("%{}%", wolves_id)) + .bind(format!("%{wolves_id}%")) .fetch_all(db) .await .unwrap_or_else(|e| { diff --git a/src/common/database.rs b/src/common/database.rs index 190baf2..0c18668 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -225,7 +225,7 @@ pub async fn db_init(config: &Config) -> Result, Error> { let pool = SqlitePoolOptions::new() .max_connections(5) .connect_with( - SqliteConnectOptions::from_str(&format!("sqlite://{}", database))? + SqliteConnectOptions::from_str(&format!("sqlite://{database}"))? .foreign_keys(true) .create_if_missing(true), ) diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index b7362e6..60f7820 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -152,7 +152,7 @@ pub async fn get_minecraft_config_server(db: &Pool, g_id: GuildId) -> Ve } pub async fn whitelist_update(add: &Vec<(String, bool)>, server: &str, token: &str) { - println!("Update whitelist for {}", server); + println!("Update whitelist for {server}"); let url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}"); let bearer = format!("Bearer {token}"); diff --git a/src/common/renderer.rs b/src/common/renderer.rs index e105c16..97e73a0 100644 --- a/src/common/renderer.rs +++ b/src/common/renderer.rs @@ -179,7 +179,7 @@ impl Renderer { } fn set_color(&self, svg: &str, color: &String) -> String { - svg.replace("fill=\"currentColor\"", &format!("fill=\"#{}\"", color)) + svg.replace("fill=\"currentColor\"", &format!("fill=\"#{color}\"")) } fn get_svg_data(&self, fi: &Path) -> Result { diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 3a894ee..6352c24 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -71,7 +71,7 @@ pub mod normal { } if let Err(e) = member.add_roles(ctx, &roles).await { - println!("{:?}", e); + println!("{e:?}"); } } else { // old and never @@ -86,13 +86,13 @@ pub mod normal { roles_set.current_rem += 1; // if theya re not a current member and have the role then remove it if let Err(e) = member.remove_role(ctx, role_current).await { - println!("{:?}", e); + println!("{e:?}"); } } } for role in remove_roles.iter().flatten() { if let Err(e) = member.remove_role(ctx, role).await { - println!("{:?}", e); + println!("{e:?}"); } } } @@ -148,7 +148,7 @@ pub mod normal { Ok(_) => {} Err(e) => { println!("Failure to insert into {}", server.get()); - println!("{:?}", e); + println!("{e:?}"); } } } @@ -471,8 +471,8 @@ pub mod committee { { Ok(_) => {} Err(e) => { - println!("Failure to insert into Wolves {:?}", role); - println!("{:?}", e); + println!("Failure to insert into Wolves {role:?}"); + println!("{e:?}"); } } } @@ -489,7 +489,7 @@ pub mod committee { .await .unwrap_or_else(|e| { println!("Failure to get Roles from committee_roles"); - println!("{:?}", e); + println!("{e:?}"); vec![] }) } diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 7ee71df..17305f6 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -38,8 +38,8 @@ async fn add_users_wolves(db: &Pool, user: &WolvesResultUserMin) { { Ok(_) => {} Err(e) => { - println!("Failure to insert into Wolves {:?}", user); - println!("{:?}", e); + println!("Failure to insert into Wolves {user:?}"); + println!("{e:?}"); } } } @@ -145,7 +145,7 @@ pub mod cns { Ok(_) => {} Err(e) => { println!("Failure to set server name {}", server.get()); - println!("{:?}", e); + println!("{e:?}"); } } } @@ -184,7 +184,7 @@ pub mod cns { Ok(_) => {} Err(e) => { println!("Failure to insert into ServerMembers {} {:?}", server.get(), user); - println!("{:?}", e); + println!("{e:?}"); } } } @@ -263,8 +263,8 @@ pub mod committees { { Ok(_) => {} Err(e) => { - println!("Failure to insert into Committees {:?}", committee); - println!("{:?}", e); + println!("Failure to insert into Committees {committee:?}"); + println!("{e:?}"); } } } diff --git a/src/main.rs b/src/main.rs index c0bddb3..6a87f84 100644 --- a/src/main.rs +++ b/src/main.rs @@ -81,7 +81,7 @@ impl EventHandler for Handler { } if let Err(e) = new_member.add_roles(&ctx, &roles).await { - println!("{:?}", e); + println!("{e:?}"); } } else { let tmp = get_committee(&db, config_server.wolves_id).await; @@ -146,7 +146,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use { Ok(_) => {} Err(e) => { - println!("{:?}", e) + println!("{e:?}") } } @@ -154,7 +154,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use match config.committee_server.set_commands(&ctx.http, vec![commands::count::committee::register()]).await { Ok(_) => {} Err(e) => { - println!("{:?}", e) + println!("{e:?}") } } @@ -173,7 +173,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use { Ok(_) => {} Err(e) => { - println!("{:?}", e) + println!("{e:?}") } } } @@ -260,7 +260,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use }; if let Err(why) = command.edit_response(&ctx.http, EditInteractionResponse::new().content(content)).await { - println!("Cannot respond to slash command: {}", why); + println!("Cannot respond to slash command: {why}"); } } } @@ -312,6 +312,6 @@ async fn main() { // Shards will automatically attempt to reconnect, and will perform // exponential backoff until it reconnects. if let Err(why) = client.start().await { - println!("Client error: {:?}", why); + println!("Client error: {why:?}"); } } From 854e946a8fd692e1ed1eaca15b1805d0a90e5d35 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 21 Jul 2025 00:59:48 +0100 Subject: [PATCH 59/67] ci: improve the pipeline to test for the full suite ci: dont pull in the lfs for teh PR pipeline build ci: checkout without LFS ci: see if using the ref directly will help ci: test a slight modification ci: see if new get ldfs script works ci: test using teh new v8 --- .forgejo/workflows/check_lfs.yaml | 2 -- .forgejo/workflows/on_pr.yaml | 51 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 .forgejo/workflows/on_pr.yaml diff --git a/.forgejo/workflows/check_lfs.yaml b/.forgejo/workflows/check_lfs.yaml index dca575c..aaa2117 100644 --- a/.forgejo/workflows/check_lfs.yaml +++ b/.forgejo/workflows/check_lfs.yaml @@ -1,9 +1,7 @@ on: - - pull_request - push - workflow_dispatch - jobs: check_lfs: # nix/docker diff --git a/.forgejo/workflows/on_pr.yaml b/.forgejo/workflows/on_pr.yaml new file mode 100644 index 0000000..d17dc47 --- /dev/null +++ b/.forgejo/workflows/on_pr.yaml @@ -0,0 +1,51 @@ +on: + - pull_request + +jobs: + check_lfs: + # nix/docker + runs-on: nix + steps: + - uses: https://github.com/MPLew-is/lfs-check-action@1 + + # rust code must be formatted for standardisation + lint_fmt: + # build it using teh base nixos system, helps with caching + runs-on: nix + steps: + # get the repo first + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: https://forgejo.skynet.ie/Skynet/actions/get_lfs/nix@v8 + with: + server_url: ${{ gitea.server_url }} + repository: ${{ gitea.repository }} + - run: nix build .#fmt --verbose + + # clippy is incredibly useful for making yer code better + lint_clippy: + # build it using teh base nixos system, helps with caching + runs-on: nix + permissions: + checks: write + steps: + # get the repo first + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: https://forgejo.skynet.ie/Skynet/actions/get_lfs/nix@v8 + with: + server_url: ${{ gitea.server_url }} + repository: ${{ gitea.repository }} + - run: nix build .#clippy --verbose + + build: + # build it using teh base nixos system, helps with caching + runs-on: nix + needs: [ lint_fmt, lint_clippy ] + steps: + # get the repo first + - uses: https://code.forgejo.org/actions/checkout@v4 + - uses: https://forgejo.skynet.ie/Skynet/actions/get_lfs/nix@v8 + with: + server_url: ${{ gitea.server_url }} + repository: ${{ gitea.repository }} + - name: "Build it locally" + run: nix build --verbose From 095ff6f2cea5b3d35880d4a55b60930b60e3efcb Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 21 Jul 2025 02:51:33 +0100 Subject: [PATCH 60/67] fix: better handling of returning teh committees --- src/commands/count.rs | 6 ++++-- src/common/set_roles.rs | 26 +++++++++++++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/commands/count.rs b/src/commands/count.rs index 8a64ee7..2368bca 100644 --- a/src/commands/count.rs +++ b/src/commands/count.rs @@ -102,8 +102,10 @@ pub mod servers { let db = db_lock.read().await; let mut committees = HashMap::new(); - for committee in get_committees(&db).await { - committees.insert(committee.id, committee.to_owned()); + if let Some(x) = get_committees(&db).await { + for committee in x { + committees.insert(committee.id, committee.to_owned()); + } } let mut cs = vec![]; diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 6352c24..6e71992 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -201,7 +201,12 @@ pub mod committee { pub async fn update_committees(db: &Pool, ctx: &Context, config: &Config, members: &mut Vec) { let server = config.committee_server; let committee_member = config.committee_role; - let committees = get_committees(db).await; + let committees = match get_committees(db).await { + None => { + return; + } + Some(x) => x, + }; let categories = config.committee_category.clone(); // information about the server @@ -328,6 +333,10 @@ pub mod committee { // now we have a map of all users that should get roles time to go through all the folks on teh server for member in members { + // if member.user.id != 136522490632601600 { + // continue; + // } + // let roles_current = member.roles(ctx).unwrap_or_default(); let roles_required = match users_roles.get(&member.user.id) { @@ -494,8 +503,8 @@ pub mod committee { }) } - pub async fn get_committees(db: &Pool) -> Vec { - sqlx::query_as::<_, Committees>( + pub async fn get_committees(db: &Pool) -> Option> { + match sqlx::query_as::<_, Committees>( r#" SELECT * FROM committees @@ -503,10 +512,13 @@ pub mod committee { ) .fetch_all(db) .await - .unwrap_or_else(|e| { - dbg!(e); - vec![] - }) + { + Ok(x) => Some(x), + Err(e) => { + dbg!(e); + None + } + } } async fn get_server_member_discord(db: &Pool, user: &i64) -> Option { From a225c14b4fdaafe390dc8840065946d8c2a2e4b8 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Mon, 21 Jul 2025 04:29:03 +0100 Subject: [PATCH 61/67] fix: was ddossing the poor database guild_members_chunk is triggered for each chunk for each server it is on, and the bot is currently in 10 servers so it was runnign teh same thigns 10 times, clogging up conenctions --- src/bin/cleanup_committee.rs | 18 +++++++++++++----- src/bin/update_committee.rs | 18 +++++++++++++----- src/bin/update_users.rs | 27 ++++++++++++++++++++------- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs index c6485a7..188cc5b 100644 --- a/src/bin/cleanup_committee.rs +++ b/src/bin/cleanup_committee.rs @@ -52,15 +52,23 @@ async fn main() { struct Handler; #[async_trait] impl EventHandler for Handler { - async fn cache_ready(&self, ctx: Context, guilds: Vec) { - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache built successfully!"); + async fn cache_ready(&self, ctx: Context, _guilds: Vec) { + 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 server = config_global.committee_server; + + ctx.shard.chunk_guild(server, Some(2000), false, ChunkGuildFilter::None, None); + + println!("Cache loaded"); } async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { if (chunk.chunk_index + 1) == chunk.chunk_count { + println!("Cache built successfully!"); let db_lock = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs index b2792e0..b8cc6c2 100644 --- a/src/bin/update_committee.rs +++ b/src/bin/update_committee.rs @@ -47,15 +47,23 @@ async fn main() { struct Handler; #[async_trait] impl EventHandler for Handler { - async fn cache_ready(&self, ctx: Context, guilds: Vec) { - for guild in guilds { - ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); - } - println!("Cache built successfully!"); + async fn cache_ready(&self, ctx: Context, _guilds: Vec) { + 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 server = config_global.committee_server; + + ctx.shard.chunk_guild(server, Some(2000), false, ChunkGuildFilter::None, None); + + println!("Cache loaded"); } async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { if (chunk.chunk_index + 1) == chunk.chunk_count { + println!("Cache built successfully!"); // u[date committee server committee::check_committee(&ctx).await; diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index 6ac9e00..0cb11ac 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -12,6 +12,7 @@ use skynet_discord_bot::{ }, get_config, Config, }; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::{process, sync::Arc}; use tokio::sync::RwLock; @@ -27,7 +28,10 @@ async fn main() { 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 {}) + .event_handler(Handler { + server_count: Default::default(), + server_cached: Default::default(), + }) .cache_settings(serenity::cache::Settings::default()) .await .expect("Error creating client"); @@ -44,23 +48,32 @@ async fn main() { } } -struct Handler; +struct Handler { + server_count: AtomicUsize, + server_cached: AtomicUsize, +} #[async_trait] impl EventHandler for Handler { async fn cache_ready(&self, ctx: Context, guilds: Vec) { + self.server_count.swap(guilds.len(), Ordering::SeqCst); for guild in guilds { ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None); } - println!("Cache built successfully!"); + println!("Cache loaded {}", &self.server_count.load(Ordering::SeqCst)); } async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { if (chunk.chunk_index + 1) == chunk.chunk_count { - // this goes into each server and sets roles for each wolves member - check_bulk(&ctx).await; + self.server_cached.fetch_add(1, Ordering::SeqCst); + if (self.server_cached.load(Ordering::SeqCst) + 1) == self.server_count.load(Ordering::SeqCst) { + println!("Cache built successfully!"); - // finish up - process::exit(0); + // this goes into each server and sets roles for each wolves member + check_bulk(&ctx).await; + + // finish up + process::exit(0); + } } } } From 061b73378a9484478226559f4a70cdb6721e7e13 Mon Sep 17 00:00:00 2001 From: silver Date: Sun, 31 Aug 2025 11:51:57 +0000 Subject: [PATCH 62/67] Bump the bot --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index 6a87f84..aecabec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,7 @@ use sqlx::{Pool, Sqlite}; use std::sync::Arc; use tokio::sync::RwLock; +// Need To Define The Stuct (Committed To Bump The Bot) struct Handler; #[async_trait] From 3149a5f99fc927397fec18840ceb9cbe54d65ce0 Mon Sep 17 00:00:00 2001 From: silver Date: Sun, 31 Aug 2025 11:55:35 +0000 Subject: [PATCH 63/67] Bump 2 --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index aecabec..bbf389f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ use sqlx::{Pool, Sqlite}; use std::sync::Arc; use tokio::sync::RwLock; -// Need To Define The Stuct (Committed To Bump The Bot) +// Need To Define The Stuct (Committed To Bump The Bot) struct Handler; #[async_trait] From 7526a82bb7c384c16a09992458868b2f669ed279 Mon Sep 17 00:00:00 2001 From: Brendan Golden Date: Thu, 4 Sep 2025 23:11:24 +0100 Subject: [PATCH 64/67] feat: kill the service if it persists longer than 9 min --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index 81f4490..3a6032f 100644 --- a/flake.nix +++ b/flake.nix @@ -128,6 +128,8 @@ User = "${cfg.user}"; Group = "${cfg.user}"; ExecStart = "${self.defaultPackage."${system}"}/bin/${script}"; + # kill each service if its ran for 9 min + TimeoutStartSec=540; EnvironmentFile = [ "${cfg.env.discord}" "${cfg.env.mail}" From 7e90f451965b0edbd331765ad295a02f31d2bf24 Mon Sep 17 00:00:00 2001 From: Roman Moisieiev Date: Thu, 11 Sep 2025 12:35:50 +0100 Subject: [PATCH 65/67] Fix typos --- .taplo.toml | 2 ++ src/bin/cleanup_committee.rs | 8 ++++---- src/commands/add_server.rs | 2 +- src/commands/count.rs | 2 +- src/commands/minecraft.rs | 2 +- src/commands/role_adder.rs | 4 ++-- src/commands/server_icon.rs | 2 +- src/commands/wolves.rs | 8 ++++---- src/common/minecraft.rs | 4 ++-- src/common/renderer.rs | 5 ++--- src/common/server_icon.rs | 2 +- src/common/set_roles.rs | 12 ++++++------ src/lib.rs | 2 +- src/main.rs | 9 ++++----- 14 files changed, 32 insertions(+), 32 deletions(-) create mode 100644 .taplo.toml diff --git a/.taplo.toml b/.taplo.toml new file mode 100644 index 0000000..3b409e9 --- /dev/null +++ b/.taplo.toml @@ -0,0 +1,2 @@ +[formatting] +column_width = 120 diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs index 188cc5b..9a01f4c 100644 --- a/src/bin/cleanup_committee.rs +++ b/src/bin/cleanup_committee.rs @@ -18,8 +18,8 @@ use tokio::sync::RwLock; /// Cleanup teh Committee server /// -/// This removes any invalid roles/channels which ay have been set up accidentally -/// DO NOT run this locally unless you have a fresh copy of teh live database handy. +/// This removes any invalid roles/channels which have been set up accidentally +/// DO NOT run this locally unless you have a fresh copy of the live database handy. #[tokio::main] async fn main() { let config = get_config(); @@ -98,7 +98,7 @@ async fn cleanup(db: &Pool, ctx: &Context, config: &Config) { let name = &channel.name; let committee_tmp = committees.iter().filter(|x| &x.name_channel == name).collect::>(); let committee = match committee_tmp.first() { - // if there are no committees which match then this is not a channelw e care about + // if there are no committees which match then this is not a channel we care about None => { continue; } @@ -120,7 +120,7 @@ async fn cleanup(db: &Pool, ctx: &Context, config: &Config) { let name = &role.name; let committee_tmp = committees.iter().filter(|x| &x.name_role == name).collect::>(); let committee = match committee_tmp.first() { - // if there are no committees which match then this is not a channelw e care about + // if there are no committees which match then this is not a channel we care about None => { continue; } diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 0c7dd2f..4511c1b 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -102,7 +102,7 @@ async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Resul .fetch_optional(db) .await; - // if the entry does not exist already tehn do a user update + // if the entry does not exist already then do a user update let (update, current_remove, current_role, past_remove, past_role) = match &existing { None => (true, false, None, false, None), Some(x) => { diff --git a/src/commands/count.rs b/src/commands/count.rs index 2368bca..d354db0 100644 --- a/src/commands/count.rs +++ b/src/commands/count.rs @@ -152,7 +152,7 @@ pub mod servers { let length = line.len() + 1; - // +3 is to account for the closing fense + // +3 is to account for the closing fence if length < (limit + 3) { response.push(line); limit -= length; diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index a4d1b1b..61f4280 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -196,7 +196,7 @@ pub(crate) mod server { model::id::GuildId, }; use sqlx::Error; - // this is to managfe the server side of commands related to minecraft + // this is to manage the server side of commands related to minecraft use super::*; use skynet_discord_bot::{ common::minecraft::{update_server, Minecraft}, diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs index e60ac83..7f59c08 100644 --- a/src/commands/role_adder.rs +++ b/src/commands/role_adder.rs @@ -147,7 +147,7 @@ pub mod tools { use sqlx::{Pool, Sqlite}; pub async fn on_role_change(db: &Pool, ctx: &Context, new_data: Member) { - // check if the role changed is part of the oens for this server + // check if the role changed is part of the ones for this server if let Ok(role_adders) = sqlx::query_as::<_, RoleAdder>( r#" SELECT * @@ -163,7 +163,7 @@ pub mod tools { let mut roles_remove = vec![]; for role_adder in role_adders { - // if the user has both A dnd B give them C + // if the user has both A and B give them C if new_data.roles.contains(&role_adder.role_a) && new_data.roles.contains(&role_adder.role_b) && !new_data.roles.contains(&role_adder.role_c) { roles_add.push(role_adder.role_c); diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs index 0cba1b1..2ab087e 100644 --- a/src/commands/server_icon.rs +++ b/src/commands/server_icon.rs @@ -211,7 +211,7 @@ pub(crate) mod user { let length = line.len() + 1; - // +3 is to account for the closing fense + // +3 is to account for the closing fence if length < (limit + 3) { response.push(line); limit -= length; diff --git a/src/commands/wolves.rs b/src/commands/wolves.rs index dc40ad0..0c8ddfc 100644 --- a/src/commands/wolves.rs +++ b/src/commands/wolves.rs @@ -101,7 +101,7 @@ pub mod link { return "Email already verified".to_string(); } - // generate a auth key + // generate an auth key let auth = random_string(20); match send_mail(&config, &details.email, &auth, &command.user.name) { Ok(_) => match save_to_db(&db, &details, &auth, &command.user.id).await { @@ -210,7 +210,7 @@ pub mod link { .subject("Skynet: Link Discord to Wolves.") .multipart( // This is composed of two parts. - // also helps not trip spam settings (uneven number of url's + // also helps not trip spam settings (uneven number of urls) MultiPart::alternative() .singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body_text)) .singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML).body(html.into_string())), @@ -460,7 +460,7 @@ pub mod verify { let config = config_lock.read().await; if let Some(x) = get_server_member_discord(db, &discord.id).await { - // if they are a member of one or more committees, and in teh committee server then give the teh general committee role + // if they are a member of one or more committees, and in teh committee server then give them the general committee role // they will get teh more specific vanity role later if !get_committees_id(db, x.id_wolves).await.is_empty() { let server = config.committee_server; @@ -500,7 +500,7 @@ pub mod unlink { }; let db = db_lock.read().await; - // dosent matter if there is one or not, it will be removed regardless + // doesn't matter if there is one or not, it will be removed regardless delete_link(&db, &command.user.id).await; "Discord link removed".to_string() diff --git a/src/common/minecraft.rs b/src/common/minecraft.rs index 60f7820..77cd754 100644 --- a/src/common/minecraft.rs +++ b/src/common/minecraft.rs @@ -24,7 +24,7 @@ impl<'r> FromRow<'r, SqliteRow> for Minecraft { /** loop through all members of server get a list of folks with mc accounts that are members -and a list that arent members +and a list that aren't members */ pub async fn update_server(server_id: &str, db: &Pool, g_id: &GuildId, config: &Config) { let mut usernames = vec![]; @@ -109,7 +109,7 @@ pub async fn whitelist_wipe(server: &str, token: &str) { }; post(&format!("{url_base}/files/delete"), &bearer, &deletion).await; - // recreate teh file, passing in the type here so the compiler knows what type of vec it is + // recreate the file, passing in the type here so the compiler knows what type of Vec it is post::>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await; // reload the whitelist diff --git a/src/common/renderer.rs b/src/common/renderer.rs index 97e73a0..f2c50cc 100644 --- a/src/common/renderer.rs +++ b/src/common/renderer.rs @@ -1,12 +1,11 @@ // 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. +// I was unable to figure out how to use usvg myself so yoinked it from here. use std::{ ffi::OsStr, path::{Path, PathBuf}, }; -// use clap::builder::OsStr; use color_eyre::{eyre::bail, Result}; use usvg_text_layout::TreeTextToPath; @@ -17,7 +16,7 @@ pub struct Args { /// Output folder where the PNG's will be placed pub output: PathBuf, - /// Comma seperated colors that will be used in HEX Eg. 000000,ffffff + /// Comma separated colors that will be used in HEX Eg. 000000,ffffff /// Can be like an object: black:000000,white:ffffff pub colors: String, diff --git a/src/common/server_icon.rs b/src/common/server_icon.rs index d99f265..1a7c2ef 100644 --- a/src/common/server_icon.rs +++ b/src/common/server_icon.rs @@ -281,7 +281,7 @@ pub mod update_icon { // check if exists if !path_new.exists() { - // convert if it hasnt been converted already + // convert if it hasn't been converted already match r.render(&path_local, &args) { Ok(_) => {} Err(_e) => { diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 6e71992..9115986 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -48,7 +48,7 @@ pub mod normal { if let Ok(x) = server.members(ctx, None, None).await { for member in x { - // members_changed acts as an override to only deal with teh users in it + // members_changed acts as an override to only deal with the users in it if !members_changed.is_empty() && !members_changed.contains(&member.user.id) { continue; } @@ -84,7 +84,7 @@ pub mod normal { if member.roles.contains(role_current) { roles_set.current_rem += 1; - // if theya re not a current member and have the role then remove it + // if they're not a current member and have the role then remove it if let Err(e) = member.remove_role(ctx, role_current).await { println!("{e:?}"); } @@ -196,7 +196,7 @@ pub mod committee { } /** - This function can take a vec of members (or just one) and gives tehm the appropiate roles on teh committee server + This function can take a Vec of members (or just one) and gives them the appropriate roles on teh committee server */ pub async fn update_committees(db: &Pool, ctx: &Context, config: &Config, members: &mut Vec) { let server = config.committee_server; @@ -228,11 +228,11 @@ pub mod committee { let mut channels = server.channels(&ctx).await.unwrap_or_default(); - // a map of users and the roles they are goign to be getting + // a map of users and the roles they are going to be getting let mut users_roles = HashMap::new(); let mut re_order = false; - // we need to create roles and channels if tehy dont already exist + // we need to create roles and channels if they don't already exist let mut category_index = 0; let mut i = 0; loop { @@ -367,7 +367,7 @@ pub mod committee { let has_committee_role = roles_current_id.contains(&committee_member); if on_committee && !has_committee_role { - // if there are committee roles then give the general purporse role + // if there are committee roles then give the general purpose role roles_add.push(committee_member); } if !on_committee && has_committee_role { diff --git a/src/lib.rs b/src/lib.rs index 75c9d78..1a6afb1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,7 @@ pub struct Config { pub committee_role: RoleId, pub committee_category: Vec, - // items pertaining to compsoc only + // items pertaining to CompSoc only pub compsoc_server: GuildId, } impl TypeMapKey for Config { diff --git a/src/main.rs b/src/main.rs index bbf389f..aeebac7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,6 @@ use sqlx::{Pool, Sqlite}; use std::sync::Arc; use tokio::sync::RwLock; -// Need To Define The Stuct (Committed To Bump The Bot) struct Handler; #[async_trait] @@ -117,7 +116,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use let db = db_lock.read().await; - // check if the role changed is part of the oens for this server + // check if the role changed is part of the ones for this server if let Some(x) = new_data { on_role_change(&db, &ctx, x).await; } @@ -159,13 +158,13 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use } } - // compsoc Server + // CompSoc Server match config .compsoc_server .set_commands( &ctx.http, vec![ - // commands just for the compsoc server + // commands just for the CompSoc server commands::count::servers::register(), commands::server_icon::user::register(), ], @@ -182,7 +181,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use async fn interaction_create(&self, ctx: Context, interaction: Interaction) { if let Interaction::Command(command) = interaction { let _ = command.defer_ephemeral(&ctx.http).await; - //println!("Received command interaction: {:#?}", command); + // println!("Received command interaction: {:#?}", command); let content = match command.data.name.as_str() { // user commands From d70a037057c6209b2340b22a749359a16f7d164a Mon Sep 17 00:00:00 2001 From: Roman Moisieiev Date: Thu, 11 Sep 2025 12:36:48 +0100 Subject: [PATCH 66/67] Blame: ignore Fix Typos commit --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..a338a00 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Fix typos +7e90f451965b0edbd331765ad295a02f31d2bf24 From 062f826d28b4d385423656b10cca84b3644c8e60 Mon Sep 17 00:00:00 2001 From: Roman Moisieiev Date: Thu, 11 Sep 2025 12:54:54 +0100 Subject: [PATCH 67/67] Remove RwLock for database --- src/bin/cleanup_committee.rs | 6 ++---- src/bin/update_committee.rs | 2 +- src/bin/update_data.rs | 2 +- src/bin/update_server-icon.rs | 5 ++--- src/bin/update_users.rs | 15 +++++++++------ src/commands/add_server.rs | 3 +-- src/commands/count.rs | 6 ++---- src/commands/minecraft.rs | 16 ++++++---------- src/commands/role_adder.rs | 3 +-- src/commands/server_icon.rs | 9 +++------ src/commands/wolves.rs | 9 +++------ src/common/database.rs | 3 +-- src/common/set_roles.rs | 8 ++------ src/common/wolves.rs | 6 ++---- src/main.rs | 10 +++------- 15 files changed, 39 insertions(+), 64 deletions(-) diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs index 9a01f4c..141af89 100644 --- a/src/bin/cleanup_committee.rs +++ b/src/bin/cleanup_committee.rs @@ -41,7 +41,7 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(RwLock::new(db))); + data.insert::(Arc::new(db)); } if let Err(why) = client.start().await { @@ -69,13 +69,11 @@ impl EventHandler for Handler { async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { if (chunk.chunk_index + 1) == chunk.chunk_count { println!("Cache built successfully!"); - let db_lock = { + let db = { 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() diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs index b8cc6c2..d90cbac 100644 --- a/src/bin/update_committee.rs +++ b/src/bin/update_committee.rs @@ -36,7 +36,7 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(RwLock::new(db))); + data.insert::(Arc::new(db)); } if let Err(why) = client.start().await { diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs index 8f73ce9..fe4138f 100644 --- a/src/bin/update_data.rs +++ b/src/bin/update_data.rs @@ -38,7 +38,7 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(RwLock::new(db))); + data.insert::(Arc::new(db)); } if let Err(why) = client.start().await { diff --git a/src/bin/update_server-icon.rs b/src/bin/update_server-icon.rs index 56957da..c4f9eca 100644 --- a/src/bin/update_server-icon.rs +++ b/src/bin/update_server-icon.rs @@ -35,7 +35,7 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(RwLock::new(db))); + data.insert::(Arc::new(db)); } if let Err(why) = client.start().await { @@ -50,11 +50,10 @@ impl EventHandler for Handler { let ctx = Arc::new(ctx); println!("{} is connected!", ready.user.name); - let db_lock = { + let db = { 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; diff --git a/src/bin/update_users.rs b/src/bin/update_users.rs index 0cb11ac..3150bcf 100644 --- a/src/bin/update_users.rs +++ b/src/bin/update_users.rs @@ -12,8 +12,13 @@ use skynet_discord_bot::{ }, get_config, Config, }; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::{process, sync::Arc}; +use std::{ + process, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, +}; use tokio::sync::RwLock; #[tokio::main] @@ -40,7 +45,7 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(RwLock::new(db))); + data.insert::(Arc::new(db)); } if let Err(why) = client.start().await { @@ -79,13 +84,11 @@ impl EventHandler for Handler { } async fn check_bulk(ctx: &Context) { - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() }; - let db = db_lock.read().await; - for server_config in get_server_config_bulk(&db).await { normal::update_server(ctx, &server_config, &[], &[]).await; } diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs index 4511c1b..eaf0971 100644 --- a/src/commands/add_server.rs +++ b/src/commands/add_server.rs @@ -56,11 +56,10 @@ pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { return "Please provide a valid channel for ``Bot Channel``".to_string(); }; - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; - let db = db_lock.read().await; let server_data = Servers { server: command.guild_id.unwrap_or_default(), diff --git a/src/commands/count.rs b/src/commands/count.rs index d354db0..41b9d81 100644 --- a/src/commands/count.rs +++ b/src/commands/count.rs @@ -27,11 +27,10 @@ pub mod committee { false }; - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; - let db = db_lock.read().await; let mut cs = vec![]; // pull it from a DB @@ -95,11 +94,10 @@ pub mod servers { use std::collections::HashMap; pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; - let db = db_lock.read().await; let mut committees = HashMap::new(); if let Some(x) = get_committees(&db).await { diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs index 61f4280..5098f11 100644 --- a/src/commands/minecraft.rs +++ b/src/commands/minecraft.rs @@ -23,11 +23,10 @@ pub(crate) mod user { use sqlx::Error; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db_lock = { + let db = { 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; @@ -229,11 +228,10 @@ pub(crate) mod server { return String::from("Expected Server ID"); }; - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; - let db = db_lock.read().await; match add_server(&db, &g_id, &server_minecraft).await { Ok(_) => {} @@ -290,11 +288,10 @@ pub(crate) mod server { Some(x) => x, }; - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; - let db = db_lock.read().await; let servers = get_minecraft_config_server(&db, g_id).await; @@ -366,11 +363,10 @@ pub(crate) mod server { return String::from("Expected Server ID"); }; - let db_lock = { - let data_read = ctx.data.read().await; - data_read.get::().expect("Expected Databse in TypeMap.").clone() + let db = { + let data = ctx.data.read().await; + data.get::().expect("Expected Databse in TypeMap.").clone() }; - let db = db_lock.read().await; match server_remove(&db, &g_id, &server_minecraft).await { Ok(_) => {} diff --git a/src/commands/role_adder.rs b/src/commands/role_adder.rs index 7f59c08..0573b37 100644 --- a/src/commands/role_adder.rs +++ b/src/commands/role_adder.rs @@ -62,11 +62,10 @@ pub mod edit { false }; - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; - let db = db_lock.read().await; let server = command.guild_id.unwrap_or_default(); let server_data = RoleAdder { diff --git a/src/commands/server_icon.rs b/src/commands/server_icon.rs index 2ab087e..d4a78d5 100644 --- a/src/commands/server_icon.rs +++ b/src/commands/server_icon.rs @@ -18,11 +18,10 @@ pub(crate) mod admin { use super::*; pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { - let db_lock = { + let db = { 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; @@ -69,11 +68,10 @@ pub(crate) mod user { use sqlx::{Pool, Sqlite}; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db_lock = { + let db = { 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(); @@ -145,11 +143,10 @@ pub(crate) mod user { use sqlx::{Pool, Sqlite}; pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { - let db_lock = { + let db = { 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(); diff --git a/src/commands/wolves.rs b/src/commands/wolves.rs index 0c8ddfc..843ca70 100644 --- a/src/commands/wolves.rs +++ b/src/commands/wolves.rs @@ -21,11 +21,10 @@ pub mod link { use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db_lock = { + let db = { 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; @@ -314,11 +313,10 @@ pub mod verify { use sqlx::Error; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() }; - let db = db_lock.read().await; // check if user has used /link_wolves let details = if let Some(x) = get_verify_from_db(&db, &command.user.id).await { @@ -494,11 +492,10 @@ pub mod unlink { use sqlx::{Pool, Sqlite}; pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Databse in TypeMap.").clone() }; - let db = db_lock.read().await; // doesn't matter if there is one or not, it will be removed regardless delete_link(&db, &command.user.id).await; diff --git a/src/common/database.rs b/src/common/database.rs index 0c18668..2eaf5df 100644 --- a/src/common/database.rs +++ b/src/common/database.rs @@ -12,11 +12,10 @@ use sqlx::{ Error, FromRow, Pool, Row, Sqlite, }; use std::{str::FromStr, sync::Arc}; -use tokio::sync::RwLock; pub struct DataBase; impl TypeMapKey for DataBase { - type Value = Arc>>; + type Value = Arc>; } #[derive(Debug, Clone, Deserialize, Serialize)] diff --git a/src/common/set_roles.rs b/src/common/set_roles.rs index 9115986..e11c637 100644 --- a/src/common/set_roles.rs +++ b/src/common/set_roles.rs @@ -17,13 +17,11 @@ pub mod normal { } pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option], members_changed: &[UserId]) { - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() }; - let db = db_lock.read().await; - let Servers { server, role_past, @@ -174,13 +172,11 @@ pub mod committee { use std::collections::HashMap; pub async fn check_committee(ctx: &Context) { - let db_lock = { + let db = { 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() diff --git a/src/common/wolves.rs b/src/common/wolves.rs index 17305f6..3744558 100644 --- a/src/common/wolves.rs +++ b/src/common/wolves.rs @@ -69,11 +69,10 @@ pub mod cns { } pub async fn get_wolves(ctx: &Context) { - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() }; - let db = db_lock.read().await; let config_lock = { let data_read = ctx.data.read().await; @@ -224,11 +223,10 @@ pub mod committees { } pub async fn get_cns(ctx: &Context) { - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Database in TypeMap.").clone() }; - let db = db_lock.read().await; let config_lock = { let data_read = ctx.data.read().await; diff --git a/src/main.rs b/src/main.rs index aeebac7..8dff675 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,13 +41,11 @@ impl EventHandler for Handler { // handles previously linked accounts joining the server async fn guild_member_addition(&self, ctx: Context, new_member: Member) { - let db_lock = { + let db = { 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() @@ -109,13 +107,11 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use // handles role updates async fn guild_member_update(&self, ctx: Context, _old_data: Option, new_data: Option, _: GuildMemberUpdateEvent) { // get config/db - let db_lock = { + let db = { let data_read = ctx.data.read().await; data_read.get::().expect("Expected Config in TypeMap.").clone() }; - let db = db_lock.read().await; - // check if the role changed is part of the ones for this server if let Some(x) = new_data { on_role_change(&db, &ctx, x).await; @@ -304,7 +300,7 @@ async fn main() { let mut data = client.data.write().await; data.insert::(Arc::new(RwLock::new(config))); - data.insert::(Arc::new(RwLock::new(db))); + data.insert::(Arc::new(db)); } // Finally, start a single shard, and start listening to events.