mirror of
https://github.com/revanced/revanced-discord-bot.git
synced 2025-04-29 22:14:28 +02:00
feat: migrate to command framework
This commit is contained in:
parent
2aa1559df5
commit
ba7b82a6de
5
.vscode/launch.json
vendored
5
.vscode/launch.json
vendored
@ -1,4 +1,7 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
@ -39,4 +42,4 @@
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,3 +0,0 @@
|
||||
{
|
||||
"conventionalCommits.scopes": ["clippy"]
|
||||
}
|
328
Cargo.lock
generated
328
Cargo.lock
generated
@ -28,9 +28,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.56"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
|
||||
checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -94,9 +94,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.1.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
@ -112,14 +112,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
checksum = "6127248204b9aba09a362f6c930ef6a78f2c1b2215f8a7b398c06e1083f17af0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time 0.1.44",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@ -143,14 +145,79 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.4"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decancer"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b136f741547a1813c4cbc9b5b214770b6812207191feb065ac238beeea882fc"
|
||||
|
||||
[[package]]
|
||||
name = "derivative"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.3"
|
||||
@ -289,9 +356,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.5"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
@ -329,9 +396,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.1"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@ -413,6 +480,12 @@ dependencies = [
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.3"
|
||||
@ -442,15 +515,15 @@ checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.58"
|
||||
version = "0.3.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
|
||||
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@ -463,9 +536,19 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
version = "0.2.127"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@ -578,6 +661,29 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@ -596,6 +702,37 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "poise"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6c01d22dcda434b0dfe956c60f6ac9b0352c4c2f4af852afb3155a971cd306d"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"derivative",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"poise_macros",
|
||||
"regex",
|
||||
"serenity",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "poise_macros"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ff861b6a52ec47bc54eb17424c025feeb040e82836036276c25dda045a8a0c"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
@ -604,18 +741,18 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -652,9 +789,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.13"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@ -730,16 +867,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "revanced-discord-bot"
|
||||
version = "0.1.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"decancer",
|
||||
"dirs",
|
||||
"dotenv",
|
||||
"poise",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_regex",
|
||||
"serenity",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
@ -774,18 +912,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9"
|
||||
checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
name = "rustversion"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
||||
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
@ -799,9 +949,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.138"
|
||||
version = "1.0.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
|
||||
checksum = "e590c437916fb6b221e1d00df6e3294f3fccd70ca7e92541c475d6ed6ef5fee2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -818,9 +968,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.138"
|
||||
version = "1.0.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
|
||||
checksum = "34b5b8d809babe02f538c2cfec6f2c1ed10804c0e5a6a041a049a4f5588ccc2e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -829,9 +979,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
|
||||
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@ -862,8 +1012,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serenity"
|
||||
version = "0.11.2"
|
||||
source = "git+https://github.com/serenity-rs/serenity.git#24c02845a0e9e7bde38dacd84607de973ffec10f"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82fd5e7b5858ad96e99d440138f34f5b98e1b959ebcd3a1036203b30e78eb788"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"async-tungstenite",
|
||||
@ -871,16 +1022,20 @@ dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"dashmap",
|
||||
"flate2",
|
||||
"futures",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"reqwest",
|
||||
"rustversion",
|
||||
"serde",
|
||||
"serde-value",
|
||||
"serde_json",
|
||||
"time 0.3.11",
|
||||
"time 0.3.12",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"typemap_rev",
|
||||
@ -909,9 +1064,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
||||
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
@ -936,10 +1094,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -948,18 +1112,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -988,11 +1152,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.11"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
|
||||
checksum = "74b7cc93fc23ba97fde84f7eea56c55d1ba183f495c6715defdfc7b9cb8c870f"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"serde",
|
||||
@ -1015,10 +1180,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.19.2"
|
||||
version = "1.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439"
|
||||
checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
@ -1075,9 +1241,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.35"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
|
||||
checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
@ -1099,9 +1265,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.28"
|
||||
version = "0.1.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
|
||||
checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@ -1120,9 +1286,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.14"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59"
|
||||
checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"sharded-slab",
|
||||
@ -1140,9 +1306,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.17.2"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5"
|
||||
checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"byteorder",
|
||||
@ -1188,9 +1354,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@ -1262,9 +1428,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
|
||||
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@ -1272,13 +1438,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
|
||||
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@ -1287,9 +1453,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.31"
|
||||
version = "0.4.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f"
|
||||
checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@ -1299,9 +1465,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
|
||||
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@ -1309,9 +1475,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
|
||||
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1322,15 +1488,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
|
||||
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.58"
|
||||
version = "0.3.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
|
||||
checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@ -1348,9 +1514,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.3"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf"
|
||||
checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
@ -6,7 +6,7 @@ keywords = ["ReVanced"]
|
||||
license = "GPL-3.0"
|
||||
name = "revanced-discord-bot"
|
||||
repository = "https://github.com/revanced/revanced-discord-bot"
|
||||
version = "0.1.0"
|
||||
version = "1.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
@ -17,14 +17,15 @@ codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
poise = "0.3.0"
|
||||
decancer = "1.4.1" # todo
|
||||
tokio = { version = "1.20.1", features = ["rt-multi-thread"] }
|
||||
dotenv = "0.15"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.0", features = ["rt-multi-thread"] }
|
||||
regex = "1.0"
|
||||
serde_regex = "1.1"
|
||||
chrono = "0.4"
|
||||
serenity = { git = "https://github.com/serenity-rs/serenity.git", default_features = false, features = ["client", "gateway", "rustls_backend", "model"] }
|
||||
dirs = "4.0.0"
|
||||
tracing = { version = "0.1", features = ["max_level_debug", "release_max_level_info"] }
|
||||
tracing-subscriber = "0.3"
|
@ -1,5 +1,8 @@
|
||||
{
|
||||
"$schema": "./configuration.schema.json",
|
||||
"general": {
|
||||
"embed_color": 0
|
||||
},
|
||||
"administrators": {
|
||||
"roles": [0],
|
||||
"users": [0]
|
||||
|
@ -5,6 +5,14 @@
|
||||
"description": "The Revanced Discord bot configuration schema.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"general": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"embed_color": {
|
||||
"$ref": "#/$defs/color"
|
||||
}
|
||||
}
|
||||
},
|
||||
"administrators": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -97,6 +105,10 @@
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"color": {
|
||||
"type": "integer",
|
||||
"description": "The color of the embed."
|
||||
},
|
||||
"roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@ -135,7 +147,7 @@
|
||||
"description": "The description of the embed."
|
||||
},
|
||||
"color": {
|
||||
"type": "integer",
|
||||
"$ref": "#/$defs/color",
|
||||
"description": "The color of the embed."
|
||||
},
|
||||
"fields": {
|
||||
|
46
src/commands/configuration.rs
Normal file
46
src/commands/configuration.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use crate::{utils::load_configuration, Context, Error};
|
||||
use tracing::debug;
|
||||
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
pub async fn reload(ctx: Context<'_>) -> Result<(), Error> {
|
||||
// Update the configuration
|
||||
let configuration = load_configuration();
|
||||
// Use the embed color from the updated configuration
|
||||
let embed_color = configuration.general.embed_color;
|
||||
// Also save the new configuration to the user data
|
||||
*ctx.data().write().await = configuration;
|
||||
|
||||
debug!("{:?} reloaded the configuration.", ctx.author().name);
|
||||
|
||||
ctx.send(|f| {
|
||||
f.ephemeral(true).embed(|f| {
|
||||
f.description("Successfully reloaded configuration.")
|
||||
.color(embed_color)
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
pub async fn stop(ctx: Context<'_>) -> Result<(), Error> {
|
||||
debug!("{:?} stopped the bot.", ctx.author().name);
|
||||
|
||||
let color = ctx.data().read().await.general.embed_color;
|
||||
ctx.send(|f| {
|
||||
f.ephemeral(true)
|
||||
.embed(|f| f.description("Stopped the bot.").color(color))
|
||||
})
|
||||
.await?;
|
||||
|
||||
ctx.discord().shard.shutdown_clean();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(prefix_command, slash_command, ephemeral = true)]
|
||||
pub async fn register(ctx: Context<'_>) -> Result<(), Error> {
|
||||
poise::builtins::register_application_commands_buttons(ctx).await?;
|
||||
Ok(())
|
||||
}
|
3
src/commands/mod.rs
Normal file
3
src/commands/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod configuration;
|
||||
pub mod moderation;
|
||||
pub mod utils;
|
1
src/commands/moderation.rs
Normal file
1
src/commands/moderation.rs
Normal file
@ -0,0 +1 @@
|
||||
// TODO mute/kick/ban/warn via database
|
1
src/commands/utils.rs
Normal file
1
src/commands/utils.rs
Normal file
@ -0,0 +1 @@
|
||||
// TODO role assign buttons, embeds
|
82
src/events/message_create.rs
Normal file
82
src/events/message_create.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
|
||||
use regex::Regex;
|
||||
use tracing::debug;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn contains_match(regex: &[Regex], text: &str) -> bool {
|
||||
regex.iter().any(|r| r.is_match(text))
|
||||
}
|
||||
|
||||
pub async fn message_create(ctx: &serenity::Context, new_message: &serenity::Message) {
|
||||
debug!("Received message: {}", new_message.content);
|
||||
if new_message.guild_id.is_none() || new_message.author.bot {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(message_response) = get_configuration_lock(&ctx)
|
||||
.await
|
||||
.read()
|
||||
.await
|
||||
.message_responses
|
||||
.iter()
|
||||
.find(|&response| {
|
||||
// check if the message was sent in a channel that is included in the responder
|
||||
response.includes.channels.iter().any(|&channel_id| channel_id == new_message.channel_id.0)
|
||||
// check if the message was sent by a user that is not excluded from the responder
|
||||
&& !response.excludes.roles.iter().any(|&role_id| role_id == new_message.author.id.0)
|
||||
// check if the message does not match any of the excludes
|
||||
&& !contains_match(&response.excludes.match_field, &new_message.content)
|
||||
// check if the message matches any of the includes
|
||||
&& contains_match(&response.includes.match_field, &new_message.content)
|
||||
})
|
||||
{
|
||||
let min_age = message_response.condition.user.server_age;
|
||||
|
||||
if min_age != 0 {
|
||||
let joined_at = ctx
|
||||
.http
|
||||
.get_member(new_message.guild_id.unwrap().0, new_message.author.id.0)
|
||||
.await
|
||||
.unwrap()
|
||||
.joined_at
|
||||
.unwrap()
|
||||
.unix_timestamp();
|
||||
|
||||
let must_joined_at =
|
||||
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(joined_at, 0), Utc);
|
||||
let but_joined_at = Utc::now() - Duration::days(min_age);
|
||||
|
||||
if must_joined_at <= but_joined_at {
|
||||
return;
|
||||
}
|
||||
|
||||
new_message.channel_id
|
||||
.send_message(&ctx.http, |m| {
|
||||
m.reference_message(new_message);
|
||||
match &message_response.response.embed {
|
||||
Some(embed) => m.embed(|e| {
|
||||
e.title(&embed.title)
|
||||
.description(&embed.description)
|
||||
.color(embed.color)
|
||||
.fields(embed.fields.iter().map(|field| {
|
||||
(field.name.clone(), field.value.clone(), field.inline)
|
||||
}))
|
||||
.footer(|f| {
|
||||
f.text(&embed.footer.text);
|
||||
f.icon_url(&embed.footer.icon_url)
|
||||
})
|
||||
.thumbnail(&embed.thumbnail.url)
|
||||
.image(&embed.image.url)
|
||||
.author(|a| {
|
||||
a.name(&embed.author.name).icon_url(&embed.author.icon_url)
|
||||
})
|
||||
}),
|
||||
None => m.content(message_response.response.message.as_ref().unwrap()),
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("Could not reply to message author.");
|
||||
}
|
||||
}
|
||||
}
|
94
src/events/mod.rs
Normal file
94
src/events/mod.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use poise::serenity_prelude::{self as serenity, Mutex, RwLock, ShardManager, UserId};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{model::application::Configuration, Error};
|
||||
|
||||
mod message_create;
|
||||
mod thread_create;
|
||||
|
||||
// Share the lock reference between the threads in serenity framework
|
||||
async fn get_configuration_lock(ctx: &serenity::Context) -> Arc<RwLock<Configuration>> {
|
||||
ctx.data
|
||||
.read()
|
||||
.await
|
||||
.get::<Configuration>()
|
||||
.expect("Expected Configuration in TypeMap.")
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub struct Handler<T> {
|
||||
options: poise::FrameworkOptions<T, Error>,
|
||||
data: T,
|
||||
bot_id: RwLock<Option<UserId>>,
|
||||
shard_manager: RwLock<Option<Arc<Mutex<ShardManager>>>>,
|
||||
}
|
||||
|
||||
// Custom handler to dispatch poise events
|
||||
impl<T: Send + Sync> Handler<T> {
|
||||
pub fn new(options: poise::FrameworkOptions<T, Error>, data: T) -> Self {
|
||||
Self {
|
||||
options,
|
||||
data,
|
||||
shard_manager: RwLock::new(None),
|
||||
bot_id: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_shard_manager(&self, shard_manager: Arc<Mutex<serenity::ShardManager>>) {
|
||||
*self.shard_manager.write().await = Some(shard_manager);
|
||||
}
|
||||
|
||||
async fn dispatch_poise_event(&self, ctx: &serenity::Context, event: &poise::Event<'_>) {
|
||||
let framework_data = poise::FrameworkContext {
|
||||
bot_id: self.bot_id.read().await.unwrap(),
|
||||
options: &self.options,
|
||||
user_data: &self.data,
|
||||
shard_manager: &(*self.shard_manager.read().await).clone().unwrap(), // Shard manager can be read between all poise events without locks
|
||||
};
|
||||
poise::dispatch_event(framework_data, ctx, event).await;
|
||||
}
|
||||
}
|
||||
|
||||
// Manually dispatch events from serenity to poise
|
||||
#[serenity::async_trait]
|
||||
impl serenity::EventHandler for Handler<Arc<RwLock<Configuration>>> {
|
||||
async fn ready(&self, _ctx: serenity::Context, ready: serenity::Ready) {
|
||||
*self.bot_id.write().await = Some(ready.user.id);
|
||||
}
|
||||
|
||||
async fn message(&self, ctx: serenity::Context, new_message: serenity::Message) {
|
||||
message_create::message_create(&ctx, &new_message).await;
|
||||
|
||||
self.dispatch_poise_event(&ctx, &poise::Event::Message { new_message })
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn interaction_create(&self, ctx: serenity::Context, interaction: serenity::Interaction) {
|
||||
self.dispatch_poise_event(&ctx, &poise::Event::InteractionCreate { interaction })
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn message_update(
|
||||
&self,
|
||||
ctx: serenity::Context,
|
||||
old_if_available: Option<serenity::Message>,
|
||||
new: Option<serenity::Message>,
|
||||
event: serenity::MessageUpdateEvent,
|
||||
) {
|
||||
self.dispatch_poise_event(
|
||||
&ctx,
|
||||
&poise::Event::MessageUpdate {
|
||||
old_if_available,
|
||||
new,
|
||||
event,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn thread_create(&self, ctx: serenity::Context, thread: serenity::GuildChannel) {
|
||||
thread_create::thread_create(&ctx, &thread).await;
|
||||
self.dispatch_poise_event(&ctx, &poise::Event::ThreadCreate { thread })
|
||||
.await;
|
||||
}
|
||||
}
|
29
src/events/thread_create.rs
Normal file
29
src/events/thread_create.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use tracing::{debug, error};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn thread_create(ctx: &serenity::Context, thread: &serenity::GuildChannel) {
|
||||
if thread.member.is_some() {
|
||||
debug!("Thread was joined. Block dispatch.");
|
||||
return;
|
||||
}
|
||||
|
||||
debug!("Thread created: {:?}", thread);
|
||||
|
||||
let configuration_lock = get_configuration_lock(&ctx).await;
|
||||
let thread_introductions = &configuration_lock.read().await.thread_introductions;
|
||||
|
||||
if let Some(introducer) = thread_introductions.iter().find(|introducer| {
|
||||
introducer
|
||||
.channels
|
||||
.iter()
|
||||
.any(|channel_id| *channel_id == thread.parent_id.unwrap().0)
|
||||
}) {
|
||||
if let Err(why) = thread
|
||||
.say(&ctx.http, &introducer.response.message.as_ref().unwrap())
|
||||
.await
|
||||
{
|
||||
error!("Error sending message: {:?}", why);
|
||||
}
|
||||
}
|
||||
}
|
329
src/main.rs
329
src/main.rs
@ -1,258 +1,107 @@
|
||||
use std::sync::Arc;
|
||||
use std::{env, process};
|
||||
use crate::model::application::Configuration;
|
||||
use std::{env, sync::Arc};
|
||||
|
||||
use chrono::{DateTime, Duration, NaiveDateTime, Utc};
|
||||
use model::application::Configuration;
|
||||
use regex::Regex;
|
||||
use serenity::client::{Context, EventHandler};
|
||||
use serenity::model::channel::{GuildChannel, Message};
|
||||
use serenity::model::gateway::Ready;
|
||||
use serenity::model::prelude::command::Command;
|
||||
use serenity::model::prelude::interaction::{Interaction, InteractionResponseType, MessageFlags};
|
||||
use serenity::prelude::{GatewayIntents, RwLock, TypeMapKey};
|
||||
use serenity::{async_trait, Client};
|
||||
use tracing::{debug, error, info};
|
||||
use commands::configuration;
|
||||
use events::Handler;
|
||||
use poise::serenity_prelude::{self as serenity, RwLock};
|
||||
use utils::load_configuration;
|
||||
|
||||
mod commands;
|
||||
mod events;
|
||||
mod logger;
|
||||
mod model;
|
||||
mod utils;
|
||||
|
||||
struct BotConfiguration;
|
||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||
type Context<'a> = poise::Context<'a, Arc<RwLock<Configuration>>, Error>;
|
||||
|
||||
impl TypeMapKey for BotConfiguration {
|
||||
type Value = Arc<RwLock<Configuration>>;
|
||||
}
|
||||
|
||||
pub struct Handler;
|
||||
|
||||
async fn get_configuration_lock(ctx: &Context) -> Arc<RwLock<Configuration>> {
|
||||
ctx.data
|
||||
.read()
|
||||
.await
|
||||
.get::<BotConfiguration>()
|
||||
.expect("Expected Configuration in TypeMap.")
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn contains_match(regex: &[Regex], text: &str) -> bool {
|
||||
regex.iter().any(|r| r.is_match(text))
|
||||
}
|
||||
|
||||
fn load_configuration() -> Configuration {
|
||||
Configuration::load().expect("Failed to load configuration")
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for Handler {
|
||||
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
||||
debug!("Created an interaction: {:?}", interaction);
|
||||
|
||||
if let Interaction::ApplicationCommand(command) = interaction {
|
||||
let configuration_lock = get_configuration_lock(&ctx).await;
|
||||
let mut configuration = configuration_lock.write().await;
|
||||
|
||||
let administrators = &configuration.administrators;
|
||||
let member = command.member.as_ref().unwrap();
|
||||
let user_id = member.user.id.0;
|
||||
let mut stop_command = false;
|
||||
let mut permission_granted = false;
|
||||
|
||||
// check if the user is an administrator
|
||||
if administrators.users.iter().any(|&id| user_id == id) {
|
||||
permission_granted = true
|
||||
}
|
||||
// check if the user has an administrating role
|
||||
if !permission_granted
|
||||
&& administrators
|
||||
.roles
|
||||
.iter()
|
||||
.any(|role_id| member.roles.iter().any(|member_role| member_role == role_id))
|
||||
{
|
||||
permission_granted = true
|
||||
}
|
||||
|
||||
let content = if permission_granted {
|
||||
match command.data.name.as_str() {
|
||||
"reload" => {
|
||||
debug!("{:?} reloaded the configuration.", command.user);
|
||||
|
||||
let new_config = load_configuration();
|
||||
|
||||
configuration.administrators = new_config.administrators;
|
||||
configuration.message_responses = new_config.message_responses;
|
||||
configuration.thread_introductions = new_config.thread_introductions;
|
||||
|
||||
"Successfully reloaded configuration.".to_string()
|
||||
},
|
||||
"stop" => {
|
||||
debug!("{:?} stopped the bot.", command.user);
|
||||
stop_command = true;
|
||||
"Stopped the bot.".to_string()
|
||||
},
|
||||
_ => "Unknown command.".to_string(),
|
||||
}
|
||||
} else {
|
||||
"You do not have permission to use this command.".to_string()
|
||||
};
|
||||
|
||||
// send the response
|
||||
if let Err(why) = command
|
||||
.create_interaction_response(&ctx.http, |response| {
|
||||
response
|
||||
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|message| {
|
||||
message.content(content).flags(MessageFlags::EPHEMERAL)
|
||||
})
|
||||
})
|
||||
.await
|
||||
{
|
||||
error!("Cannot respond to slash command: {}", why);
|
||||
}
|
||||
|
||||
if stop_command {
|
||||
process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn message(&self, ctx: Context, msg: Message) {
|
||||
debug!("Received message: {}", msg.content);
|
||||
if msg.guild_id.is_none() || msg.author.bot {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(message_response) =
|
||||
get_configuration_lock(&ctx).await.read().await.message_responses.iter().find(
|
||||
|&response| {
|
||||
// check if the message was sent in a channel that is included in the responder
|
||||
response.includes.channels.iter().any(|&channel_id| channel_id == msg.channel_id.0)
|
||||
// check if the message was sent by a user that is not excluded from the responder
|
||||
&& !response.excludes.roles.iter().any(|&role_id| role_id == msg.author.id.0)
|
||||
// check if the message does not match any of the excludes
|
||||
&& !contains_match(&response.excludes.match_field, &msg.content)
|
||||
// check if the message matches any of the includes
|
||||
&& contains_match(&response.includes.match_field, &msg.content)
|
||||
},
|
||||
) {
|
||||
let min_age = message_response.condition.user.server_age;
|
||||
|
||||
if min_age != 0 {
|
||||
let joined_at = ctx
|
||||
.http
|
||||
.get_member(msg.guild_id.unwrap().0, msg.author.id.0)
|
||||
.await
|
||||
.unwrap()
|
||||
.joined_at
|
||||
.unwrap()
|
||||
.unix_timestamp();
|
||||
|
||||
let must_joined_at =
|
||||
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(joined_at, 0), Utc);
|
||||
let but_joined_at = Utc::now() - Duration::days(min_age);
|
||||
|
||||
if must_joined_at <= but_joined_at {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.channel_id
|
||||
.send_message(&ctx.http, |m| {
|
||||
m.reference_message(&msg);
|
||||
match &message_response.response.embed {
|
||||
Some(embed) => m.embed(|e| {
|
||||
e.title(&embed.title)
|
||||
.description(&embed.description)
|
||||
.color(embed.color)
|
||||
.fields(embed.fields.iter().map(|field| {
|
||||
(field.name.clone(), field.value.clone(), field.inline)
|
||||
}))
|
||||
.footer(|f| {
|
||||
f.text(&embed.footer.text);
|
||||
f.icon_url(&embed.footer.icon_url)
|
||||
})
|
||||
.thumbnail(&embed.thumbnail.url)
|
||||
.image(&embed.image.url)
|
||||
.author(|a| {
|
||||
a.name(&embed.author.name).icon_url(&embed.author.icon_url)
|
||||
})
|
||||
}),
|
||||
None => m.content(message_response.response.message.as_ref().unwrap()),
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("Could not reply to message author.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn thread_create(&self, ctx: Context, thread: GuildChannel) {
|
||||
if thread.member.is_some() {
|
||||
debug!("Thread was joined. Block dispatch.");
|
||||
return;
|
||||
}
|
||||
|
||||
debug!("Thread created: {:?}", thread);
|
||||
|
||||
let configuration_lock = get_configuration_lock(&ctx).await;
|
||||
let configuration = configuration_lock.read().await;
|
||||
|
||||
if let Some(introducer) = &configuration.thread_introductions.iter().find(|introducer| {
|
||||
introducer.channels.iter().any(|channel_id| *channel_id == thread.parent_id.unwrap().0)
|
||||
}) {
|
||||
if let Err(why) =
|
||||
thread.say(&ctx.http, &introducer.response.message.as_ref().unwrap()).await
|
||||
{
|
||||
error!("Error sending message: {:?}", why);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn ready(&self, ctx: Context, ready: Ready) {
|
||||
info!("Connected as {}", ready.user.name);
|
||||
|
||||
for (cmd, description) in
|
||||
[("repload", "Reloads the configuration."), ("stop", "Stop the Discord bot.")]
|
||||
{
|
||||
Command::create_global_application_command(&ctx.http, |command| {
|
||||
command.name(cmd).description(description)
|
||||
})
|
||||
.await
|
||||
.expect("Could not create command.");
|
||||
}
|
||||
}
|
||||
impl serenity::TypeMapKey for Configuration {
|
||||
type Value = Arc<RwLock<Configuration>>;
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Initialize the logging framework.
|
||||
logger::init();
|
||||
// Initialize the logging framework
|
||||
logger::init();
|
||||
|
||||
// Set up the configuration.
|
||||
let configuration = load_configuration();
|
||||
// Load environment variables from .env file
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
// Load environment variables from .env file
|
||||
dotenv::dotenv().ok();
|
||||
// Define poise framework commands (also in src/commands/mod.rs for serenity framework's manually dispatched events)
|
||||
let mut commands = vec![
|
||||
configuration::register(),
|
||||
configuration::reload(),
|
||||
configuration::stop(),
|
||||
];
|
||||
poise::set_qualified_names(&mut commands);
|
||||
|
||||
// Get the Discord authorization token.
|
||||
let token = env::var("DISCORD_AUTHORIZATION_TOKEN")
|
||||
.expect("Could not load Discord authorization token");
|
||||
if token.len() != 70 {
|
||||
error!("Invalid Discord authorization token.");
|
||||
process::exit(1);
|
||||
}
|
||||
let configuration = Arc::new(RwLock::new(load_configuration()));
|
||||
|
||||
// Create the Discord bot client.
|
||||
let mut client = Client::builder(
|
||||
&token,
|
||||
GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT,
|
||||
)
|
||||
.event_handler(Handler)
|
||||
.await
|
||||
.expect("Failed to create client");
|
||||
let handler = Arc::new(Handler::new(
|
||||
poise::FrameworkOptions {
|
||||
commands,
|
||||
on_error: |error| {
|
||||
Box::pin(async {
|
||||
poise::samples::on_error(error)
|
||||
.await
|
||||
.unwrap_or_else(|error| tracing::error!("{}", error));
|
||||
})
|
||||
},
|
||||
command_check: Some(|ctx| {
|
||||
Box::pin(async move {
|
||||
if let Some(member) = ctx.author_member().await {
|
||||
let administrators = &ctx.data().read().await.administrators;
|
||||
|
||||
// Save the configuration.
|
||||
client.data.write().await.insert::<BotConfiguration>(Arc::new(RwLock::new(configuration)));
|
||||
if !(administrators
|
||||
.users
|
||||
// Check if the user is an administrator
|
||||
.contains(&member.user.id.0)
|
||||
|| administrators
|
||||
.roles
|
||||
.iter()
|
||||
// Has one of the administative roles
|
||||
.any(|&role_id| {
|
||||
member
|
||||
.roles
|
||||
.iter()
|
||||
.any(|member_role| member_role.0 == role_id)
|
||||
}))
|
||||
{
|
||||
return Ok(false); // Not an administrator, don't allow command execution
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
})
|
||||
}),
|
||||
listener: |_ctx, event, _framework, _data| {
|
||||
Box::pin(async move {
|
||||
tracing::info!("{:?}", event.name());
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
configuration.clone(), // Pass configuration as user data for the framework
|
||||
));
|
||||
|
||||
// Start the Discord bot.
|
||||
client.start().await.expect("failed to start discord bot");
|
||||
let mut client = serenity::Client::builder(
|
||||
env::var("DISCORD_AUTHORIZATION_TOKEN")
|
||||
.expect("Could not load Discord authorization token"),
|
||||
serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT,
|
||||
)
|
||||
.event_handler_arc(handler.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
info!("Client started.");
|
||||
client
|
||||
.data
|
||||
.write()
|
||||
.await
|
||||
.insert::<Configuration>(configuration);
|
||||
|
||||
handler
|
||||
.set_shard_manager(client.shard_manager.clone())
|
||||
.await;
|
||||
|
||||
client.start().await.unwrap();
|
||||
}
|
||||
|
@ -10,9 +10,10 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct Configuration {
|
||||
pub administrators: Administrators,
|
||||
pub thread_introductions: Vec<Introduction>,
|
||||
pub message_responses: Vec<MessageResponse>,
|
||||
pub general: General,
|
||||
pub administrators: Administrators,
|
||||
pub thread_introductions: Vec<Introduction>,
|
||||
pub message_responses: Vec<MessageResponse>,
|
||||
}
|
||||
|
||||
const CONFIG_PATH: &str = "configuration.json";
|
||||
@ -58,6 +59,11 @@ impl Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct General {
|
||||
pub embed_color: i32,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct Administrators {
|
||||
pub roles: Vec<u64>,
|
||||
|
32
src/utils.rs
Normal file
32
src/utils.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use poise::serenity_prelude::CreateEmbed;
|
||||
|
||||
use crate::model::application::Configuration;
|
||||
|
||||
pub(crate) fn load_configuration() -> Configuration {
|
||||
Configuration::load().expect("Failed to load configuration")
|
||||
}
|
||||
|
||||
trait PoiseEmbed {
|
||||
fn create_embed(self, embed: &mut CreateEmbed) -> &mut CreateEmbed;
|
||||
}
|
||||
|
||||
impl PoiseEmbed for crate::model::application::Embed {
|
||||
fn create_embed(self, embed: &mut CreateEmbed) -> &mut CreateEmbed {
|
||||
embed
|
||||
.title(self.title)
|
||||
.description(self.description)
|
||||
.color(self.color)
|
||||
.fields(
|
||||
self.fields
|
||||
.iter()
|
||||
.map(|field| (field.name.clone(), field.value.clone(), field.inline)),
|
||||
)
|
||||
.footer(|f| {
|
||||
f.text(self.footer.text);
|
||||
f.icon_url(self.footer.icon_url)
|
||||
})
|
||||
.thumbnail(self.thumbnail.url)
|
||||
.image(self.image.url)
|
||||
.author(|a| a.name(self.author.name).icon_url(self.author.icon_url))
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user