Compare commits

..

No commits in common. "main" and "update-dm" have entirely different histories.

36 changed files with 335 additions and 2233 deletions

View file

@ -1,10 +0,0 @@
on:
- push
- workflow_dispatch
jobs:
check_lfs:
# nix/docker
runs-on: nix
steps:
- uses: https://github.com/MPLew-is/lfs-check-action@1

View file

@ -1,51 +0,0 @@
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

View file

@ -1,2 +0,0 @@
# Fix typos
7e90f451965b0edbd331765ad295a02f31d2bf24

View file

@ -6,5 +6,4 @@ fn_params_layout = "Compressed"
#brace_style = "PreferSameLine" #brace_style = "PreferSameLine"
struct_lit_width = 0 struct_lit_width = 0
tab_spaces = 2 tab_spaces = 2
use_small_heuristics = "Max" use_small_heuristics = "Max"
imports_granularity = "Crate"

View file

@ -1,6 +0,0 @@
# this file controls the
[source]
repo = "https://forgejo.skynet.ie/Computer_Society/open-goverance"
directory = "Resources/Logo_Variants"
file = "_festivals.toml"

View file

@ -1,2 +0,0 @@
[formatting]
column_width = 120

555
Cargo.lock generated
View file

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 3
[[package]] [[package]]
name = "addr2line" name = "addr2line"
@ -110,12 +110,6 @@ version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.6" version = "0.7.6"
@ -389,12 +383,6 @@ version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytemuck"
version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.5.0" version = "1.5.0"
@ -461,39 +449,6 @@ dependencies = [
"generic-array", "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]] [[package]]
name = "concurrent-queue" name = "concurrent-queue"
version = "2.5.0" version = "2.5.0"
@ -682,12 +637,6 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
[[package]]
name = "data-url"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5"
[[package]] [[package]]
name = "der" name = "der"
version = "0.7.9" version = "0.7.9"
@ -841,16 +790,6 @@ dependencies = [
"pin-project-lite", "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]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.9.0" version = "1.9.0"
@ -866,15 +805,6 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" 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]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.33" version = "1.0.33"
@ -885,12 +815,6 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.9.2" version = "0.9.2"
@ -925,27 +849,6 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" 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]] [[package]]
name = "foreign-types" name = "foreign-types"
version = "0.3.2" version = "0.3.2"
@ -1160,16 +1063,6 @@ dependencies = [
"polyval", "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]] [[package]]
name = "gimli" name = "gimli"
version = "0.31.0" version = "0.31.0"
@ -1693,6 +1586,16 @@ dependencies = [
"syn 2.0.89", "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]] [[package]]
name = "idna" name = "idna"
version = "1.0.3" version = "1.0.3"
@ -1714,18 +1617,6 @@ dependencies = [
"icu_properties", "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]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.5.0" version = "2.5.0"
@ -1786,12 +1677,6 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jpeg-decoder"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.70" version = "0.3.70"
@ -1801,24 +1686,6 @@ dependencies = [
"wasm-bindgen", "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]] [[package]]
name = "kv-log-macro" name = "kv-log-macro"
version = "1.0.7" version = "1.0.7"
@ -1851,7 +1718,7 @@ dependencies = [
"futures-util", "futures-util",
"hostname", "hostname",
"httpdate", "httpdate",
"idna", "idna 1.0.3",
"mime", "mime",
"native-tls", "native-tls",
"nom", "nom",
@ -1976,15 +1843,6 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "memmap2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"
@ -2014,7 +1872,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [ dependencies = [
"adler2", "adler2",
"simd-adler32",
] ]
[[package]] [[package]]
@ -2174,12 +2031,6 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "owo-colors"
version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec"
[[package]] [[package]]
name = "parking" name = "parking"
version = "2.2.1" version = "2.2.1"
@ -2224,12 +2075,6 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.7" version = "1.1.7"
@ -2300,19 +2145,6 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 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]] [[package]]
name = "polling" name = "polling"
version = "3.7.4" version = "3.7.4"
@ -2507,12 +2339,6 @@ dependencies = [
"rand_core 0.5.1", "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]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.4" version = "0.5.4"
@ -2609,34 +2435,6 @@ dependencies = [
"windows-registry", "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]] [[package]]
name = "ring" name = "ring"
version = "0.17.8" version = "0.17.8"
@ -2652,34 +2450,6 @@ dependencies = [
"windows-sys 0.52.0", "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]] [[package]]
name = "rsa" name = "rsa"
version = "0.9.6" version = "0.9.6"
@ -2812,22 +2582,6 @@ dependencies = [
"untrusted", "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]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.18" version = "1.0.18"
@ -2959,15 +2713,6 @@ dependencies = [
"thiserror 1.0.63", "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]] [[package]]
name = "serde_urlencoded" name = "serde_urlencoded"
version = "0.7.1" version = "0.7.1"
@ -3061,15 +2806,6 @@ dependencies = [
"digest 0.10.7", "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]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
@ -3095,49 +2831,21 @@ dependencies = [
"rand_core 0.6.4", "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]] [[package]]
name = "skynet_discord_bot" name = "skynet_discord_bot"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"color-eyre",
"dotenvy", "dotenvy",
"eyre",
"lettre", "lettre",
"maud", "maud",
"rand 0.9.0", "rand 0.9.0",
"resvg",
"serde", "serde",
"serde_json", "serde_json",
"serenity", "serenity",
"sqlx", "sqlx",
"surf", "surf",
"tiny-skia",
"tokio", "tokio",
"toml",
"usvg",
"usvg-text-layout",
"wolves_oxidised", "wolves_oxidised",
] ]
@ -3472,15 +3180,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" 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]] [[package]]
name = "stringprep" name = "stringprep"
version = "0.1.5" version = "0.1.5"
@ -3521,36 +3220,6 @@ dependencies = [
"web-sys", "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]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -3694,16 +3363,6 @@ dependencies = [
"syn 2.0.89", "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]] [[package]]
name = "time" name = "time"
version = "0.2.27" version = "0.2.27"
@ -3773,31 +3432,6 @@ dependencies = [
"syn 1.0.109", "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]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.7.6" version = "0.7.6"
@ -3947,47 +3581,6 @@ dependencies = [
"tokio", "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]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.3" version = "0.3.3"
@ -4024,17 +3617,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [ dependencies = [
"once_cell", "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]] [[package]]
@ -4047,29 +3629,12 @@ dependencies = [
"tracing", "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]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.5" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "ttf-parser"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633"
[[package]] [[package]]
name = "tungstenite" name = "tungstenite"
version = "0.21.0" version = "0.21.0"
@ -4118,24 +3683,6 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 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]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.13"
@ -4157,18 +3704,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" 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]] [[package]]
name = "universal-hash" name = "universal-hash"
version = "0.4.0" version = "0.4.0"
@ -4187,49 +3722,16 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.4" version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna 0.5.0",
"percent-encoding", "percent-encoding",
"serde", "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]] [[package]]
name = "utf-8" name = "utf-8"
version = "0.7.6" version = "0.7.6"
@ -4248,12 +3750,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]] [[package]]
name = "value-bag" name = "value-bag"
version = "1.10.0" version = "1.10.0"
@ -4419,12 +3915,6 @@ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]
[[package]]
name = "weezl"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
[[package]] [[package]]
name = "whoami" name = "whoami"
version = "1.5.2" version = "1.5.2"
@ -4654,15 +4144,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.50.0" version = "0.50.0"
@ -4705,12 +4186,6 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
[[package]]
name = "xmlparser"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.7.5" version = "0.7.5"

View file

@ -4,21 +4,19 @@ version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]] [[bin]]
name = "update_data" name = "update_data"
[[bin]]
name = "update_users"
[[bin]] [[bin]]
name = "update_committee" name = "update_committee"
[[bin]] [[bin]]
name = "update_minecraft" name = "update_minecraft"
[[bin]]
name = "update_server-icon"
[[bin]]
name = "cleanup_committee"
[dependencies] [dependencies]
# discord library # discord library
serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] }
@ -34,7 +32,7 @@ surf = "2.3"
dotenvy = "0.15" dotenvy = "0.15"
# For sqlite # 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"] } serde_json = { version = "1.0", features = ["raw_value"] }
# create random strings # create random strings
@ -47,13 +45,4 @@ chrono = "0.4"
lettre = "0.11" lettre = "0.11"
maud = "0.27" maud = "0.27"
toml = "0.8.23" serde = "1.0"
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"

View file

@ -1,7 +1,7 @@
# Skynet Discord Bot # Skynet Discord Bot
The Skynet bot is designed to manage users on Discord. 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. It allows users to link their UL Wolves account with Wolves in a GDPR compliant manner.
Skynet (bot) is hosted by the Computer Society on Skynet (computer cluster). Skynet (bot) is hosted is hosted by the Computer Society on Skynet (computer cluster).
## Documentation ## Documentation
We have split up the documentation into different segments depending on who the user is. We have split up the documentation into different segments depending on who the user is.

View file

@ -1,9 +0,0 @@
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);

17
flake.lock generated
View file

@ -32,22 +32,6 @@
"type": "indirect" "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": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1722995383, "lastModified": 1722995383,
@ -67,7 +51,6 @@
"inputs": { "inputs": {
"naersk": "naersk", "naersk": "naersk",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_2",
"nixpkgs-mozilla": "nixpkgs-mozilla",
"utils": "utils" "utils": "utils"
} }
}, },

View file

@ -4,10 +4,6 @@
inputs = { inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable"; nixpkgs.url = "nixpkgs/nixos-unstable";
naersk.url = "github:nix-community/naersk"; naersk.url = "github:nix-community/naersk";
nixpkgs-mozilla = {
url = "github:mozilla/nixpkgs-mozilla";
flake = false;
};
utils.url = "github:numtide/flake-utils"; utils.url = "github:numtide/flake-utils";
}; };
@ -21,33 +17,16 @@
nixpkgs, nixpkgs,
utils, utils,
naersk, naersk,
nixpkgs-mozilla,
}: }:
utils.lib.eachDefaultSystem ( utils.lib.eachDefaultSystem (
system: let system: let
overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml)); overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
pkgs = (import nixpkgs) { pkgs = (import nixpkgs) {inherit system;};
inherit system; naersk' = pkgs.callPackage naersk {};
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"; package_name = "skynet_discord_bot";
desc = "Skynet Discord Bot"; desc = "Skynet Discord Bot";
buildInputs = with pkgs; [ buildInputs = with pkgs; [
openssl openssl
glib
gdk-pixbuf
pkg-config pkg-config
rustfmt rustfmt
]; ];
@ -58,10 +37,6 @@
pname = "${package_name}"; pname = "${package_name}";
src = ./.; src = ./.;
buildInputs = buildInputs; buildInputs = buildInputs;
postInstall = ''
mkdir $out/config
cp .server-icons.toml $out/config
'';
}; };
# Run `nix build .#fmt` to run tests # Run `nix build .#fmt` to run tests
fmt = naersk'.buildPackage { fmt = naersk'.buildPackage {
@ -88,9 +63,9 @@
# `nix develop` # `nix develop`
devShell = pkgs.mkShell { devShell = pkgs.mkShell {
nativeBuildInputs = with pkgs; [rustup rustPlatform.bindgenHook]; nativeBuildInputs = with pkgs; [rustup rustPlatform.bindgenHook pkg-config openssl];
# libraries here # libraries here
buildInputs = buildInputs; buildInputs = [ ];
RUSTC_VERSION = overrides.toolchain.channel; RUSTC_VERSION = overrides.toolchain.channel;
# https://github.com/rust-lang/rust-bindgen#environment-variables # https://github.com/rust-lang/rust-bindgen#environment-variables
shellHook = '' shellHook = ''
@ -122,14 +97,12 @@
wantedBy = []; wantedBy = [];
after = ["network-online.target"]; after = ["network-online.target"];
environment = environment_config; environment = environment_config;
path = with pkgs; [ git git-lfs ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
User = "${cfg.user}"; User = "${cfg.user}";
Group = "${cfg.user}"; Group = "${cfg.user}";
ExecStart = "${self.defaultPackage."${system}"}/bin/${script}"; ExecStart = "${self.defaultPackage."${system}"}/bin/${script}";
# kill each service if its ran for 9 min
TimeoutStartSec=540;
EnvironmentFile = [ EnvironmentFile = [
"${cfg.env.discord}" "${cfg.env.discord}"
"${cfg.env.mail}" "${cfg.env.mail}"
@ -154,17 +127,14 @@
# modify these # modify these
scripts = { scripts = {
# every 10 min # every 20 min
"update_data" = "*:0,10,20,30,40,50"; "update_data" = "*:0,10,20,30,40,50";
# groups are updated every hour, offset from teh ldap # groups are updated every hour, offset from teh ldap
"update_users" = "*:5,15,25,35,45,55"; "update_users" = "*:05:00";
# Committee server has its own timer # Committee server has its own timer
"update_committee" = "*:5,15,25,35,45,55"; "update_committee" = "*:15:00";
# minecraft stuff is updated at 5am # minecraft stuff is updated at 5am
# this service does not depend on teh discord cache
"update_minecraft" = "5:10:00"; "update_minecraft" = "5:10:00";
# server icon gets updated daily at midnight
"update_server-icon" = "0:01:00";
}; };
in { in {
options.services."${package_name}" = { options.services."${package_name}" = {
@ -224,7 +194,6 @@
after = ["network-online.target"]; after = ["network-online.target"];
wants = []; wants = [];
environment = environment_config; environment = environment_config;
path = with pkgs; [ git git-lfs ];
serviceConfig = { serviceConfig = {
User = "${cfg.user}"; User = "${cfg.user}";

View file

@ -1,2 +1,2 @@
[toolchain] [toolchain]
channel = "1.87" channel = "1.80"

View file

@ -1,137 +0,0 @@
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::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(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<GuildId>) {
let config_lock = {
let data_read = ctx.data.read().await;
data_read.get::<Config>().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::<DataBase>().expect("Expected Config in TypeMap.").clone()
};
let config_lock = {
let data_read = ctx.data.read().await;
data_read.get::<Config>().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<Sqlite>, 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::<Vec<_>>();
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::<Vec<_>>();
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);
}
}
}
}
}

View file

@ -1,17 +1,12 @@
use serenity::{ use serenity::{
all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
async_trait, async_trait,
client::{Context, EventHandler}, client::{Context, EventHandler},
model::gateway::GatewayIntents, model::gateway::{GatewayIntents, Ready},
Client, Client,
}; };
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{db_init, DataBase};
common::{ use skynet_discord_bot::common::set_roles::committee;
database::{db_init, DataBase}, use skynet_discord_bot::{get_config, Config};
set_roles::committee,
},
get_config, Config,
};
use std::{process, sync::Arc}; use std::{process, sync::Arc};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -28,7 +23,6 @@ async fn main() {
// Build our client. // Build our client.
let mut client = Client::builder(&config.discord_token, intents) let mut client = Client::builder(&config.discord_token, intents)
.event_handler(Handler {}) .event_handler(Handler {})
.cache_settings(serenity::cache::Settings::default())
.await .await
.expect("Error creating client"); .expect("Error creating client");
@ -36,39 +30,25 @@ async fn main() {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config))); data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(db)); data.insert::<DataBase>(Arc::new(RwLock::new(db)));
} }
if let Err(why) = client.start().await { if let Err(why) = client.start().await {
println!("Client error: {why:?}"); println!("Client error: {:?}", why);
} }
} }
struct Handler; struct Handler;
#[async_trait] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
async fn cache_ready(&self, ctx: Context, _guilds: Vec<GuildId>) { async fn ready(&self, ctx: Context, ready: Ready) {
let config_lock = { let ctx = Arc::new(ctx);
let data_read = ctx.data.read().await; println!("{} is connected!", ready.user.name);
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone()
};
let config_global = config_lock.read().await;
let server = config_global.committee_server; // u[date committee server
committee::check_committee(Arc::clone(&ctx)).await;
ctx.shard.chunk_guild(server, Some(2000), false, ChunkGuildFilter::None, None); // finish up
process::exit(0);
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);
}
} }
} }

View file

@ -4,13 +4,10 @@ use serenity::{
model::gateway::{GatewayIntents, Ready}, model::gateway::{GatewayIntents, Ready},
Client, Client,
}; };
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{db_init, DataBase};
common::{ use skynet_discord_bot::common::wolves::cns::get_wolves;
database::{db_init, DataBase}, use skynet_discord_bot::common::wolves::committees::get_cns;
wolves::{cns::get_wolves, committees::get_cns}, use skynet_discord_bot::{get_config, Config};
},
get_config, Config,
};
use std::{process, sync::Arc}; use std::{process, sync::Arc};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -30,7 +27,6 @@ async fn main() {
// Build our client. // Build our client.
let mut client = Client::builder(&config.discord_token, intents) let mut client = Client::builder(&config.discord_token, intents)
.event_handler(Handler {}) .event_handler(Handler {})
.cache_settings(serenity::cache::Settings::default())
.await .await
.expect("Error creating client"); .expect("Error creating client");
@ -38,11 +34,11 @@ async fn main() {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config))); data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(db)); data.insert::<DataBase>(Arc::new(RwLock::new(db)));
} }
if let Err(why) = client.start().await { if let Err(why) = client.start().await {
println!("Client error: {why:?}"); println!("Client error: {:?}", why);
} }
} }

View file

@ -1,10 +1,6 @@
use skynet_discord_bot::{ use skynet_discord_bot::common::database::db_init;
common::{ use skynet_discord_bot::common::minecraft::{get_minecraft_config, update_server, whitelist_wipe};
database::db_init, use skynet_discord_bot::get_config;
minecraft::{get_minecraft_config, update_server, whitelist_wipe},
},
get_config,
};
use std::collections::HashSet; use std::collections::HashSet;
#[tokio::main] #[tokio::main]

