Compare commits
74 commits
Author | SHA1 | Date | |
---|---|---|---|
6353d77360 | |||
|
062f826d28 | ||
d8f785b0db | |||
|
d70a037057 | ||
|
7e90f45196 | ||
7526a82bb7 | |||
3149a5f99f | |||
061b73378a | |||
a225c14b4f | |||
095ff6f2ce | |||
18fd45d39b | |||
854e946a8f | |||
d0726169ee | |||
9d409e3692 | |||
57d4947edf | |||
6d08312f48 | |||
bd9d0cd43f | |||
1af7f28a45 | |||
feff293043 | |||
227db8a741 | |||
13eb230754 | |||
eb88216740 | |||
96eb81293b | |||
5815cde38b | |||
1729ec0a54 | |||
43ef787d59 | |||
a8bed0bacc | |||
3dd81a5c54 | |||
04aa0e63d4 | |||
2b2dfc2531 | |||
e901f3ed74 | |||
3abbb8d485 | |||
b8ffd42184 | |||
764e8cd620 | |||
76f8aa2712 | |||
c4da3e9109 | |||
d27befdac6 | |||
7403f531eb | |||
1dc5c105df | |||
3a56d7bba5 | |||
327ff99b69 | |||
dedf8c3644 | |||
a6eff75e39 | |||
72226cc59b | |||
f841039c53 | |||
87dd04e12f | |||
9134feee4e | |||
652dd6ff1c | |||
cae383a186 | |||
b4cadffdb5 | |||
721c8246ac | |||
0f4524ea63 | |||
86f54aec6d | |||
86a3af2a65 | |||
6d5ad8e418 | |||
9d50efb757 | |||
51bc2f177f | |||
3523dac46e | |||
51d5904ffd | |||
1555a94656 | |||
7bcf30fb3a | |||
4f96c9087f | |||
1ff993d236 | |||
acdfe4b423 | |||
537fdfd40c | |||
ffd6e40d0b | |||
a7423959dc | |||
b4f6835704 | |||
0034bd34d6 | |||
725bfa41cc | |||
fcfcfb8409 | |||
e449204863 | |||
f1dbbec32d | |||
8560ed6de5 |
36 changed files with 2233 additions and 335 deletions
10
.forgejo/workflows/check_lfs.yaml
Normal file
10
.forgejo/workflows/check_lfs.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
on:
|
||||
- push
|
||||
- workflow_dispatch
|
||||
|
||||
jobs:
|
||||
check_lfs:
|
||||
# nix/docker
|
||||
runs-on: nix
|
||||
steps:
|
||||
- uses: https://github.com/MPLew-is/lfs-check-action@1
|
51
.forgejo/workflows/on_pr.yaml
Normal file
51
.forgejo/workflows/on_pr.yaml
Normal file
|
@ -0,0 +1,51 @@
|
|||
on:
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
check_lfs:
|
||||
# nix/docker
|
||||
runs-on: nix
|
||||
steps:
|
||||
- uses: https://github.com/MPLew-is/lfs-check-action@1
|
||||
|
||||
# rust code must be formatted for standardisation
|
||||
lint_fmt:
|
||||
# build it using teh base nixos system, helps with caching
|
||||
runs-on: nix
|
||||
steps:
|
||||
# get the repo first
|
||||
- uses: https://code.forgejo.org/actions/checkout@v4
|
||||
- uses: https://forgejo.skynet.ie/Skynet/actions/get_lfs/nix@v8
|
||||
with:
|
||||
server_url: ${{ gitea.server_url }}
|
||||
repository: ${{ gitea.repository }}
|
||||
- run: nix build .#fmt --verbose
|
||||
|
||||
# clippy is incredibly useful for making yer code better
|
||||
lint_clippy:
|
||||
# build it using teh base nixos system, helps with caching
|
||||
runs-on: nix
|
||||
permissions:
|
||||
checks: write
|
||||
steps:
|
||||
# get the repo first
|
||||
- uses: https://code.forgejo.org/actions/checkout@v4
|
||||
- uses: https://forgejo.skynet.ie/Skynet/actions/get_lfs/nix@v8
|
||||
with:
|
||||
server_url: ${{ gitea.server_url }}
|
||||
repository: ${{ gitea.repository }}
|
||||
- run: nix build .#clippy --verbose
|
||||
|
||||
build:
|
||||
# build it using teh base nixos system, helps with caching
|
||||
runs-on: nix
|
||||
needs: [ lint_fmt, lint_clippy ]
|
||||
steps:
|
||||
# get the repo first
|
||||
- uses: https://code.forgejo.org/actions/checkout@v4
|
||||
- uses: https://forgejo.skynet.ie/Skynet/actions/get_lfs/nix@v8
|
||||
with:
|
||||
server_url: ${{ gitea.server_url }}
|
||||
repository: ${{ gitea.repository }}
|
||||
- name: "Build it locally"
|
||||
run: nix build --verbose
|
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Fix typos
|
||||
7e90f451965b0edbd331765ad295a02f31d2bf24
|
|
@ -7,3 +7,4 @@ fn_params_layout = "Compressed"
|
|||
struct_lit_width = 0
|
||||
tab_spaces = 2
|
||||
use_small_heuristics = "Max"
|
||||
imports_granularity = "Crate"
|
6
.server-icons.toml
Normal file
6
.server-icons.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
# this file controls the
|
||||
|
||||
[source]
|
||||
repo = "https://forgejo.skynet.ie/Computer_Society/open-goverance"
|
||||
directory = "Resources/Logo_Variants"
|
||||
file = "_festivals.toml"
|
2
.taplo.toml
Normal file
2
.taplo.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[formatting]
|
||||
column_width = 120
|
555
Cargo.lock
generated
555
Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
|
@ -110,6 +110,12 @@ version = "1.0.93"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
|
@ -383,6 +389,12 @@ version = "3.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
|
@ -449,6 +461,39 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-eyre"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"color-spantrace",
|
||||
"eyre",
|
||||
"indenter",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-spantrace"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-core",
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
|
@ -637,6 +682,12 @@ version = "2.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
|
||||
|
||||
[[package]]
|
||||
name = "data-url"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5"
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.9"
|
||||
|
@ -790,6 +841,16 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
|
||||
dependencies = [
|
||||
"indenter",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
|
@ -805,6 +866,15 @@ version = "2.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.33"
|
||||
|
@ -815,6 +885,12 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.9.2"
|
||||
|
@ -849,6 +925,27 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
||||
|
||||
[[package]]
|
||||
name = "fontconfig-parser"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646"
|
||||
dependencies = [
|
||||
"roxmltree 0.20.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fontdb"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff20bef7942a72af07104346154a70a70b089c572e454b41bef6eb6cb10e9c06"
|
||||
dependencies = [
|
||||
"fontconfig-parser",
|
||||
"log",
|
||||
"memmap2",
|
||||
"ttf-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
|
@ -1063,6 +1160,16 @@ dependencies = [
|
|||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.0"
|
||||
|
@ -1586,16 +1693,6 @@ dependencies = [
|
|||
"syn 2.0.89",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
|
@ -1617,6 +1714,18 @@ dependencies = [
|
|||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imagesize"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b72ad49b554c1728b1e83254a1b1565aea4161e28dabbfa171fc15fe62299caf"
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.5.0"
|
||||
|
@ -1677,6 +1786,12 @@ version = "1.0.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.70"
|
||||
|
@ -1686,6 +1801,24 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kurbo"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kurbo"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kv-log-macro"
|
||||
version = "1.0.7"
|
||||
|
@ -1718,7 +1851,7 @@ dependencies = [
|
|||
"futures-util",
|
||||
"hostname",
|
||||
"httpdate",
|
||||
"idna 1.0.3",
|
||||
"idna",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"nom",
|
||||
|
@ -1843,6 +1976,15 @@ version = "2.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
|
@ -1872,6 +2014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2031,6 +2174,12 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "4.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec"
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.1"
|
||||
|
@ -2075,6 +2224,12 @@ version = "2.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.7"
|
||||
|
@ -2145,6 +2300,19 @@ version = "0.3.30"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "3.7.4"
|
||||
|
@ -2339,6 +2507,12 @@ dependencies = [
|
|||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rctree"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.4"
|
||||
|
@ -2435,6 +2609,34 @@ dependencies = [
|
|||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resvg"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76888219c0881e22b0ceab06fddcfe83163cd81642bd60c7842387f9c968a72e"
|
||||
dependencies = [
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"log",
|
||||
"pico-args",
|
||||
"png",
|
||||
"rgb",
|
||||
"svgfilters",
|
||||
"svgtypes 0.10.0",
|
||||
"tiny-skia",
|
||||
"usvg",
|
||||
"usvg-text-layout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.8"
|
||||
|
@ -2450,6 +2652,34 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosvgtree"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdc23d1ace03d6b8153c7d16f0708cd80b61ee8e80304954803354e67e40d150"
|
||||
dependencies = [
|
||||
"log",
|
||||
"roxmltree 0.18.1",
|
||||
"simplecss",
|
||||
"siphasher",
|
||||
"svgtypes 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302"
|
||||
dependencies = [
|
||||
"xmlparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.6"
|
||||
|
@ -2582,6 +2812,22 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustybuzz"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162bdf42e261bee271b3957691018634488084ef577dddeb6420a9684cab2a6a"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bytemuck",
|
||||
"smallvec",
|
||||
"ttf-parser",
|
||||
"unicode-bidi-mirroring",
|
||||
"unicode-ccc",
|
||||
"unicode-general-category",
|
||||
"unicode-script",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
|
@ -2713,6 +2959,15 @@ dependencies = [
|
|||
"thiserror 1.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
|
@ -2806,6 +3061,15 @@ dependencies = [
|
|||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
@ -2831,21 +3095,49 @@ dependencies = [
|
|||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "simplecss"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "skynet_discord_bot"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"color-eyre",
|
||||
"dotenvy",
|
||||
"eyre",
|
||||
"lettre",
|
||||
"maud",
|
||||
"rand 0.9.0",
|
||||
"resvg",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serenity",
|
||||
"sqlx",
|
||||
"surf",
|
||||
"tiny-skia",
|
||||
"tokio",
|
||||
"toml",
|
||||
"usvg",
|
||||
"usvg-text-layout",
|
||||
"wolves_oxidised",
|
||||
]
|
||||
|
||||
|
@ -3180,6 +3472,15 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||
|
||||
[[package]]
|
||||
name = "strict-num"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||
dependencies = [
|
||||
"float-cmp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.5"
|
||||
|
@ -3220,6 +3521,36 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "svgfilters"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "639abcebc15fdc2df179f37d6f5463d660c1c79cd552c12343a4600827a04bce"
|
||||
dependencies = [
|
||||
"float-cmp",
|
||||
"rgb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "svgtypes"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9ee29c1407a5b18ccfe5f6ac82ac11bab3b14407e09c209a6c1a32098b19734"
|
||||
dependencies = [
|
||||
"kurbo 0.8.3",
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "svgtypes"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98ffacedcdcf1da6579c907279b4f3c5492fbce99fbbf227f5ed270a589c2765"
|
||||
dependencies = [
|
||||
"kurbo 0.9.5",
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
@ -3363,6 +3694,16 @@ dependencies = [
|
|||
"syn 2.0.89",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.2.27"
|
||||
|
@ -3432,6 +3773,31 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-skia"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"png",
|
||||
"tiny-skia-path",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-skia-path"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"bytemuck",
|
||||
"strict-num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
|
@ -3581,6 +3947,47 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_write",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
|
@ -3617,6 +4024,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-error"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3629,12 +4047,29 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
dependencies = [
|
||||
"sharded-slab",
|
||||
"thread_local",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.21.0"
|
||||
|
@ -3683,6 +4118,24 @@ version = "0.3.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi-mirroring"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ccc"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-general-category"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
|
@ -3704,6 +4157,18 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-script"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-vo"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.4.0"
|
||||
|
@ -3722,16 +4187,49 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
|||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.2"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna 0.5.0",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "usvg"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b6bb4e62619d9f68aa2d8a823fea2bff302340a1f2d45c264d5b0be170832e"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"data-url",
|
||||
"flate2",
|
||||
"imagesize",
|
||||
"kurbo 0.9.5",
|
||||
"log",
|
||||
"rctree",
|
||||
"rosvgtree",
|
||||
"strict-num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "usvg-text-layout"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "195386e01bc35f860db024de275a76e7a31afdf975d18beb6d0e44764118b4db"
|
||||
dependencies = [
|
||||
"fontdb",
|
||||
"kurbo 0.9.5",
|
||||
"log",
|
||||
"rustybuzz",
|
||||
"unicode-bidi",
|
||||
"unicode-script",
|
||||
"unicode-vo",
|
||||
"usvg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
|
@ -3750,6 +4248,12 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.10.0"
|
||||
|
@ -3915,6 +4419,12 @@ dependencies = [
|
|||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.5.2"
|
||||
|
@ -4144,6 +4654,15 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
|
@ -4186,6 +4705,12 @@ version = "0.5.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "xmlparser"
|
||||
version = "0.13.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.5"
|
||||
|
|
21
Cargo.toml
21
Cargo.toml
|
@ -4,19 +4,21 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[[bin]]
|
||||
name = "update_data"
|
||||
|
||||
[[bin]]
|
||||
name = "update_users"
|
||||
|
||||
[[bin]]
|
||||
name = "update_committee"
|
||||
|
||||
[[bin]]
|
||||
name = "update_minecraft"
|
||||
|
||||
[[bin]]
|
||||
name = "update_server-icon"
|
||||
|
||||
[[bin]]
|
||||
name = "cleanup_committee"
|
||||
|
||||
[dependencies]
|
||||
# discord library
|
||||
serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] }
|
||||
|
@ -32,7 +34,7 @@ surf = "2.3"
|
|||
dotenvy = "0.15"
|
||||
|
||||
# For sqlite
|
||||
sqlx = { version = "0.8", features = [ "runtime-tokio", "sqlite", "migrate" ] }
|
||||
sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite", "migrate"] }
|
||||
serde_json = { version = "1.0", features = ["raw_value"] }
|
||||
|
||||
# create random strings
|
||||
|
@ -45,4 +47,13 @@ chrono = "0.4"
|
|||
lettre = "0.11"
|
||||
maud = "0.27"
|
||||
|
||||
toml = "0.8.23"
|
||||
serde = "1.0"
|
||||
|
||||
# for image conversion
|
||||
eyre = "0.6.8"
|
||||
color-eyre = "0.6.2"
|
||||
usvg-text-layout = "0.29.0"
|
||||
usvg = "0.29.0"
|
||||
resvg = "0.29.0"
|
||||
tiny-skia = "0.8.3"
|
|
@ -1,7 +1,7 @@
|
|||
# Skynet Discord Bot
|
||||
The Skynet bot is designed to manage users on Discord.
|
||||
It allows users to link their UL Wolves account with Wolves in a GDPR compliant manner.
|
||||
Skynet (bot) is hosted is hosted by the Computer Society on Skynet (computer cluster).
|
||||
Skynet (bot) is hosted by the Computer Society on Skynet (computer cluster).
|
||||
|
||||
## Documentation
|
||||
We have split up the documentation into different segments depending on who the user is.
|
||||
|
|
9
db/migrations/11_server-icons.sql
Normal file
9
db/migrations/11_server-icons.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
CREATE TABLE IF NOT EXISTS server_icons (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
path TEXT NOT NULL,
|
||||
date TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS index_name ON server_icons (name);
|
17
flake.lock
generated
17
flake.lock
generated
|
@ -32,6 +32,22 @@
|
|||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs-mozilla": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1744624473,
|
||||
"narHash": "sha256-S6zT/w5SyAkJ//dYdjbrXgm+6Vkd/k7qqUl4WgZ6jjk=",
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"rev": "2292d4b35aa854e312ad2e95c4bb5c293656f21a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1722995383,
|
||||
|
@ -51,6 +67,7 @@
|
|||
"inputs": {
|
||||
"naersk": "naersk",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-mozilla": "nixpkgs-mozilla",
|
||||
"utils": "utils"
|
||||
}
|
||||
},
|
||||
|
|
47
flake.nix
47
flake.nix
|
@ -4,6 +4,10 @@
|
|||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||
naersk.url = "github:nix-community/naersk";
|
||||
nixpkgs-mozilla = {
|
||||
url = "github:mozilla/nixpkgs-mozilla";
|
||||
flake = false;
|
||||
};
|
||||
utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
|
@ -17,16 +21,33 @@
|
|||
nixpkgs,
|
||||
utils,
|
||||
naersk,
|
||||
nixpkgs-mozilla,
|
||||
}:
|
||||
utils.lib.eachDefaultSystem (
|
||||
system: let
|
||||
overrides = (builtins.fromTOML (builtins.readFile ./rust-toolchain.toml));
|
||||
pkgs = (import nixpkgs) {inherit system;};
|
||||
naersk' = pkgs.callPackage naersk {};
|
||||
pkgs = (import nixpkgs) {
|
||||
inherit system;
|
||||
|
||||
overlays = [
|
||||
(import nixpkgs-mozilla)
|
||||
];
|
||||
};
|
||||
toolchain = (pkgs.rustChannelOf {
|
||||
rustToolchain = ./rust-toolchain.toml;
|
||||
sha256 = "sha256-KUm16pHj+cRedf8vxs/Hd2YWxpOrWZ7UOrwhILdSJBU=";
|
||||
}).rust;
|
||||
|
||||
naersk' = pkgs.callPackage naersk {
|
||||
cargo = toolchain;
|
||||
rustc = toolchain;
|
||||
};
|
||||
package_name = "skynet_discord_bot";
|
||||
desc = "Skynet Discord Bot";
|
||||
buildInputs = with pkgs; [
|
||||
openssl
|
||||
glib
|
||||
gdk-pixbuf
|
||||
pkg-config
|
||||
rustfmt
|
||||
];
|
||||
|
@ -37,6 +58,10 @@
|
|||
pname = "${package_name}";
|
||||
src = ./.;
|
||||
buildInputs = buildInputs;
|
||||
postInstall = ''
|
||||
mkdir $out/config
|
||||
cp .server-icons.toml $out/config
|
||||
'';
|
||||
};
|
||||
# Run `nix build .#fmt` to run tests
|
||||
fmt = naersk'.buildPackage {
|
||||
|
@ -63,9 +88,9 @@
|
|||
|
||||
# `nix develop`
|
||||
devShell = pkgs.mkShell {
|
||||
nativeBuildInputs = with pkgs; [rustup rustPlatform.bindgenHook pkg-config openssl];
|
||||
nativeBuildInputs = with pkgs; [rustup rustPlatform.bindgenHook];
|
||||
# libraries here
|
||||
buildInputs = [ ];
|
||||
buildInputs = buildInputs;
|
||||
RUSTC_VERSION = overrides.toolchain.channel;
|
||||
# https://github.com/rust-lang/rust-bindgen#environment-variables
|
||||
shellHook = ''
|
||||
|
@ -97,12 +122,14 @@
|
|||
wantedBy = [];
|
||||
after = ["network-online.target"];
|
||||
environment = environment_config;
|
||||
|
||||
path = with pkgs; [ git git-lfs ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "${cfg.user}";
|
||||
Group = "${cfg.user}";
|
||||
ExecStart = "${self.defaultPackage."${system}"}/bin/${script}";
|
||||
# kill each service if its ran for 9 min
|
||||
TimeoutStartSec=540;
|
||||
EnvironmentFile = [
|
||||
"${cfg.env.discord}"
|
||||
"${cfg.env.mail}"
|
||||
|
@ -127,14 +154,17 @@
|
|||
|
||||
# modify these
|
||||
scripts = {
|
||||
# every 20 min
|
||||
# every 10 min
|
||||
"update_data" = "*:0,10,20,30,40,50";
|
||||
# groups are updated every hour, offset from teh ldap
|
||||
"update_users" = "*:05:00";
|
||||
"update_users" = "*:5,15,25,35,45,55";
|
||||
# Committee server has its own timer
|
||||
"update_committee" = "*:15:00";
|
||||
"update_committee" = "*:5,15,25,35,45,55";
|
||||
# minecraft stuff is updated at 5am
|
||||
# this service does not depend on teh discord cache
|
||||
"update_minecraft" = "5:10:00";
|
||||
# server icon gets updated daily at midnight
|
||||
"update_server-icon" = "0:01:00";
|
||||
};
|
||||
in {
|
||||
options.services."${package_name}" = {
|
||||
|
@ -194,6 +224,7 @@
|
|||
after = ["network-online.target"];
|
||||
wants = [];
|
||||
environment = environment_config;
|
||||
path = with pkgs; [ git git-lfs ];
|
||||
|
||||
serviceConfig = {
|
||||
User = "${cfg.user}";
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[toolchain]
|
||||
channel = "1.80"
|
||||
channel = "1.87"
|
||||
|
|
137
src/bin/cleanup_committee.rs
Normal file
137
src/bin/cleanup_committee.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use serenity::{
|
||||
all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
|
||||
async_trait,
|
||||
client::{Context, EventHandler},
|
||||
model::gateway::GatewayIntents,
|
||||
Client,
|
||||
};
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::{db_init, DataBase},
|
||||
set_roles::committee::db_roles_get,
|
||||
},
|
||||
get_config, Config,
|
||||
};
|
||||
use sqlx::{Pool, Sqlite};
|
||||
use std::{process, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
/// Cleanup teh Committee server
|
||||
///
|
||||
/// This removes any invalid roles/channels which have been set up accidentally
|
||||
/// DO NOT run this locally unless you have a fresh copy of the live database handy.
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let config = get_config();
|
||||
let db = match db_init(&config).await {
|
||||
Ok(x) => x,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
// Intents are a bitflag, bitwise operations can be used to dictate which intents to use
|
||||
let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS;
|
||||
// Build our client.
|
||||
let mut client = Client::builder(&config.discord_token, intents)
|
||||
.event_handler(Handler {})
|
||||
.cache_settings(serenity::cache::Settings::default())
|
||||
.await
|
||||
.expect("Error creating client");
|
||||
|
||||
{
|
||||
let mut data = client.data.write().await;
|
||||
|
||||
data.insert::<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,17 @@
|
|||
use serenity::{
|
||||
all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
|
||||
async_trait,
|
||||
client::{Context, EventHandler},
|
||||
model::gateway::{GatewayIntents, Ready},
|
||||
model::gateway::GatewayIntents,
|
||||
Client,
|
||||
};
|
||||
use skynet_discord_bot::common::database::{db_init, DataBase};
|
||||
use skynet_discord_bot::common::set_roles::committee;
|
||||
use skynet_discord_bot::{get_config, Config};
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::{db_init, DataBase},
|
||||
set_roles::committee,
|
||||
},
|
||||
get_config, Config,
|
||||
};
|
||||
use std::{process, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
|
@ -23,6 +28,7 @@ async fn main() {
|
|||
// Build our client.
|
||||
let mut client = Client::builder(&config.discord_token, intents)
|
||||
.event_handler(Handler {})
|
||||
.cache_settings(serenity::cache::Settings::default())
|
||||
.await
|
||||
.expect("Error creating client");
|
||||
|
||||
|
@ -30,25 +36,39 @@ async fn main() {
|
|||
let mut data = client.data.write().await;
|
||||
|
||||
data.insert::<Config>(Arc::new(RwLock::new(config)));
|
||||
data.insert::<DataBase>(Arc::new(RwLock::new(db)));
|
||||
data.insert::<DataBase>(Arc::new(db));
|
||||
}
|
||||
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Client error: {:?}", why);
|
||||
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);
|
||||
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!");
|
||||
// u[date committee server
|
||||
committee::check_committee(Arc::clone(&ctx)).await;
|
||||
committee::check_committee(&ctx).await;
|
||||
|
||||
// finish up
|
||||
process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,13 @@ use serenity::{
|
|||
model::gateway::{GatewayIntents, Ready},
|
||||
Client,
|
||||
};
|
||||
use skynet_discord_bot::common::database::{db_init, DataBase};
|
||||
use skynet_discord_bot::common::wolves::cns::get_wolves;
|
||||
use skynet_discord_bot::common::wolves::committees::get_cns;
|
||||
use skynet_discord_bot::{get_config, Config};
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::{db_init, DataBase},
|
||||
wolves::{cns::get_wolves, committees::get_cns},
|
||||
},
|
||||
get_config, Config,
|
||||
};
|
||||
use std::{process, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
|
@ -27,6 +30,7 @@ async fn main() {
|
|||
// Build our client.
|
||||
let mut client = Client::builder(&config.discord_token, intents)
|
||||
.event_handler(Handler {})
|
||||
.cache_settings(serenity::cache::Settings::default())
|
||||
.await
|
||||
.expect("Error creating client");
|
||||
|
||||
|
@ -34,11 +38,11 @@ async fn main() {
|
|||
let mut data = client.data.write().await;
|
||||
|
||||
data.insert::<Config>(Arc::new(RwLock::new(config)));
|
||||
data.insert::<DataBase>(Arc::new(RwLock::new(db)));
|
||||
data.insert::<DataBase>(Arc::new(db));
|
||||
}
|
||||
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Client error: {:?}", why);
|
||||
println!("Client error: {why:?}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use skynet_discord_bot::common::database::db_init;
|
||||
use skynet_discord_bot::common::minecraft::{get_minecraft_config, update_server, whitelist_wipe};
|
||||
use skynet_discord_bot::get_config;
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::db_init,
|
||||
minecraft::{get_minecraft_config, update_server, whitelist_wipe},
|
||||
},
|
||||
get_config,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[tokio::main]
|
||||
|
|
71
src/bin/update_server-icon.rs
Normal file
71
src/bin/update_server-icon.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use serenity::{
|
||||
async_trait,
|
||||
client::{Context, EventHandler},
|
||||
model::gateway::{GatewayIntents, Ready},
|
||||
Client,
|
||||
};
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::{db_init, DataBase},
|
||||
server_icon::{get_config_icons, update_icon},
|
||||
},
|
||||
get_config, Config,
|
||||
};
|
||||
use std::{process, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let config = get_config();
|
||||
let db = match db_init(&config).await {
|
||||
Ok(x) => x,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
// Intents are a bitflag, bitwise operations can be used to dictate which intents to use
|
||||
let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS;
|
||||
// Build our client.
|
||||
let mut client = Client::builder(&config.discord_token, intents)
|
||||
.event_handler(Handler {})
|
||||
.cache_settings(serenity::cache::Settings::default())
|
||||
.await
|
||||
.expect("Error creating client");
|
||||
|
||||
{
|
||||
let mut data = client.data.write().await;
|
||||
|
||||
data.insert::<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);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,24 @@
|
|||
use serenity::{
|
||||
all::{ChunkGuildFilter, GuildId, GuildMembersChunkEvent},
|
||||
async_trait,
|
||||
client::{Context, EventHandler},
|
||||
model::gateway::{GatewayIntents, Ready},
|
||||
model::gateway::GatewayIntents,
|
||||
Client,
|
||||
};
|
||||
use skynet_discord_bot::common::database::{db_init, get_server_config_bulk, DataBase};
|
||||
use skynet_discord_bot::common::set_roles::normal;
|
||||
use skynet_discord_bot::{get_config, Config};
|
||||
use std::{process, sync::Arc};
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::{db_init, get_server_config_bulk, DataBase},
|
||||
set_roles::normal,
|
||||
},
|
||||
get_config, Config,
|
||||
};
|
||||
use std::{
|
||||
process,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[tokio::main]
|
||||
|
@ -22,7 +33,11 @@ async fn main() {
|
|||
let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS;
|
||||
// Build our client.
|
||||
let mut client = Client::builder(&config.discord_token, intents)
|
||||
.event_handler(Handler {})
|
||||
.event_handler(Handler {
|
||||
server_count: Default::default(),
|
||||
server_cached: Default::default(),
|
||||
})
|
||||
.cache_settings(serenity::cache::Settings::default())
|
||||
.await
|
||||
.expect("Error creating client");
|
||||
|
||||
|
@ -30,38 +45,51 @@ async fn main() {
|
|||
let mut data = client.data.write().await;
|
||||
|
||||
data.insert::<Config>(Arc::new(RwLock::new(config)));
|
||||
data.insert::<DataBase>(Arc::new(RwLock::new(db)));
|
||||
data.insert::<DataBase>(Arc::new(db));
|
||||
}
|
||||
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Client error: {:?}", why);
|
||||
println!("Client error: {why:?}");
|
||||
}
|
||||
}
|
||||
|
||||
struct Handler;
|
||||
struct Handler {
|
||||
server_count: AtomicUsize,
|
||||
server_cached: AtomicUsize,
|
||||
}
|
||||
#[async_trait]
|
||||
impl EventHandler for Handler {
|
||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||
let ctx = Arc::new(ctx);
|
||||
println!("{} is connected!", ready.user.name);
|
||||
async fn cache_ready(&self, ctx: Context, guilds: Vec<GuildId>) {
|
||||
self.server_count.swap(guilds.len(), Ordering::SeqCst);
|
||||
for guild in guilds {
|
||||
ctx.shard.chunk_guild(guild, Some(2000), false, ChunkGuildFilter::None, None);
|
||||
}
|
||||
println!("Cache loaded {}", &self.server_count.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
async fn guild_members_chunk(&self, ctx: Context, chunk: GuildMembersChunkEvent) {
|
||||
if (chunk.chunk_index + 1) == chunk.chunk_count {
|
||||
self.server_cached.fetch_add(1, Ordering::SeqCst);
|
||||
if (self.server_cached.load(Ordering::SeqCst) + 1) == self.server_count.load(Ordering::SeqCst) {
|
||||
println!("Cache built successfully!");
|
||||
|
||||
// this goes into each server and sets roles for each wolves member
|
||||
check_bulk(Arc::clone(&ctx)).await;
|
||||
check_bulk(&ctx).await;
|
||||
|
||||
// finish up
|
||||
process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_bulk(ctx: Arc<Context>) {
|
||||
let db_lock = {
|
||||
async fn check_bulk(ctx: &Context) {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
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 {
|
||||
normal::update_server(&ctx, &server_config, &[], &[]).await;
|
||||
normal::update_server(ctx, &server_config, &[], &[]).await;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
|
||||
use serenity::client::Context;
|
||||
use skynet_discord_bot::common::database::{get_server_config, DataBase, Servers};
|
||||
use skynet_discord_bot::common::set_roles::normal::update_server;
|
||||
use skynet_discord_bot::common::wolves::cns::get_wolves;
|
||||
use serenity::{
|
||||
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction},
|
||||
client::Context,
|
||||
};
|
||||
use skynet_discord_bot::common::{
|
||||
database::{get_server_config, DataBase, Servers},
|
||||
set_roles::normal::update_server,
|
||||
wolves::cns::get_wolves,
|
||||
};
|
||||
use sqlx::{Error, Pool, Sqlite};
|
||||
|
||||
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
|
||||
|
@ -52,11 +56,10 @@ pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
|
|||
return "Please provide a valid channel for ``Bot Channel``".to_string();
|
||||
};
|
||||
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let server_data = Servers {
|
||||
server: command.guild_id.unwrap_or_default(),
|
||||
|
@ -72,8 +75,8 @@ pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
|
|||
match add_server(&db, ctx, &server_data).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
return format!("Failure to insert into Servers {:?}", server_data);
|
||||
println!("{e:?}");
|
||||
return format!("Failure to insert into Servers {server_data:?}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +101,7 @@ async fn add_server(db: &Pool<Sqlite>, ctx: &Context, server: &Servers) -> Resul
|
|||
.fetch_optional(db)
|
||||
.await;
|
||||
|
||||
// if the entry does not exist already tehn do a user update
|
||||
// if the entry does not exist already then do a user update
|
||||
let (update, current_remove, current_role, past_remove, past_role) = match &existing {
|
||||
None => (true, false, None, false, None),
|
||||
Some(x) => {
|
||||
|
|
|
@ -20,4 +20,8 @@ pub fn register() -> CreateCommand {
|
|||
.add_sub_option(CreateCommandOption::new(CommandOptionType::Role, "role_c", "Sum of A and B").required(true))
|
||||
.add_sub_option(CreateCommandOption::new(CommandOptionType::Boolean, "delete", "Delete this entry.").required(false)),
|
||||
)
|
||||
.add_option(
|
||||
CreateCommandOption::new(CommandOptionType::SubCommandGroup, "icon", "Committee commands for the server icon")
|
||||
.add_sub_option(CreateCommandOption::new(CommandOptionType::SubCommand, "change", "Change the server icon.")),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@ pub mod committee {
|
|||
use serenity::all::{
|
||||
CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption,
|
||||
};
|
||||
use skynet_discord_bot::common::database::DataBase;
|
||||
use skynet_discord_bot::common::set_roles::committee::db_roles_get;
|
||||
use skynet_discord_bot::common::{database::DataBase, set_roles::committee::db_roles_get};
|
||||
|
||||
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
|
||||
let sub_options = if let Some(CommandDataOption {
|
||||
|
@ -28,11 +27,10 @@ pub mod committee {
|
|||
false
|
||||
};
|
||||
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let mut cs = vec![];
|
||||
// pull it from a DB
|
||||
|
@ -53,7 +51,7 @@ pub mod committee {
|
|||
for (count, name) in cs {
|
||||
let leading = if count < 10 { " " } else { "" };
|
||||
|
||||
let line = format!("{}{} {}", leading, count, name);
|
||||
let line = format!("{leading}{count} {name}");
|
||||
|
||||
let length = line.len() + 1;
|
||||
|
||||
|
@ -85,23 +83,28 @@ pub mod servers {
|
|||
// get the list of all the current clubs/socs
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serenity::all::{CommandInteraction, CommandOptionType, Context, CreateCommand, CreateCommandOption};
|
||||
use skynet_discord_bot::common::database::{get_server_config_bulk, DataBase};
|
||||
use skynet_discord_bot::common::set_roles::committee::get_committees;
|
||||
use skynet_discord_bot::get_now_iso;
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::{get_server_config_bulk, DataBase},
|
||||
set_roles::committee::get_committees,
|
||||
},
|
||||
get_now_iso,
|
||||
};
|
||||
use sqlx::{Pool, Sqlite};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub async fn run(_command: &CommandInteraction, ctx: &Context) -> String {
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let mut committees = HashMap::new();
|
||||
for committee in get_committees(&db).await {
|
||||
if let Some(x) = get_committees(&db).await {
|
||||
for committee in x {
|
||||
committees.insert(committee.id, committee.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
let mut cs = vec![];
|
||||
// pull it from a DB
|
||||
|
@ -143,11 +146,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;
|
||||
|
||||
// +3 is to account for the closing fense
|
||||
// +3 is to account for the closing fence
|
||||
if length < (limit + 3) {
|
||||
response.push(line);
|
||||
limit -= length;
|
||||
|
|
|
@ -9,19 +9,24 @@ pub(crate) mod user {
|
|||
use super::*;
|
||||
use crate::commands::wolves::link::get_server_member_discord;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
|
||||
use serenity::model::id::UserId;
|
||||
use skynet_discord_bot::common::database::Wolves;
|
||||
use skynet_discord_bot::common::minecraft::{whitelist_update, Minecraft};
|
||||
use skynet_discord_bot::Config;
|
||||
use serenity::{
|
||||
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction},
|
||||
model::id::UserId,
|
||||
};
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::Wolves,
|
||||
minecraft::{whitelist_update, Minecraft},
|
||||
},
|
||||
Config,
|
||||
};
|
||||
use sqlx::Error;
|
||||
|
||||
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let config_lock = {
|
||||
let data_read = ctx.data.read().await;
|
||||
|
@ -69,14 +74,14 @@ pub(crate) mod user {
|
|||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
dbg!("{:?}", e);
|
||||
return format!("Failure to minecraft username {:?}", username);
|
||||
return format!("Failure to minecraft username {username:?}");
|
||||
}
|
||||
}
|
||||
username_mc = username.to_string();
|
||||
} else {
|
||||
match get_minecraft_bedrock(username, &config.minecraft_mcprofile).await {
|
||||
None => {
|
||||
return format!("No UID found for {:?}", username);
|
||||
return format!("No UID found for {username:?}");
|
||||
}
|
||||
Some(x) => {
|
||||
match add_minecraft_bedrock(&db, &command.user.id, &x.floodgateuid).await {
|
||||
|
@ -185,14 +190,17 @@ pub(crate) mod server {
|
|||
use super::*;
|
||||
|
||||
pub(crate) mod add {
|
||||
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption};
|
||||
use serenity::model::id::GuildId;
|
||||
use serenity::{
|
||||
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption},
|
||||
model::id::GuildId,
|
||||
};
|
||||
use sqlx::Error;
|
||||
// this is to managfe the server side of commands related to minecraft
|
||||
// this is to manage the server side of commands related to minecraft
|
||||
use super::*;
|
||||
use skynet_discord_bot::common::minecraft::update_server;
|
||||
use skynet_discord_bot::common::minecraft::Minecraft;
|
||||
use skynet_discord_bot::Config;
|
||||
use skynet_discord_bot::{
|
||||
common::minecraft::{update_server, Minecraft},
|
||||
Config,
|
||||
};
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
CreateCommand::new("minecraft_add")
|
||||
|
@ -220,16 +228,15 @@ pub(crate) mod server {
|
|||
return String::from("Expected Server ID");
|
||||
};
|
||||
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
match add_server(&db, &g_id, &server_minecraft).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft);
|
||||
}
|
||||
}
|
||||
|
@ -260,12 +267,14 @@ pub(crate) mod server {
|
|||
}
|
||||
|
||||
pub(crate) mod list {
|
||||
use serenity::all::CommandInteraction;
|
||||
use serenity::builder::CreateCommand;
|
||||
use serenity::client::Context;
|
||||
use skynet_discord_bot::common::database::DataBase;
|
||||
use skynet_discord_bot::common::minecraft::{get_minecraft_config_server, server_information};
|
||||
use skynet_discord_bot::Config;
|
||||
use serenity::{all::CommandInteraction, builder::CreateCommand, client::Context};
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::DataBase,
|
||||
minecraft::{get_minecraft_config_server, server_information},
|
||||
},
|
||||
Config,
|
||||
};
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
CreateCommand::new("minecraft_list")
|
||||
|
@ -279,11 +288,10 @@ pub(crate) mod server {
|
|||
Some(x) => x,
|
||||
};
|
||||
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let servers = get_minecraft_config_server(&db, g_id).await;
|
||||
|
||||
|
@ -320,12 +328,13 @@ pub(crate) mod server {
|
|||
}
|
||||
|
||||
pub(crate) mod delete {
|
||||
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption};
|
||||
use serenity::builder::CreateCommand;
|
||||
use serenity::client::Context;
|
||||
use serenity::model::id::GuildId;
|
||||
use skynet_discord_bot::common::database::DataBase;
|
||||
use skynet_discord_bot::common::minecraft::Minecraft;
|
||||
use serenity::{
|
||||
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, CommandOptionType, CreateCommandOption},
|
||||
builder::CreateCommand,
|
||||
client::Context,
|
||||
model::id::GuildId,
|
||||
};
|
||||
use skynet_discord_bot::common::{database::DataBase, minecraft::Minecraft};
|
||||
use sqlx::{Error, Pool, Sqlite};
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
|
@ -354,16 +363,15 @@ pub(crate) mod server {
|
|||
return String::from("Expected Server ID");
|
||||
};
|
||||
|
||||
let db_lock = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
let db = {
|
||||
let data = ctx.data.read().await;
|
||||
data.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
match server_remove(&db, &g_id, &server_minecraft).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
return format!("Failure to insert into Minecraft {} {}", &g_id, &server_minecraft);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,5 @@ pub mod committee;
|
|||
pub mod count;
|
||||
pub mod minecraft;
|
||||
pub mod role_adder;
|
||||
pub mod server_icon;
|
||||
pub mod wolves;
|
||||
|
|
|
@ -62,11 +62,10 @@ pub mod edit {
|
|||
false
|
||||
};
|
||||
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
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_data = RoleAdder {
|
||||
|
@ -79,8 +78,8 @@ pub mod edit {
|
|||
match add_server(&db, &server_data, delete).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
return format!("Failure to insert into Servers {:?}", server_data);
|
||||
println!("{e:?}");
|
||||
return format!("Failure to insert into Servers {server_data:?}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,9 +100,9 @@ pub mod edit {
|
|||
}
|
||||
|
||||
if delete {
|
||||
format!("Removed {} + {} = {}", role_a_name, role_b_name, role_c_name)
|
||||
format!("Removed {role_a_name} + {role_b_name} = {role_c_name}")
|
||||
} else {
|
||||
format!("Added {} + {} = {}", role_a_name, role_b_name, role_c_name)
|
||||
format!("Added {role_a_name} + {role_b_name} = {role_c_name}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,13 +141,12 @@ pub mod edit {
|
|||
pub mod list {}
|
||||
|
||||
pub mod tools {
|
||||
use serenity::client::Context;
|
||||
use serenity::model::guild::Member;
|
||||
use serenity::{client::Context, model::guild::Member};
|
||||
use skynet_discord_bot::common::database::RoleAdder;
|
||||
use sqlx::{Pool, Sqlite};
|
||||
|
||||
pub async fn on_role_change(db: &Pool<Sqlite>, ctx: &Context, new_data: Member) {
|
||||
// check if the role changed is part of the oens for this server
|
||||
// check if the role changed is part of the ones for this server
|
||||
if let Ok(role_adders) = sqlx::query_as::<_, RoleAdder>(
|
||||
r#"
|
||||
SELECT *
|
||||
|
@ -164,7 +162,7 @@ pub mod tools {
|
|||
let mut roles_remove = vec![];
|
||||
|
||||
for role_adder in role_adders {
|
||||
// if the user has both A dnd B give them C
|
||||
// if the user has both A and B give them C
|
||||
if new_data.roles.contains(&role_adder.role_a) && new_data.roles.contains(&role_adder.role_b) && !new_data.roles.contains(&role_adder.role_c)
|
||||
{
|
||||
roles_add.push(role_adder.role_c);
|
||||
|
@ -180,13 +178,13 @@ pub mod tools {
|
|||
|
||||
if !roles_add.is_empty() {
|
||||
if let Err(e) = new_data.add_roles(&ctx, &roles_add).await {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
if !roles_remove.is_empty() {
|
||||
if let Err(e) = new_data.remove_roles(&ctx, &roles_remove).await {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
223
src/commands/server_icon.rs
Normal file
223
src/commands/server_icon.rs
Normal file
|
@ -0,0 +1,223 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,11 +4,16 @@ use lettre::{
|
|||
Message, SmtpTransport, Transport,
|
||||
};
|
||||
use maud::html;
|
||||
use serenity::all::CommandOptionType;
|
||||
use serenity::builder::CreateCommandOption;
|
||||
use serenity::{builder::CreateCommand, client::Context, model::id::UserId};
|
||||
use skynet_discord_bot::common::database::{DataBase, Wolves, WolvesVerify};
|
||||
use skynet_discord_bot::{get_now_iso, random_string, Config};
|
||||
use serenity::{
|
||||
all::CommandOptionType,
|
||||
builder::{CreateCommand, CreateCommandOption},
|
||||
client::Context,
|
||||
model::id::UserId,
|
||||
};
|
||||
use skynet_discord_bot::{
|
||||
common::database::{DataBase, Wolves, WolvesVerify},
|
||||
get_now_iso, random_string, Config,
|
||||
};
|
||||
use sqlx::{Pool, Sqlite};
|
||||
|
||||
pub mod link {
|
||||
|
@ -16,11 +21,10 @@ pub mod link {
|
|||
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction};
|
||||
|
||||
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let config_lock = {
|
||||
let data_read = ctx.data.read().await;
|
||||
|
@ -96,7 +100,7 @@ pub mod link {
|
|||
return "Email already verified".to_string();
|
||||
}
|
||||
|
||||
// generate a auth key
|
||||
// generate an auth key
|
||||
let auth = random_string(20);
|
||||
match send_mail(&config, &details.email, &auth, &command.user.name) {
|
||||
Ok(_) => match save_to_db(&db, &details, &auth, &command.user.id).await {
|
||||
|
@ -110,7 +114,7 @@ pub mod link {
|
|||
}
|
||||
}
|
||||
|
||||
format!("Verification email sent to {}, it may take up to 15 min for it to arrive. If it takes longer check the Junk folder.", email)
|
||||
format!("Verification email sent to {email}, it may take up to 15 min for it to arrive. If it takes longer check the Junk folder.")
|
||||
}
|
||||
|
||||
pub async fn get_server_member_discord(db: &Pool<Sqlite>, user: &UserId) -> Option<Wolves> {
|
||||
|
@ -205,7 +209,7 @@ pub mod link {
|
|||
.subject("Skynet: Link Discord to Wolves.")
|
||||
.multipart(
|
||||
// This is composed of two parts.
|
||||
// also helps not trip spam settings (uneven number of url's
|
||||
// also helps not trip spam settings (uneven number of urls)
|
||||
MultiPart::alternative()
|
||||
.singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN).body(body_text))
|
||||
.singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML).body(html.into_string())),
|
||||
|
@ -279,22 +283,40 @@ pub mod link {
|
|||
}
|
||||
}
|
||||
|
||||
pub mod link_docs {
|
||||
use super::*;
|
||||
pub mod users {
|
||||
use super::*;
|
||||
use serenity::all::CommandInteraction;
|
||||
|
||||
pub async fn run(_command: &CommandInteraction, _ctx: &Context) -> String {
|
||||
"https://forgejo.skynet.ie/Skynet/discord-bot/src/branch/main/doc/User.md".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// pub mod committee {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
pub mod verify {
|
||||
use super::*;
|
||||
use crate::commands::wolves::link::{db_pending_clear_expired, get_server_member_discord, get_verify_from_db};
|
||||
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, GuildId, RoleId};
|
||||
use serenity::model::user::User;
|
||||
use skynet_discord_bot::common::database::get_server_config;
|
||||
use skynet_discord_bot::common::database::{ServerMembersWolves, Servers};
|
||||
use skynet_discord_bot::common::wolves::committees::Committees;
|
||||
use serenity::{
|
||||
all::{CommandDataOption, CommandDataOptionValue, CommandInteraction},
|
||||
model::user::User,
|
||||
};
|
||||
use skynet_discord_bot::common::{
|
||||
database::{get_server_config, ServerMembersWolves, Servers},
|
||||
wolves::committees::Committees,
|
||||
};
|
||||
use sqlx::Error;
|
||||
|
||||
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
// check if user has used /link_wolves
|
||||
let details = if let Some(x) = get_verify_from_db(&db, &command.user.id).await {
|
||||
|
@ -341,12 +363,12 @@ pub mod verify {
|
|||
"Discord username linked to Wolves".to_string()
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
"Failed to save, please try /link_wolves again".to_string()
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(e) => println!("{:?}", e),
|
||||
Err(e) => println!("{e:?}"),
|
||||
}
|
||||
|
||||
"Failed to verify".to_string()
|
||||
|
@ -403,7 +425,7 @@ pub mod verify {
|
|||
}
|
||||
|
||||
if let Err(e) = member.add_roles(&ctx, &roles).await {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -419,7 +441,7 @@ pub mod verify {
|
|||
WHERE committee LIKE ?1
|
||||
"#,
|
||||
)
|
||||
.bind(format!("%{}%", wolves_id))
|
||||
.bind(format!("%{wolves_id}%"))
|
||||
.fetch_all(db)
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
|
@ -429,12 +451,18 @@ pub mod verify {
|
|||
}
|
||||
|
||||
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 they are a member of one or more committees, and in teh committee server then give the teh general committee role
|
||||
// if they are a member of one or more committees, and in teh committee server then give them the general committee role
|
||||
// they will get teh more specific vanity role later
|
||||
if !get_committees_id(db, x.id_wolves).await.is_empty() {
|
||||
let server = GuildId::new(1220150752656363520);
|
||||
let committee_member = RoleId::new(1226602779968274573);
|
||||
let server = config.committee_server;
|
||||
let committee_member = config.committee_role;
|
||||
|
||||
if let Ok(member) = server.member(ctx, &discord.id).await {
|
||||
member.add_roles(&ctx, &[committee_member]).await.unwrap_or_default();
|
||||
|
@ -464,13 +492,12 @@ pub mod unlink {
|
|||
use sqlx::{Pool, Sqlite};
|
||||
|
||||
pub async fn run(command: &CommandInteraction, ctx: &Context) -> String {
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Databse in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
// dosent matter if there is one or not, it will be removed regardless
|
||||
// doesn't matter if there is one or not, it will be removed regardless
|
||||
delete_link(&db, &command.user.id).await;
|
||||
|
||||
"Discord link removed".to_string()
|
||||
|
@ -516,4 +543,5 @@ pub fn register() -> CreateCommand {
|
|||
.add_sub_option(CreateCommandOption::new(CommandOptionType::String, "minecraft_username", "Your Minecraft username").required(true))
|
||||
.add_sub_option(CreateCommandOption::new(CommandOptionType::Boolean, "bedrock_account", "Is this a Bedrock account?").required(false)),
|
||||
)
|
||||
.add_option(CreateCommandOption::new(CommandOptionType::SubCommand, "docs", "Link to where the documentation can be found."))
|
||||
}
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
use crate::Config;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serenity::model::guild;
|
||||
use serenity::model::id::{ChannelId, GuildId, RoleId, UserId};
|
||||
use serenity::prelude::TypeMapKey;
|
||||
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow};
|
||||
use sqlx::{Error, FromRow, Pool, Row, Sqlite};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use serenity::{
|
||||
model::{
|
||||
guild,
|
||||
id::{ChannelId, GuildId, RoleId, UserId},
|
||||
},
|
||||
prelude::TypeMapKey,
|
||||
};
|
||||
use sqlx::{
|
||||
sqlite::{SqliteConnectOptions, SqlitePoolOptions, SqliteRow},
|
||||
Error, FromRow, Pool, Row, Sqlite,
|
||||
};
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
pub struct DataBase;
|
||||
impl TypeMapKey for DataBase {
|
||||
type Value = Arc<RwLock<Pool<Sqlite>>>;
|
||||
type Value = Arc<Pool<Sqlite>>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
|
@ -220,7 +224,7 @@ pub async fn db_init(config: &Config) -> Result<Pool<Sqlite>, Error> {
|
|||
let pool = SqlitePoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect_with(
|
||||
SqliteConnectOptions::from_str(&format!("sqlite://{}", database))?
|
||||
SqliteConnectOptions::from_str(&format!("sqlite://{database}"))?
|
||||
.foreign_keys(true)
|
||||
.create_if_missing(true),
|
||||
)
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use crate::common::set_roles::normal::get_server_member_bulk;
|
||||
use crate::Config;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::{common::set_roles::normal::get_server_member_bulk, Config};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serenity::model::id::GuildId;
|
||||
use sqlx::sqlite::SqliteRow;
|
||||
use sqlx::{Error, FromRow, Pool, Row, Sqlite};
|
||||
use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Minecraft {
|
||||
|
@ -27,7 +24,7 @@ impl<'r> FromRow<'r, SqliteRow> for Minecraft {
|
|||
/**
|
||||
loop through all members of server
|
||||
get a list of folks with mc accounts that are members
|
||||
and a list that arent members
|
||||
and a list that aren't members
|
||||
*/
|
||||
pub async fn update_server(server_id: &str, db: &Pool<Sqlite>, g_id: &GuildId, config: &Config) {
|
||||
let mut usernames = vec![];
|
||||
|
@ -112,7 +109,7 @@ pub async fn whitelist_wipe(server: &str, token: &str) {
|
|||
};
|
||||
post(&format!("{url_base}/files/delete"), &bearer, &deletion).await;
|
||||
|
||||
// recreate teh file, passing in the type here so the compiler knows what type of vec it is
|
||||
// recreate the file, passing in the type here so the compiler knows what type of Vec it is
|
||||
post::<Vec<&str>>(&format!("{url_base}/files/write?file=%2Fwhitelist.json"), &bearer, &vec![]).await;
|
||||
|
||||
// reload the whitelist
|
||||
|
@ -155,7 +152,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) {
|
||||
println!("Update whitelist for {}", server);
|
||||
println!("Update whitelist for {server}");
|
||||
let url_base = format!("https://panel.games.skynet.ie/api/client/servers/{server}");
|
||||
let bearer = format!("Bearer {token}");
|
||||
|
||||
|
|
|
@ -2,3 +2,6 @@ pub mod database;
|
|||
pub mod minecraft;
|
||||
pub mod set_roles;
|
||||
pub mod wolves;
|
||||
|
||||
pub mod renderer;
|
||||
pub mod server_icon;
|
||||
|
|
193
src/common/renderer.rs
Normal file
193
src/common/renderer.rs
Normal file
|
@ -0,0 +1,193 @@
|
|||
// 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
404
src/common/server_icon.rs
Normal file
404
src/common/server_icon.rs
Normal file
|
@ -0,0 +1,404 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,18 +1,27 @@
|
|||
pub mod normal {
|
||||
use crate::common::database::{DataBase, ServerMembersWolves, Servers, Wolves};
|
||||
use crate::get_now_iso;
|
||||
use serenity::client::Context;
|
||||
use serenity::model::id::{GuildId, RoleId, UserId};
|
||||
use crate::{
|
||||
common::database::{DataBase, ServerMembersWolves, Servers, Wolves},
|
||||
get_now_iso,
|
||||
};
|
||||
use serenity::{
|
||||
client::Context,
|
||||
model::id::{GuildId, RoleId, UserId},
|
||||
};
|
||||
use sqlx::{Pool, Sqlite};
|
||||
|
||||
struct RolesChange {
|
||||
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]) {
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
|
||||
};
|
||||
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let Servers {
|
||||
server,
|
||||
role_past,
|
||||
|
@ -20,7 +29,12 @@ pub mod normal {
|
|||
..
|
||||
} = server;
|
||||
|
||||
let mut roles_set = [0, 0, 0];
|
||||
let mut roles_set = RolesChange {
|
||||
total: 0,
|
||||
new: 0,
|
||||
current_add: 0,
|
||||
current_rem: 0,
|
||||
};
|
||||
let mut members = vec![];
|
||||
|
||||
for member in get_server_member_bulk(&db, server).await {
|
||||
|
@ -32,28 +46,30 @@ pub mod normal {
|
|||
|
||||
if let Ok(x) = server.members(ctx, None, None).await {
|
||||
for member in x {
|
||||
// members_changed acts as an override to only deal with teh users in it
|
||||
// members_changed acts as an override to only deal with the users in it
|
||||
if !members_changed.is_empty() && !members_changed.contains(&member.user.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if members.contains(&member.user.id) {
|
||||
roles_set.total += 1;
|
||||
|
||||
let mut roles = vec![];
|
||||
|
||||
if let Some(role) = &role_past {
|
||||
if !member.roles.contains(role) {
|
||||
roles_set[0] += 1;
|
||||
roles_set.new += 1;
|
||||
roles.push(role.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
if !member.roles.contains(role_current) {
|
||||
roles_set[1] += 1;
|
||||
roles_set.current_add += 1;
|
||||
roles.push(role_current.to_owned());
|
||||
}
|
||||
|
||||
if let Err(e) = member.add_roles(ctx, &roles).await {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
} else {
|
||||
// old and never
|
||||
|
@ -65,16 +81,16 @@ pub mod normal {
|
|||
}
|
||||
|
||||
if member.roles.contains(role_current) {
|
||||
roles_set[2] += 1;
|
||||
// if theya re not a current member and have the role then remove it
|
||||
roles_set.current_rem += 1;
|
||||
// if they're not a current member and have the role then remove it
|
||||
if let Err(e) = member.remove_role(ctx, role_current).await {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
for role in remove_roles.iter().flatten() {
|
||||
if let Err(e) = member.remove_role(ctx, role).await {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +99,14 @@ pub mod normal {
|
|||
set_server_numbers(&db, server, members_all as i64, members.len() as i64).await;
|
||||
|
||||
// small bit of logging to note changes over time
|
||||
println!("{:?} Changes: New: +{}, Current: +{}/-{}", server.get(), roles_set[0], roles_set[1], roles_set[2]);
|
||||
println!(
|
||||
"{:?} Total: {} Changes: New: +{}, Current: +{}/-{}",
|
||||
server.get(),
|
||||
roles_set.total,
|
||||
roles_set.new,
|
||||
roles_set.current_add,
|
||||
roles_set.current_rem
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn get_server_member_bulk(db: &Pool<Sqlite>, server: &GuildId) -> Vec<ServerMembersWolves> {
|
||||
|
@ -123,7 +146,7 @@ pub mod normal {
|
|||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("Failure to insert into {}", server.get());
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,58 +154,56 @@ pub mod normal {
|
|||
|
||||
// for updating committee members
|
||||
pub mod committee {
|
||||
use crate::common::database::{get_channel_from_row, get_role_from_row, DataBase, Wolves};
|
||||
use crate::common::wolves::committees::Committees;
|
||||
use crate::Config;
|
||||
use crate::{
|
||||
common::{
|
||||
database::{get_channel_from_row, get_role_from_row, DataBase, Wolves},
|
||||
wolves::committees::Committees,
|
||||
},
|
||||
Config,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serenity::all::{EditRole, GuildId};
|
||||
use serenity::builder::CreateChannel;
|
||||
use serenity::client::Context;
|
||||
use serenity::model::channel::ChannelType;
|
||||
use serenity::model::guild::Member;
|
||||
use serenity::model::id::ChannelId;
|
||||
use serenity::model::prelude::RoleId;
|
||||
use sqlx::sqlite::SqliteRow;
|
||||
use sqlx::{Error, FromRow, Pool, Row, Sqlite};
|
||||
use serenity::{
|
||||
all::EditRole,
|
||||
builder::CreateChannel,
|
||||
client::Context,
|
||||
model::{channel::ChannelType, guild::Member, id::ChannelId, prelude::RoleId},
|
||||
};
|
||||
use sqlx::{sqlite::SqliteRow, Error, FromRow, Pool, Row, Sqlite};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn check_committee(ctx: Arc<Context>) {
|
||||
let db_lock = {
|
||||
pub async fn check_committee(ctx: &Context) {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
|
||||
};
|
||||
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let config_lock = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone()
|
||||
};
|
||||
let config_global = config_lock.read().await;
|
||||
|
||||
let server = GuildId::new(1220150752656363520);
|
||||
let server = config_global.committee_server;
|
||||
|
||||
// because to use it to update a single user we need to pre-get the members of teh server
|
||||
let mut members = server.members(&ctx, None, None).await.unwrap_or_default();
|
||||
|
||||
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 tehm the appropiate roles on teh committee server
|
||||
This function can take a Vec of members (or just one) and gives them the appropriate roles on teh committee server
|
||||
*/
|
||||
pub async fn update_committees(db: &Pool<Sqlite>, ctx: &Context, _config: &Config, members: &mut Vec<Member>) {
|
||||
let server = GuildId::new(1220150752656363520);
|
||||
let committee_member = RoleId::new(1226602779968274573);
|
||||
let committees = get_committees(db).await;
|
||||
let categories = [
|
||||
ChannelId::new(1226606560973815839),
|
||||
// C&S Chats 2
|
||||
ChannelId::new(1341457244973305927),
|
||||
// C&S Chats 3
|
||||
ChannelId::new(1341457509717639279),
|
||||
];
|
||||
pub async fn update_committees(db: &Pool<Sqlite>, ctx: &Context, config: &Config, members: &mut Vec<Member>) {
|
||||
let server = config.committee_server;
|
||||
let committee_member = config.committee_role;
|
||||
let committees = match get_committees(db).await {
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
Some(x) => x,
|
||||
};
|
||||
let categories = config.committee_category.clone();
|
||||
|
||||
// information about the server
|
||||
let mut roles_db = HashMap::new();
|
||||
|
@ -203,11 +224,11 @@ pub mod committee {
|
|||
|
||||
let mut channels = server.channels(&ctx).await.unwrap_or_default();
|
||||
|
||||
// a map of users and the roles they are goign to be getting
|
||||
// a map of users and the roles they are going to be getting
|
||||
let mut users_roles = HashMap::new();
|
||||
|
||||
let mut re_order = false;
|
||||
// we need to create roles and channels if tehy dont already exist
|
||||
// we need to create roles and channels if they don't already exist
|
||||
let mut category_index = 0;
|
||||
let mut i = 0;
|
||||
loop {
|
||||
|
@ -308,21 +329,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
|
||||
for member in members {
|
||||
// if member.user.id != 136522490632601600 {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
let roles_current = member.roles(ctx).unwrap_or_default();
|
||||
|
||||
let roles_required = match users_roles.get(&member.user.id) {
|
||||
None => {
|
||||
vec![]
|
||||
}
|
||||
Some(x) => {
|
||||
let mut tmp = x.to_owned();
|
||||
if !tmp.is_empty() {
|
||||
tmp.push(committee_member);
|
||||
}
|
||||
tmp
|
||||
}
|
||||
Some(x) => x.to_owned(),
|
||||
};
|
||||
|
||||
let on_committee = !roles_required.is_empty();
|
||||
|
||||
let mut roles_rem = vec![];
|
||||
let mut roles_add = vec![];
|
||||
// get a list of all the roles to remove from someone
|
||||
|
@ -331,14 +352,25 @@ pub mod committee {
|
|||
for role in &roles_current {
|
||||
roles_current_id.push(role.id.to_owned());
|
||||
if !roles_required.contains(&role.id) {
|
||||
if role.id == committee_member && on_committee {
|
||||
continue;
|
||||
}
|
||||
|
||||
roles_rem.push(role.id.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
if !roles_required.is_empty() {
|
||||
// if there are committee roles then give the general purporse role
|
||||
roles_add.push(committee_member);
|
||||
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 let Some(x) = roles_db.get_mut(&0) {
|
||||
x.count += 1;
|
||||
}
|
||||
|
@ -357,8 +389,6 @@ pub mod committee {
|
|||
if !roles_add.is_empty() {
|
||||
// these roles are flavor roles, only there to make folks mentionable
|
||||
member.add_roles(&ctx, &roles_add).await.unwrap_or_default();
|
||||
} else {
|
||||
member.remove_roles(&ctx, &[committee_member]).await.unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,10 +436,10 @@ pub mod committee {
|
|||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct CommitteeRoles {
|
||||
id_wolves: i64,
|
||||
id_role: RoleId,
|
||||
id_channel: ChannelId,
|
||||
pub id_role: RoleId,
|
||||
pub id_channel: ChannelId,
|
||||
pub name_role: String,
|
||||
name_channel: String,
|
||||
pub name_channel: String,
|
||||
pub count: i64,
|
||||
}
|
||||
|
||||
|
@ -446,8 +476,8 @@ pub mod committee {
|
|||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("Failure to insert into Wolves {:?}", role);
|
||||
println!("{:?}", e);
|
||||
println!("Failure to insert into Wolves {role:?}");
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -464,13 +494,13 @@ pub mod committee {
|
|||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
println!("Failure to get Roles from committee_roles");
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
vec![]
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_committees(db: &Pool<Sqlite>) -> Vec<Committees> {
|
||||
sqlx::query_as::<_, Committees>(
|
||||
pub async fn get_committees(db: &Pool<Sqlite>) -> Option<Vec<Committees>> {
|
||||
match sqlx::query_as::<_, Committees>(
|
||||
r#"
|
||||
SELECT *
|
||||
FROM committees
|
||||
|
@ -478,10 +508,13 @@ pub mod committee {
|
|||
)
|
||||
.fetch_all(db)
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
{
|
||||
Ok(x) => Some(x),
|
||||
Err(e) => {
|
||||
dbg!(e);
|
||||
vec![]
|
||||
})
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_server_member_discord(db: &Pool<Sqlite>, user: &i64) -> Option<Wolves> {
|
||||
|
|
|
@ -38,8 +38,8 @@ async fn add_users_wolves(db: &Pool<Sqlite>, user: &WolvesResultUserMin) {
|
|||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("Failure to insert into Wolves {:?}", user);
|
||||
println!("{:?}", e);
|
||||
println!("Failure to insert into Wolves {user:?}");
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,12 +48,14 @@ async fn add_users_wolves(db: &Pool<Sqlite>, user: &WolvesResultUserMin) {
|
|||
This is getting data for Clubs and Socs
|
||||
*/
|
||||
pub mod cns {
|
||||
use crate::common::database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers};
|
||||
use crate::common::set_roles::normal::update_server;
|
||||
use crate::common::wolves::{add_users_wolves, WolvesResultUserMin};
|
||||
use crate::Config;
|
||||
use serenity::client::Context;
|
||||
use serenity::model::id::GuildId;
|
||||
use crate::{
|
||||
common::{
|
||||
database::{get_server_config_bulk, DataBase, ServerMembers, ServerMembersWolves, Servers},
|
||||
wolves::{add_users_wolves, WolvesResultUserMin},
|
||||
},
|
||||
Config,
|
||||
};
|
||||
use serenity::{client::Context, model::id::GuildId};
|
||||
use sqlx::{Pool, Sqlite};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
|
@ -67,11 +69,10 @@ pub mod cns {
|
|||
}
|
||||
|
||||
pub async fn get_wolves(ctx: &Context) {
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let config_lock = {
|
||||
let data_read = ctx.data.read().await;
|
||||
|
@ -96,7 +97,6 @@ pub mod cns {
|
|||
let existing = existing_tmp.iter().map(|data| (data.id_wolves, data)).collect::<BTreeMap<_, _>>();
|
||||
|
||||
// list of users that need to be updated for this server
|
||||
let mut user_to_update = vec![];
|
||||
let mut server_name_tmp = None;
|
||||
for user in wolves.get_members(wolves_api).await {
|
||||
// dbg!(&user.committee);
|
||||
|
@ -115,10 +115,6 @@ pub mod cns {
|
|||
add_users_wolves(&db, &WolvesResultUserMin::from(&user)).await;
|
||||
if old.expiry != user.expiry {
|
||||
add_users_server_members(&db, server, &user).await;
|
||||
|
||||
if let Some(discord_id) = old.discord {
|
||||
user_to_update.push(discord_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,9 +125,6 @@ pub mod cns {
|
|||
set_server_member(&db, server, cs_id).await;
|
||||
}
|
||||
}
|
||||
if !user_to_update.is_empty() {
|
||||
update_server(ctx, &server_config, &[], &user_to_update).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +144,7 @@ pub mod cns {
|
|||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("Failure to set server name {}", server.get());
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +183,7 @@ pub mod cns {
|
|||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("Failure to insert into ServerMembers {} {:?}", server.get(), user);
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,8 +193,7 @@ pub mod cns {
|
|||
Get and store the data on C&S committees
|
||||
*/
|
||||
pub mod committees {
|
||||
use crate::common::database::DataBase;
|
||||
use crate::Config;
|
||||
use crate::{common::database::DataBase, Config};
|
||||
use serenity::client::Context;
|
||||
use sqlx::{Pool, Sqlite};
|
||||
|
||||
|
@ -231,11 +223,10 @@ pub mod committees {
|
|||
}
|
||||
|
||||
pub async fn get_cns(ctx: &Context) {
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Database in TypeMap.").clone()
|
||||
};
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let config_lock = {
|
||||
let data_read = ctx.data.read().await;
|
||||
|
@ -270,8 +261,8 @@ pub mod committees {
|
|||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("Failure to insert into Committees {:?}", committee);
|
||||
println!("{:?}", e);
|
||||
println!("Failure to insert into Committees {committee:?}");
|
||||
println!("{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
26
src/lib.rs
26
src/lib.rs
|
@ -3,8 +3,10 @@ pub mod common;
|
|||
use chrono::{Datelike, SecondsFormat, Utc};
|
||||
use dotenvy::dotenv;
|
||||
use rand::{distr::Alphanumeric, rng, Rng};
|
||||
use serenity::model::id::{ChannelId, GuildId, RoleId};
|
||||
use serenity::prelude::TypeMapKey;
|
||||
use serenity::{
|
||||
model::id::{ChannelId, GuildId, RoleId},
|
||||
prelude::TypeMapKey,
|
||||
};
|
||||
use std::{env, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
|
@ -32,7 +34,10 @@ pub struct Config {
|
|||
// discord server for committee
|
||||
pub committee_server: GuildId,
|
||||
pub committee_role: RoleId,
|
||||
pub committee_category: ChannelId,
|
||||
pub committee_category: Vec<ChannelId>,
|
||||
|
||||
// items pertaining to CompSoc only
|
||||
pub compsoc_server: GuildId,
|
||||
}
|
||||
impl TypeMapKey for Config {
|
||||
type Value = Arc<RwLock<Config>>;
|
||||
|
@ -57,7 +62,8 @@ pub fn get_config() -> Config {
|
|||
wolves_api: "".to_string(),
|
||||
committee_server: GuildId::new(1),
|
||||
committee_role: RoleId::new(1),
|
||||
committee_category: ChannelId::new(1),
|
||||
committee_category: vec![],
|
||||
compsoc_server: GuildId::new(1),
|
||||
};
|
||||
|
||||
if let Ok(x) = env::var("DATABASE_HOME") {
|
||||
|
@ -99,14 +105,22 @@ pub fn get_config() -> Config {
|
|||
config.committee_server = GuildId::new(x);
|
||||
}
|
||||
}
|
||||
if let Ok(x) = env::var("COMMITTEE_DISCORD") {
|
||||
if let Ok(x) = env::var("COMMITTEE_ROLE") {
|
||||
if let Ok(x) = x.trim().parse::<u64>() {
|
||||
config.committee_role = RoleId::new(x);
|
||||
}
|
||||
}
|
||||
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>() {
|
||||
config.committee_category = ChannelId::new(x);
|
||||
config.compsoc_server = GuildId::new(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
130
src/main.rs
130
src/main.rs
|
@ -1,21 +1,28 @@
|
|||
pub mod commands;
|
||||
|
||||
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::{
|
||||
all::{Command, CommandDataOptionValue, CreateMessage, EditInteractionResponse, Interaction},
|
||||
async_trait,
|
||||
client::{Context, EventHandler},
|
||||
gateway::{ActivityData, ChunkGuildFilter},
|
||||
model::{
|
||||
event::GuildMemberUpdateEvent,
|
||||
gateway::{GatewayIntents, Ready},
|
||||
guild::Member,
|
||||
id::GuildId,
|
||||
user::OnlineStatus,
|
||||
},
|
||||
Client,
|
||||
};
|
||||
use skynet_discord_bot::common::database::{db_init, get_server_config, get_server_member, DataBase};
|
||||
use skynet_discord_bot::common::set_roles::committee::update_committees;
|
||||
use skynet_discord_bot::common::wolves::committees::Committees;
|
||||
use skynet_discord_bot::{get_config, Config};
|
||||
use skynet_discord_bot::{
|
||||
common::{
|
||||
database::{db_init, get_server_config, get_server_member, DataBase},
|
||||
set_roles::committee::update_committees,
|
||||
wolves::committees::Committees,
|
||||
},
|
||||
get_config, Config,
|
||||
};
|
||||
use sqlx::{Pool, Sqlite};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
@ -24,15 +31,21 @@ struct Handler;
|
|||
|
||||
#[async_trait]
|
||||
impl EventHandler for Handler {
|
||||
// this caches members of all servers teh bot is in
|
||||
async fn cache_ready(&self, ctx: Context, guilds: Vec<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
|
||||
async fn guild_member_addition(&self, ctx: Context, new_member: Member) {
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
|
||||
};
|
||||
|
||||
let db = db_lock.read().await;
|
||||
|
||||
let config_lock = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone()
|
||||
|
@ -40,7 +53,7 @@ impl EventHandler for Handler {
|
|||
let config_global = config_lock.read().await;
|
||||
|
||||
// committee server takes priority
|
||||
let committee_server = GuildId::new(1220150752656363520);
|
||||
let committee_server = config_global.committee_server;
|
||||
if new_member.guild_id.get() == committee_server.get() {
|
||||
let mut member = vec![new_member.clone()];
|
||||
update_committees(&db, &ctx, &config_global, &mut member).await;
|
||||
|
@ -66,7 +79,7 @@ impl EventHandler for Handler {
|
|||
}
|
||||
|
||||
if let Err(e) = new_member.add_roles(&ctx, &roles).await {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
}
|
||||
} else {
|
||||
let tmp = get_committee(&db, config_server.wolves_id).await;
|
||||
|
@ -94,14 +107,12 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
|
|||
// handles role updates
|
||||
async fn guild_member_update(&self, ctx: Context, _old_data: Option<Member>, new_data: Option<Member>, _: GuildMemberUpdateEvent) {
|
||||
// get config/db
|
||||
let db_lock = {
|
||||
let db = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<DataBase>().expect("Expected Config in TypeMap.").clone()
|
||||
};
|
||||
|
||||
let db = db_lock.read().await;
|
||||
|
||||
// check if the role changed is part of the oens for this server
|
||||
// check if the role changed is part of the ones for this server
|
||||
if let Some(x) = new_data {
|
||||
on_role_change(&db, &ctx, x).await;
|
||||
}
|
||||
|
@ -111,6 +122,12 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
|
|||
println!("[Main] {} is connected!", ready.user.name);
|
||||
ctx.set_presence(Some(ActivityData::playing("with humanity's fate")), OnlineStatus::Online);
|
||||
|
||||
let config_lock = {
|
||||
let data_read = ctx.data.read().await;
|
||||
data_read.get::<Config>().expect("Expected Config in TypeMap.").clone()
|
||||
};
|
||||
let config = config_lock.read().await;
|
||||
|
||||
match Command::set_global_commands(
|
||||
&ctx.http,
|
||||
vec and go to https://discord.com/channels/{}/{} and use
|
|||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e)
|
||||
println!("{e:?}")
|
||||
}
|
||||
}
|
||||
|
||||
match GuildId::new(1220150752656363520)
|
||||
.set_commands(&ctx.http, vec![commands::count::committee::register()])
|
||||
// Inter-Committee server
|
||||
match config.committee_server.set_commands(&ctx.http, vec![commands::count::committee::register()]).await {
|
||||
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
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e)
|
||||
}
|
||||
}
|
||||
|
||||
match GuildId::new(689189992417067052)
|
||||
.set_commands(&ctx.http, vec![commands::count::servers::register()])
|
||||
.await
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e)
|
||||
println!("{e:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +177,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
|
|||
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
||||
if let Interaction::Command(command) = interaction {
|
||||
let _ = command.defer_ephemeral(&ctx.http).await;
|
||||
//println!("Received command interaction: {:#?}", command);
|
||||
// println!("Received command interaction: {:#?}", command);
|
||||
|
||||
let content = match command.data.name.as_str() {
|
||||
// user commands
|
||||
|
@ -164,6 +188,7 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
|
|||
"verify" => commands::wolves::verify::run(&command, &ctx).await,
|
||||
"unlink" => commands::wolves::unlink::run(&command, &ctx).await,
|
||||
"link_minecraft" => commands::minecraft::user::add::run(&command, &ctx).await,
|
||||
"docs" => commands::wolves::link_docs::users::run(&command, &ctx).await,
|
||||
// "link" => commands::count::servers::run(&command, &ctx).await,
|
||||
&_ => format!("not implemented :( wolves {}", x.name.as_str()),
|
||||
},
|
||||
|
@ -175,6 +200,19 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
|
|||
Some(x) => match x.name.as_str() {
|
||||
"add" => commands::add_server::run(&command, &ctx).await,
|
||||
"roles_adder" => commands::role_adder::edit::run(&command, &ctx).await,
|
||||
"icon" => match &x.value {
|
||||
CommandDataOptionValue::SubCommandGroup(y) => match y.first() {
|
||||
None => "error".to_string(),
|
||||
Some(z) => match z.name.as_str() {
|
||||
"change" => commands::server_icon::admin::change::run(&command, &ctx).await,
|
||||
&_ => format!("not implemented :( count {}", x.name.as_str()),
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
format!("not implemented :( committee {}", x.name.as_str())
|
||||
}
|
||||
},
|
||||
// TODO: move teh minecraft commands in here as a subgroup
|
||||
// "link" => commands::count::servers::run(&command, &ctx).await,
|
||||
&_ => format!("not implemented :( committee {}", x.name.as_str()),
|
||||
},
|
||||
|
@ -191,11 +229,34 @@ Sign up on [UL Wolves]({}) and go to https://discord.com/channels/{}/{} and use
|
|||
&_ => format!("not implemented :( count {}", x.name.as_str()),
|
||||
},
|
||||
},
|
||||
|
||||
"icon" => match command.data.options.first() {
|
||||
None => "Invalid Command".to_string(),
|
||||
Some(x) => match x.name.as_str() {
|
||||
"current" => {
|
||||
let result = match &x.value {
|
||||
CommandDataOptionValue::SubCommandGroup(y) => match y.first() {
|
||||
None => "error".to_string(),
|
||||
Some(z) => match z.name.as_str() {
|
||||
"icon" => commands::server_icon::user::current::icon::run(&command, &ctx).await,
|
||||
"festival" => commands::server_icon::user::current::festival::run(&command, &ctx).await,
|
||||
&_ => format!("not implemented :( count {}", x.name.as_str()),
|
||||
},
|
||||
},
|
||||
&_ => format!("not implemented :( {}", command.data.name.as_str()),
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
"stats" => commands::server_icon::user::stats::run(&command, &ctx).await,
|
||||
&_ => format!("not implemented :( count {}", x.name.as_str()),
|
||||
},
|
||||
},
|
||||
_ => format!("not implemented :( {}", command.data.name.as_str()),
|
||||
};
|
||||
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +291,8 @@ async fn main() {
|
|||
let intents = GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_MEMBERS;
|
||||
// Build our client.
|
||||
let mut client = Client::builder(&config.discord_token, intents)
|
||||
.event_handler(Handler {})
|
||||
.event_handler(Handler)
|
||||
.cache_settings(serenity::cache::Settings::default())
|
||||
.await
|
||||
.expect("Error creating client");
|
||||
|
||||
|
@ -238,7 +300,7 @@ async fn main() {
|
|||
let mut data = client.data.write().await;
|
||||
|
||||
data.insert::<Config>(Arc::new(RwLock::new(config)));
|
||||
data.insert::<DataBase>(Arc::new(RwLock::new(db)));
|
||||
data.insert::<DataBase>(Arc::new(db));
|
||||
}
|
||||
|
||||
// Finally, start a single shard, and start listening to events.
|
||||
|
@ -246,6 +308,6 @@ async fn main() {
|
|||
// Shards will automatically attempt to reconnect, and will perform
|
||||
// exponential backoff until it reconnects.
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Client error: {:?}", why);
|
||||
println!("Client error: {why:?}");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue