diff --git a/.forgejo/workflows/check_lfs.yaml b/.forgejo/workflows/check_lfs.yaml
new file mode 100644
index 0000000..aaa2117
--- /dev/null
+++ b/.forgejo/workflows/check_lfs.yaml
@@ -0,0 +1,10 @@
+on:
+ - 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
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
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
diff --git a/.rustfmt.toml b/.rustfmt.toml
index b8ae8dd..2b0831a 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
diff --git a/.server-icons.toml b/.server-icons.toml
new file mode 100644
index 0000000..eeb302c
--- /dev/null
+++ b/.server-icons.toml
@@ -0,0 +1,6 @@
+# this file controls the
+
+[source]
+repo = "https://forgejo.skynet.ie/Computer_Society/open-goverance"
+directory = "Resources/Logo_Variants"
+file = "_festivals.toml"
\ No newline at end of file
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/Cargo.lock b/Cargo.lock
index 416c3c4..66e67f1 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"
@@ -110,6 +110,12 @@ version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
[[package]]
name = "arrayvec"
version = "0.7.6"
@@ -383,6 +389,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"
@@ -449,6 +461,39 @@ dependencies = [
"generic-array",
]
+[[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 = "concurrent-queue"
version = "2.5.0"
@@ -637,6 +682,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"
@@ -790,6 +841,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"
@@ -805,6 +866,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"
@@ -815,6 +885,12 @@ dependencies = [
"miniz_oxide",
]
+[[package]]
+name = "float-cmp"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
+
[[package]]
name = "flume"
version = "0.9.2"
@@ -849,6 +925,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"
@@ -1063,6 +1160,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"
@@ -1586,16 +1693,6 @@ dependencies = [
"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"
@@ -1617,6 +1714,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"
@@ -1677,6 +1786,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"
@@ -1686,6 +1801,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"
@@ -1718,7 +1851,7 @@ dependencies = [
"futures-util",
"hostname",
"httpdate",
- "idna 1.0.3",
+ "idna",
"mime",
"native-tls",
"nom",
@@ -1843,6 +1976,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"
@@ -1872,6 +2014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
"adler2",
+ "simd-adler32",
]
[[package]]
@@ -2031,6 +2174,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"
@@ -2075,6 +2224,12 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+[[package]]
+name = "pico-args"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
+
[[package]]
name = "pin-project"
version = "1.1.7"
@@ -2145,6 +2300,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"
@@ -2339,6 +2507,12 @@ dependencies = [
"rand_core 0.5.1",
]
+[[package]]
+name = "rctree"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
+
[[package]]
name = "redox_syscall"
version = "0.5.4"
@@ -2435,6 +2609,34 @@ dependencies = [
"windows-registry",
]
+[[package]]
+name = "resvg"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76888219c0881e22b0ceab06fddcfe83163cd81642bd60c7842387f9c968a72e"
+dependencies = [
+ "gif",
+ "jpeg-decoder",
+ "log",
+ "pico-args",
+ "png",
+ "rgb",
+ "svgfilters",
+ "svgtypes 0.10.0",
+ "tiny-skia",
+ "usvg",
+ "usvg-text-layout",
+]
+
+[[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 +2652,34 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "rosvgtree"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdc23d1ace03d6b8153c7d16f0708cd80b61ee8e80304954803354e67e40d150"
+dependencies = [
+ "log",
+ "roxmltree 0.18.1",
+ "simplecss",
+ "siphasher",
+ "svgtypes 0.9.0",
+]
+
+[[package]]
+name = "roxmltree"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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"
@@ -2582,6 +2812,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"
@@ -2713,6 +2959,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"
@@ -2806,6 +3061,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"
@@ -2831,21 +3095,49 @@ dependencies = [
"rand_core 0.6.4",
]
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[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.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
[[package]]
name = "skynet_discord_bot"
version = "0.1.0"
dependencies = [
"chrono",
+ "color-eyre",
"dotenvy",
+ "eyre",
"lettre",
"maud",
"rand 0.9.0",
+ "resvg",
"serde",
"serde_json",
"serenity",
"sqlx",
"surf",
+ "tiny-skia",
"tokio",
+ "toml",
+ "usvg",
+ "usvg-text-layout",
"wolves_oxidised",
]
@@ -3180,6 +3472,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"
@@ -3220,6 +3521,36 @@ dependencies = [
"web-sys",
]
+[[package]]
+name = "svgfilters"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "639abcebc15fdc2df179f37d6f5463d660c1c79cd552c12343a4600827a04bce"
+dependencies = [
+ "float-cmp",
+ "rgb",
+]
+
+[[package]]
+name = "svgtypes"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9ee29c1407a5b18ccfe5f6ac82ac11bab3b14407e09c209a6c1a32098b19734"
+dependencies = [
+ "kurbo 0.8.3",
+ "siphasher",
+]
+
+[[package]]
+name = "svgtypes"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98ffacedcdcf1da6579c907279b4f3c5492fbce99fbbf227f5ed270a589c2765"
+dependencies = [
+ "kurbo 0.9.5",
+ "siphasher",
+]
+
[[package]]
name = "syn"
version = "1.0.109"
@@ -3363,6 +3694,16 @@ dependencies = [
"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"
@@ -3432,6 +3773,31 @@ dependencies = [
"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"
@@ -3581,6 +3947,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"
@@ -3617,6 +4024,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]]
@@ -3629,12 +4047,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"
@@ -3683,6 +4118,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"
@@ -3704,6 +4157,18 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524"
+[[package]]
+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 = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
+
[[package]]
name = "universal-hash"
version = "0.4.0"
@@ -3722,16 +4187,49 @@ 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.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b6bb4e62619d9f68aa2d8a823fea2bff302340a1f2d45c264d5b0be170832e"
+dependencies = [
+ "base64 0.21.7",
+ "data-url",
+ "flate2",
+ "imagesize",
+ "kurbo 0.9.5",
+ "log",
+ "rctree",
+ "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]]
name = "utf-8"
version = "0.7.6"
@@ -3750,6 +4248,12 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+[[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"
@@ -3915,6 +4419,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"
@@ -4144,6 +4654,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"
@@ -4166,7 +4685,7 @@ dependencies = [
[[package]]
name = "wolves_oxidised"
version = "0.1.0"
-source = "git+https://forgejo.skynet.ie/Skynet/wolves-oxidised.git#74f00e3dcfd52744583b0ded08efb8bb27fdcec8"
+source = "git+https://forgejo.skynet.ie/Skynet/wolves-oxidised.git#265c8c81d1eb870a6149da5ce72556d44f57937f"
dependencies = [
"reqwest 0.12.9",
"serde",
@@ -4186,6 +4705,12 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+[[package]]
+name = "xmlparser"
+version = "0.13.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
+
[[package]]
name = "yoke"
version = "0.7.5"
diff --git a/Cargo.toml b/Cargo.toml
index aa0cd6b..832eb09 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,16 +4,21 @@ 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_users"
+name = "update_committee"
[[bin]]
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"] }
@@ -29,7 +34,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
@@ -42,4 +47,13 @@ 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"
+
+# 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"
\ No newline at end of file
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.
diff --git a/db/migrations/10_member_committee-roles.sql b/db/migrations/10_member_committee-roles.sql
new file mode 100644
index 0000000..03264cd
--- /dev/null
+++ b/db/migrations/10_member_committee-roles.sql
@@ -0,0 +1,10 @@
+
+CREATE TABLE IF NOT EXISTS committee_roles (
+ id_wolves integer PRIMARY KEY,
+ id_role integer DEFAULT 1,
+ id_channel integer DEFAULT 1,
+ -- not strictly required but for readability and debugging
+ name_role text NOT NULL DEFAULT '',
+ name_channel text NOT NULL DEFAULT '',
+ count integer DEFAULT 0
+);
diff --git a/db/migrations/11_server-icons.sql b/db/migrations/11_server-icons.sql
new file mode 100644
index 0000000..20fb472
--- /dev/null
+++ b/db/migrations/11_server-icons.sql
@@ -0,0 +1,9 @@
+
+CREATE TABLE IF NOT EXISTS server_icons (
+ id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL,
+ path 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/db/migrations/9_member_committee-id.sql b/db/migrations/9_member_committee-id.sql
new file mode 100644
index 0000000..77552ce
--- /dev/null
+++ b/db/migrations/9_member_committee-id.sql
@@ -0,0 +1,6 @@
+-- No need for this col since it is goign to be in "committees" anyways
+ALTER TABLE servers DROP COLUMN server_name;
+
+-- we do care about teh ID of the club/soc though
+ALTER TABLE servers ADD COLUMN wolves_id integer DEFAULT 0;
+
diff --git a/doc/Committee.md b/doc/Committee.md
index cfe718e..a400536 100644
--- a/doc/Committee.md
+++ b/doc/Committee.md
@@ -7,7 +7,7 @@ For example is a user links on the CompSoc Discord then they will also get their
## Setup - Committee
You need admin access to run any of the commands in this section.
-Either the server owner or a user with the ``Administrator`` permission.
+Either the server owner or a user with the ``Manage Server`` permission.
### Get the API Key
The ``api_key`` is used by the Bot in order to request information, it will be used later in the process.
@@ -38,9 +38,9 @@ The reason for both roles is ye have one for active members while the second is
### Setup Bot
This is where the bot is configured.
You will need the ``api_key`` from the start of the process.
-You (personally) will need a role with ``Administrator`` permission to be able to do this.
+You (personally) will need a role with ``Manage Server`` permission to be able to do this.
-1. Use the command ``/add`` and a list of options will pop up.
+1. Use the command ``/committee add`` and a list of options will pop up.
2. ``api_key`` is the key you got from Keith earlier.
3. ``role_current`` is the ``member-current`` that you created earlier.
4. ``role_past`` (optional) is the role for all current and past members.
diff --git a/doc/User.md b/doc/User.md
index fec6abe..570877c 100644
--- a/doc/User.md
+++ b/doc/User.md
@@ -8,13 +8,13 @@ This is to link your Discord account with your UL Wolves account.
**You will only need to do this once**.
### Setup
-1. In a Discord server with the Skynet Bot enter ``/link_wolves YOUR_WOLVES_CONTACT_EMAIL``
+1. In a Discord server with the Skynet Bot enter ``/wolves link YOUR_WOLVES_CONTACT_EMAIL``
* Your ``YOUR_WOLVES_CONTACT_EMAIL`` is the email in the Contact Email here:
* This is most likely your student mail
2. An email will be sent to you with a verification code.
-3. Verify the code using ``/verify CODE_FROM_EMAIL`` in Discord.
+3. Verify the code using ``/wolves verify CODE_FROM_EMAIL`` in Discord.
4. Once complete your Wolves and Discord accounts will be linked.
@@ -23,4 +23,4 @@ You will get member roles on any Discord that is using the bot that you are a me
### Minecraft
You can link your Minecraft username to grant you access to any Minecraft server run by UL Computer Society.
-``/link_minecraft MINECRAFT_USERNAME``
+``/wolves link_minecraft MINECRAFT_USERNAME``
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 12ae36e..3a6032f 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,15 +21,33 @@
nixpkgs,
utils,
naersk,
+ nixpkgs-mozilla,
}:
utils.lib.eachDefaultSystem (
system: let
- pkgs = (import nixpkgs) {inherit system;};
- naersk' = pkgs.callPackage naersk {};
+ overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
+ 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; [
openssl
+ glib
+ gdk-pixbuf
pkg-config
rustfmt
];
@@ -36,6 +58,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 {
@@ -62,7 +88,15 @@
# `nix develop`
devShell = pkgs.mkShell {
- nativeBuildInputs = with pkgs; [rustc cargo pkg-config openssl rustfmt];
+ nativeBuildInputs = with pkgs; [rustup rustPlatform.bindgenHook];
+ # libraries here
+ buildInputs = buildInputs;
+ RUSTC_VERSION = overrides.toolchain.channel;
+ # https://github.com/rust-lang/rust-bindgen#environment-variables
+ shellHook = ''
+ export PATH="''${CARGO_HOME:-~/.cargo}/bin":"$PATH"
+ export PATH="''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-${pkgs.stdenv.hostPlatform.rust.rustcTarget}/bin":"$PATH"
+ '';
};
nixosModule = {
@@ -88,12 +122,14 @@
wantedBy = [];
after = ["network-online.target"];
environment = environment_config;
-
+ path = with pkgs; [ git git-lfs ];
serviceConfig = {
Type = "oneshot";
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}"
@@ -118,12 +154,17 @@
# modify these
scripts = {
- # every 20 min
+ # every 10 min
"update_data" = "*:0,10,20,30,40,50";
# groups are updated every hour, offset from teh ldap
- "update_users" = "*:05:00";
+ "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}" = {
@@ -183,6 +224,7 @@
after = ["network-online.target"];
wants = [];
environment = environment_config;
+ path = with pkgs; [ git git-lfs ];
serviceConfig = {
User = "${cfg.user}";
diff --git a/media/setup_user_01.png b/media/setup_user_01.png
index b5e207b..c8ac1a8 100644
--- a/media/setup_user_01.png
+++ b/media/setup_user_01.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f6440b2d302c5a7e46493687bb0bbf941c29c5552c71869303397da3c4f0a52b
-size 72163
+oid sha256:684bcaa532d75d90a63512c648c44d9cd12a6e34fce6c2a55bf1d5d4f7446371
+size 42969
diff --git a/media/setup_user_03.png b/media/setup_user_03.png
index fda3bda..f9f4e3a 100644
--- a/media/setup_user_03.png
+++ b/media/setup_user_03.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f19d24686bb9da8857263d1f146e29413fd6bd316981281c18ef2b4ec3621596
-size 51740
+oid sha256:3952b8c2a55604b88a0034f68dd29abd3a72b3e1ced8074bacb358b96f14c7b1
+size 48708
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"
diff --git a/src/bin/cleanup_committee.rs b/src/bin/cleanup_committee.rs
new file mode 100644
index 0000000..141af89
--- /dev/null
+++ b/src/bin/cleanup_committee.rs
@@ -0,0 +1,137 @@
+use serenity::{
+ all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
+ async_trait,
+ client::{Context, EventHandler},
+ model::gateway::GatewayIntents,
+ Client,
+};
+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;
+
+/// Cleanup teh Committee server
+///
+/// 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();
+ 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(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) {
+ 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 = {
+ let data_read = ctx.data.read().await;
+ data_read.get::().expect("Expected Config in TypeMap.").clone()
+ };
+
+ 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 channel we 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 channel we 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);
+ }
+ }
+ }
+ }
+}
diff --git a/src/bin/update_committee.rs b/src/bin/update_committee.rs
new file mode 100644
index 0000000..d90cbac
--- /dev/null
+++ b/src/bin/update_committee.rs
@@ -0,0 +1,74 @@
+use serenity::{
+ all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
+ async_trait,
+ client::{Context, EventHandler},
+ model::gateway::GatewayIntents,
+ Client,
+};
+use skynet_discord_bot::{
+ common::{
+ database::{db_init, DataBase},
+ set_roles::committee,
+ },
+ 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(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) {
+ 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;
+
+ // finish up
+ process::exit(0);
+ }
+ }
+}
diff --git a/src/bin/update_data.rs b/src/bin/update_data.rs
index 2d36892..fe4138f 100644
--- a/src/bin/update_data.rs
+++ b/src/bin/update_data.rs
@@ -4,10 +4,13 @@ use serenity::{
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;
@@ -27,6 +30,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");
@@ -34,11 +38,11 @@ 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 {
- println!("Client error: {:?}", why);
+ println!("Client error: {why:?}");
}
}
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
new file mode 100644
index 0000000..c4f9eca
--- /dev/null
+++ b/src/bin/update_server-icon.rs
@@ -0,0 +1,71 @@
+use serenity::{
+ async_trait,
+ client::{Context, EventHandler},
+ model::gateway::{GatewayIntents, Ready},
+ Client,
+};
+use skynet_discord_bot::{
+ common::{
+ database::{db_init, DataBase},
+ server_icon::{get_config_icons, update_icon},
+ },
+ 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(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);
+
+ let db = {
+ let data_read = ctx.data.read().await;
+ data_read.get::().expect("Expected Config in TypeMap.").clone()
+ };
+
+ 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
index e3170eb..3150bcf 100644
--- a/src/bin/update_users.rs
+++ b/src/bin/update_users.rs
@@ -1,13 +1,24 @@
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::{committee, normal};
-use skynet_discord_bot::{get_config, Config};
-use std::{process, sync::Arc};
+use skynet_discord_bot::{
+ common::{
+ database::{db_init, get_server_config_bulk, DataBase},
+ set_roles::normal,
+ },
+ get_config, Config,
+};
+use std::{
+ process,
+ sync::{
+ atomic::{AtomicUsize, Ordering},
+ Arc,
+ },
+};
use tokio::sync::RwLock;
#[tokio::main]
@@ -22,7 +33,11 @@ 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");
@@ -30,41 +45,51 @@ 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 {
- println!("Client error: {:?}", why);
+ println!("Client error: {why:?}");
}
}
-struct Handler;
+struct Handler {
+ server_count: AtomicUsize,
+ server_cached: AtomicUsize,
+}
#[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);
+ 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 loaded {}", &self.server_count.load(Ordering::SeqCst));
+ }
- // this goes into each server and sets roles for each wolves member
- check_bulk(Arc::clone(&ctx)).await;
+ async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) {
+ if (chunk.chunk_index + 1) == chunk.chunk_count {
+ 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!");
- // u[date committee server
- committee::check_committee(Arc::clone(&ctx)).await;
+ // this goes into each server and sets roles for each wolves member
+ check_bulk(&ctx).await;
- // finish up
- process::exit(0);
+ // finish up
+ process::exit(0);
+ }
+ }
}
}
-async fn check_bulk(ctx: Arc) {
- let db_lock = {
+async fn check_bulk(ctx: &Context) {
+ 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;
+ normal::update_server(ctx, &server_config, &[], &[]).await;
}
}
diff --git a/src/commands/add_server.rs b/src/commands/add_server.rs
index fdc0f23..eaf0971 100644
--- a/src/commands/add_server.rs
+++ b/src/commands/add_server.rs
@@ -1,94 +1,88 @@
-use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption};
-use serenity::{builder::CreateCommand, 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 skynet_discord_bot::is_admin;
+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 {
- // check if user has high enough permisssions
- if let Some(msg) = is_admin(command, ctx).await {
- return msg;
- }
-
- let wolves_api = if let Some(CommandDataOption {
- value: CommandDataOptionValue::String(key),
+ let sub_options = if let Some(CommandDataOption {
+ value: CommandDataOptionValue::SubCommand(options),
..
}) = command.data.options.first()
{
- key.to_string()
+ options
+ } else {
+ return "Please provide sub options".to_string();
+ };
+
+ let wolves_api = if let Some(x) = sub_options.first() {
+ match &x.value {
+ CommandDataOptionValue::String(key) => key.to_string(),
+ _ => return "Please provide a wolves API key".to_string(),
+ }
} else {
return "Please provide a wolves API key".to_string();
};
- let role_current = if let Some(CommandDataOption {
- value: CommandDataOptionValue::Role(role),
- ..
- }) = command.data.options.get(1)
- {
- role.to_owned()
+ let role_current = if let Some(x) = sub_options.get(1) {
+ match &x.value {
+ CommandDataOptionValue::Role(role) => role.to_owned(),
+ _ => return "Please provide a valid role for ``Role Current``".to_string(),
+ }
} else {
return "Please provide a valid role for ``Role Current``".to_string();
};
- let role_past = if let Some(CommandDataOption {
- value: CommandDataOptionValue::Role(role),
- ..
- }) = command.data.options.get(5)
- {
- Some(role.to_owned())
+ let role_past = if let Some(x) = sub_options.get(5) {
+ match &x.value {
+ CommandDataOptionValue::Role(role) => Some(role.to_owned()),
+ _ => None,
+ }
} else {
None
};
- let bot_channel_id = if let Some(CommandDataOption {
- value: CommandDataOptionValue::Channel(channel),
- ..
- }) = command.data.options.get(2)
- {
- channel.to_owned()
+ let bot_channel_id = if let Some(x) = sub_options.get(2) {
+ match &x.value {
+ CommandDataOptionValue::Channel(channel) => channel.to_owned(),
+ _ => return "Please provide a valid channel for ``Bot Channel``".to_string(),
+ }
} else {
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(),
wolves_api,
+ wolves_id: 0,
role_past,
role_current,
member_past: 0,
member_current: 0,
bot_channel_id,
- server_name: "".to_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:?}");
}
}
"Added/Updated server info".to_string()
}
-pub fn register() -> CreateCommand {
- CreateCommand::new("add")
- .description("Enable the bot for this discord")
- .add_option(CreateCommandOption::new(CommandOptionType::String, "api_key", "UL Wolves API Key").required(true))
- .add_option(CreateCommandOption::new(CommandOptionType::Role, "role_current", "Role for Current members").required(true))
- .add_option(CreateCommandOption::new(CommandOptionType::Channel, "bot_channel", "Safe space for folks to use the bot commands.").required(true))
- .add_option(CreateCommandOption::new(CommandOptionType::Role, "role_past", "Role for Past members").required(false))
-}
-
async fn add_server(db: &Pool, ctx: &Context, server: &Servers) -> Result