discord bot
This commit is contained in:
parent
4b6bfc892c
commit
e666250fa9
18 changed files with 677 additions and 143 deletions
226
Cargo.lock
generated
226
Cargo.lock
generated
|
@ -185,6 +185,22 @@ dependencies = [
|
||||||
"syn 2.0.18",
|
"syn 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-tungstenite"
|
||||||
|
version = "0.17.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb"
|
||||||
|
dependencies = [
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"log",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls 0.23.4",
|
||||||
|
"tungstenite",
|
||||||
|
"webpki-roots",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -328,6 +344,12 @@ version = "3.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -362,6 +384,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono-humanize"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32dce1ea1988dbdf9f9815ff11425828523bd2a134ec0805d2ac8af26ee6096e"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cipher"
|
name = "cipher"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
|
@ -496,6 +527,20 @@ dependencies = [
|
||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dashmap"
|
||||||
|
version = "5.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"hashbrown",
|
||||||
|
"lock_api",
|
||||||
|
"once_cell",
|
||||||
|
"parking_lot_core",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diesel"
|
name = "diesel"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -609,7 +654,7 @@ dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-rustls",
|
"hyper-rustls 0.23.2",
|
||||||
"mime",
|
"mime",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -942,6 +987,19 @@ dependencies = [
|
||||||
"tokio-rustls 0.23.4",
|
"tokio-rustls 0.23.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-rustls"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7"
|
||||||
|
dependencies = [
|
||||||
|
"http",
|
||||||
|
"hyper",
|
||||||
|
"rustls 0.21.1",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls 0.24.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-tls"
|
name = "hyper-tls"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -1336,6 +1394,15 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-float"
|
||||||
|
version = "2.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -1426,6 +1493,12 @@ dependencies = [
|
||||||
"universal-hash",
|
"universal-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error"
|
name = "proc-macro-error"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
@ -1474,6 +1547,27 @@ version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49"
|
checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
|
@ -1524,25 +1618,33 @@ dependencies = [
|
||||||
"http",
|
"http",
|
||||||
"http-body",
|
"http-body",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
"hyper-rustls 0.24.0",
|
||||||
"hyper-tls",
|
"hyper-tls",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"mime",
|
"mime",
|
||||||
|
"mime_guess",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"rustls 0.21.1",
|
||||||
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
|
"tokio-rustls 0.24.0",
|
||||||
|
"tokio-util",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
|
"wasm-streams",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
|
"webpki-roots",
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1742,6 +1844,16 @@ dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde-value"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
|
||||||
|
dependencies = [
|
||||||
|
"ordered-float",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.163"
|
version = "1.0.163"
|
||||||
|
@ -1819,6 +1931,48 @@ dependencies = [
|
||||||
"unsafe-libyaml",
|
"unsafe-libyaml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serenity"
|
||||||
|
version = "0.11.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82fd5e7b5858ad96e99d440138f34f5b98e1b959ebcd3a1036203b30e78eb788"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"async-tungstenite",
|
||||||
|
"base64 0.13.1",
|
||||||
|
"bitflags",
|
||||||
|
"bytes",
|
||||||
|
"cfg-if",
|
||||||
|
"chrono",
|
||||||
|
"dashmap",
|
||||||
|
"flate2",
|
||||||
|
"futures",
|
||||||
|
"mime",
|
||||||
|
"mime_guess",
|
||||||
|
"parking_lot",
|
||||||
|
"percent-encoding",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde-value",
|
||||||
|
"serde_json",
|
||||||
|
"time 0.3.21",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"typemap_rev",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha-1"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
@ -2214,9 +2368,21 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"log",
|
"log",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8803eee176538f94ae9a14b55b2804eb7e1441f8210b1c31290b3bccdccff73b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-core"
|
name = "tracing-core"
|
||||||
version = "0.1.31"
|
version = "0.1.31"
|
||||||
|
@ -2232,6 +2398,33 @@ version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tungstenite"
|
||||||
|
version = "0.17.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.13.1",
|
||||||
|
"byteorder",
|
||||||
|
"bytes",
|
||||||
|
"http",
|
||||||
|
"httparse",
|
||||||
|
"log",
|
||||||
|
"rand",
|
||||||
|
"rustls 0.20.8",
|
||||||
|
"sha-1",
|
||||||
|
"thiserror",
|
||||||
|
"url",
|
||||||
|
"utf-8",
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typemap_rev"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed5b74f0a24b5454580a79abb6994393b09adf0ab8070f15827cb666255de155"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
@ -2305,6 +2498,7 @@ dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna 0.4.0",
|
"idna 0.4.0",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2313,6 +2507,12 @@ version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "110352d4e9076c67839003c7788d8604e24dcded13e0b375af3efaa8cf468517"
|
checksum = "110352d4e9076c67839003c7788d8604e24dcded13e0b375af3efaa8cf468517"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf-8"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -2438,6 +2638,19 @@ version = "0.2.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
|
checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-streams"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.63"
|
version = "0.3.63"
|
||||||
|
@ -2477,6 +2690,15 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.22.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
||||||
|
dependencies = [
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -2671,6 +2893,7 @@ dependencies = [
|
||||||
"axum-server",
|
"axum-server",
|
||||||
"base64 0.21.2",
|
"base64 0.21.2",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"chrono-humanize",
|
||||||
"clap",
|
"clap",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
|
@ -2687,6 +2910,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
"serenity",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thirtyfour",
|
"thirtyfour",
|
||||||
|
|
|
@ -18,6 +18,7 @@ axum = { version = "0.6.18", features = ["http2"] }
|
||||||
axum-server = { version = "0.5.1", features = ["rustls", "rustls-pemfile", "tls-rustls"] }
|
axum-server = { version = "0.5.1", features = ["rustls", "rustls-pemfile", "tls-rustls"] }
|
||||||
base64 = "0.21.2"
|
base64 = "0.21.2"
|
||||||
chrono = { version = "0.4.26", features = ["serde"] }
|
chrono = { version = "0.4.26", features = ["serde"] }
|
||||||
|
chrono-humanize = "0.2.2"
|
||||||
clap = { version = "4.3.2", features = ["derive"] }
|
clap = { version = "4.3.2", features = ["derive"] }
|
||||||
diesel = { version = "2.1.0", features = ["sqlite", "chrono"] }
|
diesel = { version = "2.1.0", features = ["sqlite", "chrono"] }
|
||||||
diesel_migrations = { version = "2.1.0", features = ["sqlite"] }
|
diesel_migrations = { version = "2.1.0", features = ["sqlite"] }
|
||||||
|
@ -28,12 +29,13 @@ lettre = "0.10.4"
|
||||||
log = "0.4.18"
|
log = "0.4.18"
|
||||||
rand_core = { version = "0.6.4", features = ["getrandom"] }
|
rand_core = { version = "0.6.4", features = ["getrandom"] }
|
||||||
regex = "1.8.4"
|
regex = "1.8.4"
|
||||||
reqwest = { version = "0.11.18", features = ["json", "blocking"] }
|
reqwest = { version = "0.11.18", features = ["json"] }
|
||||||
rust-embed = "6.7.0"
|
rust-embed = "6.7.0"
|
||||||
rustls-pemfile = "1.0.2"
|
rustls-pemfile = "1.0.2"
|
||||||
serde = { version = "1.0.163", features = ["derive"] }
|
serde = { version = "1.0.163", features = ["derive"] }
|
||||||
serde_json = "1.0.96"
|
serde_json = "1.0.96"
|
||||||
serde_yaml = "0.9.21"
|
serde_yaml = "0.9.21"
|
||||||
|
serenity = { version = "0.11.5", default-features = false, features = ["builder", "cache", "client", "chrono","gateway", "model", "utils", "rustls_backend"] }
|
||||||
simple_logger = "4.1.0"
|
simple_logger = "4.1.0"
|
||||||
tempfile = "3.6.0"
|
tempfile = "3.6.0"
|
||||||
thirtyfour = "0.31.0"
|
thirtyfour = "0.31.0"
|
||||||
|
|
|
@ -11,6 +11,8 @@ canvas_lms:
|
||||||
refresh_interval: 300
|
refresh_interval: 300
|
||||||
|
|
||||||
comm:
|
comm:
|
||||||
|
discord:
|
||||||
|
token: "xxx"
|
||||||
gotify:
|
gotify:
|
||||||
url: https://gotify.yumechi.jp
|
url: https://gotify.yumechi.jp
|
||||||
token: Axxxxxx
|
token: Axxxxxx
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use std::{
|
use std::sync::Arc;
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use axum::{
|
use axum::{
|
||||||
body::HttpBody,
|
body::HttpBody,
|
||||||
error_handling::HandleErrorLayer,
|
error_handling::HandleErrorLayer,
|
||||||
|
@ -14,6 +11,7 @@ use axum::{
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
use tower::ServiceBuilder;
|
use tower::ServiceBuilder;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -66,7 +64,7 @@ pub async fn route_login(
|
||||||
) -> Result<(Extension<SessionStore>, ApiResponse<()>), ApiResponse<()>> {
|
) -> Result<(Extension<SessionStore>, ApiResponse<()>), ApiResponse<()>> {
|
||||||
let failed_response = ApiResponse::<()>::error("Invalid credentials".to_string(), 401, None);
|
let failed_response = ApiResponse::<()>::error("Invalid credentials".to_string(), 401, None);
|
||||||
|
|
||||||
let state = app.state.lock().unwrap();
|
let state = app.state.lock().await;
|
||||||
let state = state.as_ref().unwrap();
|
let state = state.as_ref().unwrap();
|
||||||
|
|
||||||
let Some(user) = state.config.auth.users.get(&form.username) else {
|
let Some(user) = state.config.auth.users.get(&form.username) else {
|
||||||
|
@ -125,19 +123,14 @@ impl AuthApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl App for AuthApp {
|
impl App for AuthApp {
|
||||||
fn initialize(
|
async fn initialize(self: Arc<Self>, config: &'static Config, app_state: Arc<Mutex<AppState>>) {
|
||||||
self: Arc<Self>,
|
let mut state = self.state.lock().await;
|
||||||
config: &'static Config,
|
|
||||||
app_state: Arc<Mutex<AppState>>,
|
|
||||||
) -> Pin<Box<dyn Future<Output = ()>>> {
|
|
||||||
let mut state = self.state.lock().unwrap();
|
|
||||||
*state = Some(AuthAppState {
|
*state = Some(AuthAppState {
|
||||||
config,
|
config,
|
||||||
_app_state: app_state,
|
_app_state: app_state,
|
||||||
});
|
});
|
||||||
|
|
||||||
Box::pin(async {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn api_routes(self: Arc<Self>) -> Router {
|
fn api_routes(self: Arc<Self>) -> Router {
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
use std::{
|
use std::sync::Arc;
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
use async_trait::async_trait;
|
||||||
use axum::{body::HttpBody, extract::Query, http::Request, routing::get, Extension, Router};
|
use axum::{body::HttpBody, extract::Query, http::Request, routing::get, Extension, Router};
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::Mutex as AsyncMutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
apps::{
|
apps::{
|
||||||
|
@ -62,7 +59,7 @@ pub async fn query_grades(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CanvasLMSApp {
|
pub struct CanvasLMSApp {
|
||||||
state: AsyncMutex<CanvasLMSAppState>,
|
state: Mutex<CanvasLMSAppState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CanvasLMSAppState {
|
struct CanvasLMSAppState {
|
||||||
|
@ -114,7 +111,7 @@ where
|
||||||
impl CanvasLMSApp {
|
impl CanvasLMSApp {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: AsyncMutex::new(CanvasLMSAppState {
|
state: Mutex::new(CanvasLMSAppState {
|
||||||
config: None,
|
config: None,
|
||||||
grade_cache: GradeCache {
|
grade_cache: GradeCache {
|
||||||
last_updated: chrono::Local::now(),
|
last_updated: chrono::Local::now(),
|
||||||
|
@ -149,13 +146,16 @@ impl CanvasLMSApp {
|
||||||
};
|
};
|
||||||
let template_rendered = template_ctx.render().unwrap();
|
let template_rendered = template_ctx.render().unwrap();
|
||||||
let global_app_state =
|
let global_app_state =
|
||||||
state.global_app_state.as_ref().unwrap().lock().unwrap();
|
state.global_app_state.as_ref().unwrap().lock().await;
|
||||||
let email_result = global_app_state.comm.send_message(&Message {
|
let email_result = global_app_state
|
||||||
|
.comm
|
||||||
|
.send_message(&Message {
|
||||||
subject: "New grades available".to_string(),
|
subject: "New grades available".to_string(),
|
||||||
body: template_rendered,
|
body: template_rendered,
|
||||||
mime: "text/html",
|
mime: "text/html",
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
})
|
||||||
|
.await;
|
||||||
match email_result {
|
match email_result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Sent email notification for new grades");
|
info!("Sent email notification for new grades");
|
||||||
|
@ -176,12 +176,9 @@ impl CanvasLMSApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl App for CanvasLMSApp {
|
impl App for CanvasLMSApp {
|
||||||
fn initialize(
|
async fn initialize(self: Arc<Self>, config: &'static Config, app_state: Arc<Mutex<AppState>>) {
|
||||||
self: Arc<Self>,
|
|
||||||
config: &'static Config,
|
|
||||||
app_state: Arc<Mutex<AppState>>,
|
|
||||||
) -> Pin<Box<dyn Future<Output = ()>>> {
|
|
||||||
let self_clone = self.clone();
|
let self_clone = self.clone();
|
||||||
|
|
||||||
let refresh_interval = config.canvas_lms.refresh_interval;
|
let refresh_interval = config.canvas_lms.refresh_interval;
|
||||||
|
@ -189,7 +186,6 @@ impl App for CanvasLMSApp {
|
||||||
panic!("Canvas LMS refresh interval cannot be 0");
|
panic!("Canvas LMS refresh interval cannot be 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
let init_async = Box::pin(async move {
|
|
||||||
let mut state = self.state.lock().await;
|
let mut state = self.state.lock().await;
|
||||||
state.global_app_state = Some(app_state);
|
state.global_app_state = Some(app_state);
|
||||||
state.config = Some(config);
|
state.config = Some(config);
|
||||||
|
@ -207,9 +203,6 @@ impl App for CanvasLMSApp {
|
||||||
ticker.tick().await;
|
ticker.tick().await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
init_async
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn api_routes(self: Arc<Self>) -> Router {
|
fn api_routes(self: Arc<Self>) -> Router {
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub async fn route_get_directive(
|
||||||
|
|
||||||
let state = app.state.lock().await;
|
let state = app.state.lock().await;
|
||||||
let state = state.as_ref().unwrap();
|
let state = state.as_ref().unwrap();
|
||||||
let mut global_app_state = state.global_app_state.lock().unwrap();
|
let mut global_app_state = state.global_app_state.lock().await;
|
||||||
|
|
||||||
let meds = {
|
let meds = {
|
||||||
use crate::schema::medications::dsl::*;
|
use crate::schema::medications::dsl::*;
|
||||||
|
@ -77,7 +77,7 @@ pub async fn route_post_directive(
|
||||||
|
|
||||||
let state = app.state.lock().await;
|
let state = app.state.lock().await;
|
||||||
let state = state.as_ref().unwrap();
|
let state = state.as_ref().unwrap();
|
||||||
let mut global_app_state = state.global_app_state.lock().unwrap();
|
let mut global_app_state = state.global_app_state.lock().await;
|
||||||
|
|
||||||
form.uuid = uuid::Uuid::new_v4().to_string();
|
form.uuid = uuid::Uuid::new_v4().to_string();
|
||||||
form.created = chrono::Utc::now().naive_local();
|
form.created = chrono::Utc::now().naive_local();
|
||||||
|
@ -111,7 +111,7 @@ pub async fn route_patch_directive(
|
||||||
|
|
||||||
let state = app.state.lock().await;
|
let state = app.state.lock().await;
|
||||||
let state = state.as_ref().unwrap();
|
let state = state.as_ref().unwrap();
|
||||||
let mut global_app_state = state.global_app_state.lock().unwrap();
|
let mut global_app_state = state.global_app_state.lock().await;
|
||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
use crate::schema::medications;
|
use crate::schema::medications;
|
||||||
|
@ -155,7 +155,7 @@ pub async fn route_delete_directive(
|
||||||
|
|
||||||
let state = app.state.lock().await;
|
let state = app.state.lock().await;
|
||||||
let state = state.as_ref().unwrap();
|
let state = state.as_ref().unwrap();
|
||||||
let mut global_app_state = state.global_app_state.lock().unwrap();
|
let mut global_app_state = state.global_app_state.lock().await;
|
||||||
|
|
||||||
{
|
{
|
||||||
use crate::schema::medications::dsl::medications;
|
use crate::schema::medications::dsl::medications;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::{
|
||||||
models::med::{Medication, MedicationLog},
|
models::med::{Medication, MedicationLog},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, Query},
|
extract::{Path, Query},
|
||||||
Extension,
|
Extension,
|
||||||
|
@ -60,6 +61,55 @@ pub fn project_next_dose(med: &Medication, med_logs: Vec<MedicationLog>) -> Medi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn project_next_doses(
|
||||||
|
app: &MedManagementApp,
|
||||||
|
) -> Result<Vec<(Medication, MedicationLog)>> {
|
||||||
|
let state = app.state.lock().await;
|
||||||
|
let state = state.as_ref().unwrap();
|
||||||
|
let mut global_app_state = state.global_app_state.lock().await;
|
||||||
|
|
||||||
|
let meds = {
|
||||||
|
use crate::schema::medications::dsl::*;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
medications
|
||||||
|
.load::<Medication>(&mut global_app_state.db)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Failed to load meds: {:?}", e);
|
||||||
|
e
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut med_logs = Vec::with_capacity(meds.len());
|
||||||
|
for med in &meds {
|
||||||
|
let logs = {
|
||||||
|
use crate::schema::medication_logs::dsl::*;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
|
||||||
|
medication_logs
|
||||||
|
.order(time_actual.desc())
|
||||||
|
.filter(med_uuid.eq(&med.uuid))
|
||||||
|
.limit(10)
|
||||||
|
.load::<MedicationLog>(&mut global_app_state.db)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Failed to load logs: {:?}", e);
|
||||||
|
e
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
|
med_logs.push(logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = Vec::with_capacity(meds.len());
|
||||||
|
|
||||||
|
for (med, logs) in meds.into_iter().zip(med_logs.into_iter()) {
|
||||||
|
let next_dose = project_next_dose(&med, logs);
|
||||||
|
res.push((med, next_dose));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn route_project_next_dose(
|
pub async fn route_project_next_dose(
|
||||||
auth: AuthInfo,
|
auth: AuthInfo,
|
||||||
app: Extension<Arc<MedManagementApp>>,
|
app: Extension<Arc<MedManagementApp>>,
|
||||||
|
@ -69,7 +119,7 @@ pub async fn route_project_next_dose(
|
||||||
|
|
||||||
let state = app.state.lock().await;
|
let state = app.state.lock().await;
|
||||||
let state = state.as_ref().unwrap();
|
let state = state.as_ref().unwrap();
|
||||||
let mut global_app_state = state.global_app_state.lock().unwrap();
|
let mut global_app_state = state.global_app_state.lock().await;
|
||||||
|
|
||||||
let med = {
|
let med = {
|
||||||
use crate::schema::medications::dsl::*;
|
use crate::schema::medications::dsl::*;
|
||||||
|
@ -122,7 +172,7 @@ pub async fn route_get_log(
|
||||||
|
|
||||||
let state = app.state.lock().await;
|
let state = app.state.lock().await;
|
||||||
let state = state.as_ref().unwrap();
|
let state = state.as_ref().unwrap();
|
||||||
let mut global_app_state = state.global_app_state.lock().unwrap();
|
let mut global_app_state = state.global_app_state.lock().await;
|
||||||
|
|
||||||
let med_logs = {
|
let med_logs = {
|
||||||
use crate::schema::medication_logs::dsl::*;
|
use crate::schema::medication_logs::dsl::*;
|
||||||
|
@ -169,7 +219,7 @@ pub async fn route_post_log(
|
||||||
|
|
||||||
let state = app.state.lock().await;
|
let state = app.state.lock().await;
|
||||||
let state = state.as_ref().unwrap();
|
let state = state.as_ref().unwrap();
|
||||||
let mut global_app_state = state.global_app_state.lock().unwrap();
|
let mut global_app_state = state.global_app_state.lock().await;
|
||||||
|
|
||||||
let med = {
|
let med = {
|
||||||
use crate::schema::medications::dsl::*;
|
use crate::schema::medications::dsl::*;
|
||||||
|
@ -249,7 +299,7 @@ pub async fn route_delete_log(
|
||||||
|
|
||||||
let state = app.state.lock().await;
|
let state = app.state.lock().await;
|
||||||
let state = state.as_ref().unwrap();
|
let state = state.as_ref().unwrap();
|
||||||
let mut global_app_state = state.global_app_state.lock().unwrap();
|
let mut global_app_state = state.global_app_state.lock().await;
|
||||||
|
|
||||||
{
|
{
|
||||||
use crate::schema::medication_logs::dsl::*;
|
use crate::schema::medication_logs::dsl::*;
|
||||||
|
|
|
@ -1,24 +1,29 @@
|
||||||
use std::{
|
use std::sync::Arc;
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{config::Config, AppState};
|
use crate::{
|
||||||
|
comm::{Message, MessageDigestor},
|
||||||
|
config::Config,
|
||||||
|
AppState,
|
||||||
|
};
|
||||||
|
|
||||||
use super::App;
|
use super::App;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use async_trait::async_trait;
|
||||||
use axum::{
|
use axum::{
|
||||||
routing::{delete, get, post},
|
routing::{delete, get, post},
|
||||||
Extension, Router,
|
Extension, Router,
|
||||||
};
|
};
|
||||||
use tokio::sync::Mutex as AsyncMutex;
|
use chrono::DateTime;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use serenity::utils::MessageBuilder;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
mod directive;
|
mod directive;
|
||||||
mod log;
|
mod log;
|
||||||
|
|
||||||
pub struct MedManagementApp {
|
pub struct MedManagementApp {
|
||||||
state: AsyncMutex<Option<MedManagementAppState>>,
|
state: Mutex<Option<MedManagementAppState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MedManagementAppState {
|
struct MedManagementAppState {
|
||||||
|
@ -28,23 +33,70 @@ struct MedManagementAppState {
|
||||||
impl MedManagementApp {
|
impl MedManagementApp {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: AsyncMutex::new(None),
|
state: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn format_relative_time<Tz: chrono::TimeZone>(
|
||||||
|
time: chrono::DateTime<Tz>,
|
||||||
|
relative_to: chrono::DateTime<Tz>,
|
||||||
|
) -> String {
|
||||||
|
let duration = time.signed_duration_since(relative_to);
|
||||||
|
let duration = chrono_humanize::HumanTime::from(duration);
|
||||||
|
duration.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl MessageDigestor for Arc<MedManagementApp> {
|
||||||
|
async fn digest(&self, message: &Message) -> Result<Option<Message>> {
|
||||||
|
lazy_static! {
|
||||||
|
static ref REGEX_GET_DIRECTIVE: regex::Regex =
|
||||||
|
regex::Regex::new(r"get med info$").unwrap();
|
||||||
|
}
|
||||||
|
if REGEX_GET_DIRECTIVE.is_match(&message.body) {
|
||||||
|
let next_doses = log::project_next_doses(self).await?;
|
||||||
|
|
||||||
|
let mut msg = MessageBuilder::new();
|
||||||
|
msg.push_line("");
|
||||||
|
|
||||||
|
for next_dose in next_doses.iter() {
|
||||||
|
msg.push_bold(next_dose.0.name.to_string());
|
||||||
|
msg.push_line(":");
|
||||||
|
msg.push_line(format!("Offset: {:.2}", next_dose.1.dose_offset,));
|
||||||
|
msg.push_line(format!(
|
||||||
|
"Next dose: {}",
|
||||||
|
format_relative_time(
|
||||||
|
DateTime::<chrono::Utc>::from_utc(next_dose.1.time_expected, chrono::Utc),
|
||||||
|
chrono::Utc::now()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
msg.push_line("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(Some(Message {
|
||||||
|
subject: "".to_string(),
|
||||||
|
priority: 0,
|
||||||
|
body: msg.build(),
|
||||||
|
mime: message.mime.clone(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl App for MedManagementApp {
|
impl App for MedManagementApp {
|
||||||
fn initialize(
|
async fn initialize(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_config: &'static Config,
|
_config: &'static Config,
|
||||||
app_state: Arc<Mutex<AppState>>,
|
app_state: Arc<Mutex<AppState>>,
|
||||||
) -> Pin<Box<dyn Future<Output = ()>>> {
|
) {
|
||||||
Box::pin(async move {
|
|
||||||
let mut state = self.state.lock().await;
|
let mut state = self.state.lock().await;
|
||||||
*state = Some(MedManagementAppState {
|
*state = Some(MedManagementAppState {
|
||||||
global_app_state: app_state,
|
global_app_state: app_state,
|
||||||
});
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn api_routes(self: Arc<Self>) -> Router {
|
fn api_routes(self: Arc<Self>) -> Router {
|
||||||
|
@ -81,4 +133,7 @@ impl App for MedManagementApp {
|
||||||
)
|
)
|
||||||
.layer(Extension(self.clone()))
|
.layer(Extension(self.clone()))
|
||||||
}
|
}
|
||||||
|
fn message_digestors(self: Arc<Self>) -> Vec<Box<dyn MessageDigestor + Send + Sync>> {
|
||||||
|
vec![Box::new(self)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,22 @@
|
||||||
use crate::{config::Config, AppState};
|
use crate::{comm::MessageDigestor, config::Config, AppState};
|
||||||
|
use async_trait::async_trait;
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use std::{
|
#[async_trait]
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait App {
|
pub trait App {
|
||||||
fn initialize(
|
async fn initialize(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_config: &'static Config,
|
_config: &'static Config,
|
||||||
_app_state: Arc<Mutex<AppState>>,
|
_app_state: Arc<Mutex<AppState>>,
|
||||||
) -> Pin<Box<dyn Future<Output = ()>>> {
|
);
|
||||||
Box::pin(async {})
|
|
||||||
}
|
|
||||||
fn api_routes(self: Arc<Self>) -> Router {
|
fn api_routes(self: Arc<Self>) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
}
|
}
|
||||||
|
fn message_digestors(self: Arc<Self>) -> Vec<Box<dyn MessageDigestor + Send + Sync>> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{apps::App, http::ApiResponse};
|
use crate::{
|
||||||
|
apps::App,
|
||||||
|
comm::{discord::MIME_DISCORD, Message, MessageDigestor},
|
||||||
|
config::Config,
|
||||||
|
http::ApiResponse,
|
||||||
|
AppState,
|
||||||
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
use async_trait::async_trait;
|
||||||
use axum::{
|
use axum::{
|
||||||
body::HttpBody,
|
body::HttpBody,
|
||||||
http::Request,
|
http::Request,
|
||||||
|
@ -8,7 +16,9 @@ use axum::{
|
||||||
routing::get,
|
routing::get,
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
pub struct ServerInfoApp {}
|
pub struct ServerInfoApp {}
|
||||||
|
|
||||||
|
@ -44,8 +54,46 @@ impl ServerInfoApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl MessageDigestor for Arc<ServerInfoApp> {
|
||||||
|
async fn digest(&self, message: &Message) -> Result<Option<Message>> {
|
||||||
|
lazy_static! {
|
||||||
|
static ref REGEXP_ASK_SERVER_INFO: regex::Regex =
|
||||||
|
regex::Regex::new(r"server info$").unwrap();
|
||||||
|
}
|
||||||
|
if REGEXP_ASK_SERVER_INFO.is_match(&message.body) {
|
||||||
|
let server_info = ServerInfo {
|
||||||
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
|
profile: PROFILE.to_string(),
|
||||||
|
};
|
||||||
|
let ret = format!(
|
||||||
|
"Server info:\nVersion: {}\nProfile: {}",
|
||||||
|
server_info.version, server_info.profile
|
||||||
|
);
|
||||||
|
return Ok(Some(Message {
|
||||||
|
body: ret,
|
||||||
|
subject: "".to_string(),
|
||||||
|
priority: 0,
|
||||||
|
mime: MIME_DISCORD,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl App for ServerInfoApp {
|
impl App for ServerInfoApp {
|
||||||
|
async fn initialize(
|
||||||
|
self: Arc<Self>,
|
||||||
|
_config: &'static Config,
|
||||||
|
_app_state: Arc<Mutex<AppState>>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
fn api_routes(self: Arc<Self>) -> Router {
|
fn api_routes(self: Arc<Self>) -> Router {
|
||||||
Router::new().route("/server_info", get(get_server_info))
|
Router::new().route("/server_info", get(get_server_info))
|
||||||
}
|
}
|
||||||
|
fn message_digestors(self: Arc<Self>) -> Vec<Box<dyn MessageDigestor + Send + Sync>> {
|
||||||
|
vec![Box::new(self)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub struct ChromeDriver {
|
||||||
|
|
||||||
impl ChromeDriver {
|
impl ChromeDriver {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let port = rand_core::OsRng.next_u32() as u16 + 10000;
|
let port = (rand_core::OsRng.next_u32() % 1024) as u16 + 10000;
|
||||||
Self::new_port(port)
|
Self::new_port(port)
|
||||||
}
|
}
|
||||||
pub fn new_port(port: u16) -> Self {
|
pub fn new_port(port: u16) -> Self {
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
use std::{
|
use std::{collections::HashMap, sync::Arc};
|
||||||
collections::HashMap,
|
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use axum::{routing::get, Extension, Router};
|
use axum::{routing::get, Extension, Router};
|
||||||
|
@ -11,7 +6,7 @@ use chrono::DateTime;
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thirtyfour::{DesiredCapabilities, WebDriver};
|
use thirtyfour::{DesiredCapabilities, WebDriver};
|
||||||
use tokio::sync::Mutex as AsyncMutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
comm::{Communicator, Message},
|
comm::{Communicator, Message},
|
||||||
|
@ -29,7 +24,7 @@ mod driver;
|
||||||
mod utd_app;
|
mod utd_app;
|
||||||
|
|
||||||
pub struct WebcheckApp {
|
pub struct WebcheckApp {
|
||||||
state: AsyncMutex<WebcheckAppState>,
|
state: Mutex<WebcheckAppState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WebcheckAppState {
|
struct WebcheckAppState {
|
||||||
|
@ -69,7 +64,7 @@ pub trait WebDriverChecker {
|
||||||
impl WebcheckApp {
|
impl WebcheckApp {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: AsyncMutex::new(WebcheckAppState {
|
state: Mutex::new(WebcheckAppState {
|
||||||
config: None,
|
config: None,
|
||||||
global_app_state: None,
|
global_app_state: None,
|
||||||
last_response: HashMap::new(),
|
last_response: HashMap::new(),
|
||||||
|
@ -116,14 +111,15 @@ impl WebcheckApp {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.await
|
||||||
.comm
|
.comm
|
||||||
.send_message(&Message {
|
.send_message(&Message {
|
||||||
subject: format!("webcheck {} changed", key),
|
subject: format!("webcheck {} changed", key),
|
||||||
body: format!("{} changed to {}", key, response),
|
body: format!("{} changed to {}", key, response),
|
||||||
mime: "text/plain",
|
mime: "text/plain",
|
||||||
priority: 0,
|
priority: 0,
|
||||||
})?;
|
})
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
|
@ -167,13 +163,9 @@ impl WebcheckApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl App for WebcheckApp {
|
impl App for WebcheckApp {
|
||||||
fn initialize(
|
async fn initialize(self: Arc<Self>, config: &'static Config, app_state: Arc<Mutex<AppState>>) {
|
||||||
self: Arc<Self>,
|
|
||||||
config: &'static Config,
|
|
||||||
app_state: Arc<Mutex<AppState>>,
|
|
||||||
) -> Pin<Box<dyn Future<Output = ()>>> {
|
|
||||||
Box::pin(async move {
|
|
||||||
let mut state = self.state.lock().await;
|
let mut state = self.state.lock().await;
|
||||||
state.config = Some(config);
|
state.config = Some(config);
|
||||||
state.global_app_state = Some(app_state);
|
state.global_app_state = Some(app_state);
|
||||||
|
@ -195,7 +187,6 @@ impl App for WebcheckApp {
|
||||||
|
|
||||||
let self_clone = self.clone();
|
let self_clone = self.clone();
|
||||||
tokio::spawn(self_clone.run_check_loops());
|
tokio::spawn(self_clone.run_check_loops());
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn api_routes(self: Arc<Self>) -> Router {
|
fn api_routes(self: Arc<Self>) -> Router {
|
||||||
|
|
128
src/comm/discord.rs
Normal file
128
src/comm/discord.rs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
use serenity::model::channel::Message;
|
||||||
|
use serenity::model::gateway::Ready;
|
||||||
|
use serenity::prelude::*;
|
||||||
|
|
||||||
|
use serenity::model::prelude::{ChannelId, UserId};
|
||||||
|
|
||||||
|
use crate::config::comm::DiscordConfig;
|
||||||
|
|
||||||
|
use super::{Communicator, MessageDigestor};
|
||||||
|
|
||||||
|
struct Handler {
|
||||||
|
state: Mutex<HandlerState>,
|
||||||
|
channel_id: ChannelId,
|
||||||
|
digestor: Arc<Vec<Box<dyn MessageDigestor + Send + Sync>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler {
|
||||||
|
fn new(
|
||||||
|
channel_id: ChannelId,
|
||||||
|
digestor: Arc<Vec<Box<dyn MessageDigestor + Send + Sync>>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
state: Mutex::new(HandlerState { bot_id: UserId(0) }),
|
||||||
|
channel_id,
|
||||||
|
digestor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HandlerState {
|
||||||
|
bot_id: UserId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl EventHandler for Handler {
|
||||||
|
async fn message(&self, ctx: Context, msg: Message) {
|
||||||
|
if msg.channel_id == self.channel_id {
|
||||||
|
info!("Received message: {:?}", msg.content);
|
||||||
|
let msg_generic = super::Message {
|
||||||
|
subject: "".to_string(),
|
||||||
|
body: msg.content.to_string(),
|
||||||
|
mime: MIME_DISCORD,
|
||||||
|
priority: 0,
|
||||||
|
};
|
||||||
|
for digestor in self.digestor.iter() {
|
||||||
|
match digestor.digest(&msg_generic).await {
|
||||||
|
Err(e) => {
|
||||||
|
info!("Error digesting message: {:?}", e);
|
||||||
|
}
|
||||||
|
Ok(Some(reply)) => {
|
||||||
|
if let Err(why) = msg.channel_id.say(&ctx.http, reply.body).await {
|
||||||
|
info!("Error sending message: {:?}", why);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn ready(&self, _: Context, ready: Ready) {
|
||||||
|
let mut state = self.state.lock().await;
|
||||||
|
state.bot_id = ready.user.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiscordCommunicator {
|
||||||
|
client: Client,
|
||||||
|
config: &'static DiscordConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiscordCommunicator {
|
||||||
|
pub async fn new(
|
||||||
|
config: &'static DiscordConfig,
|
||||||
|
digestor: Arc<Vec<Box<dyn MessageDigestor + Send + Sync>>>,
|
||||||
|
) -> Self {
|
||||||
|
let channel_id = ChannelId(config.channel_id);
|
||||||
|
|
||||||
|
let intents = GatewayIntents::non_privileged()
|
||||||
|
| GatewayIntents::GUILD_MESSAGES
|
||||||
|
| GatewayIntents::DIRECT_MESSAGES
|
||||||
|
| GatewayIntents::MESSAGE_CONTENT;
|
||||||
|
|
||||||
|
let handler = Handler::new(channel_id, digestor);
|
||||||
|
|
||||||
|
let mut client = Client::builder(config.token.to_string(), intents)
|
||||||
|
.event_handler(handler)
|
||||||
|
.await
|
||||||
|
.expect("Error creating client");
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
if let Err(why) = client.start().await {
|
||||||
|
info!("Client error: {:?}", why);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let client = Client::builder(config.token.to_string(), intents)
|
||||||
|
.await
|
||||||
|
.expect("Error creating client");
|
||||||
|
|
||||||
|
Self { client, config }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MIME_DISCORD: &'static str = "application/vnd.discord";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Communicator for DiscordCommunicator {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Discord"
|
||||||
|
}
|
||||||
|
fn supported_mimes(&self) -> Vec<&'static str> {
|
||||||
|
vec![MIME_DISCORD]
|
||||||
|
}
|
||||||
|
async fn send_message(&self, message: &super::Message) -> Result<()> {
|
||||||
|
let channel = ChannelId(self.config.channel_id);
|
||||||
|
channel
|
||||||
|
.say(&self.client.cache_and_http.http, message.body.to_string())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
use async_trait::async_trait;
|
||||||
use lettre::{
|
use lettre::{
|
||||||
message::header::ContentType, transport::smtp::authentication::Credentials, Transport,
|
message::header::ContentType, transport::smtp::authentication::Credentials, Transport,
|
||||||
};
|
};
|
||||||
|
@ -12,11 +13,16 @@ pub struct EmailCommunicator {
|
||||||
impl EmailCommunicator {
|
impl EmailCommunicator {
|
||||||
pub fn new(config: &'static Config) -> Self {
|
pub fn new(config: &'static Config) -> Self {
|
||||||
Self {
|
Self {
|
||||||
config: &config.comm.email,
|
config: &config
|
||||||
|
.comm
|
||||||
|
.email
|
||||||
|
.as_ref()
|
||||||
|
.expect("Email communicator not configured"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Communicator for EmailCommunicator {
|
impl Communicator for EmailCommunicator {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"email"
|
"email"
|
||||||
|
@ -24,7 +30,7 @@ impl Communicator for EmailCommunicator {
|
||||||
fn supported_mimes(&self) -> Vec<&'static str> {
|
fn supported_mimes(&self) -> Vec<&'static str> {
|
||||||
vec!["text/plain", "text/html"]
|
vec!["text/plain", "text/html"]
|
||||||
}
|
}
|
||||||
fn send_message(&self, message: &super::Message) -> anyhow::Result<()> {
|
async fn send_message(&self, message: &super::Message) -> anyhow::Result<()> {
|
||||||
let mailer = lettre::SmtpTransport::relay(&self.config.host)?
|
let mailer = lettre::SmtpTransport::relay(&self.config.host)?
|
||||||
.credentials(Credentials::new(
|
.credentials(Credentials::new(
|
||||||
self.config.username.clone(),
|
self.config.username.clone(),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::Communicator;
|
use super::Communicator;
|
||||||
use crate::config::{comm::GotifyConfig, Config};
|
use crate::config::{comm::GotifyConfig, Config};
|
||||||
|
use async_trait::async_trait;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
pub struct GotifyCommunicator {
|
pub struct GotifyCommunicator {
|
||||||
config: &'static GotifyConfig,
|
config: &'static GotifyConfig,
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,11 @@ pub struct GotifyCommunicator {
|
||||||
impl GotifyCommunicator {
|
impl GotifyCommunicator {
|
||||||
pub fn new(config: &'static Config) -> Self {
|
pub fn new(config: &'static Config) -> Self {
|
||||||
Self {
|
Self {
|
||||||
config: &config.comm.gotify,
|
config: &config
|
||||||
|
.comm
|
||||||
|
.gotify
|
||||||
|
.as_ref()
|
||||||
|
.expect("Gotify communicator not configured"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +53,7 @@ struct GotifyMessageExtrasClientDisplay {
|
||||||
content_type: String,
|
content_type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Communicator for GotifyCommunicator {
|
impl Communicator for GotifyCommunicator {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"gotify"
|
"gotify"
|
||||||
|
@ -56,13 +61,14 @@ impl Communicator for GotifyCommunicator {
|
||||||
fn supported_mimes(&self) -> Vec<&'static str> {
|
fn supported_mimes(&self) -> Vec<&'static str> {
|
||||||
vec!["text/plain", "text/markdown"]
|
vec!["text/plain", "text/markdown"]
|
||||||
}
|
}
|
||||||
fn send_message(&self, message: &super::Message) -> anyhow::Result<()> {
|
async fn send_message(&self, message: &super::Message) -> anyhow::Result<()> {
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let response = client
|
let response = client
|
||||||
.post(&format!("{}/message", self.config.url))
|
.post(&format!("{}/message", self.config.url))
|
||||||
.header("X-Gotify-Key", &self.config.token)
|
.header("X-Gotify-Key", &self.config.token)
|
||||||
.json::<GotifyMessage>(&message.into())
|
.json::<GotifyMessage>(&message.into())
|
||||||
.send()?;
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
if !response.status().is_success() {
|
if !response.status().is_success() {
|
||||||
anyhow::bail!("Gotify returned an error: {:?}", response);
|
anyhow::bail!("Gotify returned an error: {:?}", response);
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use async_trait::async_trait;
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
|
|
||||||
|
pub mod discord;
|
||||||
pub mod email;
|
pub mod email;
|
||||||
pub mod gotify;
|
pub mod gotify;
|
||||||
|
|
||||||
|
@ -28,10 +30,16 @@ impl Default for Message {
|
||||||
pub const MIME_PLAIN: &'static str = "text/plain";
|
pub const MIME_PLAIN: &'static str = "text/plain";
|
||||||
pub const MIME_HTML: &'static str = "text/html";
|
pub const MIME_HTML: &'static str = "text/html";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
pub trait Communicator {
|
pub trait Communicator {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
fn supported_mimes(&self) -> Vec<&'static str>;
|
fn supported_mimes(&self) -> Vec<&'static str>;
|
||||||
fn send_message(&self, message: &Message) -> Result<()>;
|
async fn send_message(&self, message: &Message) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait MessageDigestor {
|
||||||
|
async fn digest(&self, message: &Message) -> Result<Option<Message>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GlobalCommunicator {
|
pub struct GlobalCommunicator {
|
||||||
|
@ -70,6 +78,7 @@ impl GlobalCommunicator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Communicator for GlobalCommunicator {
|
impl Communicator for GlobalCommunicator {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"global"
|
"global"
|
||||||
|
@ -79,11 +88,11 @@ impl Communicator for GlobalCommunicator {
|
||||||
self.communicators.keys().map(|k| *k).collect()
|
self.communicators.keys().map(|k| *k).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_message(&self, message: &Message) -> Result<()> {
|
async fn send_message(&self, message: &Message) -> Result<()> {
|
||||||
let mime = message.mime;
|
let mime = message.mime;
|
||||||
if let Some(communicators) = self.communicators.get(mime) {
|
if let Some(communicators) = self.communicators.get(mime) {
|
||||||
for communicator in communicators {
|
for communicator in communicators {
|
||||||
if let Err(e) = communicator.send_message(message) {
|
if let Err(e) = communicator.send_message(message).await {
|
||||||
warn!("Failed to send message with {}: {}", communicator.name(), e);
|
warn!("Failed to send message with {}: {}", communicator.name(), e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@ use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub email: EmailConfig,
|
pub email: Option<EmailConfig>,
|
||||||
pub gotify: GotifyConfig,
|
pub gotify: Option<GotifyConfig>,
|
||||||
|
pub discord: Option<DiscordConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -22,3 +23,9 @@ pub struct GotifyConfig {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct DiscordConfig {
|
||||||
|
pub token: String,
|
||||||
|
pub channel_id: u64,
|
||||||
|
}
|
||||||
|
|
25
src/lib.rs
25
src/lib.rs
|
@ -1,4 +1,4 @@
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use apps::{med, webcheck};
|
use apps::{med, webcheck};
|
||||||
use axum::{http::Request, middleware::Next, response::Response, routing::get, Extension, Router};
|
use axum::{http::Request, middleware::Next, response::Response, routing::get, Extension, Router};
|
||||||
|
@ -7,6 +7,7 @@ use diesel::{sqlite, Connection};
|
||||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||||
use hyper::Method;
|
use hyper::Method;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
use tower_http::cors::{self, CorsLayer};
|
use tower_http::cors::{self, CorsLayer};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -85,7 +86,7 @@ pub async fn server_listen(router: Router) {
|
||||||
pub async fn main_server(dev: bool) {
|
pub async fn main_server(dev: bool) {
|
||||||
let config = config::get_config();
|
let config = config::get_config();
|
||||||
|
|
||||||
let apps: &mut [Arc<dyn App>] = &mut [
|
let apps: &mut [Arc<dyn App + Send + Sync>] = &mut [
|
||||||
Arc::new(server_info::ServerInfoApp::new()),
|
Arc::new(server_info::ServerInfoApp::new()),
|
||||||
Arc::new(auth::AuthApp::new()),
|
Arc::new(auth::AuthApp::new()),
|
||||||
Arc::new(canvas_lms::CanvasLMSApp::new()),
|
Arc::new(canvas_lms::CanvasLMSApp::new()),
|
||||||
|
@ -93,9 +94,29 @@ pub async fn main_server(dev: bool) {
|
||||||
Arc::new(webcheck::WebcheckApp::new()),
|
Arc::new(webcheck::WebcheckApp::new()),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let mut message_digestor = Vec::new();
|
||||||
|
for app in &mut *apps {
|
||||||
|
message_digestor.extend(app.clone().message_digestors());
|
||||||
|
}
|
||||||
|
let message_digestor = Arc::new(message_digestor);
|
||||||
|
|
||||||
let mut comm = GlobalCommunicator::new();
|
let mut comm = GlobalCommunicator::new();
|
||||||
|
if config.comm.discord.is_some() {
|
||||||
|
let discord_comm = Arc::new(
|
||||||
|
comm::discord::DiscordCommunicator::new(
|
||||||
|
config.comm.discord.as_ref().unwrap(),
|
||||||
|
message_digestor,
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
);
|
||||||
|
comm.add_communicator(discord_comm);
|
||||||
|
}
|
||||||
|
if config.comm.gotify.is_some() {
|
||||||
comm.add_communicator(Arc::new(comm::gotify::GotifyCommunicator::new(config)));
|
comm.add_communicator(Arc::new(comm::gotify::GotifyCommunicator::new(config)));
|
||||||
|
}
|
||||||
|
if config.comm.email.is_some() {
|
||||||
comm.add_communicator(Arc::new(comm::email::EmailCommunicator::new(config)));
|
comm.add_communicator(Arc::new(comm::email::EmailCommunicator::new(config)));
|
||||||
|
}
|
||||||
let app_state = Arc::new(Mutex::new(AppState {
|
let app_state = Arc::new(Mutex::new(AppState {
|
||||||
db: establish_db_connection(),
|
db: establish_db_connection(),
|
||||||
comm: comm,
|
comm: comm,
|
||||||
|
|
Loading…
Reference in a new issue