View file

@ -1,71 +0,0 @@
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::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(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::<DataBase>().expect("Expected Config in TypeMap.").clone()
};
let config_lock = {
let data_read = ctx.data.read().await;
data_read.get::<Config>().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);
}
}

View file

@ -1,24 +1,13 @@
use serenity::{ use serenity::{
all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
async_trait, async_trait,
client::{Context, EventHandler}, client::{Context, EventHandler},
model::gateway::GatewayIntents, model::gateway::{GatewayIntents, Ready},
Client, Client,
}; };
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase};
common::{ use skynet_discord_bot::common::set_roles::normal;
database::{db_init, get_server_config_bulk, DataBase}, use skynet_discord_bot::{get_config, Config};
set_roles::normal, use std::{process, sync::Arc};
},
get_config, Config,
};
use std::{
process,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use tokio::sync::RwLock; use tokio::sync::RwLock;
#[tokio::main] #[tokio::main]
@ -33,11 +22,7 @@ async fn main() {
let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS; let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS;
// Build our client. // Build our client.
let mut client = Client::builder(&config.discord_token, intents) 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 .await
.expect("Error creating client"); .expect("Error creating client");
@ -45,51 +30,38 @@ async fn main() {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config))); data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(db)); data.insert::<DataBase>(Arc::new(RwLock::new(db)));
} }
if let Err(why) = client.start().await { 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] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
async fn cache_ready(&self, ctx: Context, guilds: Vec<GuildId>) { async fn ready(&self, ctx: Context, ready: Ready) {
self.server_count.swap(guilds.len(), Ordering::SeqCst); let ctx = Arc::new(ctx);
for guild in guilds { println!("{} is connected!", ready.user.name);
ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None);
}
println!("Cache loaded {}", &self.server_count.load(Ordering::SeqCst));
}
async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) { // this goes into each server and sets roles for each wolves member
if (chunk.chunk_index + 1) == chunk.chunk_count { check_bulk(Arc::clone(&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!");
// this goes into each server and sets roles for each wolves member // finish up
check_bulk(&ctx).await; process::exit(0);
// finish up
process::exit(0);
}
}
} }
} }
async fn check_bulk(ctx: &Context) { async fn check_bulk(ctx: Arc<Context>) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
}; };
let db = db_lock.read().await;
for server_config in get_server_config_bulk(&db).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;
} }
} }

View file

@ -1,12 +1,8 @@
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, use serenity::client::Context;
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::{ use skynet_discord_bot::common::wolves::cns::get_wolves;
database::{get_server_config, DataBase, Servers},
set_roles::normal::update_server,
wolves::cns::get_wolves,
};
use sqlx::{Error, Pool, Sqlite}; use sqlx::{Error, Pool, Sqlite};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
@ -56,10 +52,11 @@ pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
return "Please provide a valid channel for ``Bot Channel``".to_string(); return "Please provide a valid channel for ``Bot Channel``".to_string();
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let server_data = Servers { let server_data = Servers {
server: command.guild_id.unwrap_or_default(), server: command.guild_id.unwrap_or_default(),
@ -75,8 +72,8 @@ pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
match add_server(&db, ctx, &server_data).await { match add_server(&db, ctx, &server_data).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
return format!("Failure to insert into Servers {server_data:?}"); return format!("Failure to insert into Servers {:?}", server_data);
} }
} }
@ -101,7 +98,7 @@ async fn add_server(db: &Pool<Sqlite>, ctx: &Context, server: &Servers) -> Resul
.fetch_optional(db) .fetch_optional(db)
.await; .await;
// if the entry does not exist already then do a user update // if the entry does not exist already tehn do a user update
let (update, current_remove, current_role, past_remove, past_role) = match &existing { let (update, current_remove, current_role, past_remove, past_role) = match &existing {
None => (true, false, None, false, None), None => (true, false, None, false, None),
Some(x) => { Some(x) => {

View file

@ -20,8 +20,4 @@ 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::Role, "role_c", "Sum of A and B").required(true))
.add_sub_option(CreateCommandOption::new(CommandOptionType::Boolean, "delete", "Delete this entry.").required(false)), .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.")),
)
} }

View file

