forked from jmug/stoatchat
Compare commits
21
Commits
20230615-1
...
20230810-3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
430df03b20 | ||
|
|
f5f70287e7 | ||
|
|
a0580c5f5a | ||
|
|
121a9cd87c | ||
|
|
d87d608d9e | ||
|
|
bd9ecc1519 | ||
|
|
f5a113da47 | ||
|
|
11fdb0c1dc | ||
|
|
e0033ceb12 | ||
|
|
7318ec6ef6 | ||
|
|
a516c7adcf | ||
|
|
c48109ca66 | ||
|
|
f0ef781cd7 | ||
|
|
d9dd040db4 | ||
|
|
fec947b4c8 | ||
|
|
ec6df36c25 | ||
|
|
76150db293 | ||
|
|
815c3fe99e | ||
|
|
54878e8e8d | ||
|
|
6e4798f1d4 | ||
|
|
55bd6fb087 |
No files matched your search
@@ -4,3 +4,4 @@ target
|
|||||||
.env
|
.env
|
||||||
|
|
||||||
.vercel
|
.vercel
|
||||||
|
.DS_Store
|
||||||
Generated
+136
-37
@@ -421,6 +421,12 @@ version = "0.13.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.21.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "binascii"
|
name = "binascii"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -519,7 +525,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"uuid",
|
"uuid 0.8.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -798,12 +804,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "debugid"
|
name = "debugid"
|
||||||
version = "0.7.3"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6ee87af31d84ef885378aebca32be3d682b0e0dc119d5b4860a2c5bb5046730"
|
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"uuid",
|
"uuid 1.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1001,6 +1007,18 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "findshlibs"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -1548,9 +1566,9 @@ checksum = "90f97a5f38dd3ccfbe7aa80f4a0c00930f21b922c74195be0201c51028f22dcf"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.1"
|
version = "1.9.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.1.0",
|
"autocfg 1.1.0",
|
||||||
"hashbrown 0.12.1",
|
"hashbrown 0.12.1",
|
||||||
@@ -1613,11 +1631,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iso8601-timestamp"
|
name = "iso8601-timestamp"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "368d05885f276773b79b4578869a60ab91e880415d17fb2a8d193aea74188a29"
|
checksum = "3bd7663ae5b511b3d13c7d374dbe73b03da3da8e66b83950390173bc7c7325cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array 0.14.5",
|
"generic-array 0.14.5",
|
||||||
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"time 0.3.17",
|
"time 0.3.17",
|
||||||
]
|
]
|
||||||
@@ -2005,7 +2024,7 @@ dependencies = [
|
|||||||
"trust-dns-proto",
|
"trust-dns-proto",
|
||||||
"trust-dns-resolver",
|
"trust-dns-resolver",
|
||||||
"typed-builder",
|
"typed-builder",
|
||||||
"uuid",
|
"uuid 0.8.2",
|
||||||
"webpki-roots",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2231,11 +2250,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_info"
|
name = "os_info"
|
||||||
version = "3.4.0"
|
version = "3.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0eca3ecae1481e12c3d9379ec541b238a16f0b75c9a409942daa8ec20dbfdb62"
|
checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
|
"serde",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2496,6 +2516,20 @@ dependencies = [
|
|||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prometheus"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"fnv",
|
||||||
|
"lazy_static",
|
||||||
|
"memchr",
|
||||||
|
"parking_lot",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "querystring"
|
name = "querystring"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -2843,7 +2877,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
@@ -2860,7 +2894,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -2868,7 +2902,8 @@ dependencies = [
|
|||||||
"authifier",
|
"authifier",
|
||||||
"bson",
|
"bson",
|
||||||
"futures",
|
"futures",
|
||||||
"iso8601-timestamp 0.2.10",
|
"indexmap",
|
||||||
|
"iso8601-timestamp 0.2.11",
|
||||||
"log",
|
"log",
|
||||||
"mongodb",
|
"mongodb",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
@@ -2891,7 +2926,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -2920,6 +2955,7 @@ dependencies = [
|
|||||||
"rocket",
|
"rocket",
|
||||||
"rocket_authifier",
|
"rocket_authifier",
|
||||||
"rocket_empty",
|
"rocket_empty",
|
||||||
|
"rocket_prometheus",
|
||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -2931,8 +2967,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"iso8601-timestamp 0.2.11",
|
||||||
"revolt-permissions",
|
"revolt-permissions",
|
||||||
"revolt_optional_struct",
|
"revolt_optional_struct",
|
||||||
"schemars",
|
"schemars",
|
||||||
@@ -2942,7 +2980,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -2956,7 +2994,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"log",
|
"log",
|
||||||
@@ -2967,7 +3005,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
@@ -3020,7 +3058,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"revolt_okapi",
|
"revolt_okapi",
|
||||||
"revolt_rocket_okapi",
|
"revolt_rocket_okapi",
|
||||||
@@ -3243,6 +3281,16 @@ dependencies = [
|
|||||||
"uncased",
|
"uncased",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rocket_prometheus"
|
||||||
|
version = "0.10.0-rc.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3e70efcf6b0234723d0d295b95d64283ed02bb98a8a3f0c8c7ebb5b8da69165"
|
||||||
|
dependencies = [
|
||||||
|
"prometheus",
|
||||||
|
"rocket",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-argon2"
|
name = "rust-argon2"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -3431,39 +3479,44 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry"
|
name = "sentry"
|
||||||
version = "0.25.0"
|
version = "0.31.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2d23af89cf3e40dffb53f974e9a21653353b3e21cf51633aa58006f2a0caf8a"
|
checksum = "01b0ad16faa5d12372f914ed40d00bda21a6d1bdcc99264c5e5e1c9495cf3654"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"httpdate 1.0.2",
|
"httpdate 1.0.2",
|
||||||
|
"native-tls",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"sentry-backtrace",
|
"sentry-backtrace",
|
||||||
"sentry-contexts",
|
"sentry-contexts",
|
||||||
"sentry-core",
|
"sentry-core",
|
||||||
|
"sentry-debug-images",
|
||||||
"sentry-panic",
|
"sentry-panic",
|
||||||
|
"sentry-tracing",
|
||||||
"tokio 1.18.2",
|
"tokio 1.18.2",
|
||||||
|
"ureq",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-backtrace"
|
name = "sentry-backtrace"
|
||||||
version = "0.25.0"
|
version = "0.31.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8158a446429420acdf6a4f75192ee8929da16a0c41c89a1c34b2e0f1eaebcc02"
|
checksum = "11f2ee8f147bb5f22ac59b5c35754a759b9a6f6722402e2a14750b2a63fc59bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"lazy_static",
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
"sentry-core",
|
"sentry-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-contexts"
|
name = "sentry-contexts"
|
||||||
version = "0.25.0"
|
version = "0.31.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a3bda8a1e3213f1944da2d42f3081ea9f3717105bb2a6b0a8fe4f5e603010a3"
|
checksum = "dcd133362c745151eeba0ac61e3ba8350f034e9fe7509877d08059fe1d7720c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hostname",
|
"hostname",
|
||||||
"libc",
|
"libc",
|
||||||
|
"os_info",
|
||||||
"rustc_version 0.4.0",
|
"rustc_version 0.4.0",
|
||||||
"sentry-core",
|
"sentry-core",
|
||||||
"uname",
|
"uname",
|
||||||
@@ -3471,11 +3524,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-core"
|
name = "sentry-core"
|
||||||
version = "0.25.0"
|
version = "0.31.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56333f11be3a78131c67637f7611339df8af7ad9af831226585a457df75f9e3b"
|
checksum = "7163491708804a74446642ff2c80b3acd668d4b9e9f497f85621f3d250fd012b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"once_cell",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"sentry-types",
|
"sentry-types",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -3483,20 +3536,43 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-panic"
|
name = "sentry-debug-images"
|
||||||
version = "0.25.0"
|
version = "0.31.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b957b1965c450acd220a27806fe1f2dec998d393973ebae797936b12df1c7416"
|
checksum = "6a5003d7ff08aa3b2b76994080b183e8cfa06c083e280737c9cee02ca1c70f5e"
|
||||||
|
dependencies = [
|
||||||
|
"findshlibs",
|
||||||
|
"once_cell",
|
||||||
|
"sentry-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sentry-panic"
|
||||||
|
version = "0.31.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4dfe8371c9b2e126a8b64f6fefa54cef716ff2a50e63b5558a48b899265bccd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sentry-backtrace",
|
"sentry-backtrace",
|
||||||
"sentry-core",
|
"sentry-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sentry-types"
|
name = "sentry-tracing"
|
||||||
version = "0.25.0"
|
version = "0.31.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "825fd3382e2397007499a910e0184e55f7837cb0df4af30ae62bd2123e2ebcd6"
|
checksum = "5aca8b88978677a27ee1a91beafe4052306c474c06f582321fde72d2e2cc2f7f"
|
||||||
|
dependencies = [
|
||||||
|
"sentry-backtrace",
|
||||||
|
"sentry-core",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sentry-types"
|
||||||
|
version = "0.31.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e7a88e0c1922d19b3efee12a8215f6a8a806e442e665ada71cc222cab72985f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"debugid",
|
"debugid",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
@@ -3506,7 +3582,7 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.17",
|
"time 0.3.17",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid 1.4.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4378,6 +4454,19 @@ version = "0.7.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ureq"
|
||||||
|
version = "2.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.2",
|
||||||
|
"log",
|
||||||
|
"native-tls",
|
||||||
|
"once_cell",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.2.2"
|
version = "2.2.2"
|
||||||
@@ -4407,6 +4496,16 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "validator"
|
name = "validator"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ Note: `january`, `autumn`, and `vortex` are yet to be moved into this monorepo.
|
|||||||
- [Revolt Testers Server](https://app.revolt.chat/invite/Testers)
|
- [Revolt Testers Server](https://app.revolt.chat/invite/Testers)
|
||||||
- [Contribution Guide](https://developers.revolt.chat/contributing)
|
- [Contribution Guide](https://developers.revolt.chat/contributing)
|
||||||
|
|
||||||
|
## Minimum Supported Rust Version
|
||||||
|
|
||||||
|
Rust 1.70 or higher.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
The contribution guide is located at [developers.revolt.chat/contributing](https://developers.revolt.chat/contributing).
|
The contribution guide is located at [developers.revolt.chat/contributing](https://developers.revolt.chat/contributing).
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
Beyond using Cargo format and Clippy, there are some specific code style guidelines laid out in this document for different parts of the project.
|
Beyond using Cargo format and Clippy, there are some specific code style guidelines laid out in this document for different parts of the project.
|
||||||
|
|
||||||
|
## Writing Style
|
||||||
|
|
||||||
|
- Shorten "identifier" to "Id" with that exact casing, i.e. Server Id.
|
||||||
|
|
||||||
## `core/database` crate
|
## `core/database` crate
|
||||||
|
|
||||||
w.r.t. `model.rs` files
|
w.r.t. `model.rs` files
|
||||||
@@ -22,6 +26,7 @@ w.r.t. `model.rs` files
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
- `auto_derived!` macro accepts multiple entries and should be used as such:
|
- `auto_derived!` macro accepts multiple entries and should be used as such:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
auto_derived!(
|
auto_derived!(
|
||||||
/// Optional fields on server object
|
/// Optional fields on server object
|
||||||
@@ -31,6 +36,7 @@ w.r.t. `model.rs` files
|
|||||||
pub enum FieldsRole { .. }
|
pub enum FieldsRole { .. }
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
- If special serialisation conditions are required, such as checking if a boolean is false, use the existing definitions for these functions from the crate root:
|
- If special serialisation conditions are required, such as checking if a boolean is false, use the existing definitions for these functions from the crate root:
|
||||||
```rust
|
```rust
|
||||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -25,7 +25,11 @@ serde = "1.0.136"
|
|||||||
# async
|
# async
|
||||||
futures = "0.3.21"
|
futures = "0.3.21"
|
||||||
async-tungstenite = { version = "0.17.0", features = ["async-std-runtime"] }
|
async-tungstenite = { version = "0.17.0", features = ["async-std-runtime"] }
|
||||||
async-std = { version = "1.8.0", features = ["tokio1", "tokio02", "attributes"] }
|
async-std = { version = "1.8.0", features = [
|
||||||
|
"tokio1",
|
||||||
|
"tokio02",
|
||||||
|
"attributes",
|
||||||
|
] }
|
||||||
|
|
||||||
# core
|
# core
|
||||||
revolt-presence = { path = "../core/presence", features = [ "redis-is-patched" ] }
|
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
|
||||||
@@ -1,31 +1,34 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = [ "Paul Makles <me@insrt.uk>" ]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: Database Implementation"
|
description = "Revolt Backend: Database Implementation"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# Databases
|
# Databases
|
||||||
mongodb = [ "dep:mongodb", "bson" ]
|
mongodb = ["dep:mongodb", "bson"]
|
||||||
|
|
||||||
# ... Other
|
# ... Other
|
||||||
async-std-runtime = [ "async-std" ]
|
async-std-runtime = ["async-std"]
|
||||||
rocket-impl = [ "rocket", "schemars" ]
|
rocket-impl = ["rocket", "schemars"]
|
||||||
redis-is-patched = [ "revolt-presence/redis-is-patched" ]
|
redis-is-patched = ["revolt-presence/redis-is-patched"]
|
||||||
|
|
||||||
# Default Features
|
# Default Features
|
||||||
default = [ "mongodb", "async-std-runtime" ]
|
default = ["mongodb", "async-std-runtime"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-result = { version = "0.6.3", path = "../result" }
|
revolt-result = { version = "0.6.6", path = "../result" }
|
||||||
revolt-models = { version = "0.6.3", path = "../models" }
|
revolt-models = { version = "0.6.6", path = "../models" }
|
||||||
revolt-presence = { version = "0.6.3", path = "../presence" }
|
revolt-presence = { version = "0.6.6", path = "../presence" }
|
||||||
revolt-permissions = { version = "0.6.3", path = "../permissions", features = [ "serde", "bson" ] }
|
revolt-permissions = { version = "0.6.6", path = "../permissions", features = [
|
||||||
|
"serde",
|
||||||
|
"bson",
|
||||||
|
] }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
@@ -33,6 +36,7 @@ rand = "0.8.5"
|
|||||||
ulid = "1.0.0"
|
ulid = "1.0.0"
|
||||||
nanoid = "0.4.0"
|
nanoid = "0.4.0"
|
||||||
once_cell = "1.17"
|
once_cell = "1.17"
|
||||||
|
indexmap = "1.9.1"
|
||||||
|
|
||||||
# Serialisation
|
# Serialisation
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
@@ -61,7 +65,9 @@ async-std = { version = "1.8.0", features = ["attributes"], optional = true }
|
|||||||
|
|
||||||
# Rocket Impl
|
# Rocket Impl
|
||||||
schemars = { version = "0.8.8", optional = true }
|
schemars = { version = "0.8.8", optional = true }
|
||||||
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"], optional = true }
|
rocket = { version = "0.5.0-rc.2", default-features = false, features = [
|
||||||
|
"json",
|
||||||
|
], optional = true }
|
||||||
|
|
||||||
# Authifier
|
# Authifier
|
||||||
authifier = { version = "1.0" }
|
authifier = { version = "1.0" }
|
||||||
@@ -3,29 +3,28 @@ use std::{collections::HashMap, sync::Arc};
|
|||||||
use futures::lock::Mutex;
|
use futures::lock::Mutex;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AccountStrike, Bot, Channel, File, Member, MemberCompositeKey, Server, User, UserSettings,
|
Bot, Channel, ChannelCompositeKey, ChannelUnread, Emoji, File, Invite, Member,
|
||||||
Webhook,
|
MemberCompositeKey, Server, ServerBan, User, UserSettings, Webhook,
|
||||||
};
|
};
|
||||||
|
|
||||||
database_derived!(
|
database_derived!(
|
||||||
/// Reference implementation
|
/// Reference implementation
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ReferenceDb {
|
pub struct ReferenceDb {
|
||||||
pub account_strikes: Arc<Mutex<HashMap<String, AccountStrike>>>,
|
|
||||||
pub bots: Arc<Mutex<HashMap<String, Bot>>>,
|
pub bots: Arc<Mutex<HashMap<String, Bot>>>,
|
||||||
|
pub channels: Arc<Mutex<HashMap<String, Channel>>>,
|
||||||
|
pub channel_invites: Arc<Mutex<HashMap<String, Invite>>>,
|
||||||
|
pub channel_unreads: Arc<Mutex<HashMap<ChannelCompositeKey, ChannelUnread>>>,
|
||||||
pub channel_webhooks: Arc<Mutex<HashMap<String, Webhook>>>,
|
pub channel_webhooks: Arc<Mutex<HashMap<String, Webhook>>>,
|
||||||
|
pub emojis: Arc<Mutex<HashMap<String, Emoji>>>,
|
||||||
pub user_settings: Arc<Mutex<HashMap<String, UserSettings>>>,
|
pub user_settings: Arc<Mutex<HashMap<String, UserSettings>>>,
|
||||||
pub users: Arc<Mutex<HashMap<String, User>>>,
|
pub users: Arc<Mutex<HashMap<String, User>>>,
|
||||||
|
pub server_bans: Arc<Mutex<HashMap<MemberCompositeKey, ServerBan>>>,
|
||||||
pub server_members: Arc<Mutex<HashMap<MemberCompositeKey, Member>>>,
|
pub server_members: Arc<Mutex<HashMap<MemberCompositeKey, Member>>>,
|
||||||
pub servers: Arc<Mutex<HashMap<String, Server>>>,
|
pub servers: Arc<Mutex<HashMap<String, Server>>>,
|
||||||
pub files: Arc<Mutex<HashMap<String, File>>>,
|
pub files: Arc<Mutex<HashMap<String, File>>>,
|
||||||
pub server_bans: Arc<Mutex<HashMap<String, ()>>>,
|
|
||||||
pub safety_reports: Arc<Mutex<HashMap<String, ()>>>,
|
pub safety_reports: Arc<Mutex<HashMap<String, ()>>>,
|
||||||
pub safety_snapshots: Arc<Mutex<HashMap<String, ()>>>,
|
pub safety_snapshots: Arc<Mutex<HashMap<String, ()>>>,
|
||||||
pub emoji: Arc<Mutex<HashMap<String, ()>>>,
|
|
||||||
pub messages: Arc<Mutex<HashMap<String, ()>>>,
|
pub messages: Arc<Mutex<HashMap<String, ()>>>,
|
||||||
pub channels: Arc<Mutex<HashMap<String, Channel>>>,
|
|
||||||
pub channel_invites: Arc<Mutex<HashMap<String, ()>>>,
|
|
||||||
pub channel_unreads: Arc<Mutex<HashMap<String, ()>>>,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -2,7 +2,9 @@ use authifier::AuthifierEvent;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use revolt_models::v0::{
|
use revolt_models::v0::{
|
||||||
Channel, FieldsChannel, FieldsWebhook, PartialChannel, PartialWebhook, Webhook,
|
Channel, Emoji, FieldsChannel, FieldsMember, FieldsRole, FieldsServer, FieldsWebhook,
|
||||||
|
MemberCompositeKey, PartialChannel, PartialMember, PartialRole, PartialServer, PartialWebhook,
|
||||||
|
Server, UserSettings, Webhook,
|
||||||
};
|
};
|
||||||
use revolt_result::Error;
|
use revolt_result::Error;
|
||||||
|
|
||||||
@@ -101,8 +103,7 @@ pub enum EventV1 {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// Bulk delete messages
|
/// Bulk delete messages
|
||||||
BulkMessageDelete { channel: String, ids: Vec<String> },
|
BulkMessageDelete { channel: String, ids: Vec<String> },*/
|
||||||
|
|
||||||
/// New server
|
/// New server
|
||||||
ServerCreate {
|
ServerCreate {
|
||||||
id: String,
|
id: String,
|
||||||
@@ -144,7 +145,7 @@ pub enum EventV1 {
|
|||||||
/// Server role deleted
|
/// Server role deleted
|
||||||
ServerRoleDelete { id: String, role_id: String },
|
ServerRoleDelete { id: String, role_id: String },
|
||||||
|
|
||||||
/// Update existing user
|
/*/// Update existing user
|
||||||
UserUpdate {
|
UserUpdate {
|
||||||
id: String,
|
id: String,
|
||||||
data: PartialUser,
|
data: PartialUser,
|
||||||
@@ -158,12 +159,11 @@ pub enum EventV1 {
|
|||||||
user: User,
|
user: User,
|
||||||
// ! this field can be deprecated
|
// ! this field can be deprecated
|
||||||
status: RelationshipStatus,
|
status: RelationshipStatus,
|
||||||
},
|
},*/
|
||||||
|
|
||||||
/// Settings updated remotely
|
/// Settings updated remotely
|
||||||
UserSettingsUpdate { id: String, update: UserSettings },
|
UserSettingsUpdate { id: String, update: UserSettings },
|
||||||
|
|
||||||
/// User has been platform banned or deleted their account
|
/*/// User has been platform banned or deleted their account
|
||||||
///
|
///
|
||||||
/// Clients should remove the following associated data:
|
/// Clients should remove the following associated data:
|
||||||
/// - Messages
|
/// - Messages
|
||||||
@@ -172,15 +172,14 @@ pub enum EventV1 {
|
|||||||
/// - Server Memberships
|
/// - Server Memberships
|
||||||
///
|
///
|
||||||
/// User flags are specified to explain why a wipe is occurring though not all reasons will necessarily ever appear.
|
/// User flags are specified to explain why a wipe is occurring though not all reasons will necessarily ever appear.
|
||||||
UserPlatformWipe { user_id: String, flags: i32 },
|
UserPlatformWipe { user_id: String, flags: i32 }, */
|
||||||
|
|
||||||
/// New emoji
|
/// New emoji
|
||||||
EmojiCreate(Emoji),
|
EmojiCreate(Emoji),
|
||||||
|
|
||||||
/// Delete emoji
|
/// Delete emoji
|
||||||
EmojiDelete { id: String },
|
EmojiDelete { id: String },
|
||||||
|
|
||||||
/// New report
|
/*/// New report
|
||||||
ReportCreate(Report), */
|
ReportCreate(Report), */
|
||||||
/// New channel
|
/// New channel
|
||||||
ChannelCreate(Channel),
|
ChannelCreate(Channel),
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
use revolt_permissions::DEFAULT_WEBHOOK_PERMISSIONS;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
@@ -696,7 +697,9 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if revision <= 19 {
|
if revision <= 19 {
|
||||||
info!("Running migration [revision 19 / 27-02-2023]: Create report / snapshot collections, migrate to new model if applicable.");
|
info!(
|
||||||
|
"Running migration [revision 19 / 27-02-2023]: Create report / snapshot collections."
|
||||||
|
);
|
||||||
|
|
||||||
db.db()
|
db.db()
|
||||||
.create_collection("safety_reports", None)
|
.create_collection("safety_reports", None)
|
||||||
@@ -707,19 +710,6 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
.create_collection("safety_snapshots", None)
|
.create_collection("safety_snapshots", None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
db.col::<Document>("safety_reports")
|
|
||||||
.update_many(
|
|
||||||
doc! {},
|
|
||||||
doc! {
|
|
||||||
"$set": {
|
|
||||||
"status": "Created"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if revision <= 20 {
|
if revision <= 20 {
|
||||||
@@ -945,6 +935,23 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create username index.");
|
.expect("Failed to create username index.");
|
||||||
|
};
|
||||||
|
|
||||||
|
if revision <= 25 {
|
||||||
|
info!("Running migration [revision 25 / 11-06-2023]: Add permissions to webhooks.");
|
||||||
|
|
||||||
|
db.col::<Document>("webhooks")
|
||||||
|
.update_many(
|
||||||
|
doc! {},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"permissions": *DEFAULT_WEBHOOK_PERMISSIONS as i64
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to update webhooks.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if revision <= 25 {
|
if revision <= 25 {
|
||||||
|
|||||||
File renamed without changes.
@@ -0,0 +1,101 @@
|
|||||||
|
use revolt_result::{create_error, Result};
|
||||||
|
|
||||||
|
use crate::Database;
|
||||||
|
|
||||||
|
/* static ALPHABET: [char; 54] = [
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J',
|
||||||
|
'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||||
|
'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z',
|
||||||
|
]; */
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// Invite
|
||||||
|
pub enum Invite {
|
||||||
|
/// Invite to a specific server channel
|
||||||
|
Server {
|
||||||
|
/// Invite code
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
code: String,
|
||||||
|
/// Id of the server this invite points to
|
||||||
|
server: String,
|
||||||
|
/// Id of user who created this invite
|
||||||
|
creator: String,
|
||||||
|
/// Id of the server channel this invite points to
|
||||||
|
channel: String,
|
||||||
|
},
|
||||||
|
/// Invite to a group channel
|
||||||
|
Group {
|
||||||
|
/// Invite code
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
code: String,
|
||||||
|
/// Id of user who created this invite
|
||||||
|
creator: String,
|
||||||
|
/// Id of the group channel this invite points to
|
||||||
|
channel: String,
|
||||||
|
}, /* User {
|
||||||
|
code: String,
|
||||||
|
user: String
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
impl Invite {
|
||||||
|
/// Get the invite code for this invite
|
||||||
|
pub fn code(&'_ self) -> &'_ str {
|
||||||
|
match self {
|
||||||
|
Invite::Server { code, .. } | Invite::Group { code, .. } => code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the ID of the user who created this invite
|
||||||
|
pub fn creator(&'_ self) -> &'_ str {
|
||||||
|
match self {
|
||||||
|
Invite::Server { creator, .. } | Invite::Group { creator, .. } => creator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new invite from given information
|
||||||
|
/*pub async fn create_channel_invite(db: &Database, creator_id: String, target: &Channel) -> Result<Invite> {
|
||||||
|
let code = nanoid::nanoid!(8, &ALPHABET);
|
||||||
|
let invite = match &target {
|
||||||
|
Channel::Group { id, .. } => Ok(Invite::Group {
|
||||||
|
code,
|
||||||
|
creator: creator.id.clone(),
|
||||||
|
channel: id.clone(),
|
||||||
|
}),
|
||||||
|
Channel::TextChannel { id, server, .. } | Channel::VoiceChannel { id, server, .. } => {
|
||||||
|
Ok(Invite::Server {
|
||||||
|
code,
|
||||||
|
creator: creator.id.clone(),
|
||||||
|
server: server.clone(),
|
||||||
|
channel: id.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(Error::InvalidOperation),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
db.insert_invite(&invite).await?;
|
||||||
|
Ok(invite)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/// Resolve an invite by its ID or by a public server ID
|
||||||
|
pub async fn find(db: &Database, code: &str) -> Result<Invite> {
|
||||||
|
if let Ok(invite) = db.fetch_invite(code).await {
|
||||||
|
return Ok(invite);
|
||||||
|
} else if let Ok(server) = db.fetch_server(code).await {
|
||||||
|
if server.discoverable {
|
||||||
|
if let Some(channel) = server.channels.into_iter().next() {
|
||||||
|
return Ok(Invite::Server {
|
||||||
|
code: code.to_string(),
|
||||||
|
server: server.id,
|
||||||
|
creator: server.owner,
|
||||||
|
channel,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::Invite;
|
||||||
|
|
||||||
|
mod mongodb;
|
||||||
|
mod reference;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractChannelInvites: Sync + Send {
|
||||||
|
/// Insert a new invite into the database
|
||||||
|
async fn insert_invite(&self, invite: &Invite) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch an invite by its id
|
||||||
|
async fn fetch_invite(&self, code: &str) -> Result<Invite>;
|
||||||
|
|
||||||
|
/// Fetch all invites for a server
|
||||||
|
async fn fetch_invites_for_server(&self, server_id: &str) -> Result<Vec<Invite>>;
|
||||||
|
|
||||||
|
/// Delete an invite by its id
|
||||||
|
async fn delete_invite(&self, code: &str) -> Result<()>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
use futures::StreamExt;
|
||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::Invite;
|
||||||
|
use crate::MongoDb;
|
||||||
|
|
||||||
|
use super::AbstractChannelInvites;
|
||||||
|
|
||||||
|
static COL: &str = "channel_invites";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractChannelInvites for MongoDb {
|
||||||
|
/// Insert a new invite into the database
|
||||||
|
async fn insert_invite(&self, invite: &Invite) -> Result<()> {
|
||||||
|
query!(self, insert_one, COL, &invite).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch an invite by the code
|
||||||
|
async fn fetch_invite(&self, code: &str) -> Result<Invite> {
|
||||||
|
query!(self, find_one_by_id, COL, code)?.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch all invites for a server
|
||||||
|
async fn fetch_invites_for_server(&self, server_id: &str) -> Result<Vec<Invite>> {
|
||||||
|
Ok(self
|
||||||
|
.col::<Invite>(COL)
|
||||||
|
.find(
|
||||||
|
doc! {
|
||||||
|
"server": server_id,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("find", COL))?
|
||||||
|
.filter_map(|s| async {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
Some(s.unwrap())
|
||||||
|
} else {
|
||||||
|
s.ok()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
.await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete an invite by its code
|
||||||
|
async fn delete_invite(&self, code: &str) -> Result<()> {
|
||||||
|
query!(self, delete_one_by_id, COL, code).map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::Invite;
|
||||||
|
use crate::ReferenceDb;
|
||||||
|
|
||||||
|
use super::AbstractChannelInvites;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractChannelInvites for ReferenceDb {
|
||||||
|
/// Insert a new invite into the database
|
||||||
|
async fn insert_invite(&self, invite: &Invite) -> Result<()> {
|
||||||
|
let mut invites = self.channel_invites.lock().await;
|
||||||
|
if invites.contains_key(invite.code()) {
|
||||||
|
Err(create_database_error!("insert", "invite"))
|
||||||
|
} else {
|
||||||
|
invites.insert(invite.code().to_string(), invite.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch an invite by the code
|
||||||
|
async fn fetch_invite(&self, code: &str) -> Result<Invite> {
|
||||||
|
let invites = self.channel_invites.lock().await;
|
||||||
|
invites
|
||||||
|
.get(code)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch all invites for a server
|
||||||
|
async fn fetch_invites_for_server(&self, server_id: &str) -> Result<Vec<Invite>> {
|
||||||
|
let invites = self.channel_invites.lock().await;
|
||||||
|
Ok(invites
|
||||||
|
.values()
|
||||||
|
.filter(|invite| match invite {
|
||||||
|
Invite::Server { server, .. } => server == server_id,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete an invite by its code
|
||||||
|
async fn delete_invite(&self, code: &str) -> Result<()> {
|
||||||
|
let mut invites = self.channel_invites.lock().await;
|
||||||
|
if invites.remove(code).is_some() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
mod model;
|
||||||
|
mod ops;
|
||||||
|
|
||||||
|
pub use model::*;
|
||||||
|
pub use ops::*;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
auto_derived!(
|
||||||
|
/// Channel Unread
|
||||||
|
pub struct ChannelUnread {
|
||||||
|
/// Composite key pointing to a user's view of a channel
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: ChannelCompositeKey,
|
||||||
|
|
||||||
|
/// Id of the last message read in this channel by a user
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub last_id: Option<String>,
|
||||||
|
/// Array of message ids that mention the user
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mentions: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Composite primary key consisting of channel and user id
|
||||||
|
#[derive(Hash)]
|
||||||
|
pub struct ChannelCompositeKey {
|
||||||
|
/// Channel Id
|
||||||
|
pub channel: String,
|
||||||
|
/// User Id
|
||||||
|
pub user: String,
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::ChannelUnread;
|
||||||
|
|
||||||
|
mod mongodb;
|
||||||
|
mod reference;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractChannelUnreads: Sync + Send {
|
||||||
|
/// Acknowledge a message.
|
||||||
|
async fn acknowledge_message(
|
||||||
|
&self,
|
||||||
|
channel_id: &str,
|
||||||
|
user_id: &str,
|
||||||
|
message_id: &str,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
|
/// Acknowledge many channels.
|
||||||
|
async fn acknowledge_channels(&self, user_id: &str, channel_ids: &[String]) -> Result<()>;
|
||||||
|
|
||||||
|
/// Add a mention.
|
||||||
|
async fn add_mention_to_unread<'a>(
|
||||||
|
&self,
|
||||||
|
channel_id: &str,
|
||||||
|
user_id: &str,
|
||||||
|
message_ids: &[String],
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch all channel unreads for a user.
|
||||||
|
async fn fetch_unreads(&self, user_id: &str) -> Result<Vec<ChannelUnread>>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
use bson::Document;
|
||||||
|
use mongodb::options::UpdateOptions;
|
||||||
|
use revolt_result::Result;
|
||||||
|
use ulid::Ulid;
|
||||||
|
|
||||||
|
use crate::ChannelUnread;
|
||||||
|
use crate::MongoDb;
|
||||||
|
|
||||||
|
use super::AbstractChannelUnreads;
|
||||||
|
|
||||||
|
static COL: &str = "channel_unreads";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractChannelUnreads for MongoDb {
|
||||||
|
/// Acknowledge a message.
|
||||||
|
async fn acknowledge_message(
|
||||||
|
&self,
|
||||||
|
channel_id: &str,
|
||||||
|
user_id: &str,
|
||||||
|
message_id: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id.channel": channel_id,
|
||||||
|
"_id.user": user_id,
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$unset": {
|
||||||
|
"mentions": 1_i32
|
||||||
|
},
|
||||||
|
"$set": {
|
||||||
|
"last_id": message_id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateOptions::builder().upsert(true).build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("update_one", COL))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acknowledge many channels.
|
||||||
|
async fn acknowledge_channels(&self, user_id: &str, channel_ids: &[String]) -> Result<()> {
|
||||||
|
let current_time = Ulid::new().to_string();
|
||||||
|
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.delete_many(
|
||||||
|
doc! {
|
||||||
|
"_id.channel": {
|
||||||
|
"$in": channel_ids
|
||||||
|
},
|
||||||
|
"_id.user": user_id
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("delete_many", COL))?;
|
||||||
|
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.insert_many(
|
||||||
|
channel_ids
|
||||||
|
.iter()
|
||||||
|
.map(|channel_id| {
|
||||||
|
doc! {
|
||||||
|
"_id": {
|
||||||
|
"channel": channel_id,
|
||||||
|
"user": user_id
|
||||||
|
},
|
||||||
|
"last_id": ¤t_time
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<Document>>(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("update_many", COL))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a mention.
|
||||||
|
async fn add_mention_to_unread<'a>(
|
||||||
|
&self,
|
||||||
|
channel_id: &str,
|
||||||
|
user_id: &str,
|
||||||
|
message_ids: &[String],
|
||||||
|
) -> Result<()> {
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id.channel": channel_id,
|
||||||
|
"_id.user": user_id,
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$push": {
|
||||||
|
"mentions": {
|
||||||
|
"$each": message_ids
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateOptions::builder().upsert(true).build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("update_one", COL))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch all channel unreads for a user.
|
||||||
|
async fn fetch_unreads(&self, user_id: &str) -> Result<Vec<ChannelUnread>> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
find,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"_id.user": user_id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
use ulid::Ulid;
|
||||||
|
|
||||||
|
use crate::{ChannelCompositeKey, ChannelUnread, ReferenceDb};
|
||||||
|
|
||||||
|
use super::AbstractChannelUnreads;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractChannelUnreads for ReferenceDb {
|
||||||
|
/// Acknowledge a message.
|
||||||
|
async fn acknowledge_message(
|
||||||
|
&self,
|
||||||
|
channel_id: &str,
|
||||||
|
user_id: &str,
|
||||||
|
message_id: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut unreads = self.channel_unreads.lock().await;
|
||||||
|
let key = ChannelCompositeKey {
|
||||||
|
channel: channel_id.to_string(),
|
||||||
|
user: user_id.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(mut unread) = unreads.get_mut(&key) {
|
||||||
|
unread.mentions = None;
|
||||||
|
unread.last_id.replace(message_id.to_string());
|
||||||
|
} else {
|
||||||
|
unreads.insert(
|
||||||
|
key.clone(),
|
||||||
|
ChannelUnread {
|
||||||
|
id: key,
|
||||||
|
last_id: Some(message_id.to_string()),
|
||||||
|
mentions: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acknowledge many channels.
|
||||||
|
async fn acknowledge_channels(&self, user_id: &str, channel_ids: &[String]) -> Result<()> {
|
||||||
|
let current_time = Ulid::new().to_string();
|
||||||
|
for channel_id in channel_ids {
|
||||||
|
self.acknowledge_message(channel_id, user_id, ¤t_time)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a mention.
|
||||||
|
async fn add_mention_to_unread<'a>(
|
||||||
|
&self,
|
||||||
|
channel_id: &str,
|
||||||
|
user_id: &str,
|
||||||
|
message_ids: &[String],
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut unreads = self.channel_unreads.lock().await;
|
||||||
|
let key = ChannelCompositeKey {
|
||||||
|
channel: channel_id.to_string(),
|
||||||
|
user: user_id.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(unread) = unreads.get_mut(&key) {
|
||||||
|
unread.mentions.replace(message_ids.to_vec());
|
||||||
|
} else {
|
||||||
|
unreads.insert(
|
||||||
|
key.clone(),
|
||||||
|
ChannelUnread {
|
||||||
|
id: key,
|
||||||
|
last_id: None,
|
||||||
|
mentions: Some(message_ids.to_vec()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch all channel unreads for a user.
|
||||||
|
async fn fetch_unreads(&self, user_id: &str) -> Result<Vec<ChannelUnread>> {
|
||||||
|
let unreads = self.channel_unreads.lock().await;
|
||||||
|
Ok(unreads
|
||||||
|
.values()
|
||||||
|
.filter(|unread| unread.id.user == user_id)
|
||||||
|
.cloned()
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,9 @@ auto_derived_partial!(
|
|||||||
/// The channel this webhook belongs to
|
/// The channel this webhook belongs to
|
||||||
pub channel_id: String,
|
pub channel_id: String,
|
||||||
|
|
||||||
|
/// The permissions of the webhook
|
||||||
|
pub permissions: u64,
|
||||||
|
|
||||||
/// The private token for the webhook
|
/// The private token for the webhook
|
||||||
pub token: Option<String>,
|
pub token: Option<String>,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -169,6 +169,19 @@ impl Channel {
|
|||||||
pub async fn create(&self, db: &Database) -> Result<()> {
|
pub async fn create(&self, db: &Database) -> Result<()> {
|
||||||
db.insert_channel(self).await?;
|
db.insert_channel(self).await?;
|
||||||
|
|
||||||
|
let event = EventV1::ChannelCreate(self.clone().into());
|
||||||
|
match self {
|
||||||
|
Self::SavedMessages { user, .. } => event.private(user.clone()).await,
|
||||||
|
Self::DirectMessage { recipients, .. } | Self::Group { recipients, .. } => {
|
||||||
|
for recipient in recipients {
|
||||||
|
event.clone().private(recipient.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::TextChannel { server, .. } | Self::VoiceChannel { server, .. } => {
|
||||||
|
event.p(server.clone()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +306,7 @@ impl Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update channel data
|
/// Update channel data
|
||||||
pub async fn update<'a>(
|
pub async fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
db: &Database,
|
db: &Database,
|
||||||
partial: PartialChannel,
|
partial: PartialChannel,
|
||||||
@@ -305,8 +318,19 @@ impl Channel {
|
|||||||
|
|
||||||
self.apply_options(partial.clone());
|
self.apply_options(partial.clone());
|
||||||
|
|
||||||
db.update_channel(&self.id(), &partial, remove.clone())
|
let id = self.id().to_string();
|
||||||
.await?;
|
db.update_channel(&id, &partial, remove.clone()).await?;
|
||||||
|
|
||||||
|
EventV1::ChannelUpdate {
|
||||||
|
id: id.clone(),
|
||||||
|
data: partial.into(),
|
||||||
|
clear: remove.into_iter().map(|v| v.into()).collect(),
|
||||||
|
}
|
||||||
|
.p(match self {
|
||||||
|
Self::TextChannel { server, .. } | Self::VoiceChannel { server, .. } => server.clone(),
|
||||||
|
_ => id,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -514,6 +538,8 @@ impl Channel {
|
|||||||
|
|
||||||
/// Delete a channel
|
/// Delete a channel
|
||||||
pub async fn delete(&self, db: &Database) -> Result<()> {
|
pub async fn delete(&self, db: &Database) -> Result<()> {
|
||||||
|
let id = self.id().to_string();
|
||||||
|
EventV1::ChannelDelete { id: id.clone() }.p(id).await;
|
||||||
db.delete_channel(self).await
|
db.delete_channel(self).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
mod model;
|
||||||
|
mod ops;
|
||||||
|
|
||||||
|
pub use model::*;
|
||||||
|
pub use ops::*;
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use revolt_result::Result;
|
||||||
|
use ulid::Ulid;
|
||||||
|
|
||||||
|
use crate::events::client::EventV1;
|
||||||
|
use crate::Database;
|
||||||
|
|
||||||
|
static PERMISSIBLE_EMOJIS: Lazy<HashSet<String>> = Lazy::new(|| {
|
||||||
|
include_str!("unicode_emoji.txt")
|
||||||
|
.split('\n')
|
||||||
|
.map(|x| x.into())
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// Emoji
|
||||||
|
pub struct Emoji {
|
||||||
|
/// Unique Id
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: String,
|
||||||
|
/// What owns this emoji
|
||||||
|
pub parent: EmojiParent,
|
||||||
|
/// Uploader user id
|
||||||
|
pub creator_id: String,
|
||||||
|
/// Emoji name
|
||||||
|
pub name: String,
|
||||||
|
/// Whether the emoji is animated
|
||||||
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
|
pub animated: bool,
|
||||||
|
/// Whether the emoji is marked as nsfw
|
||||||
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
|
pub nsfw: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parent Id of the emoji
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum EmojiParent {
|
||||||
|
Server { id: String },
|
||||||
|
Detached,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
impl Emoji {
|
||||||
|
/// Get parent id
|
||||||
|
fn parent(&self) -> &str {
|
||||||
|
match &self.parent {
|
||||||
|
EmojiParent::Server { id } => id,
|
||||||
|
EmojiParent::Detached => "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an emoji
|
||||||
|
pub async fn create(&self, db: &Database) -> Result<()> {
|
||||||
|
db.insert_emoji(self).await?;
|
||||||
|
|
||||||
|
EventV1::EmojiCreate(self.clone().into())
|
||||||
|
.p(self.parent().to_string())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete an emoji
|
||||||
|
pub async fn delete(self, db: &Database) -> Result<()> {
|
||||||
|
EventV1::EmojiDelete {
|
||||||
|
id: self.id.to_string(),
|
||||||
|
}
|
||||||
|
.p(self.parent().to_string())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
db.detach_emoji(&self).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether we can use a given emoji
|
||||||
|
pub async fn can_use(db: &Database, emoji: &str) -> Result<bool> {
|
||||||
|
if Ulid::from_str(emoji).is_ok() {
|
||||||
|
db.fetch_emoji(emoji).await?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(PERMISSIBLE_EMOJIS.contains(emoji))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::Emoji;
|
||||||
|
|
||||||
|
mod mongodb;
|
||||||
|
mod reference;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractEmojis: Sync + Send {
|
||||||
|
/// Insert emoji into database.
|
||||||
|
async fn insert_emoji(&self, emoji: &Emoji) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch an emoji by its id
|
||||||
|
async fn fetch_emoji(&self, id: &str) -> Result<Emoji>;
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent id
|
||||||
|
async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result<Vec<Emoji>>;
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent ids
|
||||||
|
async fn fetch_emoji_by_parent_ids(&self, parent_ids: &[String]) -> Result<Vec<Emoji>>;
|
||||||
|
|
||||||
|
/// Detach an emoji by its id
|
||||||
|
async fn detach_emoji(&self, emoji: &Emoji) -> Result<()>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
use bson::Document;
|
||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::Emoji;
|
||||||
|
use crate::MongoDb;
|
||||||
|
|
||||||
|
use super::AbstractEmojis;
|
||||||
|
|
||||||
|
static COL: &str = "emojis";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractEmojis for MongoDb {
|
||||||
|
/// Insert emoji into database.
|
||||||
|
async fn insert_emoji(&self, emoji: &Emoji) -> Result<()> {
|
||||||
|
query!(self, insert_one, COL, &emoji).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch an emoji by its id
|
||||||
|
async fn fetch_emoji(&self, id: &str) -> Result<Emoji> {
|
||||||
|
query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent id
|
||||||
|
async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result<Vec<Emoji>> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
find_one,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"parent.id": parent_id
|
||||||
|
}
|
||||||
|
)?
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent ids
|
||||||
|
async fn fetch_emoji_by_parent_ids(&self, parent_ids: &[String]) -> Result<Vec<Emoji>> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
find,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"parent.id": {
|
||||||
|
"$in": parent_ids
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detach an emoji by its id
|
||||||
|
async fn detach_emoji(&self, emoji: &Emoji) -> Result<()> {
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": &emoji.id
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"parent": {
|
||||||
|
"type": "Detached"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("update_one", COL))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::Emoji;
|
||||||
|
use crate::EmojiParent;
|
||||||
|
use crate::ReferenceDb;
|
||||||
|
|
||||||
|
use super::AbstractEmojis;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractEmojis for ReferenceDb {
|
||||||
|
/// Insert emoji into database.
|
||||||
|
async fn insert_emoji(&self, emoji: &Emoji) -> Result<()> {
|
||||||
|
let mut emojis = self.emojis.lock().await;
|
||||||
|
if emojis.contains_key(&emoji.id) {
|
||||||
|
Err(create_database_error!("insert", "emoji"))
|
||||||
|
} else {
|
||||||
|
emojis.insert(emoji.id.to_string(), emoji.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch an emoji by its id
|
||||||
|
async fn fetch_emoji(&self, id: &str) -> Result<Emoji> {
|
||||||
|
let emojis = self.emojis.lock().await;
|
||||||
|
emojis
|
||||||
|
.get(id)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent id
|
||||||
|
async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result<Vec<Emoji>> {
|
||||||
|
let emojis = self.emojis.lock().await;
|
||||||
|
Ok(emojis
|
||||||
|
.values()
|
||||||
|
.filter(|emoji| match &emoji.parent {
|
||||||
|
EmojiParent::Server { id } => id == parent_id,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent ids
|
||||||
|
async fn fetch_emoji_by_parent_ids(&self, parent_ids: &[String]) -> Result<Vec<Emoji>> {
|
||||||
|
let emojis = self.emojis.lock().await;
|
||||||
|
Ok(emojis
|
||||||
|
.values()
|
||||||
|
.filter(|emoji| match &emoji.parent {
|
||||||
|
EmojiParent::Server { id } => parent_ids.contains(id),
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detach an emoji by its id
|
||||||
|
async fn detach_emoji(&self, emoji: &Emoji) -> Result<()> {
|
||||||
|
let mut emojis = self.emojis.lock().await;
|
||||||
|
if let Some(bot) = emojis.get_mut(&emoji.id) {
|
||||||
|
bot.parent = EmojiParent::Detached;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,5 @@
|
|||||||
|
mod model;
|
||||||
|
mod ops;
|
||||||
|
|
||||||
|
pub use model::*;
|
||||||
|
pub use ops::*;
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
use indexmap::{IndexMap, IndexSet};
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
|
use revolt_models::v0::{Embed, MessageSort, MessageWebhook};
|
||||||
|
|
||||||
|
use crate::File;
|
||||||
|
|
||||||
|
auto_derived_partial!(
|
||||||
|
/// Message
|
||||||
|
pub struct Message {
|
||||||
|
/// Unique Id
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: String,
|
||||||
|
/// Unique value generated by client sending this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub nonce: Option<String>,
|
||||||
|
/// Id of the channel this message was sent in
|
||||||
|
pub channel: String,
|
||||||
|
/// Id of the user or webhook that sent this message
|
||||||
|
pub author: String,
|
||||||
|
/// The webhook that sent this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub webhook: Option<MessageWebhook>,
|
||||||
|
/// Message content
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub content: Option<String>,
|
||||||
|
/// System message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub system: Option<SystemMessage>,
|
||||||
|
/// Array of attachments
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub attachments: Option<Vec<File>>,
|
||||||
|
/// Time at which this message was last edited
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub edited: Option<Timestamp>,
|
||||||
|
/// Attached embeds to this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub embeds: Option<Vec<Embed>>,
|
||||||
|
/// Array of user ids mentioned in this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mentions: Option<Vec<String>>,
|
||||||
|
/// Array of message ids this message is replying to
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub replies: Option<Vec<String>>,
|
||||||
|
/// Hashmap of emoji IDs to array of user IDs
|
||||||
|
#[serde(skip_serializing_if = "IndexMap::is_empty", default)]
|
||||||
|
pub reactions: IndexMap<String, IndexSet<String>>,
|
||||||
|
/// Information about how this message should be interacted with
|
||||||
|
#[serde(skip_serializing_if = "Interactions::is_default", default)]
|
||||||
|
pub interactions: Interactions,
|
||||||
|
/// Name and / or avatar overrides for this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub masquerade: Option<Masquerade>,
|
||||||
|
},
|
||||||
|
"PartialMessage"
|
||||||
|
);
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// System Event
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum SystemMessage {
|
||||||
|
#[serde(rename = "text")]
|
||||||
|
Text { content: String },
|
||||||
|
#[serde(rename = "user_added")]
|
||||||
|
UserAdded { id: String, by: String },
|
||||||
|
#[serde(rename = "user_remove")]
|
||||||
|
UserRemove { id: String, by: String },
|
||||||
|
#[serde(rename = "user_joined")]
|
||||||
|
UserJoined { id: String },
|
||||||
|
#[serde(rename = "user_left")]
|
||||||
|
UserLeft { id: String },
|
||||||
|
#[serde(rename = "user_kicked")]
|
||||||
|
UserKicked { id: String },
|
||||||
|
#[serde(rename = "user_banned")]
|
||||||
|
UserBanned { id: String },
|
||||||
|
#[serde(rename = "channel_renamed")]
|
||||||
|
ChannelRenamed { name: String, by: String },
|
||||||
|
#[serde(rename = "channel_description_changed")]
|
||||||
|
ChannelDescriptionChanged { by: String },
|
||||||
|
#[serde(rename = "channel_icon_changed")]
|
||||||
|
ChannelIconChanged { by: String },
|
||||||
|
#[serde(rename = "channel_ownership_changed")]
|
||||||
|
ChannelOwnershipChanged { from: String, to: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Name and / or avatar override information
|
||||||
|
pub struct Masquerade {
|
||||||
|
/// Replace the display name shown on this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub name: Option<String>,
|
||||||
|
/// Replace the avatar shown on this message (URL to image file)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub avatar: Option<String>,
|
||||||
|
/// Replace the display role colour shown on this message
|
||||||
|
///
|
||||||
|
/// Must have `ManageRole` permission to use
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub colour: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information to guide interactions on this message
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Interactions {
|
||||||
|
/// Reactions which should always appear and be distinct
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
|
pub reactions: Option<IndexSet<String>>,
|
||||||
|
/// Whether reactions should be restricted to the given list
|
||||||
|
///
|
||||||
|
/// Can only be set to true if reactions list is of at least length 1
|
||||||
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
|
pub restrict_reactions: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appended Information
|
||||||
|
pub struct AppendMessage {
|
||||||
|
/// Additional embeds to include in this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub embeds: Option<Vec<Embed>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message Time Period
|
||||||
|
///
|
||||||
|
/// Filter and sort messages by time
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum MessageTimePeriod {
|
||||||
|
Relative {
|
||||||
|
/// Message id to search around
|
||||||
|
///
|
||||||
|
/// Specifying 'nearby' ignores 'before', 'after' and 'sort'.
|
||||||
|
/// It will also take half of limit rounded as the limits to each side.
|
||||||
|
/// It also fetches the message ID specified.
|
||||||
|
nearby: String,
|
||||||
|
},
|
||||||
|
Absolute {
|
||||||
|
/// Message id before which messages should be fetched
|
||||||
|
before: Option<String>,
|
||||||
|
/// Message id after which messages should be fetched
|
||||||
|
after: Option<String>,
|
||||||
|
/// Message sort direction
|
||||||
|
sort: Option<MessageSort>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message Filter
|
||||||
|
pub struct MessageFilter {
|
||||||
|
/// Parent channel ID
|
||||||
|
pub channel: Option<String>,
|
||||||
|
/// Message author ID
|
||||||
|
pub author: Option<String>,
|
||||||
|
/// Search query
|
||||||
|
pub query: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message Query
|
||||||
|
pub struct MessageQuery {
|
||||||
|
/// Maximum number of messages to fetch
|
||||||
|
///
|
||||||
|
/// For fetching nearby messages, this is \`(limit + 1)\`.
|
||||||
|
pub limit: Option<i64>,
|
||||||
|
/// Filter to apply
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub filter: MessageFilter,
|
||||||
|
/// Time period to fetch
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub time_period: MessageTimePeriod,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[allow(clippy::disallowed_methods)]
|
||||||
|
impl Message {}
|
||||||
|
|
||||||
|
impl Interactions {
|
||||||
|
/// Validate interactions info is correct
|
||||||
|
/* pub async fn validate(
|
||||||
|
&self,
|
||||||
|
db: &Database,
|
||||||
|
permissions: &mut PermissionCalculator<'_>,
|
||||||
|
) -> Result<()> {
|
||||||
|
if let Some(reactions) = &self.reactions {
|
||||||
|
permissions.throw_permission(db, Permission::React).await?;
|
||||||
|
|
||||||
|
if reactions.len() > 20 {
|
||||||
|
return Err(Error::InvalidOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
for reaction in reactions {
|
||||||
|
if !Emoji::can_use(db, reaction).await? {
|
||||||
|
return Err(Error::InvalidOperation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/// Check if we can use a given emoji to react
|
||||||
|
pub fn can_use(&self, emoji: &str) -> bool {
|
||||||
|
if self.restrict_reactions {
|
||||||
|
if let Some(reactions) = &self.reactions {
|
||||||
|
reactions.contains(emoji)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if default initialisation of fields
|
||||||
|
pub fn is_default(&self) -> bool {
|
||||||
|
!self.restrict_reactions && self.reactions.is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::{AppendMessage, Message, MessageQuery, PartialMessage};
|
||||||
|
|
||||||
|
// mod mongodb;
|
||||||
|
// mod reference;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractMessages: Sync + Send {
|
||||||
|
/// Insert a new message into the database
|
||||||
|
async fn insert_message(&self, message: &Message) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch a message by its id
|
||||||
|
async fn fetch_message(&self, id: &str) -> Result<Message>;
|
||||||
|
|
||||||
|
/// Fetch multiple messages by given query
|
||||||
|
async fn fetch_messages(&self, query: MessageQuery) -> Result<Vec<Message>>;
|
||||||
|
|
||||||
|
/// Update a given message with new information
|
||||||
|
async fn update_message(&self, id: &str, message: &PartialMessage) -> Result<()>;
|
||||||
|
|
||||||
|
/// Append information to a given message
|
||||||
|
async fn append_message(&self, id: &str, append: &AppendMessage) -> Result<()>;
|
||||||
|
|
||||||
|
/// Add a new reaction to a message
|
||||||
|
async fn add_reaction(&self, id: &str, emoji: &str, user: &str) -> Result<()>;
|
||||||
|
|
||||||
|
/// Remove a reaction from a message
|
||||||
|
async fn remove_reaction(&self, id: &str, emoji: &str, user: &str) -> Result<()>;
|
||||||
|
|
||||||
|
/// Remove reaction from a message
|
||||||
|
async fn clear_reaction(&self, id: &str, emoji: &str) -> Result<()>;
|
||||||
|
|
||||||
|
/// Delete a message from the database by its id
|
||||||
|
async fn delete_message(&self, id: &str) -> Result<()>;
|
||||||
|
|
||||||
|
/// Delete messages from a channel by their ids and corresponding channel id
|
||||||
|
async fn delete_messages(&self, channel: &str, ids: Vec<String>) -> Result<()>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
use bson::Document;
|
||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::Emoji;
|
||||||
|
use crate::MongoDb;
|
||||||
|
|
||||||
|
use super::AbstractEmojis;
|
||||||
|
|
||||||
|
static COL: &str = "emojis";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractEmojis for MongoDb {
|
||||||
|
/// Insert emoji into database.
|
||||||
|
async fn insert_emoji(&self, emoji: &Emoji) -> Result<()> {
|
||||||
|
query!(self, insert_one, COL, &emoji).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch an emoji by its id
|
||||||
|
async fn fetch_emoji(&self, id: &str) -> Result<Emoji> {
|
||||||
|
query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent id
|
||||||
|
async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result<Vec<Emoji>> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
find_one,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"parent.id": parent_id
|
||||||
|
}
|
||||||
|
)?
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent ids
|
||||||
|
async fn fetch_emoji_by_parent_ids(&self, parent_ids: &[String]) -> Result<Vec<Emoji>> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
find,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"parent.id": {
|
||||||
|
"$in": parent_ids
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detach an emoji by its id
|
||||||
|
async fn detach_emoji(&self, emoji: &Emoji) -> Result<()> {
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.update_one(
|
||||||
|
doc! {
|
||||||
|
"_id": &emoji.id
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"parent": {
|
||||||
|
"type": "Detached"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("update_one", COL))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::Emoji;
|
||||||
|
use crate::EmojiParent;
|
||||||
|
use crate::ReferenceDb;
|
||||||
|
|
||||||
|
use super::AbstractEmojis;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractEmojis for ReferenceDb {
|
||||||
|
/// Insert emoji into database.
|
||||||
|
async fn insert_emoji(&self, emoji: &Emoji) -> Result<()> {
|
||||||
|
let mut emojis = self.emojis.lock().await;
|
||||||
|
if emojis.contains_key(&emoji.id) {
|
||||||
|
Err(create_database_error!("insert", "emoji"))
|
||||||
|
} else {
|
||||||
|
emojis.insert(emoji.id.to_string(), emoji.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch an emoji by its id
|
||||||
|
async fn fetch_emoji(&self, id: &str) -> Result<Emoji> {
|
||||||
|
let emojis = self.emojis.lock().await;
|
||||||
|
emojis
|
||||||
|
.get(id)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent id
|
||||||
|
async fn fetch_emoji_by_parent_id(&self, parent_id: &str) -> Result<Vec<Emoji>> {
|
||||||
|
let emojis = self.emojis.lock().await;
|
||||||
|
Ok(emojis
|
||||||
|
.values()
|
||||||
|
.filter(|emoji| match &emoji.parent {
|
||||||
|
EmojiParent::Server { id } => id == parent_id,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch emoji by their parent ids
|
||||||
|
async fn fetch_emoji_by_parent_ids(&self, parent_ids: &[String]) -> Result<Vec<Emoji>> {
|
||||||
|
let emojis = self.emojis.lock().await;
|
||||||
|
Ok(emojis
|
||||||
|
.values()
|
||||||
|
.filter(|emoji| match &emoji.parent {
|
||||||
|
EmojiParent::Server { id } => parent_ids.contains(id),
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detach an emoji by its id
|
||||||
|
async fn detach_emoji(&self, emoji: &Emoji) -> Result<()> {
|
||||||
|
let mut emojis = self.emojis.lock().await;
|
||||||
|
if let Some(bot) = emojis.get_mut(&emoji.id) {
|
||||||
|
bot.parent = EmojiParent::Detached;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
mod admin_migrations;
|
mod admin_migrations;
|
||||||
mod bots;
|
mod bots;
|
||||||
|
mod channel_invites;
|
||||||
|
mod channel_unreads;
|
||||||
mod channel_webhooks;
|
mod channel_webhooks;
|
||||||
mod channels;
|
mod channels;
|
||||||
|
mod emojis;
|
||||||
mod files;
|
mod files;
|
||||||
|
mod messages;
|
||||||
mod ratelimit_events;
|
mod ratelimit_events;
|
||||||
mod safety_strikes;
|
mod server_bans;
|
||||||
mod server_members;
|
mod server_members;
|
||||||
mod servers;
|
mod servers;
|
||||||
mod user_settings;
|
mod user_settings;
|
||||||
@@ -12,11 +16,15 @@ mod users;
|
|||||||
|
|
||||||
pub use admin_migrations::*;
|
pub use admin_migrations::*;
|
||||||
pub use bots::*;
|
pub use bots::*;
|
||||||
|
pub use channel_invites::*;
|
||||||
|
pub use channel_unreads::*;
|
||||||
pub use channel_webhooks::*;
|
pub use channel_webhooks::*;
|
||||||
pub use channels::*;
|
pub use channels::*;
|
||||||
|
pub use emojis::*;
|
||||||
pub use files::*;
|
pub use files::*;
|
||||||
|
pub use messages::*;
|
||||||
pub use ratelimit_events::*;
|
pub use ratelimit_events::*;
|
||||||
pub use safety_strikes::*;
|
pub use server_bans::*;
|
||||||
pub use server_members::*;
|
pub use server_members::*;
|
||||||
pub use servers::*;
|
pub use servers::*;
|
||||||
pub use user_settings::*;
|
pub use user_settings::*;
|
||||||
@@ -30,10 +38,13 @@ pub trait AbstractDatabase:
|
|||||||
+ admin_migrations::AbstractMigrations
|
+ admin_migrations::AbstractMigrations
|
||||||
+ bots::AbstractBots
|
+ bots::AbstractBots
|
||||||
+ channels::AbstractChannels
|
+ channels::AbstractChannels
|
||||||
|
+ channel_invites::AbstractChannelInvites
|
||||||
|
+ channel_unreads::AbstractChannelUnreads
|
||||||
+ channel_webhooks::AbstractWebhooks
|
+ channel_webhooks::AbstractWebhooks
|
||||||
|
+ emojis::AbstractEmojis
|
||||||
+ files::AbstractAttachments
|
+ files::AbstractAttachments
|
||||||
+ ratelimit_events::AbstractRatelimitEvents
|
+ ratelimit_events::AbstractRatelimitEvents
|
||||||
+ safety_strikes::AbstractAccountStrikes
|
+ server_bans::AbstractServerBans
|
||||||
+ server_members::AbstractServerMembers
|
+ server_members::AbstractServerMembers
|
||||||
+ servers::AbstractServers
|
+ servers::AbstractServers
|
||||||
+ user_settings::AbstractUserSettings
|
+ user_settings::AbstractUserSettings
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
use revolt_result::Result;
|
|
||||||
|
|
||||||
use crate::Database;
|
|
||||||
|
|
||||||
auto_derived_partial!(
|
|
||||||
/// Account Strike
|
|
||||||
pub struct AccountStrike {
|
|
||||||
/// Strike Id
|
|
||||||
#[serde(rename = "_id")]
|
|
||||||
pub id: String,
|
|
||||||
/// Id of reported user
|
|
||||||
pub user_id: String,
|
|
||||||
/// Id of moderator
|
|
||||||
pub moderator_id: String,
|
|
||||||
|
|
||||||
/// Attached reason
|
|
||||||
pub reason: String,
|
|
||||||
},
|
|
||||||
"PartialAccountStrike"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[allow(clippy::disallowed_methods)]
|
|
||||||
impl AccountStrike {
|
|
||||||
pub async fn create(
|
|
||||||
db: &Database,
|
|
||||||
user_id: String,
|
|
||||||
reason: String,
|
|
||||||
moderator_id: String,
|
|
||||||
) -> Result<AccountStrike> {
|
|
||||||
let strike = AccountStrike {
|
|
||||||
id: ulid::Ulid::new().to_string(),
|
|
||||||
user_id,
|
|
||||||
moderator_id,
|
|
||||||
reason,
|
|
||||||
};
|
|
||||||
|
|
||||||
db.insert_account_strike(&strike).await?;
|
|
||||||
Ok(strike)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update this strike
|
|
||||||
pub async fn update(&mut self, db: &Database, partial: PartialAccountStrike) -> Result<()> {
|
|
||||||
db.update_account_strike(&self.id, &partial).await?;
|
|
||||||
self.apply_options(partial);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete this strike
|
|
||||||
pub async fn delete(&self, db: &Database) -> Result<()> {
|
|
||||||
db.delete_account_strike(&self.id).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use crate::{AccountStrike, PartialAccountStrike};
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn crud() {
|
|
||||||
database_test!(|db| async move {
|
|
||||||
let user_id = "user";
|
|
||||||
|
|
||||||
let strike = AccountStrike::create(
|
|
||||||
&db,
|
|
||||||
user_id.to_string(),
|
|
||||||
"reason 1".to_string(),
|
|
||||||
"moderator_id".to_string(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut updated_strike = strike.clone();
|
|
||||||
updated_strike
|
|
||||||
.update(
|
|
||||||
&db,
|
|
||||||
PartialAccountStrike {
|
|
||||||
reason: Some("new reason".to_string()),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let strike2 = AccountStrike::create(
|
|
||||||
&db,
|
|
||||||
user_id.to_string(),
|
|
||||||
"reason 2".to_string(),
|
|
||||||
"moderator_id".to_string(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let strikes = db.fetch_account_strikes_by_user(user_id).await.unwrap();
|
|
||||||
|
|
||||||
let ids = strikes
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|strike| strike.id)
|
|
||||||
.collect::<HashSet<String>>();
|
|
||||||
|
|
||||||
assert!(ids.contains(&strike.id));
|
|
||||||
assert!(ids.contains(&strike2.id));
|
|
||||||
|
|
||||||
let fetched_strike = strikes
|
|
||||||
.into_iter()
|
|
||||||
.find(|entry| entry.id == strike.id)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(fetched_strike, updated_strike);
|
|
||||||
assert_ne!(fetched_strike, strike);
|
|
||||||
|
|
||||||
strike.delete(&db).await.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
1,
|
|
||||||
db.fetch_account_strikes_by_user(user_id)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.len()
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
use revolt_result::Result;
|
|
||||||
|
|
||||||
use crate::{AccountStrike, PartialAccountStrike};
|
|
||||||
|
|
||||||
mod mongodb;
|
|
||||||
mod reference;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait AbstractAccountStrikes: Sync + Send {
|
|
||||||
/// Insert new strike into the database
|
|
||||||
async fn insert_account_strike(&self, strike: &AccountStrike) -> Result<()>;
|
|
||||||
|
|
||||||
/// Fetch strike by id
|
|
||||||
async fn fetch_account_strike(&self, id: &str) -> Result<AccountStrike>;
|
|
||||||
|
|
||||||
/// Fetch strikes by user id
|
|
||||||
async fn fetch_account_strikes_by_user(&self, user_id: &str) -> Result<Vec<AccountStrike>>;
|
|
||||||
|
|
||||||
/// Update strike with new information
|
|
||||||
async fn update_account_strike(&self, id: &str, partial: &PartialAccountStrike) -> Result<()>;
|
|
||||||
|
|
||||||
/// Delete a strike from the database
|
|
||||||
async fn delete_account_strike(&self, id: &str) -> Result<()>;
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
use futures::StreamExt;
|
|
||||||
use revolt_result::Result;
|
|
||||||
|
|
||||||
use crate::MongoDb;
|
|
||||||
use crate::{AccountStrike, PartialAccountStrike};
|
|
||||||
|
|
||||||
use super::AbstractAccountStrikes;
|
|
||||||
|
|
||||||
static COL: &str = "safety_strikes";
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl AbstractAccountStrikes for MongoDb {
|
|
||||||
/// Insert new strike into the database
|
|
||||||
async fn insert_account_strike(&self, strike: &AccountStrike) -> Result<()> {
|
|
||||||
query!(self, insert_one, COL, &strike).map(|_| ())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch strike by id
|
|
||||||
async fn fetch_account_strike(&self, id: &str) -> Result<AccountStrike> {
|
|
||||||
query!(self, find_one_by_id, COL, id)?.ok_or_else(|| create_error!(NotFound))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch strikes by user id
|
|
||||||
async fn fetch_account_strikes_by_user(&self, user_id: &str) -> Result<Vec<AccountStrike>> {
|
|
||||||
Ok(self
|
|
||||||
.col::<AccountStrike>(COL)
|
|
||||||
.find(
|
|
||||||
doc! {
|
|
||||||
"user_id": user_id,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|_| create_database_error!("find", COL))?
|
|
||||||
.filter_map(|s| async {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
Some(s.unwrap())
|
|
||||||
} else {
|
|
||||||
s.ok()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
.await)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update strike with new information
|
|
||||||
async fn update_account_strike(&self, id: &str, partial: &PartialAccountStrike) -> Result<()> {
|
|
||||||
query!(self, update_one_by_id, COL, id, partial, vec![], None).map(|_| ())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete a strike from the database
|
|
||||||
async fn delete_account_strike(&self, id: &str) -> Result<()> {
|
|
||||||
query!(self, delete_one_by_id, COL, id).map(|_| ())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
use revolt_result::Result;
|
|
||||||
|
|
||||||
use crate::ReferenceDb;
|
|
||||||
use crate::{AccountStrike, PartialAccountStrike};
|
|
||||||
|
|
||||||
use super::AbstractAccountStrikes;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl AbstractAccountStrikes for ReferenceDb {
|
|
||||||
/// Insert new strike into the database
|
|
||||||
async fn insert_account_strike(&self, strike: &AccountStrike) -> Result<()> {
|
|
||||||
let mut strikes = self.account_strikes.lock().await;
|
|
||||||
if strikes.contains_key(&strike.id) {
|
|
||||||
Err(create_database_error!("insert", "strike"))
|
|
||||||
} else {
|
|
||||||
strikes.insert(strike.id.to_string(), strike.clone());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch strike by id
|
|
||||||
async fn fetch_account_strike(&self, id: &str) -> Result<AccountStrike> {
|
|
||||||
let strikes = self.account_strikes.lock().await;
|
|
||||||
strikes
|
|
||||||
.get(id)
|
|
||||||
.cloned()
|
|
||||||
.ok_or_else(|| create_error!(NotFound))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch strikes by user id
|
|
||||||
async fn fetch_account_strikes_by_user(&self, user_id: &str) -> Result<Vec<AccountStrike>> {
|
|
||||||
let strikes = self.account_strikes.lock().await;
|
|
||||||
Ok(strikes
|
|
||||||
.values()
|
|
||||||
.filter(|strike| strike.user_id == user_id)
|
|
||||||
.cloned()
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update strike with new information
|
|
||||||
async fn update_account_strike(&self, id: &str, partial: &PartialAccountStrike) -> Result<()> {
|
|
||||||
let mut strikes = self.account_strikes.lock().await;
|
|
||||||
if let Some(strike) = strikes.get_mut(id) {
|
|
||||||
strike.apply_options(partial.clone());
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(create_error!(NotFound))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete a strike from the database
|
|
||||||
async fn delete_account_strike(&self, id: &str) -> Result<()> {
|
|
||||||
let mut strikes = self.account_strikes.lock().await;
|
|
||||||
if strikes.remove(id).is_some() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(create_error!(NotFound))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
mod model;
|
||||||
|
mod ops;
|
||||||
|
|
||||||
|
pub use model::*;
|
||||||
|
pub use ops::*;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
use crate::MemberCompositeKey;
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// Server Ban
|
||||||
|
pub struct ServerBan {
|
||||||
|
/// Unique member id
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: MemberCompositeKey,
|
||||||
|
/// Reason for ban creation
|
||||||
|
pub reason: Option<String>,
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::{MemberCompositeKey, ServerBan};
|
||||||
|
|
||||||
|
mod mongodb;
|
||||||
|
mod reference;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractServerBans: Sync + Send {
|
||||||
|
/// Insert new ban into database
|
||||||
|
async fn insert_ban(&self, ban: &ServerBan) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch a server ban by server and user id
|
||||||
|
async fn fetch_ban(&self, server_id: &str, user_id: &str) -> Result<ServerBan>;
|
||||||
|
|
||||||
|
/// Fetch all bans in a server
|
||||||
|
async fn fetch_bans(&self, server_id: &str) -> Result<Vec<ServerBan>>;
|
||||||
|
|
||||||
|
/// Delete a ban from the database
|
||||||
|
async fn delete_ban(&self, id: &MemberCompositeKey) -> Result<()>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::MongoDb;
|
||||||
|
use crate::{MemberCompositeKey, ServerBan};
|
||||||
|
|
||||||
|
use super::AbstractServerBans;
|
||||||
|
|
||||||
|
static COL: &str = "server_bans";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractServerBans for MongoDb {
|
||||||
|
/// Insert new ban into database
|
||||||
|
async fn insert_ban(&self, ban: &ServerBan) -> Result<()> {
|
||||||
|
query!(self, insert_one, COL, &ban).map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch a server ban by server and user id
|
||||||
|
async fn fetch_ban(&self, server_id: &str, user_id: &str) -> Result<ServerBan> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
find_one,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"_id.server": server_id,
|
||||||
|
"_id.user": user_id
|
||||||
|
}
|
||||||
|
)?
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch all bans in a server
|
||||||
|
async fn fetch_bans(&self, server_id: &str) -> Result<Vec<ServerBan>> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
find,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"_id.server": server_id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a ban from the database
|
||||||
|
async fn delete_ban(&self, id: &MemberCompositeKey) -> Result<()> {
|
||||||
|
query!(
|
||||||
|
self,
|
||||||
|
delete_one,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"_id.server": &id.server,
|
||||||
|
"_id.user": &id.user
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
use crate::ReferenceDb;
|
||||||
|
use crate::{MemberCompositeKey, ServerBan};
|
||||||
|
|
||||||
|
use super::AbstractServerBans;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractServerBans for ReferenceDb {
|
||||||
|
/// Insert new ban into database
|
||||||
|
async fn insert_ban(&self, ban: &ServerBan) -> Result<()> {
|
||||||
|
let mut server_bans = self.server_bans.lock().await;
|
||||||
|
if server_bans.contains_key(&ban.id) {
|
||||||
|
Err(create_database_error!("insert", "ban"))
|
||||||
|
} else {
|
||||||
|
server_bans.insert(ban.id.clone(), ban.clone());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch a server ban by server and user id
|
||||||
|
async fn fetch_ban(&self, server_id: &str, user_id: &str) -> Result<ServerBan> {
|
||||||
|
let server_bans = self.server_bans.lock().await;
|
||||||
|
server_bans
|
||||||
|
.get(&MemberCompositeKey {
|
||||||
|
server: server_id.to_string(),
|
||||||
|
user: user_id.to_string(),
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch all bans in a server
|
||||||
|
async fn fetch_bans(&self, server_id: &str) -> Result<Vec<ServerBan>> {
|
||||||
|
let server_bans = self.server_bans.lock().await;
|
||||||
|
Ok(server_bans
|
||||||
|
.values()
|
||||||
|
.filter(|member| member.id.server == server_id)
|
||||||
|
.cloned()
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a ban from the database
|
||||||
|
async fn delete_ban(&self, id: &MemberCompositeKey) -> Result<()> {
|
||||||
|
let mut server_bans = self.server_bans.lock().await;
|
||||||
|
if server_bans.remove(id).is_some() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ use revolt_permissions::OverrideField;
|
|||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
use crate::{Database, File};
|
use crate::{events::client::EventV1, Database, File};
|
||||||
|
|
||||||
auto_derived_partial!(
|
auto_derived_partial!(
|
||||||
/// Server
|
/// Server
|
||||||
@@ -149,24 +149,24 @@ impl Server {
|
|||||||
|
|
||||||
db.update_server(&self.id, &partial, remove.clone()).await?;
|
db.update_server(&self.id, &partial, remove.clone()).await?;
|
||||||
|
|
||||||
/* // TODO: EventV1::ServerUpdate {
|
EventV1::ServerUpdate {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
data: partial,
|
data: partial.into(),
|
||||||
clear: remove,
|
clear: remove.into_iter().map(|v| v.into()).collect(),
|
||||||
}
|
}
|
||||||
.p(self.id.clone())
|
.p(self.id.clone())
|
||||||
.await; */
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a server
|
/// Delete a server
|
||||||
pub async fn delete(self, db: &Database) -> Result<()> {
|
pub async fn delete(self, db: &Database) -> Result<()> {
|
||||||
/* // TODO: EventV1::ServerDelete {
|
EventV1::ServerDelete {
|
||||||
id: self.id.clone(),
|
id: self.id.clone(),
|
||||||
}
|
}
|
||||||
.p(self.id.clone())
|
.p(self.id.clone())
|
||||||
.await; */
|
.await;
|
||||||
|
|
||||||
db.delete_server(&self.id).await
|
db.delete_server(&self.id).await
|
||||||
}
|
}
|
||||||
@@ -364,14 +364,14 @@ impl Role {
|
|||||||
let role_id = Ulid::new().to_string();
|
let role_id = Ulid::new().to_string();
|
||||||
db.insert_role(server_id, &role_id, self).await?;
|
db.insert_role(server_id, &role_id, self).await?;
|
||||||
|
|
||||||
/* // TODO: EventV1::ServerRoleUpdate {
|
EventV1::ServerRoleUpdate {
|
||||||
id: server_id.to_string(),
|
id: server_id.to_string(),
|
||||||
role_id: role_id.to_string(),
|
role_id: role_id.to_string(),
|
||||||
data: self.clone().into_optional(),
|
data: self.clone().into_optional().into(),
|
||||||
clear: vec![],
|
clear: vec![],
|
||||||
}
|
}
|
||||||
.p(server_id.to_string())
|
.p(server_id.to_string())
|
||||||
.await; */
|
.await;
|
||||||
|
|
||||||
Ok(role_id)
|
Ok(role_id)
|
||||||
}
|
}
|
||||||
@@ -394,14 +394,14 @@ impl Role {
|
|||||||
db.update_role(server_id, role_id, &partial, remove.clone())
|
db.update_role(server_id, role_id, &partial, remove.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
/* // TODO: EventV1::ServerRoleUpdate {
|
EventV1::ServerRoleUpdate {
|
||||||
id: server_id.to_string(),
|
id: server_id.to_string(),
|
||||||
role_id: role_id.to_string(),
|
role_id: role_id.to_string(),
|
||||||
data: partial,
|
data: partial.into(),
|
||||||
clear: vec![],
|
clear: vec![],
|
||||||
}
|
}
|
||||||
.p(server_id.to_string())
|
.p(server_id.to_string())
|
||||||
.await; */
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -415,12 +415,12 @@ impl Role {
|
|||||||
|
|
||||||
/// Delete a role
|
/// Delete a role
|
||||||
pub async fn delete(self, db: &Database, server_id: &str, role_id: &str) -> Result<()> {
|
pub async fn delete(self, db: &Database, server_id: &str, role_id: &str) -> Result<()> {
|
||||||
/* // TODO: EventV1::ServerRoleDelete {
|
EventV1::ServerRoleDelete {
|
||||||
id: server_id.to_string(),
|
id: server_id.to_string(),
|
||||||
role_id: role_id.to_string(),
|
role_id: role_id.to_string(),
|
||||||
}
|
}
|
||||||
.p(server_id.to_string())
|
.p(server_id.to_string())
|
||||||
.await; */
|
.await;
|
||||||
|
|
||||||
db.delete_role(server_id, role_id).await
|
db.delete_role(server_id, role_id).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
use revolt_models::v0::*;
|
use revolt_models::v0::*;
|
||||||
|
|
||||||
impl From<crate::AccountStrike> for AccountStrike {
|
|
||||||
fn from(value: crate::AccountStrike) -> Self {
|
|
||||||
AccountStrike {
|
|
||||||
id: value.id,
|
|
||||||
user_id: value.user_id,
|
|
||||||
reason: value.reason,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl crate::Bot {
|
impl crate::Bot {
|
||||||
pub fn into_public_bot(self, user: crate::User) -> PublicBot {
|
pub fn into_public_bot(self, user: crate::User) -> PublicBot {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
@@ -44,6 +34,52 @@ impl From<crate::Bot> for Bot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<crate::Invite> for Invite {
|
||||||
|
fn from(value: crate::Invite) -> Self {
|
||||||
|
match value {
|
||||||
|
crate::Invite::Group {
|
||||||
|
code,
|
||||||
|
creator,
|
||||||
|
channel,
|
||||||
|
} => Invite::Group {
|
||||||
|
code,
|
||||||
|
creator,
|
||||||
|
channel,
|
||||||
|
},
|
||||||
|
crate::Invite::Server {
|
||||||
|
code,
|
||||||
|
server,
|
||||||
|
creator,
|
||||||
|
channel,
|
||||||
|
} => Invite::Server {
|
||||||
|
code,
|
||||||
|
server,
|
||||||
|
creator,
|
||||||
|
channel,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::ChannelUnread> for ChannelUnread {
|
||||||
|
fn from(value: crate::ChannelUnread) -> Self {
|
||||||
|
ChannelUnread {
|
||||||
|
id: value.id.into(),
|
||||||
|
last_id: value.last_id,
|
||||||
|
mentions: value.mentions.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::ChannelCompositeKey> for ChannelCompositeKey {
|
||||||
|
fn from(value: crate::ChannelCompositeKey) -> Self {
|
||||||
|
ChannelCompositeKey {
|
||||||
|
channel: value.channel,
|
||||||
|
user: value.user,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<crate::Webhook> for Webhook {
|
impl From<crate::Webhook> for Webhook {
|
||||||
fn from(value: crate::Webhook) -> Self {
|
fn from(value: crate::Webhook) -> Self {
|
||||||
Webhook {
|
Webhook {
|
||||||
@@ -52,6 +88,7 @@ impl From<crate::Webhook> for Webhook {
|
|||||||
avatar: value.avatar.map(|file| file.into()),
|
avatar: value.avatar.map(|file| file.into()),
|
||||||
channel_id: value.channel_id,
|
channel_id: value.channel_id,
|
||||||
token: value.token,
|
token: value.token,
|
||||||
|
permissions: value.permissions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,6 +101,7 @@ impl From<crate::PartialWebhook> for PartialWebhook {
|
|||||||
avatar: value.avatar.map(|file| file.into()),
|
avatar: value.avatar.map(|file| file.into()),
|
||||||
channel_id: value.channel_id,
|
channel_id: value.channel_id,
|
||||||
token: value.token,
|
token: value.token,
|
||||||
|
permissions: value.permissions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,6 +235,28 @@ impl From<crate::FieldsChannel> for FieldsChannel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<crate::Emoji> for Emoji {
|
||||||
|
fn from(value: crate::Emoji) -> Self {
|
||||||
|
Emoji {
|
||||||
|
id: value.id,
|
||||||
|
parent: value.parent.into(),
|
||||||
|
creator_id: value.creator_id,
|
||||||
|
name: value.name,
|
||||||
|
animated: value.animated,
|
||||||
|
nsfw: value.nsfw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::EmojiParent> for EmojiParent {
|
||||||
|
fn from(value: crate::EmojiParent) -> Self {
|
||||||
|
match value {
|
||||||
|
crate::EmojiParent::Detached => EmojiParent::Detached,
|
||||||
|
crate::EmojiParent::Server { id } => EmojiParent::Server { id },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<crate::File> for File {
|
impl From<crate::File> for File {
|
||||||
fn from(value: crate::File) -> Self {
|
fn from(value: crate::File) -> Self {
|
||||||
File {
|
File {
|
||||||
@@ -234,6 +294,279 @@ impl From<crate::Metadata> for Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<crate::Message> for Message {
|
||||||
|
fn from(value: crate::Message) -> Self {
|
||||||
|
Message {
|
||||||
|
id: value.id,
|
||||||
|
nonce: value.nonce,
|
||||||
|
channel: value.channel,
|
||||||
|
author: value.author,
|
||||||
|
webhook: value.webhook,
|
||||||
|
content: value.content,
|
||||||
|
system: value.system.map(|system| system.into()),
|
||||||
|
attachments: value
|
||||||
|
.attachments
|
||||||
|
.map(|v| v.into_iter().map(|f| f.into()).collect()),
|
||||||
|
edited: value.edited,
|
||||||
|
embeds: value.embeds,
|
||||||
|
mentions: value.mentions,
|
||||||
|
replies: value.replies,
|
||||||
|
reactions: value.reactions,
|
||||||
|
interactions: value.interactions.into(),
|
||||||
|
masquerade: value.masquerade.map(|masq| masq.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::PartialMessage> for PartialMessage {
|
||||||
|
fn from(value: crate::PartialMessage) -> Self {
|
||||||
|
PartialMessage {
|
||||||
|
id: value.id,
|
||||||
|
nonce: value.nonce,
|
||||||
|
channel: value.channel,
|
||||||
|
author: value.author,
|
||||||
|
webhook: value.webhook,
|
||||||
|
content: value.content,
|
||||||
|
system: value.system.map(|system| system.into()),
|
||||||
|
attachments: value
|
||||||
|
.attachments
|
||||||
|
.map(|v| v.into_iter().map(|f| f.into()).collect()),
|
||||||
|
edited: value.edited,
|
||||||
|
embeds: value.embeds,
|
||||||
|
mentions: value.mentions,
|
||||||
|
replies: value.replies,
|
||||||
|
reactions: value.reactions,
|
||||||
|
interactions: value.interactions.map(|interactions| interactions.into()),
|
||||||
|
masquerade: value.masquerade.map(|masq| masq.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::SystemMessage> for SystemMessage {
|
||||||
|
fn from(value: crate::SystemMessage) -> Self {
|
||||||
|
match value {
|
||||||
|
crate::SystemMessage::ChannelDescriptionChanged { by } => {
|
||||||
|
Self::ChannelDescriptionChanged { by }
|
||||||
|
}
|
||||||
|
crate::SystemMessage::ChannelIconChanged { by } => Self::ChannelIconChanged { by },
|
||||||
|
crate::SystemMessage::ChannelOwnershipChanged { from, to } => {
|
||||||
|
Self::ChannelOwnershipChanged { from, to }
|
||||||
|
}
|
||||||
|
crate::SystemMessage::ChannelRenamed { name, by } => Self::ChannelRenamed { name, by },
|
||||||
|
crate::SystemMessage::Text { content } => Self::Text { content },
|
||||||
|
crate::SystemMessage::UserAdded { id, by } => Self::UserAdded { id, by },
|
||||||
|
crate::SystemMessage::UserBanned { id } => Self::UserBanned { id },
|
||||||
|
crate::SystemMessage::UserJoined { id } => Self::UserJoined { id },
|
||||||
|
crate::SystemMessage::UserKicked { id } => Self::UserKicked { id },
|
||||||
|
crate::SystemMessage::UserLeft { id } => Self::UserLeft { id },
|
||||||
|
crate::SystemMessage::UserRemove { id, by } => Self::UserRemove { id, by },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::Interactions> for Interactions {
|
||||||
|
fn from(value: crate::Interactions) -> Self {
|
||||||
|
Interactions {
|
||||||
|
reactions: value.reactions,
|
||||||
|
restrict_reactions: value.restrict_reactions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::Masquerade> for Masquerade {
|
||||||
|
fn from(value: crate::Masquerade) -> Self {
|
||||||
|
Masquerade {
|
||||||
|
name: value.name,
|
||||||
|
avatar: value.avatar,
|
||||||
|
colour: value.colour,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::ServerBan> for ServerBan {
|
||||||
|
fn from(value: crate::ServerBan) -> Self {
|
||||||
|
ServerBan {
|
||||||
|
id: value.id.into(),
|
||||||
|
reason: value.reason,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::Member> for Member {
|
||||||
|
fn from(value: crate::Member) -> Self {
|
||||||
|
Member {
|
||||||
|
id: value.id.into(),
|
||||||
|
joined_at: value.joined_at,
|
||||||
|
nickname: value.nickname,
|
||||||
|
avatar: value.avatar.map(|f| f.into()),
|
||||||
|
roles: value.roles,
|
||||||
|
timeout: value.timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::PartialMember> for PartialMember {
|
||||||
|
fn from(value: crate::PartialMember) -> Self {
|
||||||
|
PartialMember {
|
||||||
|
id: value.id.map(|id| id.into()),
|
||||||
|
joined_at: value.joined_at,
|
||||||
|
nickname: value.nickname,
|
||||||
|
avatar: value.avatar.map(|f| f.into()),
|
||||||
|
roles: value.roles,
|
||||||
|
timeout: value.timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::MemberCompositeKey> for MemberCompositeKey {
|
||||||
|
fn from(value: crate::MemberCompositeKey) -> Self {
|
||||||
|
MemberCompositeKey {
|
||||||
|
server: value.server,
|
||||||
|
user: value.user,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::FieldsMember> for FieldsMember {
|
||||||
|
fn from(value: crate::FieldsMember) -> Self {
|
||||||
|
match value {
|
||||||
|
crate::FieldsMember::Avatar => FieldsMember::Avatar,
|
||||||
|
crate::FieldsMember::Nickname => FieldsMember::Nickname,
|
||||||
|
crate::FieldsMember::Roles => FieldsMember::Roles,
|
||||||
|
crate::FieldsMember::Timeout => FieldsMember::Timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::RemovalIntention> for RemovalIntention {
|
||||||
|
fn from(value: crate::RemovalIntention) -> Self {
|
||||||
|
match value {
|
||||||
|
crate::RemovalIntention::Ban => RemovalIntention::Ban,
|
||||||
|
crate::RemovalIntention::Kick => RemovalIntention::Kick,
|
||||||
|
crate::RemovalIntention::Leave => RemovalIntention::Leave,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::Server> for Server {
|
||||||
|
fn from(value: crate::Server) -> Self {
|
||||||
|
Server {
|
||||||
|
id: value.id,
|
||||||
|
owner: value.owner,
|
||||||
|
name: value.name,
|
||||||
|
description: value.description,
|
||||||
|
channels: value.channels,
|
||||||
|
categories: value
|
||||||
|
.categories
|
||||||
|
.map(|categories| categories.into_iter().map(|v| v.into()).collect()),
|
||||||
|
system_messages: value.system_messages.map(|v| v.into()),
|
||||||
|
roles: value
|
||||||
|
.roles
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| (k, v.into()))
|
||||||
|
.collect(),
|
||||||
|
default_permissions: value.default_permissions,
|
||||||
|
icon: value.icon.map(|f| f.into()),
|
||||||
|
banner: value.banner.map(|f| f.into()),
|
||||||
|
flags: value.flags.unwrap_or_default() as u32,
|
||||||
|
nsfw: value.nsfw,
|
||||||
|
analytics: value.analytics,
|
||||||
|
discoverable: value.discoverable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::PartialServer> for PartialServer {
|
||||||
|
fn from(value: crate::PartialServer) -> Self {
|
||||||
|
PartialServer {
|
||||||
|
id: value.id,
|
||||||
|
owner: value.owner,
|
||||||
|
name: value.name,
|
||||||
|
description: value.description,
|
||||||
|
channels: value.channels,
|
||||||
|
categories: value
|
||||||
|
.categories
|
||||||
|
.map(|categories| categories.into_iter().map(|v| v.into()).collect()),
|
||||||
|
system_messages: value.system_messages.map(|v| v.into()),
|
||||||
|
roles: value
|
||||||
|
.roles
|
||||||
|
.map(|roles| roles.into_iter().map(|(k, v)| (k, v.into())).collect()),
|
||||||
|
default_permissions: value.default_permissions,
|
||||||
|
icon: value.icon.map(|f| f.into()),
|
||||||
|
banner: value.banner.map(|f| f.into()),
|
||||||
|
flags: value.flags.map(|v| v as u32),
|
||||||
|
nsfw: value.nsfw,
|
||||||
|
analytics: value.analytics,
|
||||||
|
discoverable: value.discoverable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::FieldsServer> for FieldsServer {
|
||||||
|
fn from(value: crate::FieldsServer) -> Self {
|
||||||
|
match value {
|
||||||
|
crate::FieldsServer::Banner => FieldsServer::Banner,
|
||||||
|
crate::FieldsServer::Categories => FieldsServer::Categories,
|
||||||
|
crate::FieldsServer::Description => FieldsServer::Description,
|
||||||
|
crate::FieldsServer::Icon => FieldsServer::Icon,
|
||||||
|
crate::FieldsServer::SystemMessages => FieldsServer::SystemMessages,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::Category> for Category {
|
||||||
|
fn from(value: crate::Category) -> Self {
|
||||||
|
Category {
|
||||||
|
id: value.id,
|
||||||
|
title: value.title,
|
||||||
|
channels: value.channels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::SystemMessageChannels> for SystemMessageChannels {
|
||||||
|
fn from(value: crate::SystemMessageChannels) -> Self {
|
||||||
|
SystemMessageChannels {
|
||||||
|
user_joined: value.user_joined,
|
||||||
|
user_left: value.user_left,
|
||||||
|
user_kicked: value.user_kicked,
|
||||||
|
user_banned: value.user_banned,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::Role> for Role {
|
||||||
|
fn from(value: crate::Role) -> Self {
|
||||||
|
Role {
|
||||||
|
name: value.name,
|
||||||
|
permissions: value.permissions,
|
||||||
|
colour: value.colour,
|
||||||
|
hoist: value.hoist,
|
||||||
|
rank: value.rank,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::PartialRole> for PartialRole {
|
||||||
|
fn from(value: crate::PartialRole) -> Self {
|
||||||
|
PartialRole {
|
||||||
|
name: value.name,
|
||||||
|
permissions: value.permissions,
|
||||||
|
colour: value.colour,
|
||||||
|
hoist: value.hoist,
|
||||||
|
rank: value.rank,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<crate::FieldsRole> for FieldsRole {
|
||||||
|
fn from(value: crate::FieldsRole) -> Self {
|
||||||
|
match value {
|
||||||
|
crate::FieldsRole::Colour => FieldsRole::Colour,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl crate::User {
|
impl crate::User {
|
||||||
pub async fn into<P>(self, perspective: P) -> User
|
pub async fn into<P>(self, perspective: P) -> User
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -1,28 +1,30 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = [ "Paul Makles <me@insrt.uk>" ]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: API Models"
|
description = "Revolt Backend: API Models"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
serde = [ "dep:serde", "revolt-permissions/serde" ]
|
serde = ["dep:serde", "revolt-permissions/serde"]
|
||||||
schemas = [ "dep:schemars", "revolt-permissions/schemas" ]
|
schemas = ["dep:schemars", "revolt-permissions/schemas"]
|
||||||
validator = [ "dep:validator" ]
|
validator = ["dep:validator"]
|
||||||
partials = [ "dep:revolt_optional_struct", "serde", "schemas" ]
|
partials = ["dep:revolt_optional_struct", "serde", "schemas"]
|
||||||
|
|
||||||
default = [ "serde", "partials" ]
|
default = ["serde", "partials"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-permissions = { version = "0.6.3", path = "../permissions" }
|
revolt-permissions = { version = "0.6.6", path = "../permissions" }
|
||||||
|
|
||||||
# Serialisation
|
# Serialisation
|
||||||
|
indexmap = "1.9.3"
|
||||||
revolt_optional_struct = { version = "0.2.0", optional = true }
|
revolt_optional_struct = { version = "0.2.0", optional = true }
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
iso8601-timestamp = { version = "0.2.11", features = ["schema", "bson"] }
|
||||||
|
|
||||||
# Spec Generation
|
# Spec Generation
|
||||||
schemars = { version = "0.8.8", optional = true }
|
schemars = { version = "0.8.8", optional = true }
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
auto_derived!(
|
|
||||||
/// Account Strike
|
|
||||||
pub struct AccountStrike {
|
|
||||||
/// Strike Id
|
|
||||||
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
|
||||||
pub id: String,
|
|
||||||
/// Id of reported user
|
|
||||||
pub user_id: String,
|
|
||||||
|
|
||||||
/// Attached reason
|
|
||||||
pub reason: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// New strike information
|
|
||||||
pub struct DataCreateStrike {
|
|
||||||
/// Id of reported user
|
|
||||||
pub user_id: String,
|
|
||||||
|
|
||||||
/// Attached reason
|
|
||||||
pub reason: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// New strike information
|
|
||||||
pub struct DataEditAccountStrike {
|
|
||||||
/// New attached reason
|
|
||||||
pub reason: String,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -65,16 +65,16 @@ auto_derived!(
|
|||||||
/// Public Bot
|
/// Public Bot
|
||||||
pub struct PublicBot {
|
pub struct PublicBot {
|
||||||
/// Bot Id
|
/// Bot Id
|
||||||
#[serde(rename = "_id")]
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
||||||
/// Bot Username
|
/// Bot Username
|
||||||
pub username: String,
|
pub username: String,
|
||||||
/// Profile Avatar
|
/// Profile Avatar
|
||||||
#[serde(skip_serializing_if = "String::is_empty")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "String::is_empty"))]
|
||||||
pub avatar: String,
|
pub avatar: String,
|
||||||
/// Profile Description
|
/// Profile Description
|
||||||
#[serde(skip_serializing_if = "String::is_empty")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "String::is_empty"))]
|
||||||
pub description: String,
|
pub description: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
auto_derived!(
|
||||||
|
/// Invite
|
||||||
|
pub enum Invite {
|
||||||
|
/// Invite to a specific server channel
|
||||||
|
Server {
|
||||||
|
/// Invite code
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
|
code: String,
|
||||||
|
/// Id of the server this invite points to
|
||||||
|
server: String,
|
||||||
|
/// Id of user who created this invite
|
||||||
|
creator: String,
|
||||||
|
/// Id of the server channel this invite points to
|
||||||
|
channel: String,
|
||||||
|
},
|
||||||
|
/// Invite to a group channel
|
||||||
|
Group {
|
||||||
|
/// Invite code
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
|
code: String,
|
||||||
|
/// Id of user who created this invite
|
||||||
|
creator: String,
|
||||||
|
/// Id of the group channel this invite points to
|
||||||
|
channel: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
auto_derived!(
|
||||||
|
/// Channel Unread
|
||||||
|
pub struct ChannelUnread {
|
||||||
|
/// Composite key pointing to a user's view of a channel
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: ChannelCompositeKey,
|
||||||
|
|
||||||
|
/// Id of the last message read in this channel by a user
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub last_id: Option<String>,
|
||||||
|
/// Array of message ids that mention the user
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "Vec::is_empty", default)
|
||||||
|
)]
|
||||||
|
pub mentions: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Composite primary key consisting of channel and user id
|
||||||
|
#[derive(Hash)]
|
||||||
|
pub struct ChannelCompositeKey {
|
||||||
|
/// Channel Id
|
||||||
|
pub channel: String,
|
||||||
|
/// User Id
|
||||||
|
pub user: String,
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -16,6 +16,9 @@ auto_derived_partial!(
|
|||||||
/// The channel this webhook belongs to
|
/// The channel this webhook belongs to
|
||||||
pub channel_id: String,
|
pub channel_id: String,
|
||||||
|
|
||||||
|
/// The permissions for the webhook
|
||||||
|
pub permissions: u64,
|
||||||
|
|
||||||
/// The private token for the webhook
|
/// The private token for the webhook
|
||||||
pub token: Option<String>,
|
pub token: Option<String>,
|
||||||
},
|
},
|
||||||
@@ -43,6 +46,9 @@ auto_derived!(
|
|||||||
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
|
|
||||||
|
/// Webhook permissions
|
||||||
|
pub permissions: Option<u64>,
|
||||||
|
|
||||||
/// Fields to remove from webhook
|
/// Fields to remove from webhook
|
||||||
#[cfg_attr(feature = "serde", serde(default))]
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
pub remove: Vec<FieldsWebhook>,
|
pub remove: Vec<FieldsWebhook>,
|
||||||
@@ -61,6 +67,9 @@ auto_derived!(
|
|||||||
|
|
||||||
/// The channel this webhook belongs to
|
/// The channel this webhook belongs to
|
||||||
pub channel_id: String,
|
pub channel_id: String,
|
||||||
|
|
||||||
|
/// The permissions for the webhook
|
||||||
|
pub permissions: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional fields on webhook object
|
/// Optional fields on webhook object
|
||||||
@@ -85,6 +94,7 @@ impl From<Webhook> for ResponseWebhook {
|
|||||||
name: value.name,
|
name: value.name,
|
||||||
avatar: value.avatar.map(|file| file.id),
|
avatar: value.avatar.map(|file| file.id),
|
||||||
channel_id: value.channel_id,
|
channel_id: value.channel_id,
|
||||||
|
permissions: value.permissions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
use super::File;
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// Image positioning and size
|
||||||
|
pub enum ImageSize {
|
||||||
|
/// Show large preview at the bottom of the embed
|
||||||
|
Large,
|
||||||
|
/// Show small preview to the side of the embed
|
||||||
|
Preview,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Image
|
||||||
|
pub struct Image {
|
||||||
|
/// URL to the original image
|
||||||
|
pub url: String,
|
||||||
|
/// Width of the image
|
||||||
|
pub width: isize,
|
||||||
|
/// Height of the image
|
||||||
|
pub height: isize,
|
||||||
|
/// Positioning and size
|
||||||
|
pub size: ImageSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Video
|
||||||
|
pub struct Video {
|
||||||
|
/// URL to the original video
|
||||||
|
pub url: String,
|
||||||
|
/// Width of the video
|
||||||
|
pub width: isize,
|
||||||
|
/// Height of the video
|
||||||
|
pub height: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type of remote Twitch content
|
||||||
|
pub enum TwitchType {
|
||||||
|
Channel,
|
||||||
|
Video,
|
||||||
|
Clip,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type of remote Lightspeed.tv content
|
||||||
|
pub enum LightspeedType {
|
||||||
|
Channel,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type of remote Bandcamp content
|
||||||
|
pub enum BandcampType {
|
||||||
|
Album,
|
||||||
|
Track,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about special remote content
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum Special {
|
||||||
|
/// No remote content
|
||||||
|
None,
|
||||||
|
/// Content hint that this contains a GIF
|
||||||
|
///
|
||||||
|
/// Use metadata to find video or image to play
|
||||||
|
GIF,
|
||||||
|
/// YouTube video
|
||||||
|
YouTube {
|
||||||
|
id: String,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
timestamp: Option<String>,
|
||||||
|
},
|
||||||
|
/// Lightspeed.tv stream
|
||||||
|
Lightspeed {
|
||||||
|
content_type: LightspeedType,
|
||||||
|
id: String,
|
||||||
|
},
|
||||||
|
/// Twitch stream or clip
|
||||||
|
Twitch {
|
||||||
|
content_type: TwitchType,
|
||||||
|
id: String,
|
||||||
|
},
|
||||||
|
/// Spotify track
|
||||||
|
Spotify { content_type: String, id: String },
|
||||||
|
/// Soundcloud track
|
||||||
|
Soundcloud,
|
||||||
|
/// Bandcamp track
|
||||||
|
Bandcamp {
|
||||||
|
content_type: BandcampType,
|
||||||
|
id: String,
|
||||||
|
},
|
||||||
|
/// Streamable Video
|
||||||
|
Streamable { id: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Website metadata
|
||||||
|
pub struct WebsiteMetadata {
|
||||||
|
/// Direct URL to web page
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
url: Option<String>,
|
||||||
|
/// Original direct URL
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
original_url: Option<String>,
|
||||||
|
/// Remote content
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
special: Option<Special>,
|
||||||
|
|
||||||
|
/// Title of website
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
title: Option<String>,
|
||||||
|
/// Description of website
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
description: Option<String>,
|
||||||
|
/// Embedded image
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
image: Option<Image>,
|
||||||
|
/// Embedded video
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
video: Option<Video>,
|
||||||
|
|
||||||
|
// #[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
// opengraph_type: Option<String>,
|
||||||
|
/// Site name
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
site_name: Option<String>,
|
||||||
|
/// URL to site icon
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
icon_url: Option<String>,
|
||||||
|
/// CSS Colour
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
colour: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Text Embed
|
||||||
|
pub struct Text {
|
||||||
|
/// URL to icon
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub icon_url: Option<String>,
|
||||||
|
/// URL for title
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub url: Option<String>,
|
||||||
|
/// Title of text embed
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
/// Description of text embed
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// ID of uploaded autumn file
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub media: Option<File>,
|
||||||
|
/// CSS Colour
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub colour: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Embed
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum Embed {
|
||||||
|
Website(WebsiteMetadata),
|
||||||
|
Image(Image),
|
||||||
|
Video(Video),
|
||||||
|
Text(Text),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
auto_derived!(
|
||||||
|
/// Emoji
|
||||||
|
pub struct Emoji {
|
||||||
|
/// Unique Id
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
|
pub id: String,
|
||||||
|
/// What owns this emoji
|
||||||
|
pub parent: EmojiParent,
|
||||||
|
/// Uploader user id
|
||||||
|
pub creator_id: String,
|
||||||
|
/// Emoji name
|
||||||
|
pub name: String,
|
||||||
|
/// Whether the emoji is animated
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_false", default)
|
||||||
|
)]
|
||||||
|
pub animated: bool,
|
||||||
|
/// Whether the emoji is marked as nsfw
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_false", default)
|
||||||
|
)]
|
||||||
|
pub nsfw: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parent Id of the emoji
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum EmojiParent {
|
||||||
|
Server { id: String },
|
||||||
|
Detached,
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -2,7 +2,7 @@ auto_derived!(
|
|||||||
/// File
|
/// File
|
||||||
pub struct File {
|
pub struct File {
|
||||||
/// Unique Id
|
/// Unique Id
|
||||||
#[serde(rename = "_id")]
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
/// Tag / bucket this file was uploaded to
|
/// Tag / bucket this file was uploaded to
|
||||||
pub tag: String,
|
pub tag: String,
|
||||||
@@ -16,29 +16,29 @@ auto_derived!(
|
|||||||
pub size: isize,
|
pub size: isize,
|
||||||
|
|
||||||
/// Whether this file was deleted
|
/// Whether this file was deleted
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub deleted: Option<bool>,
|
pub deleted: Option<bool>,
|
||||||
/// Whether this file was reported
|
/// Whether this file was reported
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub reported: Option<bool>,
|
pub reported: Option<bool>,
|
||||||
|
|
||||||
// TODO: migrate this mess to having:
|
// TODO: migrate this mess to having:
|
||||||
// - author_id
|
// - author_id
|
||||||
// - parent: Parent { Message(id), User(id), etc }
|
// - parent: Parent { Message(id), User(id), etc }
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub message_id: Option<String>,
|
pub message_id: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub user_id: Option<String>,
|
pub user_id: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub server_id: Option<String>,
|
pub server_id: Option<String>,
|
||||||
|
|
||||||
/// Id of the object this file is associated with
|
/// Id of the object this file is associated with
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub object_id: Option<String>,
|
pub object_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Metadata associated with a file
|
/// Metadata associated with a file
|
||||||
#[serde(tag = "type")]
|
#[cfg_attr(feature = "serde", serde(tag = "type"))]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub enum Metadata {
|
pub enum Metadata {
|
||||||
/// File is just a generic uncategorised file
|
/// File is just a generic uncategorised file
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
use indexmap::{IndexMap, IndexSet};
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
|
use super::{Embed, File, MessageWebhook};
|
||||||
|
|
||||||
|
auto_derived_partial!(
|
||||||
|
/// Message
|
||||||
|
pub struct Message {
|
||||||
|
/// Unique Id
|
||||||
|
#[serde(rename = "_id")]
|
||||||
|
pub id: String,
|
||||||
|
/// Unique value generated by client sending this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub nonce: Option<String>,
|
||||||
|
/// Id of the channel this message was sent in
|
||||||
|
pub channel: String,
|
||||||
|
/// Id of the user or webhook that sent this message
|
||||||
|
pub author: String,
|
||||||
|
/// The webhook that sent this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub webhook: Option<MessageWebhook>,
|
||||||
|
/// Message content
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub content: Option<String>,
|
||||||
|
/// System message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub system: Option<SystemMessage>,
|
||||||
|
/// Array of attachments
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub attachments: Option<Vec<File>>,
|
||||||
|
/// Time at which this message was last edited
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub edited: Option<Timestamp>,
|
||||||
|
/// Attached embeds to this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub embeds: Option<Vec<Embed>>,
|
||||||
|
/// Array of user ids mentioned in this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mentions: Option<Vec<String>>,
|
||||||
|
/// Array of message ids this message is replying to
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub replies: Option<Vec<String>>,
|
||||||
|
/// Hashmap of emoji IDs to array of user IDs
|
||||||
|
#[serde(skip_serializing_if = "IndexMap::is_empty", default)]
|
||||||
|
pub reactions: IndexMap<String, IndexSet<String>>,
|
||||||
|
/// Information about how this message should be interacted with
|
||||||
|
#[serde(skip_serializing_if = "Interactions::is_default", default)]
|
||||||
|
pub interactions: Interactions,
|
||||||
|
/// Name and / or avatar overrides for this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub masquerade: Option<Masquerade>,
|
||||||
|
},
|
||||||
|
"PartialMessage"
|
||||||
|
);
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// System Event
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum SystemMessage {
|
||||||
|
#[serde(rename = "text")]
|
||||||
|
Text { content: String },
|
||||||
|
#[serde(rename = "user_added")]
|
||||||
|
UserAdded { id: String, by: String },
|
||||||
|
#[serde(rename = "user_remove")]
|
||||||
|
UserRemove { id: String, by: String },
|
||||||
|
#[serde(rename = "user_joined")]
|
||||||
|
UserJoined { id: String },
|
||||||
|
#[serde(rename = "user_left")]
|
||||||
|
UserLeft { id: String },
|
||||||
|
#[serde(rename = "user_kicked")]
|
||||||
|
UserKicked { id: String },
|
||||||
|
#[serde(rename = "user_banned")]
|
||||||
|
UserBanned { id: String },
|
||||||
|
#[serde(rename = "channel_renamed")]
|
||||||
|
ChannelRenamed { name: String, by: String },
|
||||||
|
#[serde(rename = "channel_description_changed")]
|
||||||
|
ChannelDescriptionChanged { by: String },
|
||||||
|
#[serde(rename = "channel_icon_changed")]
|
||||||
|
ChannelIconChanged { by: String },
|
||||||
|
#[serde(rename = "channel_ownership_changed")]
|
||||||
|
ChannelOwnershipChanged { from: String, to: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Name and / or avatar override information
|
||||||
|
pub struct Masquerade {
|
||||||
|
/// Replace the display name shown on this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub name: Option<String>,
|
||||||
|
/// Replace the avatar shown on this message (URL to image file)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub avatar: Option<String>,
|
||||||
|
/// Replace the display role colour shown on this message
|
||||||
|
///
|
||||||
|
/// Must have `ManageRole` permission to use
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub colour: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information to guide interactions on this message
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Interactions {
|
||||||
|
/// Reactions which should always appear and be distinct
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
|
pub reactions: Option<IndexSet<String>>,
|
||||||
|
/// Whether reactions should be restricted to the given list
|
||||||
|
///
|
||||||
|
/// Can only be set to true if reactions list is of at least length 1
|
||||||
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
|
pub restrict_reactions: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appended Information
|
||||||
|
pub struct AppendMessage {
|
||||||
|
/// Additional embeds to include in this message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub embeds: Option<Vec<Embed>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message Sort
|
||||||
|
///
|
||||||
|
/// Sort used for retrieving messages
|
||||||
|
#[derive(Default)]
|
||||||
|
pub enum MessageSort {
|
||||||
|
/// Sort by the most relevant messages
|
||||||
|
#[default]
|
||||||
|
Relevance,
|
||||||
|
/// Sort by the newest messages first
|
||||||
|
Latest,
|
||||||
|
/// Sort by the oldest messages first
|
||||||
|
Oldest,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Interactions {
|
||||||
|
/// Check if default initialisation of fields
|
||||||
|
pub fn is_default(&self) -> bool {
|
||||||
|
!self.restrict_reactions && self.reactions.is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,29 @@
|
|||||||
mod account_strikes;
|
|
||||||
mod bots;
|
mod bots;
|
||||||
|
mod channel_invites;
|
||||||
|
mod channel_unreads;
|
||||||
mod channel_webhooks;
|
mod channel_webhooks;
|
||||||
mod channels;
|
mod channels;
|
||||||
|
mod embeds;
|
||||||
|
mod emojis;
|
||||||
mod files;
|
mod files;
|
||||||
|
mod messages;
|
||||||
|
mod server_bans;
|
||||||
|
mod server_members;
|
||||||
|
mod servers;
|
||||||
|
mod user_settings;
|
||||||
mod users;
|
mod users;
|
||||||
|
|
||||||
pub use account_strikes::*;
|
|
||||||
pub use bots::*;
|
pub use bots::*;
|
||||||
|
pub use channel_invites::*;
|
||||||
|
pub use channel_unreads::*;
|
||||||
pub use channel_webhooks::*;
|
pub use channel_webhooks::*;
|
||||||
pub use channels::*;
|
pub use channels::*;
|
||||||
|
pub use embeds::*;
|
||||||
|
pub use emojis::*;
|
||||||
pub use files::*;
|
pub use files::*;
|
||||||
|
pub use messages::*;
|
||||||
|
pub use server_bans::*;
|
||||||
|
pub use server_members::*;
|
||||||
|
pub use servers::*;
|
||||||
|
pub use user_settings::*;
|
||||||
pub use users::*;
|
pub use users::*;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
use super::MemberCompositeKey;
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// Server Ban
|
||||||
|
pub struct ServerBan {
|
||||||
|
/// Unique member id
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
|
pub id: MemberCompositeKey,
|
||||||
|
/// Reason for ban creation
|
||||||
|
pub reason: Option<String>,
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
use super::File;
|
||||||
|
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
|
|
||||||
|
auto_derived_partial!(
|
||||||
|
/// Server Member
|
||||||
|
pub struct Member {
|
||||||
|
/// Unique member id
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
|
pub id: MemberCompositeKey,
|
||||||
|
|
||||||
|
/// Time at which this user joined the server
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub joined_at: Option<Timestamp>,
|
||||||
|
|
||||||
|
/// Member's nickname
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub nickname: Option<String>,
|
||||||
|
/// Avatar attachment
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub avatar: Option<File>,
|
||||||
|
|
||||||
|
/// Member's roles
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "Vec::is_empty", default)
|
||||||
|
)]
|
||||||
|
pub roles: Vec<String>,
|
||||||
|
/// Timestamp this member is timed out until
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub timeout: Option<Timestamp>,
|
||||||
|
},
|
||||||
|
"PartialMember"
|
||||||
|
);
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// Composite primary key consisting of server and user id
|
||||||
|
#[derive(Hash, Default)]
|
||||||
|
pub struct MemberCompositeKey {
|
||||||
|
/// Server Id
|
||||||
|
pub server: String,
|
||||||
|
/// User Id
|
||||||
|
pub user: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Optional fields on server member object
|
||||||
|
pub enum FieldsMember {
|
||||||
|
Nickname,
|
||||||
|
Avatar,
|
||||||
|
Roles,
|
||||||
|
Timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Member removal intention
|
||||||
|
pub enum RemovalIntention {
|
||||||
|
Leave,
|
||||||
|
Kick,
|
||||||
|
Ban,
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
use super::File;
|
||||||
|
|
||||||
|
use revolt_permissions::OverrideField;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
auto_derived_partial!(
|
||||||
|
/// Server
|
||||||
|
pub struct Server {
|
||||||
|
/// Unique Id
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
|
pub id: String,
|
||||||
|
/// User id of the owner
|
||||||
|
pub owner: String,
|
||||||
|
|
||||||
|
/// Name of the server
|
||||||
|
pub name: String,
|
||||||
|
/// Description for the server
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub description: Option<String>,
|
||||||
|
|
||||||
|
/// Channels within this server
|
||||||
|
// ! FIXME: this may be redundant
|
||||||
|
pub channels: Vec<String>,
|
||||||
|
/// Categories for this server
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub categories: Option<Vec<Category>>,
|
||||||
|
/// Configuration for sending system event messages
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub system_messages: Option<SystemMessageChannels>,
|
||||||
|
|
||||||
|
/// Roles for this server
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(
|
||||||
|
default = "HashMap::<String, Role>::new",
|
||||||
|
skip_serializing_if = "HashMap::<String, Role>::is_empty"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub roles: HashMap<String, Role>,
|
||||||
|
/// Default set of server and channel permissions
|
||||||
|
pub default_permissions: i64,
|
||||||
|
|
||||||
|
/// Icon attachment
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub icon: Option<File>,
|
||||||
|
/// Banner attachment
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub banner: Option<File>,
|
||||||
|
|
||||||
|
/// Bitfield of server flags
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_zero_u32", default)
|
||||||
|
)]
|
||||||
|
pub flags: u32,
|
||||||
|
|
||||||
|
/// Whether this server is flagged as not safe for work
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_false", default)
|
||||||
|
)]
|
||||||
|
pub nsfw: bool,
|
||||||
|
/// Whether to enable analytics
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_false", default)
|
||||||
|
)]
|
||||||
|
pub analytics: bool,
|
||||||
|
/// Whether this server should be publicly discoverable
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_false", default)
|
||||||
|
)]
|
||||||
|
pub discoverable: bool,
|
||||||
|
},
|
||||||
|
"PartialServer"
|
||||||
|
);
|
||||||
|
|
||||||
|
auto_derived_partial!(
|
||||||
|
/// Role
|
||||||
|
pub struct Role {
|
||||||
|
/// Role name
|
||||||
|
pub name: String,
|
||||||
|
/// Permissions available to this role
|
||||||
|
pub permissions: OverrideField,
|
||||||
|
/// Colour used for this role
|
||||||
|
///
|
||||||
|
/// This can be any valid CSS colour
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub colour: Option<String>,
|
||||||
|
/// Whether this role should be shown separately on the member sidebar
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_false", default)
|
||||||
|
)]
|
||||||
|
pub hoist: bool,
|
||||||
|
/// Ranking of this role
|
||||||
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
|
pub rank: i64,
|
||||||
|
},
|
||||||
|
"PartialRole"
|
||||||
|
);
|
||||||
|
|
||||||
|
auto_derived!(
|
||||||
|
/// Optional fields on server object
|
||||||
|
pub enum FieldsServer {
|
||||||
|
Description,
|
||||||
|
Categories,
|
||||||
|
SystemMessages,
|
||||||
|
Icon,
|
||||||
|
Banner,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Optional fields on server object
|
||||||
|
pub enum FieldsRole {
|
||||||
|
Colour,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Channel category
|
||||||
|
pub struct Category {
|
||||||
|
/// Unique ID for this category
|
||||||
|
pub id: String,
|
||||||
|
/// Title for this category
|
||||||
|
pub title: String,
|
||||||
|
/// Channels in this category
|
||||||
|
pub channels: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// System message channel assignments
|
||||||
|
pub struct SystemMessageChannels {
|
||||||
|
/// ID of channel to send user join messages in
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub user_joined: Option<String>,
|
||||||
|
/// ID of channel to send user left messages in
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub user_left: Option<String>,
|
||||||
|
/// ID of channel to send user kicked messages in
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub user_kicked: Option<String>,
|
||||||
|
/// ID of channel to send user banned messages in
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub user_banned: Option<String>,
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// HashMap of user settings
|
||||||
|
/// Each key is mapped to a tuple consisting of the
|
||||||
|
/// revision timestamp and serialised data (in JSON format)
|
||||||
|
pub type UserSettings = HashMap<String, (i64, String)>;
|
||||||
@@ -4,40 +4,52 @@ auto_derived!(
|
|||||||
/// User
|
/// User
|
||||||
pub struct User {
|
pub struct User {
|
||||||
/// Unique Id
|
/// Unique Id
|
||||||
#[serde(rename = "_id")]
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
/// Username
|
/// Username
|
||||||
pub username: String,
|
pub username: String,
|
||||||
/// Discriminator
|
/// Discriminator
|
||||||
pub discriminator: String,
|
pub discriminator: String,
|
||||||
/// Display name
|
/// Display name
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
/// Avatar attachment
|
/// Avatar attachment
|
||||||
pub avatar: Option<File>,
|
pub avatar: Option<File>,
|
||||||
/// Relationships with other users
|
/// Relationships with other users
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "Vec::is_empty", default)
|
||||||
|
)]
|
||||||
pub relations: Vec<Relationship>,
|
pub relations: Vec<Relationship>,
|
||||||
|
|
||||||
/// Bitfield of user badges
|
/// Bitfield of user badges
|
||||||
#[serde(skip_serializing_if = "crate::if_zero_u32", default)]
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_zero_u32", default)
|
||||||
|
)]
|
||||||
pub badges: u32,
|
pub badges: u32,
|
||||||
/// User's current status
|
/// User's current status
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub status: Option<UserStatus>,
|
pub status: Option<UserStatus>,
|
||||||
/// User's profile page
|
/// User's profile page
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub profile: Option<UserProfile>,
|
pub profile: Option<UserProfile>,
|
||||||
|
|
||||||
/// Enum of user flags
|
/// Enum of user flags
|
||||||
#[serde(skip_serializing_if = "crate::if_zero_u32", default)]
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_zero_u32", default)
|
||||||
|
)]
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
/// Whether this user is privileged
|
/// Whether this user is privileged
|
||||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(skip_serializing_if = "crate::if_false", default)
|
||||||
|
)]
|
||||||
pub privileged: bool,
|
pub privileged: bool,
|
||||||
/// Bot information
|
/// Bot information
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub bot: Option<BotInformation>,
|
pub bot: Option<BotInformation>,
|
||||||
|
|
||||||
/// Current session user's relationship with this user
|
/// Current session user's relationship with this user
|
||||||
@@ -49,20 +61,29 @@ auto_derived!(
|
|||||||
/// User's relationship with another user (or themselves)
|
/// User's relationship with another user (or themselves)
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub enum RelationshipStatus {
|
pub enum RelationshipStatus {
|
||||||
|
/// No relationship with other user
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
|
/// Other user is us
|
||||||
User,
|
User,
|
||||||
|
/// Friends with the other user
|
||||||
Friend,
|
Friend,
|
||||||
|
/// Pending friend request to user
|
||||||
Outgoing,
|
Outgoing,
|
||||||
|
/// Incoming friend request from user
|
||||||
Incoming,
|
Incoming,
|
||||||
|
/// Blocked this user
|
||||||
Blocked,
|
Blocked,
|
||||||
|
/// Blocked by this user
|
||||||
BlockedOther,
|
BlockedOther,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relationship entry indicating current status with other user
|
/// Relationship entry indicating current status with other user
|
||||||
pub struct Relationship {
|
pub struct Relationship {
|
||||||
#[serde(rename = "_id")]
|
/// Other user's Id
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
|
/// Relationship status with them
|
||||||
pub status: RelationshipStatus,
|
pub status: RelationshipStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,20 +104,20 @@ auto_derived!(
|
|||||||
/// User's active status
|
/// User's active status
|
||||||
pub struct UserStatus {
|
pub struct UserStatus {
|
||||||
/// Custom status text
|
/// Custom status text
|
||||||
#[serde(skip_serializing_if = "String::is_empty")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "String::is_empty"))]
|
||||||
pub text: String,
|
pub text: String,
|
||||||
/// Current presence option
|
/// Current presence option
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub presence: Option<Presence>,
|
pub presence: Option<Presence>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// User's profile
|
/// User's profile
|
||||||
pub struct UserProfile {
|
pub struct UserProfile {
|
||||||
/// Text content on user's profile
|
/// Text content on user's profile
|
||||||
#[serde(skip_serializing_if = "String::is_empty")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "String::is_empty"))]
|
||||||
pub content: String,
|
pub content: String,
|
||||||
/// Background visible on user's profile
|
/// Background visible on user's profile
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub background: Option<File>,
|
pub background: Option<File>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +164,7 @@ auto_derived!(
|
|||||||
/// Bot information for if the user is a bot
|
/// Bot information for if the user is a bot
|
||||||
pub struct BotInformation {
|
pub struct BotInformation {
|
||||||
/// Id of the owner of this bot
|
/// Id of the owner of this bot
|
||||||
#[serde(rename = "owner")]
|
#[cfg_attr(feature = "serde", serde(rename = "owner"))]
|
||||||
pub owner_id: String,
|
pub owner_id: String,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = [ "Paul Makles <me@insrt.uk>" ]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: Permission Logic"
|
description = "Revolt Backend: Permission Logic"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
bson = ["dep:bson"]
|
bson = ["dep:bson"]
|
||||||
serde = [ "dep:serde" ]
|
serde = ["dep:serde"]
|
||||||
schemas = [ "dep:schemars" ]
|
schemas = ["dep:schemars"]
|
||||||
try-from-primitive = [ "dep:num_enum" ]
|
try-from-primitive = ["dep:num_enum"]
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
@@ -30,7 +30,7 @@ async-trait = "0.1.51"
|
|||||||
|
|
||||||
# Serialisation
|
# Serialisation
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
bson = { version = "2.1.0", optional = true}
|
bson = { version = "2.1.0", optional = true }
|
||||||
|
|
||||||
# Spec Generation
|
# Spec Generation
|
||||||
schemars = { version = "0.8.8", optional = true }
|
schemars = { version = "0.8.8", optional = true }
|
||||||
@@ -135,3 +135,5 @@ pub static DEFAULT_PERMISSION_SERVER: Lazy<u64> = Lazy::new(|| {
|
|||||||
+ ChannelPermission::ChangeAvatar,
|
+ ChannelPermission::ChangeAvatar,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub static DEFAULT_WEBHOOK_PERMISSIONS: Lazy<u64> = Lazy::new(|| ChannelPermission::SendMessage + ChannelPermission::SendEmbeds + ChannelPermission::Masquerade + ChannelPermission::React);
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = [ "Paul Makles <me@insrt.uk>" ]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: User Presence"
|
description = "Revolt Backend: User Presence"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = [ "Paul Makles <me@insrt.uk>" ]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: Result and Error types"
|
description = "Revolt Backend: Result and Error types"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
serde = [ "dep:serde" ]
|
serde = ["dep:serde"]
|
||||||
schemas = [ "dep:schemars" ]
|
schemas = ["dep:schemars"]
|
||||||
rocket = [ "dep:rocket", "dep:serde_json" ]
|
rocket = ["dep:rocket", "dep:serde_json"]
|
||||||
okapi = [ "dep:revolt_rocket_okapi", "dep:revolt_okapi" ]
|
okapi = ["dep:revolt_rocket_okapi", "dep:revolt_okapi"]
|
||||||
|
|
||||||
default = [ "serde" ]
|
default = ["serde"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Serialisation
|
# Serialisation
|
||||||
|
|||||||
+18
-8
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@@ -38,28 +38,38 @@ futures = "0.3.8"
|
|||||||
chrono = "0.4.15"
|
chrono = "0.4.15"
|
||||||
async-channel = "1.6.1"
|
async-channel = "1.6.1"
|
||||||
reqwest = { version = "0.11.4", features = ["json"] }
|
reqwest = { version = "0.11.4", features = ["json"] }
|
||||||
async-std = { version = "1.8.0", features = ["tokio1", "tokio02", "attributes"] }
|
async-std = { version = "1.8.0", features = [
|
||||||
|
"tokio1",
|
||||||
|
"tokio02",
|
||||||
|
"attributes",
|
||||||
|
] }
|
||||||
|
|
||||||
# internal util
|
# internal util
|
||||||
lettre = "0.10.0-alpha.4"
|
lettre = "0.10.0-alpha.4"
|
||||||
|
|
||||||
# web
|
# web
|
||||||
rocket = { version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
rocket = { version = "0.5.0-rc.2", default-features = false, features = [
|
||||||
|
"json",
|
||||||
|
] }
|
||||||
rocket_empty = { version = "0.1.1", features = ["schema"] }
|
rocket_empty = { version = "0.1.1", features = ["schema"] }
|
||||||
rocket_authifier = { version = "1.0.7" }
|
rocket_authifier = { version = "1.0.7" }
|
||||||
|
rocket_prometheus = "0.10.0-rc.3"
|
||||||
|
|
||||||
# spec generation
|
# spec generation
|
||||||
schemars = "0.8.8"
|
schemars = "0.8.8"
|
||||||
revolt_rocket_okapi = { version = "0.9.1", features = [ "swagger" ] }
|
revolt_rocket_okapi = { version = "0.9.1", features = ["swagger"] }
|
||||||
|
|
||||||
# quark
|
# quark
|
||||||
revolt-quark = { path = "../quark" }
|
revolt-quark = { path = "../quark" }
|
||||||
|
|
||||||
# core
|
# core
|
||||||
revolt-database = { path = "../core/database", features = [ "rocket-impl", "redis-is-patched" ] }
|
revolt-database = { path = "../core/database", features = [
|
||||||
revolt-models = { path = "../core/models", features = [ "schemas", "validator" ] }
|
"rocket-impl",
|
||||||
revolt-result = { path = "../core/result", features = [ "rocket", "okapi" ] }
|
"redis-is-patched",
|
||||||
revolt-permissions = { path = "../core/permissions", features = [ "schemas" ] }
|
] }
|
||||||
|
revolt-models = { path = "../core/models", features = ["schemas", "validator"] }
|
||||||
|
revolt-result = { path = "../core/result", features = ["rocket", "okapi"] }
|
||||||
|
revolt-permissions = { path = "../core/permissions", features = ["schemas"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
vergen = "7.5.0"
|
vergen = "7.5.0"
|
||||||
@@ -8,6 +8,7 @@ extern crate serde_json;
|
|||||||
pub mod routes;
|
pub mod routes;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
use rocket_prometheus::PrometheusMetrics;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
use async_std::channel::unbounded;
|
use async_std::channel::unbounded;
|
||||||
@@ -65,7 +66,11 @@ async fn rocket() -> _ {
|
|||||||
|
|
||||||
// Configure Rocket
|
// Configure Rocket
|
||||||
let rocket = rocket::build();
|
let rocket = rocket::build();
|
||||||
|
let prometheus = PrometheusMetrics::new();
|
||||||
|
|
||||||
routes::mount(rocket)
|
routes::mount(rocket)
|
||||||
|
.attach(prometheus.clone())
|
||||||
|
.mount("/metrics", prometheus)
|
||||||
.mount("/", revolt_quark::web::cors::catch_all_options_routes())
|
.mount("/", revolt_quark::web::cors::catch_all_options_routes())
|
||||||
.mount("/", revolt_quark::web::ratelimiter::routes())
|
.mount("/", revolt_quark::web::ratelimiter::routes())
|
||||||
.mount("/swagger/", revolt_quark::web::swagger::routes())
|
.mount("/swagger/", revolt_quark::web::swagger::routes())
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
use revolt_quark::{
|
|
||||||
models::{
|
|
||||||
message::{BulkMessageResponse, MessageQuery},
|
|
||||||
User,
|
|
||||||
},
|
|
||||||
Db, Error, Result,
|
|
||||||
};
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
|
|
||||||
/// # Globally Fetch Messages
|
|
||||||
///
|
|
||||||
/// This is a privileged route to globally fetch messages.
|
|
||||||
#[openapi(tag = "Admin")]
|
|
||||||
#[post("/messages", data = "<data>")]
|
|
||||||
pub async fn message_query(
|
|
||||||
db: &Db,
|
|
||||||
user: User,
|
|
||||||
data: Json<MessageQuery>,
|
|
||||||
) -> Result<Json<BulkMessageResponse>> {
|
|
||||||
// Must be privileged for this route
|
|
||||||
if !user.privileged {
|
|
||||||
return Err(Error::NotPrivileged);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch data using query
|
|
||||||
let data = data.into_inner();
|
|
||||||
let messages = db.fetch_messages(data).await?;
|
|
||||||
BulkMessageResponse::transform(db, None, messages, Some(true))
|
|
||||||
.await
|
|
||||||
.map(Json)
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
|
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
|
||||||
use rocket::Route;
|
use rocket::Route;
|
||||||
|
|
||||||
mod message_query;
|
|
||||||
mod stats;
|
mod stats;
|
||||||
|
|
||||||
pub fn routes() -> (Vec<Route>, OpenApi) {
|
pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||||
openapi_get_routes_spec![stats::stats, message_query::message_query]
|
openapi_get_routes_spec![stats::stats]
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ use revolt_database::{Database, Webhook};
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
models::{Channel, User},
|
models::{Channel, User},
|
||||||
perms, Db, Error, Permission, Ref, Result,
|
perms, Db, Error, Permission, Ref, Result,
|
||||||
|
DEFAULT_WEBHOOK_PERMISSIONS,
|
||||||
};
|
};
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -60,6 +61,7 @@ pub async fn req(
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
avatar,
|
avatar,
|
||||||
channel_id: channel.id().to_string(),
|
channel_id: channel.id().to_string(),
|
||||||
|
permissions: *DEFAULT_WEBHOOK_PERMISSIONS,
|
||||||
token: Some(nanoid::nanoid!(64)),
|
token: Some(nanoid::nanoid!(64)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
use revolt_database::{AccountStrike, Database};
|
|
||||||
use revolt_models::v0::{AccountStrike as AccountStrikeModel, DataCreateStrike};
|
|
||||||
use revolt_quark::models::User;
|
|
||||||
use revolt_quark::{Error, Result};
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
use rocket::State;
|
|
||||||
|
|
||||||
/// # Create Strike
|
|
||||||
///
|
|
||||||
/// Create a new account strike
|
|
||||||
#[openapi(tag = "User Safety")]
|
|
||||||
#[post("/strikes", data = "<data>")]
|
|
||||||
pub async fn create_strike(
|
|
||||||
db: &State<Database>,
|
|
||||||
user: User,
|
|
||||||
data: Json<DataCreateStrike>,
|
|
||||||
) -> Result<Json<AccountStrikeModel>> {
|
|
||||||
// Must be privileged for this route
|
|
||||||
if !user.privileged {
|
|
||||||
return Err(Error::NotPrivileged);
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = data.into_inner();
|
|
||||||
let target = db
|
|
||||||
.fetch_user(&data.user_id)
|
|
||||||
.await
|
|
||||||
.map_err(Error::from_core)?;
|
|
||||||
|
|
||||||
AccountStrike::create(db, target.id, data.reason, user.id)
|
|
||||||
.await
|
|
||||||
.map(|strike| strike.into())
|
|
||||||
.map(Json)
|
|
||||||
.map_err(Error::from_core)
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
use revolt_database::Database;
|
|
||||||
use revolt_quark::models::User;
|
|
||||||
use revolt_quark::{Error, Result};
|
|
||||||
use rocket::State;
|
|
||||||
|
|
||||||
/// # Delete Strike
|
|
||||||
///
|
|
||||||
/// Delete a strike by its ID
|
|
||||||
#[openapi(tag = "User Safety")]
|
|
||||||
#[delete("/strikes/<strike_id>")]
|
|
||||||
pub async fn delete_strike(db: &State<Database>, user: User, strike_id: String) -> Result<()> {
|
|
||||||
// Must be privileged for this route
|
|
||||||
if !user.privileged {
|
|
||||||
return Err(Error::NotPrivileged);
|
|
||||||
}
|
|
||||||
|
|
||||||
let strike = db
|
|
||||||
.fetch_account_strike(&strike_id)
|
|
||||||
.await
|
|
||||||
.map_err(Error::from_core)?;
|
|
||||||
|
|
||||||
strike.delete(db).await.map_err(Error::from_core)
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
use revolt_quark::{
|
|
||||||
models::{
|
|
||||||
report::{PartialReport, ReportStatus},
|
|
||||||
Report, User,
|
|
||||||
},
|
|
||||||
Db, Error, Ref, Result,
|
|
||||||
};
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use validator::Validate;
|
|
||||||
|
|
||||||
/// # Report Data
|
|
||||||
#[derive(Validate, Deserialize, JsonSchema)]
|
|
||||||
pub struct DataEditReport {
|
|
||||||
/// New report status
|
|
||||||
status: Option<ReportStatus>,
|
|
||||||
/// Report notes
|
|
||||||
notes: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Edit Report
|
|
||||||
///
|
|
||||||
/// Edit a report.
|
|
||||||
#[openapi(tag = "User Safety")]
|
|
||||||
#[patch("/reports/<report>", data = "<edit>")]
|
|
||||||
pub async fn edit_report(
|
|
||||||
db: &Db,
|
|
||||||
user: User,
|
|
||||||
report: Ref,
|
|
||||||
edit: Json<DataEditReport>,
|
|
||||||
) -> Result<Json<Report>> {
|
|
||||||
// Must be privileged for this route
|
|
||||||
if !user.privileged {
|
|
||||||
return Err(Error::NotPrivileged);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate data
|
|
||||||
let edit = edit.into_inner();
|
|
||||||
edit.validate()
|
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
|
||||||
|
|
||||||
// Create and apply update to report
|
|
||||||
let mut report = report.as_report(db).await?;
|
|
||||||
report
|
|
||||||
.update(
|
|
||||||
db,
|
|
||||||
PartialReport {
|
|
||||||
status: edit.status,
|
|
||||||
notes: edit.notes,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Json(report))
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
use revolt_database::{Database, PartialAccountStrike};
|
|
||||||
use revolt_models::v0::DataEditAccountStrike;
|
|
||||||
use revolt_quark::models::User;
|
|
||||||
use revolt_quark::{Error, Result};
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
use rocket::State;
|
|
||||||
|
|
||||||
/// # Edit Strike
|
|
||||||
///
|
|
||||||
/// Edit a strike by its ID
|
|
||||||
#[openapi(tag = "User Safety")]
|
|
||||||
#[post("/strikes/<strike_id>", data = "<data>")]
|
|
||||||
pub async fn edit_strike(
|
|
||||||
db: &State<Database>,
|
|
||||||
user: User,
|
|
||||||
strike_id: String,
|
|
||||||
data: Json<DataEditAccountStrike>,
|
|
||||||
) -> Result<()> {
|
|
||||||
// Must be privileged for this route
|
|
||||||
if !user.privileged {
|
|
||||||
return Err(Error::NotPrivileged);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut strike = db
|
|
||||||
.fetch_account_strike(&strike_id)
|
|
||||||
.await
|
|
||||||
.map_err(Error::from_core)?;
|
|
||||||
|
|
||||||
strike
|
|
||||||
.update(
|
|
||||||
db,
|
|
||||||
PartialAccountStrike {
|
|
||||||
reason: Some(data.0.reason),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(Error::from_core)
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
use revolt_quark::models::{Report, User};
|
|
||||||
use revolt_quark::{Db, Error, Result};
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
|
|
||||||
/// # Fetch Report
|
|
||||||
///
|
|
||||||
/// Fetch a report by its ID
|
|
||||||
#[openapi(tag = "User Safety")]
|
|
||||||
#[get("/report/<id>")]
|
|
||||||
pub async fn fetch_report(db: &Db, user: User, id: String) -> Result<Json<Report>> {
|
|
||||||
// Must be privileged for this route
|
|
||||||
if !user.privileged {
|
|
||||||
return Err(Error::NotPrivileged);
|
|
||||||
}
|
|
||||||
|
|
||||||
db.fetch_report(&id).await.map(Json)
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
use revolt_quark::models::report::{ReportStatus, ReportStatusString, ReportedContent};
|
|
||||||
use revolt_quark::models::{Report, User};
|
|
||||||
use revolt_quark::{Db, Error, Result};
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
/// # Query Parameters
|
|
||||||
#[derive(Deserialize, JsonSchema, FromForm)]
|
|
||||||
pub struct OptionsFetchReports {
|
|
||||||
/// Find reports against messages, servers, or users
|
|
||||||
content_id: Option<String>,
|
|
||||||
|
|
||||||
/// Find reports created by user
|
|
||||||
author_id: Option<String>,
|
|
||||||
|
|
||||||
/// Report status to include in search
|
|
||||||
status: Option<ReportStatusString>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Fetch Reports
|
|
||||||
///
|
|
||||||
/// Fetch all available reports
|
|
||||||
#[openapi(tag = "User Safety")]
|
|
||||||
#[get("/reports?<options..>")]
|
|
||||||
pub async fn fetch_reports(
|
|
||||||
db: &Db,
|
|
||||||
user: User,
|
|
||||||
options: OptionsFetchReports,
|
|
||||||
) -> Result<Json<Vec<Report>>> {
|
|
||||||
// Must be privileged for this route
|
|
||||||
if !user.privileged {
|
|
||||||
return Err(Error::NotPrivileged);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut reports = db.fetch_reports().await?;
|
|
||||||
|
|
||||||
if let Some(content_id) = options.content_id {
|
|
||||||
reports.retain(|report| match &report.content {
|
|
||||||
ReportedContent::Message { id, .. }
|
|
||||||
| ReportedContent::Server { id, .. }
|
|
||||||
| ReportedContent::User { id, .. } => id == &content_id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(author_id) = options.author_id {
|
|
||||||
reports.retain(|report| report.author_id == author_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(status) = options.status {
|
|
||||||
reports.retain(|report| {
|
|
||||||
matches!(
|
|
||||||
(&status, &report.status),
|
|
||||||
(ReportStatusString::Created, ReportStatus::Created { .. })
|
|
||||||
| (ReportStatusString::Rejected, ReportStatus::Rejected { .. })
|
|
||||||
| (ReportStatusString::Resolved, ReportStatus::Resolved { .. })
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Json(reports))
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use revolt_quark::models::snapshot::{SnapshotContent, SnapshotWithContext};
|
|
||||||
use revolt_quark::models::{Channel, User};
|
|
||||||
use revolt_quark::{Db, Error, Result};
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
|
|
||||||
/// # Fetch Snapshots
|
|
||||||
///
|
|
||||||
/// Fetch a snapshots for a given report
|
|
||||||
#[openapi(tag = "User Safety")]
|
|
||||||
#[get("/snapshot/<report_id>")]
|
|
||||||
pub async fn fetch_snapshots(
|
|
||||||
db: &Db,
|
|
||||||
user: User,
|
|
||||||
report_id: String,
|
|
||||||
) -> Result<Json<Vec<SnapshotWithContext>>> {
|
|
||||||
// Must be privileged for this route
|
|
||||||
if !user.privileged {
|
|
||||||
return Err(Error::NotPrivileged);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch snapshots
|
|
||||||
let snapshots = db.fetch_snapshots(&report_id).await?;
|
|
||||||
let mut result = vec![];
|
|
||||||
|
|
||||||
for snapshot in snapshots {
|
|
||||||
// Resolve and fetch IDs of associated content
|
|
||||||
let mut user_ids: HashSet<&str> = HashSet::new();
|
|
||||||
let mut channel_ids: HashSet<&str> = HashSet::new();
|
|
||||||
|
|
||||||
match &snapshot.content {
|
|
||||||
SnapshotContent::Message {
|
|
||||||
prior_context,
|
|
||||||
leading_context,
|
|
||||||
message,
|
|
||||||
} => {
|
|
||||||
for msg in prior_context {
|
|
||||||
user_ids.insert(&msg.author);
|
|
||||||
}
|
|
||||||
|
|
||||||
for msg in leading_context {
|
|
||||||
user_ids.insert(&msg.author);
|
|
||||||
}
|
|
||||||
|
|
||||||
user_ids.insert(&message.author);
|
|
||||||
channel_ids.insert(&message.channel);
|
|
||||||
}
|
|
||||||
SnapshotContent::User(user) => {
|
|
||||||
user_ids.insert(&user.id);
|
|
||||||
}
|
|
||||||
SnapshotContent::Server(server) => {
|
|
||||||
for channel in &server.channels {
|
|
||||||
channel_ids.insert(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect user and channel IDs
|
|
||||||
let user_ids: Vec<String> = user_ids.into_iter().map(|s| s.to_owned()).collect();
|
|
||||||
let channel_ids: Vec<String> = channel_ids.into_iter().map(|s| s.to_owned()).collect();
|
|
||||||
|
|
||||||
// Fetch users and channels
|
|
||||||
let users = db.fetch_users(&user_ids).await?;
|
|
||||||
let channels = db.fetch_channels(&channel_ids).await?;
|
|
||||||
|
|
||||||
// Pull out first server from channels if possible
|
|
||||||
let server = if let Some(server_id) = channels.iter().find_map(|channel| match channel {
|
|
||||||
Channel::TextChannel { server, .. } => Some(server),
|
|
||||||
_ => None,
|
|
||||||
}) {
|
|
||||||
Some(db.fetch_server(server_id).await?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return snapshot with context
|
|
||||||
result.push(SnapshotWithContext {
|
|
||||||
snapshot,
|
|
||||||
users,
|
|
||||||
channels,
|
|
||||||
server,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Json(result))
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
use revolt_database::Database;
|
|
||||||
use revolt_models::v0::AccountStrike;
|
|
||||||
use revolt_quark::models::User;
|
|
||||||
use revolt_quark::{Error, Result};
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
use rocket::State;
|
|
||||||
|
|
||||||
/// # Fetch Strikes
|
|
||||||
///
|
|
||||||
/// Fetch strikes for a user by their ID
|
|
||||||
#[openapi(tag = "User Safety")]
|
|
||||||
#[get("/strikes/<user_id>")]
|
|
||||||
pub async fn fetch_strikes(
|
|
||||||
db: &State<Database>,
|
|
||||||
user: User,
|
|
||||||
user_id: String,
|
|
||||||
) -> Result<Json<Vec<AccountStrike>>> {
|
|
||||||
// Must be privileged for this route
|
|
||||||
if !user.privileged {
|
|
||||||
return Err(Error::NotPrivileged);
|
|
||||||
}
|
|
||||||
|
|
||||||
db.fetch_account_strikes_by_user(&user_id)
|
|
||||||
.await
|
|
||||||
.map(|v| v.into_iter().map(|e| e.into()).collect())
|
|
||||||
.map(Json)
|
|
||||||
.map_err(Error::from_core)
|
|
||||||
}
|
|
||||||
@@ -1,31 +1,11 @@
|
|||||||
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
|
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
|
||||||
use rocket::Route;
|
use rocket::Route;
|
||||||
|
|
||||||
mod edit_report;
|
|
||||||
mod fetch_report;
|
|
||||||
mod fetch_reports;
|
|
||||||
mod report_content;
|
mod report_content;
|
||||||
|
|
||||||
mod fetch_snapshots;
|
|
||||||
|
|
||||||
mod create_strike;
|
|
||||||
mod delete_strike;
|
|
||||||
mod edit_strike;
|
|
||||||
mod fetch_strikes;
|
|
||||||
|
|
||||||
pub fn routes() -> (Vec<Route>, OpenApi) {
|
pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||||
openapi_get_routes_spec![
|
openapi_get_routes_spec![
|
||||||
// Reports
|
// Reports
|
||||||
edit_report::edit_report,
|
|
||||||
fetch_report::fetch_report,
|
|
||||||
fetch_reports::fetch_reports,
|
|
||||||
report_content::report_content,
|
report_content::report_content,
|
||||||
// Snapshots
|
|
||||||
fetch_snapshots::fetch_snapshots,
|
|
||||||
// Strikes
|
|
||||||
create_strike::create_strike,
|
|
||||||
fetch_strikes::fetch_strikes,
|
|
||||||
edit_strike::edit_strike,
|
|
||||||
delete_strike::delete_strike
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ pub async fn req(db: &Db, user: User, target: Ref, role_id: String) -> Result<Em
|
|||||||
.throw_permission(db, Permission::ManageRole)
|
.throw_permission(db, Permission::ManageRole)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let member_rank = permissions.get_member_rank().unwrap_or(0);
|
let member_rank = permissions.get_member_rank().unwrap_or(i64::MIN);
|
||||||
|
|
||||||
if let Some(role) = server.roles.remove(&role_id) {
|
if let Some(role) = server.roles.remove(&role_id) {
|
||||||
if role.rank <= member_rank {
|
if role.rank <= member_rank {
|
||||||
|
|||||||
@@ -35,11 +35,13 @@ pub async fn webhook_edit(
|
|||||||
let DataEditWebhook {
|
let DataEditWebhook {
|
||||||
name,
|
name,
|
||||||
avatar,
|
avatar,
|
||||||
|
permissions,
|
||||||
remove,
|
remove,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
let mut partial = PartialWebhook {
|
let mut partial = PartialWebhook {
|
||||||
name,
|
name,
|
||||||
|
permissions,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,13 @@ pub async fn webhook_edit_token(
|
|||||||
let DataEditWebhook {
|
let DataEditWebhook {
|
||||||
name,
|
name,
|
||||||
avatar,
|
avatar,
|
||||||
remove,
|
permissions,
|
||||||
|
remove
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
let mut partial = PartialWebhook {
|
let mut partial = PartialWebhook {
|
||||||
name,
|
name,
|
||||||
|
permissions,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ pub async fn webhook_execute(
|
|||||||
let webhook = webhook_id.as_webhook(db).await.map_err(Error::from_core)?;
|
let webhook = webhook_id.as_webhook(db).await.map_err(Error::from_core)?;
|
||||||
webhook.assert_token(&token).map_err(Error::from_core)?;
|
webhook.assert_token(&token).map_err(Error::from_core)?;
|
||||||
|
|
||||||
// TODO: webhooks can currently always send masquerades, files, embeds, reactions (interactions)
|
data.validate_webhook_permissions(webhook.permissions)?;
|
||||||
// TODO: they can also mention anyone
|
|
||||||
|
|
||||||
let channel = legacy_db.fetch_channel(&webhook.channel_id).await?;
|
let channel = legacy_db.fetch_channel(&webhook.channel_id).await?;
|
||||||
let message = channel
|
let message = channel
|
||||||
|
|||||||
+14
-12
@@ -1,13 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.6.3"
|
version = "0.6.6"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mongo = [ "mongodb" ]
|
mongo = ["mongodb"]
|
||||||
rocket_impl = [
|
rocket_impl = [
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_empty",
|
"rocket_empty",
|
||||||
@@ -18,11 +18,11 @@ rocket_impl = [
|
|||||||
|
|
||||||
"authifier/database-mongodb",
|
"authifier/database-mongodb",
|
||||||
"authifier/rocket_impl",
|
"authifier/rocket_impl",
|
||||||
"authifier/okapi_impl"
|
"authifier/okapi_impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
test = [ "async-std", "mongo", "mongodb/async-std-runtime", "rocket_impl" ]
|
test = ["async-std", "mongo", "mongodb/async-std-runtime", "rocket_impl"]
|
||||||
default = [ "test" ]
|
default = ["test"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Serialisation
|
# Serialisation
|
||||||
@@ -39,7 +39,7 @@ bson = { version = "2.1.0", features = ["chrono-0_4"] }
|
|||||||
# Spec Generation
|
# Spec Generation
|
||||||
schemars = "0.8.8"
|
schemars = "0.8.8"
|
||||||
revolt_okapi = "0.9.1"
|
revolt_okapi = "0.9.1"
|
||||||
revolt_rocket_okapi = { version = "0.9.1", features = [ "swagger" ] }
|
revolt_rocket_okapi = { version = "0.9.1", features = ["swagger"] }
|
||||||
|
|
||||||
# Databases
|
# Databases
|
||||||
redis-kiss = { version = "0.1.4" }
|
redis-kiss = { version = "0.1.4" }
|
||||||
@@ -81,18 +81,20 @@ web-push = "0.7.2"
|
|||||||
|
|
||||||
# Implementations
|
# Implementations
|
||||||
rocket_http = { optional = true, version = "0.5.0-rc.2" }
|
rocket_http = { optional = true, version = "0.5.0-rc.2" }
|
||||||
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false, features = ["json"] }
|
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false, features = [
|
||||||
rocket_empty = { version = "0.1.1", optional = true, features = [ "schema" ] }
|
"json",
|
||||||
|
] }
|
||||||
|
rocket_empty = { version = "0.1.1", optional = true, features = ["schema"] }
|
||||||
rocket_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "c17e8145baa4790319fdb6a473e465b960f55e7c" }
|
rocket_cors = { optional = true, git = "https://github.com/lawliet89/rocket_cors", rev = "c17e8145baa4790319fdb6a473e465b960f55e7c" }
|
||||||
|
|
||||||
# Authifier
|
# Authifier
|
||||||
authifier = { version = "1.0.7", features = [ "async-std-runtime" ] }
|
authifier = { version = "1.0.7", features = ["async-std-runtime"] }
|
||||||
|
|
||||||
# Sentry
|
# Sentry
|
||||||
sentry = "0.25.0"
|
sentry = "0.31.5"
|
||||||
|
|
||||||
# Core
|
# Core
|
||||||
revolt-result = { path = "../core/result", features = [ "serde", "schemas" ] }
|
revolt-result = { path = "../core/result", features = ["serde", "schemas"] }
|
||||||
revolt-presence = { path = "../core/presence", features = [ "redis-is-patched" ] }
|
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
|
||||||
revolt-database = { path = "../core/database" }
|
revolt-database = { path = "../core/database" }
|
||||||
revolt-models = { path = "../core/models" }
|
revolt-models = { path = "../core/models" }
|
||||||
@@ -23,8 +23,7 @@ impl Cache {
|
|||||||
pub async fn can_view_channel(&self, db: &Database, channel: &Channel) -> bool {
|
pub async fn can_view_channel(&self, db: &Database, channel: &Channel) -> bool {
|
||||||
match &channel {
|
match &channel {
|
||||||
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
|
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
|
||||||
let member = self.members.values().find(|x| &x.id.server == server);
|
let member = self.members.get(server);
|
||||||
|
|
||||||
let server = self.servers.get(server);
|
let server = self.servers.get(server);
|
||||||
let mut perms = perms(self.users.get(&self.user_id).unwrap()).channel(channel);
|
let mut perms = perms(self.users.get(&self.user_id).unwrap()).channel(channel);
|
||||||
|
|
||||||
@@ -107,9 +106,15 @@ impl State {
|
|||||||
|
|
||||||
// Fetch all memberships with their corresponding servers.
|
// Fetch all memberships with their corresponding servers.
|
||||||
let members: Vec<Member> = db.fetch_all_memberships(&user.id).await?;
|
let members: Vec<Member> = db.fetch_all_memberships(&user.id).await?;
|
||||||
|
self.cache.members = members
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|x| (x.id.server.clone(), x))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let server_ids: Vec<String> = members.iter().map(|x| x.id.server.clone()).collect();
|
let server_ids: Vec<String> = members.iter().map(|x| x.id.server.clone()).collect();
|
||||||
let servers = db.fetch_servers(&server_ids).await?;
|
let servers = db.fetch_servers(&server_ids).await?;
|
||||||
|
self.cache.servers = servers.iter().cloned().map(|x| (x.id.clone(), x)).collect();
|
||||||
|
|
||||||
// Collect channel ids from servers.
|
// Collect channel ids from servers.
|
||||||
let mut channel_ids = vec![];
|
let mut channel_ids = vec![];
|
||||||
@@ -164,17 +169,11 @@ impl State {
|
|||||||
self.cache
|
self.cache
|
||||||
.users
|
.users
|
||||||
.insert(self.cache.user_id.clone(), user.clone());
|
.insert(self.cache.user_id.clone(), user.clone());
|
||||||
self.cache.servers = servers.iter().cloned().map(|x| (x.id.clone(), x)).collect();
|
|
||||||
self.cache.channels = channels
|
self.cache.channels = channels
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|x| (x.id().to_string(), x))
|
.map(|x| (x.id().to_string(), x))
|
||||||
.collect();
|
.collect();
|
||||||
self.cache.members = members
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|x| (x.id.server.clone(), x))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Make all users appear from our perspective.
|
// Make all users appear from our perspective.
|
||||||
let mut users: Vec<User> = users
|
let mut users: Vec<User> = users
|
||||||
@@ -353,7 +352,7 @@ impl State {
|
|||||||
let could_view: bool = if let Some(channel) = self.cache.channels.get(id) {
|
let could_view: bool = if let Some(channel) = self.cache.channels.get(id) {
|
||||||
self.cache.can_view_channel(db, channel).await
|
self.cache.can_view_channel(db, channel).await
|
||||||
} else {
|
} else {
|
||||||
true
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(channel) = self.cache.channels.get_mut(id) {
|
if let Some(channel) = self.cache.channels.get_mut(id) {
|
||||||
@@ -364,6 +363,12 @@ impl State {
|
|||||||
channel.apply_options(data.clone());
|
channel.apply_options(data.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !self.cache.channels.contains_key(id) {
|
||||||
|
if let Ok(channel) = db.fetch_channel(id).await {
|
||||||
|
self.cache.channels.insert(id.clone(), channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(channel) = self.cache.channels.get(id) {
|
if let Some(channel) = self.cache.channels.get(id) {
|
||||||
let can_view = self.cache.can_view_channel(db, channel).await;
|
let can_view = self.cache.can_view_channel(db, channel).await;
|
||||||
if could_view != can_view {
|
if could_view != can_view {
|
||||||
@@ -398,7 +403,9 @@ impl State {
|
|||||||
channels,
|
channels,
|
||||||
} => {
|
} => {
|
||||||
self.insert_subscription(id.clone());
|
self.insert_subscription(id.clone());
|
||||||
self.cache.servers.insert(id.to_string(), server.clone());
|
self.cache.servers.insert(id.clone(), server.clone());
|
||||||
|
let member = Member::new(id.clone(), self.cache.user_id.clone());
|
||||||
|
self.cache.members.insert(id.clone(), member);
|
||||||
|
|
||||||
for channel in channels {
|
for channel in channels {
|
||||||
self.cache
|
self.cache
|
||||||
@@ -436,6 +443,7 @@ impl State {
|
|||||||
self.cache.channels.remove(channel);
|
self.cache.channels.remove(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.cache.members.remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventV1::ServerDelete { id } => {
|
EventV1::ServerDelete { id } => {
|
||||||
@@ -447,6 +455,7 @@ impl State {
|
|||||||
self.cache.channels.remove(channel);
|
self.cache.channels.remove(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.cache.members.remove(id);
|
||||||
}
|
}
|
||||||
EventV1::ServerMemberUpdate { id, data, clear } => {
|
EventV1::ServerMemberUpdate { id, data, clear } => {
|
||||||
if id.user == self.cache.user_id {
|
if id.user == self.cache.user_id {
|
||||||
|
|||||||
@@ -3,22 +3,10 @@ use crate::{AbstractServerMember, Result};
|
|||||||
|
|
||||||
use super::super::DummyDb;
|
use super::super::DummyDb;
|
||||||
|
|
||||||
use iso8601_timestamp::Timestamp;
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AbstractServerMember for DummyDb {
|
impl AbstractServerMember for DummyDb {
|
||||||
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member> {
|
async fn fetch_member(&self, server: &str, user: &str) -> Result<Member> {
|
||||||
Ok(Member {
|
Ok(Member::new(server.into(), user.into()))
|
||||||
id: MemberCompositeKey {
|
|
||||||
server: server.into(),
|
|
||||||
user: user.into(),
|
|
||||||
},
|
|
||||||
joined_at: Timestamp::now_utc(),
|
|
||||||
nickname: None,
|
|
||||||
avatar: None,
|
|
||||||
roles: vec![],
|
|
||||||
timeout: None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()> {
|
async fn insert_member(&self, member: &Member) -> Result<()> {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::{
|
|||||||
models::{
|
models::{
|
||||||
message::{
|
message::{
|
||||||
AppendMessage, BulkMessageResponse, Interactions, PartialMessage, SendableEmbed,
|
AppendMessage, BulkMessageResponse, Interactions, PartialMessage, SendableEmbed,
|
||||||
SystemMessage,
|
SystemMessage, DataMessageSend,
|
||||||
},
|
},
|
||||||
Channel, Emoji, Message, User,
|
Channel, Emoji, Message, User,
|
||||||
},
|
},
|
||||||
@@ -451,3 +451,39 @@ impl Interactions {
|
|||||||
!self.restrict_reactions && self.reactions.is_none()
|
!self.restrict_reactions && self.reactions.is_none()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn throw_permission(permissions: u64, permission: Permission) -> Result<()> {
|
||||||
|
if (permission as u64) & permissions == (permission as u64) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::MissingPermission { permission })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataMessageSend {
|
||||||
|
pub fn validate_webhook_permissions(
|
||||||
|
&self,
|
||||||
|
permissions: u64,
|
||||||
|
) -> Result<()> {
|
||||||
|
throw_permission(permissions, Permission::SendMessage)?;
|
||||||
|
|
||||||
|
if self.attachments.as_ref().map_or(false, |v| !v.is_empty()) {
|
||||||
|
throw_permission(permissions, Permission::UploadFiles)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.embeds.as_ref().map_or(false, |v| !v.is_empty()) {
|
||||||
|
throw_permission(permissions, Permission::SendEmbeds)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.masquerade.is_some() {
|
||||||
|
throw_permission(permissions, Permission::Masquerade)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.interactions.is_some() {
|
||||||
|
throw_permission(permissions, Permission::React)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use iso8601_timestamp::Timestamp;
|
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -186,18 +185,7 @@ impl Server {
|
|||||||
return Err(Error::Banned);
|
return Err(Error::Banned);
|
||||||
}
|
}
|
||||||
|
|
||||||
let member = Member {
|
let member = Member::new(self.id.clone(), user.id.clone());
|
||||||
id: MemberCompositeKey {
|
|
||||||
server: self.id.clone(),
|
|
||||||
user: user.id.clone(),
|
|
||||||
},
|
|
||||||
joined_at: Timestamp::now_utc(),
|
|
||||||
nickname: None,
|
|
||||||
avatar: None,
|
|
||||||
roles: vec![],
|
|
||||||
timeout: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
db.insert_member(&member).await?;
|
db.insert_member(&member).await?;
|
||||||
|
|
||||||
let should_fetch = channels.is_none();
|
let should_fetch = channels.is_none();
|
||||||
|
|||||||
@@ -3,13 +3,27 @@ use iso8601_timestamp::Timestamp;
|
|||||||
use crate::{
|
use crate::{
|
||||||
events::client::EventV1,
|
events::client::EventV1,
|
||||||
models::{
|
models::{
|
||||||
server_member::{FieldsMember, PartialMember},
|
server_member::{FieldsMember, MemberCompositeKey, PartialMember},
|
||||||
Member, Server,
|
Member, Server,
|
||||||
},
|
},
|
||||||
Database, Result,
|
Database, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Member {
|
impl Member {
|
||||||
|
pub fn new(server_id: String, user_id: String) -> Self {
|
||||||
|
Self {
|
||||||
|
id: MemberCompositeKey {
|
||||||
|
server: server_id,
|
||||||
|
user: user_id,
|
||||||
|
},
|
||||||
|
joined_at: Timestamp::now_utc(),
|
||||||
|
nickname: None,
|
||||||
|
avatar: None,
|
||||||
|
roles: vec![],
|
||||||
|
timeout: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Update member data
|
/// Update member data
|
||||||
pub async fn update<'a>(
|
pub async fn update<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -269,7 +269,6 @@ pub struct DataMessageSend {
|
|||||||
#[validate(length(min = 0, max = 2000))]
|
#[validate(length(min = 0, max = 2000))]
|
||||||
pub content: Option<String>,
|
pub content: Option<String>,
|
||||||
/// Attachments to include in message
|
/// Attachments to include in message
|
||||||
|
|
||||||
pub attachments: Option<Vec<String>>,
|
pub attachments: Option<Vec<String>>,
|
||||||
/// Messages to reply to
|
/// Messages to reply to
|
||||||
pub replies: Option<Vec<Reply>>,
|
pub replies: Option<Vec<Reply>>,
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ pub static DEFAULT_PERMISSION: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION_VIEW_
|
|||||||
pub static DEFAULT_PERMISSION_SAVED_MESSAGES: u64 = Permission::GrantAllSafe as u64;
|
pub static DEFAULT_PERMISSION_SAVED_MESSAGES: u64 = Permission::GrantAllSafe as u64;
|
||||||
pub static DEFAULT_PERMISSION_DIRECT_MESSAGE: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::ManageChannel + Permission::React));
|
pub static DEFAULT_PERMISSION_DIRECT_MESSAGE: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::ManageChannel + Permission::React));
|
||||||
pub static DEFAULT_PERMISSION_SERVER: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::React + Permission::ChangeNickname + Permission::ChangeAvatar));
|
pub static DEFAULT_PERMISSION_SERVER: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::React + Permission::ChangeNickname + Permission::ChangeAvatar));
|
||||||
|
pub static DEFAULT_WEBHOOK_PERMISSIONS: Lazy<u64> = Lazy::new(|| Permission::SendMessage + Permission::SendEmbeds + Permission::Masquerade + Permission::React);
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/// Configure logging and common Rust variables
|
/// Configure logging and common Rust variables
|
||||||
pub fn setup_logging(release: &'static str) -> sentry::ClientInitGuard {
|
pub fn setup_logging(release: &'static str) -> Option<sentry::ClientInitGuard> {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
|
|
||||||
if std::env::var("RUST_LOG").is_err() {
|
if std::env::var("RUST_LOG").is_err() {
|
||||||
@@ -13,13 +13,17 @@ pub fn setup_logging(release: &'static str) -> sentry::ClientInitGuard {
|
|||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
info!("Starting {release}");
|
info!("Starting {release}");
|
||||||
|
|
||||||
sentry::init((
|
if let Ok(dsn) = std::env::var("SENTRY_DSN") {
|
||||||
"https://d1d2a6f15c6245a987c532bbbcb30a04@glitchtip.insert.moe/2",
|
Some(sentry::init((
|
||||||
sentry::ClientOptions {
|
dsn,
|
||||||
release: Some(release.into()),
|
sentry::ClientOptions {
|
||||||
..Default::default()
|
release: Some(release.into()),
|
||||||
},
|
..Default::default()
|
||||||
))
|
},
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|||||||
Reference in new issue
Block a user