@ -5,7 +5,8 @@ pub mod committee {
use serenity::all::{ use serenity::all::{
CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption, CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption,
}; };
use skynet_discord_bot::common::{database::DataBase, set_roles::committee::db_roles_get}; use skynet_discord_bot::common::database::DataBase;
use skynet_discord_bot::common::set_roles::committee::db_roles_get;
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let sub_options = if let Some(CommandDataOption { let sub_options = if let Some(CommandDataOption {
@ -27,10 +28,11 @@ pub mod committee {
false false
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let mut cs = vec![]; let mut cs = vec![];
// pull it from a DB // pull it from a DB
@ -51,7 +53,7 @@ pub mod committee {
for (count, name) in cs { for (count, name) in cs {
let leading = if count < 10 { " " } else { "" }; let leading = if count < 10 { " " } else { "" };
let line = format!("{leading}{count} {name}"); let line = format!("{}{} {}", leading, count, name);
let length = line.len() + 1; let length = line.len() + 1;
@ -83,27 +85,22 @@ pub mod servers {
// get the list of all the current clubs/socs // get the list of all the current clubs/socs
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption}; use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption};
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{get_server_config_bulk, DataBase};
common::{ use skynet_discord_bot::common::set_roles::committee::get_committees;
database::{get_server_config_bulk, DataBase}, use skynet_discord_bot::get_now_iso;
set_roles::committee::get_committees,
},
get_now_iso,
};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use std::collections::HashMap; use std::collections::HashMap;
pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let mut committees = HashMap::new(); let mut committees = HashMap::new();
if let Some(x) = get_committees(&db).await { for committee in get_committees(&db).await {
for committee in x { committees.insert(committee.id, committee.to_owned());
committees.insert(committee.id, committee.to_owned());
}
} }
let mut cs = vec![]; let mut cs = vec![];
@ -146,11 +143,11 @@ 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; let length = line.len() + 1;
// +3 is to account for the closing fence // +3 is to account for the closing fense
if length < (limit + 3) { if length < (limit + 3) {
response.push(line); response.push(line);
limit -= length; limit -= length;

View file

@ -9,24 +9,19 @@ pub(crate) mod user {
use super::*; use super::*;
use crate::commands::wolves::link::get_server_member_discord; use crate::commands::wolves::link::get_server_member_discord;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, use serenity::model::id::UserId;
model::id::UserId, use skynet_discord_bot::common::database::Wolves;
}; use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft};
use skynet_discord_bot::{ use skynet_discord_bot::Config;
common::{
database::Wolves,
minecraft::{whitelist_update, Minecraft},
},
Config,
};
use sqlx::Error; use sqlx::Error;
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
@ -74,14 +69,14 @@ pub(crate) mod user {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
dbg!("{:?}", e); dbg!("{:?}", e);
return format!("Failure to minecraft username {username:?}"); return format!("Failure to minecraft username {:?}", username);
} }
} }
username_mc = username.to_string(); username_mc = username.to_string();
} else { } else {
match get_minecraft_bedrock(username, &config.minecraft_mcprofile).await { match get_minecraft_bedrock(username, &config.minecraft_mcprofile).await {
None => { None => {
return format!("No UID found for {username:?}"); return format!("No UID found for {:?}", username);
} }
Some(x) => { Some(x) => {
match add_minecraft_bedrock(&db, &command.user.id, &x.floodgateuid).await { match add_minecraft_bedrock(&db, &command.user.id, &x.floodgateuid).await {
@ -190,17 +185,14 @@ pub(crate) mod server {
use super::*; use super::*;
pub(crate) mod add { pub(crate) mod add {
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption}, use serenity::model::id::GuildId;
model::id::GuildId,
};
use sqlx::Error; use sqlx::Error;
// this is to manage the server side of commands related to minecraft // this is to managfe the server side of commands related to minecraft
use super::*; use super::*;
use skynet_discord_bot::{ use skynet_discord_bot::common::minecraft::update_server;
common::minecraft::{update_server, Minecraft}, use skynet_discord_bot::common::minecraft::Minecraft;
Config, use skynet_discord_bot::Config;
};
pub fn register() -> CreateCommand { pub fn register() -> CreateCommand {
CreateCommand::new("minecraft_add") CreateCommand::new("minecraft_add")
@ -228,15 +220,16 @@ pub(crate) mod server {
return String::from("Expected Server ID"); return String::from("Expected Server ID");
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
match add_server(&db, &g_id, &server_minecraft).await { match add_server(&db, &g_id, &server_minecraft).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft); return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft);
} }
} }
@ -267,14 +260,12 @@ pub(crate) mod server {
} }
pub(crate) mod list { pub(crate) mod list {
use serenity::{all::CommandInteraction, builder::CreateCommand, client::Context}; use serenity::all::CommandInteraction;
use skynet_discord_bot::{ use serenity::builder::CreateCommand;
common::{ use serenity::client::Context;
database::DataBase, use skynet_discord_bot::common::database::DataBase;
minecraft::{get_minecraft_config_server, server_information}, use skynet_discord_bot::common::minecraft::{get_minecraft_config_server, server_information};
}, use skynet_discord_bot::Config;
Config,
};
pub fn register() -> CreateCommand { pub fn register() -> CreateCommand {
CreateCommand::new("minecraft_list") CreateCommand::new("minecraft_list")
@ -288,10 +279,11 @@ pub(crate) mod server {
Some(x) => x, Some(x) => x,
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let servers = get_minecraft_config_server(&db, g_id).await; let servers = get_minecraft_config_server(&db, g_id).await;
@ -328,13 +320,12 @@ pub(crate) mod server {
} }
pub(crate) mod delete { pub(crate) mod delete {
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption}, use serenity::builder::CreateCommand;
builder::CreateCommand, use serenity::client::Context;
client::Context, use serenity::model::id::GuildId;
model::id::GuildId, use skynet_discord_bot::common::database::DataBase;
}; use skynet_discord_bot::common::minecraft::Minecraft;
use skynet_discord_bot::common::{database::DataBase, minecraft::Minecraft};
use sqlx::{Error, Pool, Sqlite}; use sqlx::{Error, Pool, Sqlite};
pub fn register() -> CreateCommand { pub fn register() -> CreateCommand {
@ -363,15 +354,16 @@ pub(crate) mod server {
return String::from("Expected Server ID"); return String::from("Expected Server ID");
}; };
let db = { let db_lock = {
let data = ctx.data.read().await; let data_read = ctx.data.read().await;
data.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
match server_remove(&db, &g_id, &server_minecraft).await { match server_remove(&db, &g_id, &server_minecraft).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft); return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft);
} }
} }

View file

@ -3,5 +3,4 @@ pub mod committee;
pub mod count; pub mod count;
pub mod minecraft; pub mod minecraft;
pub mod role_adder; pub mod role_adder;
pub mod server_icon;
pub mod wolves; pub mod wolves;

View file

@ -62,10 +62,11 @@ pub mod edit {
false false
}; };
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let server = command.guild_id.unwrap_or_default(); let server = command.guild_id.unwrap_or_default();
let server_data = RoleAdder { let server_data = RoleAdder {
@ -78,8 +79,8 @@ pub mod edit {
match add_server(&db, &server_data, delete).await { match add_server(&db, &server_data, delete).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
return format!("Failure to insert into Servers {server_data:?}"); return format!("Failure to insert into Servers {:?}", server_data);
} }
} }
@ -100,9 +101,9 @@ pub mod edit {
} }
if delete { 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 { } else {
format!("Added {role_a_name} + {role_b_name} = {role_c_name}") format!("Added {} + {} = {}", role_a_name, role_b_name, role_c_name)
} }
} }
@ -141,12 +142,13 @@ pub mod edit {
pub mod list {} pub mod list {}
pub mod tools { pub mod tools {
use serenity::{client::Context, model::guild::Member}; use serenity::client::Context;
use serenity::model::guild::Member;
use skynet_discord_bot::common::database::RoleAdder; use skynet_discord_bot::common::database::RoleAdder;
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
pub async fn on_role_change(db: &Pool<Sqlite>, ctx: &Context, new_data: Member) { pub async fn on_role_change(db: &Pool<Sqlite>, ctx: &Context, new_data: Member) {
// check if the role changed is part of the ones for this server // check if the role changed is part of the oens for this server
if let Ok(role_adders) = sqlx::query_as::<_, RoleAdder>( if let Ok(role_adders) = sqlx::query_as::<_, RoleAdder>(
r#" r#"
SELECT * SELECT *
@ -162,7 +164,7 @@ pub mod tools {
let mut roles_remove = vec![]; let mut roles_remove = vec![];
for role_adder in role_adders { for role_adder in role_adders {
// if the user has both A and B give them C // if the user has both A dnd 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) 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); roles_add.push(role_adder.role_c);
@ -178,13 +180,13 @@ pub mod tools {
if !roles_add.is_empty() { if !roles_add.is_empty() {
if let Err(e) = new_data.add_roles(&ctx, &roles_add).await { if let Err(e) = new_data.add_roles(&ctx, &roles_add).await {
println!("{e:?}"); println!("{:?}", e);
} }
} }
if !roles_remove.is_empty() { if !roles_remove.is_empty() {
if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await { if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await {
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }

View file

@ -1,223 +0,0 @@
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 = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
};
let config_lock = {
let data_read = ctx.data.read().await;
data_read.get::<Config>().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", "How many times the particular icon has been used"))
}
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 serenity::all::{CreateAttachment, EditInteractionResponse};
use sqlx::{Pool, Sqlite};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
};
let config_toml = get_config_icons::minimal();
if let Some(logo) = get_current_icon(&db).await {
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);
}
}
}
format!("[{}]({})", &logo.name, get_logo_url(&config_toml, &logo.name))
} else {
"Could not find current icon".to_string()
}
}
pub async fn get_current_icon(db: &Pool<Sqlite>) -> Option<ServerIcons> {
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, Context};
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 {
let config_lock = {
let data_read = ctx.data.read().await;
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone()
};
let config = config_lock.read().await;
let config_toml = get_config_icons::full(&config);
let response = get_festival(&config_toml).current;
if response.is_empty() {
"No festival currently active".to_string()
} else {
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 = {
let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
};
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<Sqlite>) -> Vec<CountResult> {
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: &[CountResult]) -> String {
let mut totals_local = totals.to_owned();
totals_local.sort_by_key(|x| x.times);
totals_local.reverse();
// msg can be a max 2000 chars long
let mut limit = 2000 - 3;
let mut response = vec![];
for CountResult {
name,
times,
} in &totals_local
{
let current_leading = if times < &10 {
"00"
} else if times < &100 {
"0"
} else {
""
};
let url = get_logo_url(config_toml, name);
// 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;
// +3 is to account for the closing fence
if length < (limit + 3) {
response.push(line);
limit -= length;
} else {
break;
}
}
response.join("\n")
}
}
}

View file

@ -4,16 +4,11 @@ use lettre::{
Message, SmtpTransport, Transport, Message, SmtpTransport, Transport,
}; };
use maud::html; use maud::html;
use serenity::{ use serenity::all::CommandOptionType;
all::CommandOptionType, use serenity::builder::CreateCommandOption;
builder::{CreateCommand, CreateCommandOption}, use serenity::{builder::CreateCommand, client::Context, model::id::UserId};
client::Context, use skynet_discord_bot::common::database::{DataBase, Wolves, WolvesVerify};
model::id::UserId, use skynet_discord_bot::{get_now_iso, random_string, Config};
};
use skynet_discord_bot::{
common::database::{DataBase, Wolves, WolvesVerify},
get_now_iso, random_string, Config,
};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
pub mod link { pub mod link {
@ -21,10 +16,11 @@ pub mod link {
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}; use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
@ -100,7 +96,7 @@ pub mod link {
return "Email already verified".to_string(); return "Email already verified".to_string();
} }
// generate an auth key // generate a auth key
let auth = random_string(20); let auth = random_string(20);
match send_mail(&config, &details.email, &auth, &command.user.name) { match send_mail(&config, &details.email, &auth, &command.user.name) {
Ok(_) => match save_to_db(&db, &details, &auth, &command.user.id).await { Ok(_) => match save_to_db(&db, &details, &auth, &command.user.id).await {
@ -114,7 +110,7 @@ pub mod link {
} }
} }
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.") 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)
} }
pub async fn get_server_member_discord(db: &Pool<Sqlite>, user: &UserId) -> Option<Wolves> { pub async fn get_server_member_discord(db: &Pool<Sqlite>, user: &UserId) -> Option<Wolves> {
@ -209,7 +205,7 @@ pub mod link {
.subject("Skynet: Link Discord to Wolves.") .subject("Skynet: Link Discord to Wolves.")
.multipart( .multipart(
// This is composed of two parts. // This is composed of two parts.
// also helps not trip spam settings (uneven number of urls) // also helps not trip spam settings (uneven number of url's
MultiPart::alternative() MultiPart::alternative()
.singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body_text)) .singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body_text))
.singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML).body(html.into_string())), .singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML).body(html.into_string())),
@ -283,40 +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 { pub mod verify {
use super::*; use super::*;
use crate::commands::wolves::link::{db_pending_clear_expired, get_server_member_discord, get_verify_from_db}; use crate::commands::wolves::link::{db_pending_clear_expired, get_server_member_discord, get_verify_from_db};
use serenity::{ use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, GuildId, RoleId};
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction}, use serenity::model::user::User;
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::{ use skynet_discord_bot::common::wolves::committees::Committees;
database::{get_server_config, ServerMembersWolves, Servers},
wolves::committees::Committees,
};
use sqlx::Error; use sqlx::Error;
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
}; };
let db = db_lock.read().await;
// check if user has used /link_wolves // check if user has used /link_wolves
let details = if let Some(x) = get_verify_from_db(&db, &command.user.id).await { let details = if let Some(x) = get_verify_from_db(&db, &command.user.id).await {
@ -363,12 +341,12 @@ pub mod verify {
"Discord username linked to Wolves".to_string() "Discord username linked to Wolves".to_string()
} }
Err(e) => { Err(e) => {
println!("{e:?}"); println!("{:?}", e);
"Failed to save, please try /link_wolves again".to_string() "Failed to save, please try /link_wolves again".to_string()
} }
}; };
} }
Err(e) => println!("{e:?}"), Err(e) => println!("{:?}", e),
} }
"Failed to verify".to_string() "Failed to verify".to_string()
@ -425,7 +403,7 @@ pub mod verify {
} }
if let Err(e) = member.add_roles(&ctx, &roles).await { if let Err(e) = member.add_roles(&ctx, &roles).await {
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -441,7 +419,7 @@ pub mod verify {
WHERE committee LIKE ?1 WHERE committee LIKE ?1
"#, "#,
) )
.bind(format!("%{wolves_id}%")) .bind(format!("%{}%", wolves_id))
.fetch_all(db) .fetch_all(db)
.await .await
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
@ -451,18 +429,12 @@ pub mod verify {
} }
async fn set_server_roles_committee(db: &Pool<Sqlite>, discord: &User, ctx: &Context) { async fn set_server_roles_committee(db: &Pool<Sqlite>, discord: &User, ctx: &Context) {
let config_lock = {
let data_read = ctx.data.read().await;
data_read.get::<Config>().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 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 them the general committee role // 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 // they will get teh more specific vanity role later
if !get_committees_id(db, x.id_wolves).await.is_empty() { if !get_committees_id(db, x.id_wolves).await.is_empty() {
let server = config.committee_server; let server = GuildId::new(1220150752656363520);
let committee_member = config.committee_role; let committee_member = RoleId::new(1226602779968274573);
if let Ok(member) = server.member(ctx, &discord.id).await { if let Ok(member) = server.member(ctx, &discord.id).await {
member.add_roles(&ctx, &[committee_member]).await.unwrap_or_default(); member.add_roles(&ctx, &[committee_member]).await.unwrap_or_default();
@ -492,12 +464,13 @@ pub mod unlink {
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String { pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone() data_read.get::<DataBase>().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 // dosent matter if there is one or not, it will be removed regardless
delete_link(&db, &command.user.id).await; delete_link(&db, &command.user.id).await;
"Discord link removed".to_string() "Discord link removed".to_string()
@ -543,5 +516,4 @@ pub fn register() -> CreateCommand {
.add_sub_option(CreateCommandOption::new(CommandOptionType::String, "minecraft_username", "Your Minecraft username").required(true)) .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_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."))
} }

View file

@ -1,21 +1,17 @@
use crate::Config; use crate::Config;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::{ use serenity::model::guild;
model::{ use serenity::model::id::{ChannelId, GuildId, RoleId, UserId};
guild, use serenity::prelude::TypeMapKey;
id::{ChannelId, GuildId, RoleId, UserId}, use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow};
}, use sqlx::{Error, FromRow, Pool, Row, Sqlite};
prelude::TypeMapKey, use std::str::FromStr;
}; use std::sync::Arc;
use sqlx::{ use tokio::sync::RwLock;
sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow},
Error, FromRow, Pool, Row, Sqlite,
};
use std::{str::FromStr, sync::Arc};
pub struct DataBase; pub struct DataBase;
impl TypeMapKey for DataBase { impl TypeMapKey for DataBase {
type Value = Arc<Pool<Sqlite>>; type Value = Arc<RwLock<Pool<Sqlite>>>;
} }
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
@ -224,7 +220,7 @@ pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
let pool = SqlitePoolOptions::new() let pool = SqlitePoolOptions::new()
.max_connections(5) .max_connections(5)
.connect_with( .connect_with(
SqliteConnectOptions::from_str(&format!("sqlite://{database}"))? SqliteConnectOptions::from_str(&format!("sqlite://{}", database))?
.foreign_keys(true) .foreign_keys(true)
.create_if_missing(true), .create_if_missing(true),
) )

View file

@ -1,7 +1,10 @@
use crate::{common::set_roles::normal::get_server_member_bulk, Config}; use crate::common::set_roles::normal::get_server_member_bulk;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::Config;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serenity::model::id::GuildId; use serenity::model::id::GuildId;
use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite}; use sqlx::sqlite::SqliteRow;
use sqlx::{Error, FromRow, Pool, Row, Sqlite};
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Minecraft { pub struct Minecraft {
@ -24,7 +27,7 @@ impl<'r> FromRow<'r, SqliteRow> for Minecraft {
/** /**
loop through all members of server loop through all members of server
get a list of folks with mc accounts that are members get a list of folks with mc accounts that are members
and a list that aren't members and a list that arent members
*/ */
pub async fn update_server(server_id: &str, db: &Pool<Sqlite>, g_id: &GuildId, config: &Config) { pub async fn update_server(server_id: &str, db: &Pool<Sqlite>, g_id: &GuildId, config: &Config) {
let mut usernames = vec![]; let mut usernames = vec![];
@ -109,7 +112,7 @@ pub async fn whitelist_wipe(server: &str, token: &str) {
}; };
post(&format!("{url_base}/files/delete"), &bearer, &deletion).await; post(&format!("{url_base}/files/delete"), &bearer, &deletion).await;
// recreate the file, passing in the type here so the compiler knows what type of Vec it is // recreate teh file, passing in the type here so the compiler knows what type of vec it is
post::<Vec<&str>>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await; post::<Vec<&str>>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await;
// reload the whitelist // reload the whitelist
@ -152,7 +155,7 @@ pub async fn get_minecraft_config_server(db: &Pool<Sqlite>, g_id: GuildId) -> Ve
} }
pub async fn whitelist_update(add: &Vec<(String, bool)>, server: &str, token: &str) { 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 url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}");
let bearer = format!("Bearer {token}"); let bearer = format!("Bearer {token}");

View file

@ -2,6 +2,3 @@ pub mod database;
pub mod minecraft; pub mod minecraft;
pub mod set_roles; pub mod set_roles;
pub mod wolves; pub mod wolves;
pub mod renderer;
pub mod server_icon;

View file

@ -1,193 +0,0 @@
// 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 yoinked it from here.
use std::{
ffi::OsStr,
path::{Path, PathBuf},
};
use color_eyre::{eyre::bail, Result};
use usvg_text_layout::TreeTextToPath;
#[derive(Debug, Clone)]
pub struct Args {
pub input: PathBuf,
/// Output folder where the PNG's will be placed
pub output: PathBuf,
/// Comma separated 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
pub width: u32,
/// Height of the generated PNG's
pub height: u32,
}
#[derive(Debug, Clone)]
enum ColorType {
Array(Vec<String>),
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<Self> {
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::<Vec<&str>>();
if s.len() < 2 {
dbg!("Invalid color object, try checking help");
return None;
}
Some((s[0].to_string(), s[1].to_string()))
})
.collect::<Vec<Option<(String, String)>>>();
let mut colors = Vec::new();
for c in obj.into_iter().flatten() {
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::<Vec<String>>();
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 => {
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");
}
};
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() {
dbg!("File {fo:?} exists, skipping");
return Ok(());
}
let svg = self.set_color(&self.get_svg_data(fi)?, color);
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<String> {
match std::fs::read_to_string(fi) {
Ok(d) => Ok(d),
Err(_) => {
dbg!("File {fi:?} does not exist");
bail!("File {fi:?} does not exist");
}
}
}
}

View file

@ -1,404 +0,0 @@
use serde::Deserialize;
use std::{ffi::OsString, fs, path::PathBuf};
pub mod get_config_icons {
use super::*;
use crate::Config;
#[derive(Deserialize)]
pub struct ConfigToml {
pub source: ConfigTomlSource,
pub festivals: Vec<ConfigTomlFestivals>,
}
#[derive(Deserialize)]
pub struct ConfigTomlLocal {
pub source: ConfigTomlSource,
}
#[derive(Deserialize)]
pub struct ConfigTomlRemote {
pub festivals: Vec<ConfigTomlFestivals>,
}
#[derive(Deserialize, Debug)]
pub struct ConfigTomlSource {
pub repo: String,
pub directory: String,
pub file: 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 minimal() -> ConfigTomlLocal {
let toml_raw_min = include_str!("../../.server-icons.toml");
toml::from_str::<ConfigTomlLocal>(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
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).unwrap_or_else(|e| {
dbg!(e);
"".to_string()
});
let festivals = match toml::from_str::<ConfigTomlRemote>(&contents) {
Ok(config_festivals) => config_festivals.festivals,
Err(e) => {
dbg!(e);
vec![]
}
};
ConfigToml {
source: config_source.source,
festivals,
}
}
}
#[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 path: String,
pub date: String,
}
pub mod update_icon {
use super::*;
use crate::{
common::{
renderer::{Args, Renderer},
server_icon::get_config_icons::{self, ConfigToml, ConfigTomlLocal},
},
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::process::Command;
/// Update the server icon, pulling from open governance.
pub async fn update_icon_main(ctx: &Context, db: &Pool<Sqlite>, config_global: &Config, config_toml_local: &ConfigTomlLocal) {
let server = config_global.compsoc_server;
// clone repo into local folder
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);
// 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)]
pub struct FestivalData {
pub current: Vec<String>,
exclusions: Vec<String>,
}
pub 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: &ConfigTomlLocal) {
let url = &config_toml.source.repo;
let folder = format!("{}/open-governance", &config.home);
if let Err(e) = Command::new("git")
// clone the repo, gracefully "fails"
.arg("clone")
.arg(url)
.arg(&folder)
.output()
{
dbg!(e);
}
if let Err(e) = Command::new("git")
// Update the repo
.arg("pull")
.arg("origin")
.arg("main")
.current_dir(&folder)
.output()
{
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<LogoData> {
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 = match fs::read_dir(folder) {
Ok(x) => x,
Err(e) => {
dbg!(e);
return vec![];
}
};
let args = Args {
input: folder_path.clone(),
output: folder_output,
colors: String::from(""),
width: 1024,
height: 1024,
};
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 = match path_local2.file_name() {
None => {
dbg!(path_local2);
continue;
}
Some(x) => x.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 = match filename_tmp.file_name() {
None => {
dbg!(filename_tmp);
continue;
}
Some(x) => x,
};
path_new.pop();
path_new.push("converted");
path_new.push(filename);
// check if exists
if !path_new.exists() {
// convert if it hasn't 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<LogoData>) -> Vec<LogoData> {
let mut filtered: Vec<LogoData> = 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
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<Sqlite>, server: &GuildId, logo_selected: &LogoData) {
// add to teh database
if !logo_set_db(db, logo_selected).await {
// something went wrong
return;
}
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<Sqlite>, 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>(
"
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
{
Ok(_) => {}
Err(e) => {
dbg!(e);
return false;
}
}
true
}
}

View file

@ -1,27 +1,18 @@
pub mod normal { pub mod normal {
use crate::{ use crate::common::database::{DataBase, ServerMembersWolves, Servers, Wolves};
common::database::{DataBase, ServerMembersWolves, Servers, Wolves}, use crate::get_now_iso;
get_now_iso, use serenity::client::Context;
}; use serenity::model::id::{GuildId, RoleId, UserId};
use serenity::{
client::Context,
model::id::{GuildId, RoleId, UserId},
};
use sqlx::{Pool, Sqlite}; 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<RoleId>], members_changed: &[UserId]) { pub async fn update_server(ctx: &Context, server: &Servers, remove_roles: &[Option<RoleId>], members_changed: &[UserId]) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let Servers { let Servers {
server, server,
role_past, role_past,
@ -29,12 +20,7 @@ pub mod normal {
.. ..
} = server; } = server;
let mut roles_set = RolesChange { let mut roles_set = [0, 0, 0];
total: 0,
new: 0,
current_add: 0,
current_rem: 0,
};
let mut members = vec![]; let mut members = vec![];
for member in get_server_member_bulk(&db, server).await { for member in get_server_member_bulk(&db, server).await {
@ -46,30 +32,28 @@ pub mod normal {
if let Ok(x) = server.members(ctx, None, None).await { if let Ok(x) = server.members(ctx, None, None).await {
for member in x { for member in x {
// members_changed acts as an override to only deal with the users in it // members_changed acts as an override to only deal with teh users in it
if !members_changed.is_empty() && !members_changed.contains(&member.user.id) { if !members_changed.is_empty() && !members_changed.contains(&member.user.id) {
continue; continue;
} }
if members.contains(&member.user.id) { if members.contains(&member.user.id) {
roles_set.total += 1;
let mut roles = vec![]; let mut roles = vec![];
if let Some(role) = &role_past { if let Some(role) = &role_past {
if !member.roles.contains(role) { if !member.roles.contains(role) {
roles_set.new += 1; roles_set[0] += 1;
roles.push(role.to_owned()); roles.push(role.to_owned());
} }
} }
if !member.roles.contains(role_current) { if !member.roles.contains(role_current) {
roles_set.current_add += 1; roles_set[1] += 1;
roles.push(role_current.to_owned()); roles.push(role_current.to_owned());
} }
if let Err(e) = member.add_roles(ctx, &roles).await { if let Err(e) = member.add_roles(ctx, &roles).await {
println!("{e:?}"); println!("{:?}", e);
} }
} else { } else {
// old and never // old and never
@ -81,16 +65,16 @@ pub mod normal {
} }
if member.roles.contains(role_current) { if member.roles.contains(role_current) {
roles_set.current_rem += 1; roles_set[2] += 1;
// if they're not a current member and have the role then remove it // 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 { if let Err(e) = member.remove_role(ctx, role_current).await {
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
for role in remove_roles.iter().flatten() { for role in remove_roles.iter().flatten() {
if let Err(e) = member.remove_role(ctx, role).await { if let Err(e) = member.remove_role(ctx, role).await {
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -99,14 +83,7 @@ pub mod normal {
set_server_numbers(&db, server, members_all as i64, members.len() as i64).await; set_server_numbers(&db, server, members_all as i64, members.len() as i64).await;
// small bit of logging to note changes over time // small bit of logging to note changes over time
println!( println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.get(), roles_set[0], roles_set[1], roles_set[2]);
"{:?} 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<Sqlite>, server: &GuildId) -> Vec<ServerMembersWolves> { pub async fn get_server_member_bulk(db: &Pool<Sqlite>, server: &GuildId) -> Vec<ServerMembersWolves> {
@ -146,7 +123,7 @@ pub mod normal {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into {}", server.get()); println!("Failure to insert into {}", server.get());
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -154,56 +131,58 @@ pub mod normal {
// for updating committee members // for updating committee members
pub mod committee { pub mod committee {
use crate::{ use crate::common::database::{get_channel_from_row, get_role_from_row, DataBase, Wolves};
common::{ use crate::common::wolves::committees::Committees;
database::{get_channel_from_row, get_role_from_row, DataBase, Wolves}, use crate::Config;
wolves::committees::Committees,
},
Config,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::{ use serenity::all::{EditRole, GuildId};
all::EditRole, use serenity::builder::CreateChannel;
builder::CreateChannel, use serenity::client::Context;
client::Context, use serenity::model::channel::ChannelType;
model::{channel::ChannelType, guild::Member, id::ChannelId, prelude::RoleId}, use serenity::model::guild::Member;
}; use serenity::model::id::ChannelId;
use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite}; use serenity::model::prelude::RoleId;
use sqlx::sqlite::SqliteRow;
use sqlx::{Error, FromRow, Pool, Row, Sqlite};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
pub async fn check_committee(ctx: &Context) { pub async fn check_committee(ctx: Arc<Context>) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone() data_read.get::<Config>().expect("Expected Config in TypeMap.").clone()
}; };
let config_global = config_lock.read().await; let config_global = config_lock.read().await;
let server = config_global.committee_server; let server = GuildId::new(1220150752656363520);
// because to use it to update a single user we need to pre-get the members of teh 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(); 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;
} }
/** /**
This function can take a Vec of members (or just one) and gives them the appropriate roles on teh committee server 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<Sqlite>, ctx: &Context, config: &Config, members: &mut Vec<Member>) { pub async fn update_committees(db: &Pool<Sqlite>, ctx: &Context, _config: &Config, members: &mut Vec<Member>) {
let server = config.committee_server; let server = GuildId::new(1220150752656363520);
let committee_member = config.committee_role; let committee_member = RoleId::new(1226602779968274573);
let committees = match get_committees(db).await { let committees = get_committees(db).await;
None => { let categories = [
return; ChannelId::new(1226606560973815839),
} // C&S Chats 2
Some(x) => x, ChannelId::new(1341457244973305927),
}; // C&S Chats 3
let categories = config.committee_category.clone(); ChannelId::new(1341457509717639279),
];
// information about the server // information about the server
let mut roles_db = HashMap::new(); let mut roles_db = HashMap::new();
@ -224,11 +203,11 @@ pub mod committee {
let mut channels = server.channels(&ctx).await.unwrap_or_default(); let mut channels = server.channels(&ctx).await.unwrap_or_default();
// a map of users and the roles they are going to be getting // a map of users and the roles they are goign to be getting
let mut users_roles = HashMap::new(); let mut users_roles = HashMap::new();
let mut re_order = false; let mut re_order = false;
// we need to create roles and channels if they don't already exist // we need to create roles and channels if tehy dont already exist
let mut category_index = 0; let mut category_index = 0;
let mut i = 0; let mut i = 0;
loop { loop {
@ -329,21 +308,21 @@ 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 // 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 { for member in members {
// if member.user.id != 136522490632601600 {
// continue;
// }
//
let roles_current = member.roles(ctx).unwrap_or_default(); let roles_current = member.roles(ctx).unwrap_or_default();
let roles_required = match users_roles.get(&member.user.id) { let roles_required = match users_roles.get(&member.user.id) {
None => { None => {
vec![] vec![]
} }
Some(x) => x.to_owned(), Some(x) => {
let mut tmp = x.to_owned();
if !tmp.is_empty() {
tmp.push(committee_member);
}
tmp
}
}; };
let on_committee = !roles_required.is_empty();
let mut roles_rem = vec![]; let mut roles_rem = vec![];
let mut roles_add = vec![]; let mut roles_add = vec![];
// get a list of all the roles to remove from someone // get a list of all the roles to remove from someone
@ -352,25 +331,14 @@ pub mod committee {
for role in &roles_current { for role in &roles_current {
roles_current_id.push(role.id.to_owned()); roles_current_id.push(role.id.to_owned());
if !roles_required.contains(&role.id) { if !roles_required.contains(&role.id) {
if role.id == committee_member && on_committee {
continue;
}
roles_rem.push(role.id.to_owned()); roles_rem.push(role.id.to_owned());
} }
} }
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 purpose role
roles_add.push(committee_member);
}
if !on_committee && has_committee_role {
roles_rem.push(committee_member);
}
if !roles_required.is_empty() { if !roles_required.is_empty() {
// if there are committee roles then give the general purporse role
roles_add.push(committee_member);
if let Some(x) = roles_db.get_mut(&0) { if let Some(x) = roles_db.get_mut(&0) {
x.count += 1; x.count += 1;
} }
@ -389,6 +357,8 @@ pub mod committee {
if !roles_add.is_empty() { if !roles_add.is_empty() {
// these roles are flavor roles, only there to make folks mentionable // these roles are flavor roles, only there to make folks mentionable
member.add_roles(&ctx, &roles_add).await.unwrap_or_default(); member.add_roles(&ctx, &roles_add).await.unwrap_or_default();
} else {
member.remove_roles(&ctx, &[committee_member]).await.unwrap_or_default();
} }
} }
@ -436,10 +406,10 @@ pub mod committee {
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CommitteeRoles { pub struct CommitteeRoles {
id_wolves: i64, id_wolves: i64,
pub id_role: RoleId, id_role: RoleId,
pub id_channel: ChannelId, id_channel: ChannelId,
pub name_role: String, pub name_role: String,
pub name_channel: String, name_channel: String,
pub count: i64, pub count: i64,
} }
@ -476,8 +446,8 @@ pub mod committee {
{ {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into Wolves {role:?}"); println!("Failure to insert into Wolves {:?}", role);
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -494,13 +464,13 @@ pub mod committee {
.await .await
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
println!("Failure to get Roles from committee_roles"); println!("Failure to get Roles from committee_roles");
println!("{e:?}"); println!("{:?}", e);
vec![] vec![]
}) })
} }
pub async fn get_committees(db: &Pool<Sqlite>) -> Option<Vec<Committees>> { pub async fn get_committees(db: &Pool<Sqlite>) -> Vec<Committees> {
match sqlx::query_as::<_, Committees>( sqlx::query_as::<_, Committees>(
r#" r#"
SELECT * SELECT *
FROM committees FROM committees
@ -508,13 +478,10 @@ pub mod committee {
) )
.fetch_all(db) .fetch_all(db)
.await .await
{ .unwrap_or_else(|e| {
Ok(x) => Some(x), dbg!(e);
Err(e) => { vec![]
dbg!(e); })
None
}
}
} }
async fn get_server_member_discord(db: &Pool<Sqlite>, user: &i64) -> Option<Wolves> { async fn get_server_member_discord(db: &Pool<Sqlite>, user: &i64) -> Option<Wolves> {

View file

@ -38,8 +38,8 @@ async fn add_users_wolves(db: &Pool<Sqlite>, user: &WolvesResultUserMin) {
{ {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into Wolves {user:?}"); println!("Failure to insert into Wolves {:?}", user);
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -48,14 +48,12 @@ async fn add_users_wolves(db: &Pool<Sqlite>, user: &WolvesResultUserMin) {
This is getting data for Clubs and Socs This is getting data for Clubs and Socs
*/ */
pub mod cns { pub mod cns {
use crate::{ use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers};
common::{ use crate::common::set_roles::normal::update_server;
database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers}, use crate::common::wolves::{add_users_wolves, WolvesResultUserMin};
wolves::{add_users_wolves, WolvesResultUserMin}, use crate::Config;
}, use serenity::client::Context;
Config, use serenity::model::id::GuildId;
};
use serenity::{client::Context, model::id::GuildId};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -69,10 +67,11 @@ pub mod cns {
} }
pub async fn get_wolves(ctx: &Context) { pub async fn get_wolves(ctx: &Context) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
@ -97,6 +96,7 @@ pub mod cns {
let existing = existing_tmp.iter().map(|data| (data.id_wolves, data)).collect::<BTreeMap<_, _>>(); let existing = existing_tmp.iter().map(|data| (data.id_wolves, data)).collect::<BTreeMap<_, _>>();
// list of users that need to be updated for this server // list of users that need to be updated for this server
let mut user_to_update = vec![];
let mut server_name_tmp = None; let mut server_name_tmp = None;
for user in wolves.get_members(wolves_api).await { for user in wolves.get_members(wolves_api).await {
// dbg!(&user.committee); // dbg!(&user.committee);
@ -115,6 +115,10 @@ pub mod cns {
add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await; add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await;
if old.expiry != user.expiry { if old.expiry != user.expiry {
add_users_server_members(&db, server, &user).await; add_users_server_members(&db, server, &user).await;
if let Some(discord_id) = old.discord {
user_to_update.push(discord_id);
}
} }
} }
} }
@ -125,6 +129,9 @@ pub mod cns {
set_server_member(&db, server, cs_id).await; set_server_member(&db, server, cs_id).await;
} }
} }
if !user_to_update.is_empty() {
update_server(ctx, &server_config, &[], &user_to_update).await;
}
} }
} }
@ -144,7 +151,7 @@ pub mod cns {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to set server name {}", server.get()); println!("Failure to set server name {}", server.get());
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -183,7 +190,7 @@ pub mod cns {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into ServerMembers {} {:?}", server.get(), user); println!("Failure to insert into ServerMembers {} {:?}", server.get(), user);
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }
@ -193,7 +200,8 @@ pub mod cns {
Get and store the data on C&S committees Get and store the data on C&S committees
*/ */
pub mod committees { pub mod committees {
use crate::{common::database::DataBase, Config}; use crate::common::database::DataBase;
use crate::Config;
use serenity::client::Context; use serenity::client::Context;
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
@ -223,10 +231,11 @@ pub mod committees {
} }
pub async fn get_cns(ctx: &Context) { pub async fn get_cns(ctx: &Context) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
@ -261,8 +270,8 @@ pub mod committees {
{ {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("Failure to insert into Committees {committee:?}"); println!("Failure to insert into Committees {:?}", committee);
println!("{e:?}"); println!("{:?}", e);
} }
} }
} }

View file

@ -3,10 +3,8 @@ pub mod common;
use chrono::{Datelike, SecondsFormat, Utc}; use chrono::{Datelike, SecondsFormat, Utc};
use dotenvy::dotenv; use dotenvy::dotenv;
use rand::{distr::Alphanumeric, rng, Rng}; use rand::{distr::Alphanumeric, rng, Rng};
use serenity::{ use serenity::model::id::{ChannelId, GuildId, RoleId};
model::id::{ChannelId, GuildId, RoleId}, use serenity::prelude::TypeMapKey;
prelude::TypeMapKey,
};
use std::{env, sync::Arc}; use std::{env, sync::Arc};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -34,10 +32,7 @@ pub struct Config {
// discord server for committee // discord server for committee
pub committee_server: GuildId, pub committee_server: GuildId,
pub committee_role: RoleId, pub committee_role: RoleId,
pub committee_category: Vec<ChannelId>, pub committee_category: ChannelId,
// items pertaining to CompSoc only
pub compsoc_server: GuildId,
} }
impl TypeMapKey for Config { impl TypeMapKey for Config {
type Value = Arc<RwLock<Config>>; type Value = Arc<RwLock<Config>>;
@ -62,8 +57,7 @@ pub fn get_config() -> Config {
wolves_api: "".to_string(), wolves_api: "".to_string(),
committee_server: GuildId::new(1), committee_server: GuildId::new(1),
committee_role: RoleId::new(1), committee_role: RoleId::new(1),
committee_category: vec![], committee_category: ChannelId::new(1),
compsoc_server: GuildId::new(1),
}; };
if let Ok(x) = env::var("DATABASE_HOME") { if let Ok(x) = env::var("DATABASE_HOME") {
@ -105,22 +99,14 @@ pub fn get_config() -> Config {
config.committee_server = GuildId::new(x); config.committee_server = GuildId::new(x);
} }
} }
if let Ok(x) = env::var("COMMITTEE_ROLE") { if let Ok(x) = env::var("COMMITTEE_DISCORD") {
if let Ok(x) = x.trim().parse::<u64>() { if let Ok(x) = x.trim().parse::<u64>() {
config.committee_role = RoleId::new(x); config.committee_role = RoleId::new(x);
} }
} }
if let Ok(x) = env::var("COMMITTEE_CATEGORY") { if let Ok(x) = env::var("COMMITTEE_CATEGORY") {
for part in x.split(',') {
if let Ok(x) = part.trim().parse::<u64>() {
config.committee_category.push(ChannelId::new(x));
}
}
}
if let Ok(x) = env::var("COMPSOC_DISCORD") {
if let Ok(x) = x.trim().parse::<u64>() { if let Ok(x) = x.trim().parse::<u64>() {
config.compsoc_server = GuildId::new(x); config.committee_category = ChannelId::new(x);
} }
} }

View file

@ -1,28 +1,21 @@
pub mod commands; pub mod commands;
use crate::commands::role_adder::tools::on_role_change; use crate::commands::role_adder::tools::on_role_change;
use serenity::all::{ActivityData, Command, CreateMessage, EditInteractionResponse, GuildId, GuildMemberUpdateEvent, Interaction};
use serenity::model::guild::Member;
use serenity::{ use serenity::{
all::{Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, Interaction},
async_trait, async_trait,
client::{Context, EventHandler}, client::{Context, EventHandler},
gateway::{ActivityData, ChunkGuildFilter},
model::{ model::{
event::GuildMemberUpdateEvent,
gateway::{GatewayIntents, Ready}, gateway::{GatewayIntents, Ready},
guild::Member,
id::GuildId,
user::OnlineStatus, user::OnlineStatus,
}, },
Client, Client,
}; };
use skynet_discord_bot::{ use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_member, DataBase};
common::{ use skynet_discord_bot::common::set_roles::committee::update_committees;
database::{db_init, get_server_config, get_server_member, DataBase}, use skynet_discord_bot::common::wolves::committees::Committees;
set_roles::committee::update_committees, use skynet_discord_bot::{get_config, Config};
wolves::committees::Committees,
},
get_config, Config,
};
use sqlx::{Pool, Sqlite}; use sqlx::{Pool, Sqlite};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -31,21 +24,15 @@ struct Handler;
#[async_trait] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
// this caches members of all servers teh bot is in
async fn cache_ready(&self, ctx: Context, guilds: Vec<GuildId>) {
for guild in guilds {
ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None);
}
println!("Cache built successfully!");
}
// handles previously linked accounts joining the server // handles previously linked accounts joining the server
async fn guild_member_addition(&self, ctx: Context, new_member: Member) { async fn guild_member_addition(&self, ctx: Context, new_member: Member) {
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
}; };
let db = db_lock.read().await;
let config_lock = { let config_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone() data_read.get::<Config>().expect("Expected Config in TypeMap.").clone()
@ -53,7 +40,7 @@ impl EventHandler for Handler {
let config_global = config_lock.read().await; let config_global = config_lock.read().await;
// committee server takes priority // committee server takes priority
let committee_server = config_global.committee_server; let committee_server = GuildId::new(1220150752656363520);
if new_member.guild_id.get() == committee_server.get() { if new_member.guild_id.get() == committee_server.get() {
let mut member = vec![new_member.clone()]; let mut member = vec![new_member.clone()];
update_committees(&db, &ctx, &config_global, &mut member).await; update_committees(&db, &ctx, &config_global, &mut member).await;
@ -79,7 +66,7 @@ impl EventHandler for Handler {
} }
if let Err(e) = new_member.add_roles(&ctx, &roles).await { if let Err(e) = new_member.add_roles(&ctx, &roles).await {
println!("{e:?}"); println!("{:?}", e);
} }
} else { } else {
let tmp = get_committee(&db, config_server.wolves_id).await; let tmp = get_committee(&db, config_server.wolves_id).await;
@ -107,12 +94,14 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
// handles role updates // handles role updates
async fn guild_member_update(&self, ctx: Context, _old_data: Option<Member>, new_data: Option<Member>, _: GuildMemberUpdateEvent) { async fn guild_member_update(&self, ctx: Context, _old_data: Option<Member>, new_data: Option<Member>, _: GuildMemberUpdateEvent) {
// get config/db // get config/db
let db = { let db_lock = {
let data_read = ctx.data.read().await; let data_read = ctx.data.read().await;
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone() data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
}; };
// check if the role changed is part of the ones for this server let db = db_lock.read().await;
// check if the role changed is part of the oens for this server
if let Some(x) = new_data { if let Some(x) = new_data {
on_role_change(&db, &ctx, x).await; on_role_change(&db, &ctx, x).await;
} }
@ -122,12 +111,6 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
println!("[Main] {} is connected!", ready.user.name); println!("[Main] {} is connected!", ready.user.name);
ctx.set_presence(Some(ActivityData::playing("with humanity's fate")), OnlineStatus::Online); 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::<Config>().expect("Expected Config in TypeMap.").clone()
};
let config = config_lock.read().await;
match Command::set_global_commands( match Command::set_global_commands(
&ctx.http, &ctx.http,
vec![ vec![
@ -142,34 +125,27 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
{ {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}") println!("{:?}", e)
} }
} }
// Inter-Committee server match GuildId::new(1220150752656363520)
match config.committee_server.set_commands(&ctx.http, vec![commands::count::committee::register()]).await { .set_commands(&ctx.http, vec![commands::count::committee::register()])
Ok(_) => {}
Err(e) => {
println!("{e:?}")
}
}
// CompSoc Server
match config
.compsoc_server
.set_commands(
&ctx.http,
vec![
// commands just for the CompSoc server
commands::count::servers::register(),
commands::server_icon::user::register(),
],
)
.await .await
{ {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
println!("{e:?}") println!("{:?}", e)
}
}
match GuildId::new(689189992417067052)
.set_commands(&ctx.http, vec![commands::count::servers::register()])
.await
{
Ok(_) => {}
Err(e) => {
println!("{:?}", e)
} }
} }
} }
@ -177,7 +153,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
async fn interaction_create(&self, ctx: Context, interaction: Interaction) { async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
if let Interaction::Command(command) = interaction { if let Interaction::Command(command) = interaction {
let _ = command.defer_ephemeral(&ctx.http).await; 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() { let content = match command.data.name.as_str() {
// user commands // user commands
@ -188,7 +164,6 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
"verify" => commands::wolves::verify::run(&command, &ctx).await, "verify" => commands::wolves::verify::run(&command, &ctx).await,
"unlink" => commands::wolves::unlink::run(&command, &ctx).await, "unlink" => commands::wolves::unlink::run(&command, &ctx).await,
"link_minecraft" => commands::minecraft::user::add::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, // "link" => commands::count::servers::run(&command, &ctx).await,
&_ => format!("not implemented :( wolves {}", x.name.as_str()), &_ => format!("not implemented :( wolves {}", x.name.as_str()),
}, },
@ -200,19 +175,6 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
Some(x) => match x.name.as_str() { Some(x) => match x.name.as_str() {
"add" => commands::add_server::run(&command, &ctx).await, "add" => commands::add_server::run(&command, &ctx).await,
"roles_adder" => commands::role_adder::edit::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())
}
},
// TODO: move teh minecraft commands in here as a subgroup
// "link" => commands::count::servers::run(&command, &ctx).await, // "link" => commands::count::servers::run(&command, &ctx).await,
&_ => format!("not implemented :( committee {}", x.name.as_str()), &_ => format!("not implemented :( committee {}", x.name.as_str()),
}, },
@ -229,34 +191,11 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
&_ => format!("not implemented :( count {}", x.name.as_str()), &_ => 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()), _ => format!("not implemented :( {}", command.data.name.as_str()),
}; };
if let Err(why) = command.edit_response(&ctx.http, EditInteractionResponse::new().content(content)).await { 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);
} }
} }
} }
@ -291,8 +230,7 @@ async fn main() {
let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS; let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS;
// Build our client. // Build our client.
let mut client = Client::builder(&config.discord_token, intents) let mut client = Client::builder(&config.discord_token, intents)
.event_handler(Handler) .event_handler(Handler {})
.cache_settings(serenity::cache::Settings::default())
.await .await
.expect("Error creating client"); .expect("Error creating client");
@ -300,7 +238,7 @@ async fn main() {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<Config>(Arc::new(RwLock::new(config))); data.insert::<Config>(Arc::new(RwLock::new(config)));
data.insert::<DataBase>(Arc::new(db)); data.insert::<DataBase>(Arc::new(RwLock::new(db)));
} }
// Finally, start a single shard, and start listening to events. // Finally, start a single shard, and start listening to events.
@ -308,6 +246,6 @@ async fn main() {
// Shards will automatically attempt to reconnect, and will perform // Shards will automatically attempt to reconnect, and will perform
// exponential backoff until it reconnects. // exponential backoff until it reconnects.
if let Err(why) = client.start().await { if let Err(why) = client.start().await {
println!("Client error: {why:?}"); println!("Client error: {:?}", why);
} }
} }