This commit is contained in:
kozabrada123 2024-06-28 12:06:02 +00:00 committed by GitHub
commit 69222548bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
114 changed files with 2697 additions and 928 deletions

View File

@ -46,6 +46,26 @@ jobs:
cargo build --verbose --all-features cargo build --verbose --all-features
cargo test --verbose --all-features cargo test --verbose --all-features
fi fi
- name: Check common non-default feature configurations
run: |
echo "No features:"
cargo check --features="" --no-default-features
echo "Only client:"
cargo check --features="client" --no-default-features
echo "Only backend:"
cargo check --features="backend" --no-default-features
echo "Only voice:"
cargo check --features="voice" --no-default-features
echo "Only voice gateway:"
cargo check --features="voice_gateway" --no-default-features
echo "Backend + client:"
cargo check --features="backend, client" --no-default-features
echo "Backend + voice:"
cargo check --features="backend, voice" --no-default-features
echo "Backend + voice gateway:"
cargo check --features="backend, voice_gateway" --no-default-features
echo "Client + voice gateway:"
cargo check --features="client, voice_gateway" --no-default-features
# wasm-safari: # wasm-safari:
# runs-on: macos-latest # runs-on: macos-latest
# steps: # steps:
@ -75,7 +95,7 @@ jobs:
# cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force # cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force
# SAFARIDRIVER=$(which safaridriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt" --no-fail-fast # SAFARIDRIVER=$(which safaridriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt" --no-fail-fast
wasm-gecko: wasm-gecko:
runs-on: macos-latest runs-on: ubuntu-latest
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -101,10 +121,10 @@ jobs:
run: | run: |
rustup target add wasm32-unknown-unknown rustup target add wasm32-unknown-unknown
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.92" --force
GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway" GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
wasm-chrome: wasm-chrome:
runs-on: macos-latest runs-on: ubuntu-latest
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -130,5 +150,5 @@ jobs:
run: | run: |
rustup target add wasm32-unknown-unknown rustup target add wasm32-unknown-unknown
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.92" --force
CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway" CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"

296
Cargo.lock generated
View File

@ -101,13 +101,19 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]] [[package]]
name = "atomic-write-file" name = "atomic-write-file"
version = "0.1.2" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436"
dependencies = [ dependencies = [
"nix", "nix 0.27.1",
"rand", "rand",
] ]
@ -207,6 +213,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]] [[package]]
name = "chorus" name = "chorus"
version = "0.15.0" version = "0.15.0"
@ -219,10 +231,11 @@ dependencies = [
"crypto_secretbox", "crypto_secretbox",
"custom_error", "custom_error",
"discortp", "discortp",
"flate2",
"futures-util", "futures-util",
"getrandom", "getrandom",
"hostname", "hostname",
"http", "http 0.2.11",
"jsonwebtoken", "jsonwebtoken",
"lazy_static", "lazy_static",
"log", "log",
@ -238,6 +251,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_repr", "serde_repr",
"serde_with", "serde_with",
"simple_logger",
"sqlx", "sqlx",
"thiserror", "thiserror",
"tokio", "tokio",
@ -252,9 +266,7 @@ dependencies = [
[[package]] [[package]]
name = "chorus-macros" name = "chorus-macros"
version = "0.2.0" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a81545a60b926f815517dadbbd40cd502294ae2baea25fa8194d854d607512b0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"quote", "quote",
@ -343,6 +355,15 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crossbeam-queue" name = "crossbeam-queue"
version = "0.3.11" version = "0.3.11"
@ -543,6 +564,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6"
[[package]]
name = "flate2"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.11.0" version = "0.11.0"
@ -716,16 +747,35 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.24" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"futures-util", "futures-util",
"http", "http 0.2.11",
"indexmap 2.1.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "h2"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http 1.1.0",
"indexmap 2.1.0", "indexmap 2.1.0",
"slab", "slab",
"tokio", "tokio",
@ -760,14 +810,14 @@ dependencies = [
[[package]] [[package]]
name = "headers" name = "headers"
version = "0.3.9" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9"
dependencies = [ dependencies = [
"base64 0.21.7", "base64 0.21.7",
"bytes", "bytes",
"headers-core", "headers-core",
"http", "http 1.1.0",
"httpdate", "httpdate",
"mime", "mime",
"sha1", "sha1",
@ -775,11 +825,11 @@ dependencies = [
[[package]] [[package]]
name = "headers-core" name = "headers-core"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
dependencies = [ dependencies = [
"http", "http 1.1.0",
] ]
[[package]] [[package]]
@ -852,6 +902,17 @@ dependencies = [
"itoa", "itoa",
] ]
[[package]]
name = "http"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]] [[package]]
name = "http-body" name = "http-body"
version = "0.4.6" version = "0.4.6"
@ -859,7 +920,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [ dependencies = [
"bytes", "bytes",
"http", "http 0.2.11",
"pin-project-lite",
]
[[package]]
name = "http-body"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
dependencies = [
"bytes",
"http 1.1.0",
]
[[package]]
name = "http-body-util"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
dependencies = [
"bytes",
"futures-core",
"http 1.1.0",
"http-body 1.0.0",
"pin-project-lite", "pin-project-lite",
] ]
@ -885,9 +969,9 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2 0.3.26",
"http", "http 0.2.11",
"http-body", "http-body 0.4.6",
"httparse", "httparse",
"httpdate", "httpdate",
"itoa", "itoa",
@ -899,6 +983,26 @@ dependencies = [
"want", "want",
] ]
[[package]]
name = "hyper"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2 0.4.5",
"http 1.1.0",
"http-body 1.0.0",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"smallvec",
"tokio",
]
[[package]] [[package]]
name = "hyper-tls" name = "hyper-tls"
version = "0.5.0" version = "0.5.0"
@ -906,12 +1010,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [ dependencies = [
"bytes", "bytes",
"hyper", "hyper 0.14.28",
"native-tls", "native-tls",
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
] ]
[[package]]
name = "hyper-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56"
dependencies = [
"bytes",
"futures-util",
"http 1.1.0",
"http-body 1.0.0",
"hyper 1.3.1",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.59" version = "0.1.59"
@ -1014,9 +1133,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.66" version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@ -1046,9 +1165,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.152" version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]] [[package]]
name = "libm" name = "libm"
@ -1153,6 +1272,24 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "multer"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http 1.1.0",
"httparse",
"memchr",
"mime",
"spin 0.9.8",
"tokio",
"version_check",
]
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.11" version = "0.2.11"
@ -1182,6 +1319,18 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "nix"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.4.1",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]] [[package]]
name = "no-std-net" name = "no-std-net"
version = "0.6.0" version = "0.6.0"
@ -1466,18 +1615,20 @@ dependencies = [
[[package]] [[package]]
name = "poem" name = "poem"
version = "1.3.59" version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504774c97b0744c1ee108a37e5a65a9745a4725c4c06277521dabc28eb53a904" checksum = "e88b6912ed1e8833d7c22c9c986c517f4518d7d37e3c04566d917c789aaea591"
dependencies = [ dependencies = [
"async-trait",
"bytes", "bytes",
"futures-util", "futures-util",
"headers", "headers",
"http", "http 1.1.0",
"hyper", "http-body-util",
"hyper 1.3.1",
"hyper-util",
"mime", "mime",
"nix", "multer",
"nix 0.28.0",
"parking_lot", "parking_lot",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
@ -1488,6 +1639,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"smallvec", "smallvec",
"sync_wrapper",
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-util", "tokio-util",
@ -1497,9 +1649,9 @@ dependencies = [
[[package]] [[package]]
name = "poem-derive" name = "poem-derive"
version = "1.3.59" version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ddcf4680d8d867e1e375116203846acb088483fa2070244f90589f458bbb31" checksum = "c2b961d58a6c53380c20236394381d9292fda03577f902b158f1638932964dcf"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
@ -1532,11 +1684,10 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "2.0.1" version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
dependencies = [ dependencies = [
"toml_datetime",
"toml_edit", "toml_edit",
] ]
@ -1637,10 +1788,10 @@ dependencies = [
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2", "h2 0.3.26",
"http", "http 0.2.11",
"http-body", "http-body 0.4.6",
"hyper", "hyper 0.14.28",
"hyper-tls", "hyper-tls",
"ipnet", "ipnet",
"js-sys", "js-sys",
@ -1753,9 +1904,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.21.10" version = "0.21.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
dependencies = [ dependencies = [
"log", "log",
"ring 0.17.7", "ring 0.17.7",
@ -2013,6 +2164,16 @@ dependencies = [
"time", "time",
] ]
[[package]]
name = "simple_logger"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb"
dependencies = [
"log",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -2024,9 +2185,9 @@ dependencies = [
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.11.2" version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]] [[package]]
name = "socket2" name = "socket2"
@ -2323,6 +2484,15 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "sync_wrapper"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
dependencies = [
"futures-core",
]
[[package]] [[package]]
name = "system-configuration" name = "system-configuration"
version = "0.5.1" version = "0.5.1"
@ -2511,15 +2681,15 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.3" version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.20.2" version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
dependencies = [ dependencies = [
"indexmap 2.1.0", "indexmap 2.1.0",
"toml_datetime", "toml_datetime",
@ -2579,7 +2749,7 @@ dependencies = [
"byteorder", "byteorder",
"bytes", "bytes",
"data-encoding", "data-encoding",
"http", "http 0.2.11",
"httparse", "httparse",
"log", "log",
"rand", "rand",
@ -2727,9 +2897,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.89" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@ -2737,9 +2907,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.89" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
@ -2752,9 +2922,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.39" version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
@ -2764,9 +2934,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.89" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -2774,9 +2944,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.89" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2787,15 +2957,15 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.89" version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]] [[package]]
name = "wasm-bindgen-test" name = "wasm-bindgen-test"
version = "0.3.39" version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cf9242c0d27999b831eae4767b2a146feb0b27d332d553e605864acd2afd403" checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b"
dependencies = [ dependencies = [
"console_error_panic_hook", "console_error_panic_hook",
"js-sys", "js-sys",
@ -2807,9 +2977,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-test-macro" name = "wasm-bindgen-test-macro"
version = "0.3.39" version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "794645f5408c9a039fd09f4d113cdfb2e7eba5ff1956b07bcf701cf4b394fe89" checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -13,10 +13,10 @@ rust-version = "1.67.1"
[features] [features]
default = ["client", "rt-multi-thread"] default = ["client", "rt-multi-thread"]
backend = ["dep:poem", "dep:sqlx"] backend = ["poem", "sqlx"]
rt-multi-thread = ["tokio/rt-multi-thread"] rt-multi-thread = ["tokio/rt-multi-thread"]
rt = ["tokio/rt"] rt = ["tokio/rt"]
client = [] client = ["flate2"]
voice = ["voice_udp", "voice_gateway"] voice = ["voice_udp", "voice_gateway"]
voice_udp = ["dep:discortp", "dep:crypto_secretbox"] voice_udp = ["dep:discortp", "dep:crypto_secretbox"]
voice_gateway = [] voice_gateway = []
@ -38,12 +38,12 @@ http = "0.2.11"
base64 = "0.21.7" base64 = "0.21.7"
bitflags = { version = "2.4.1", features = ["serde"] } bitflags = { version = "2.4.1", features = ["serde"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
poem = { version = "1.3.59", optional = true } poem = { version = "3.0.1", features = ["multipart"], optional = true }
thiserror = "1.0.56" thiserror = "1.0.56"
jsonwebtoken = "8.3.0" jsonwebtoken = "8.3.0"
log = "0.4.20" log = "0.4.20"
async-trait = "0.1.77" async-trait = "0.1.77"
chorus-macros = "0.2.0" chorus-macros = { path = "./chorus-macros", version = "0" } # Note: version here is used when releasing. This will use the latest release. Make sure to republish the crate when code in macros is changed!
sqlx = { version = "0.7.3", features = [ sqlx = { version = "0.7.3", features = [
"mysql", "mysql",
"sqlite", "sqlite",
@ -56,6 +56,7 @@ sqlx = { version = "0.7.3", features = [
discortp = { version = "0.5.0", optional = true, features = ["rtp", "discord", "demux"] } discortp = { version = "0.5.0", optional = true, features = ["rtp", "discord", "demux"] }
crypto_secretbox = { version = "0.1.1", optional = true } crypto_secretbox = { version = "0.1.1", optional = true }
rand = "0.8.5" rand = "0.8.5"
flate2 = { version = "1.0.30", optional = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
rustls = "0.21.10" rustls = "0.21.10"
@ -76,5 +77,6 @@ wasmtimer = "0.2.0"
[dev-dependencies] [dev-dependencies]
lazy_static = "1.4.0" lazy_static = "1.4.0"
wasm-bindgen-test = "0.3.39" wasm-bindgen-test = "0.3.42"
wasm-bindgen = "0.2.89" wasm-bindgen = "0.2.92"
simple_logger = { version = "5.0.0", default-features=false }

View File

@ -15,7 +15,7 @@ dependencies = [
[[package]] [[package]]
name = "chorus-macros" name = "chorus-macros"
version = "0.1.0" version = "0.4.1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"quote", "quote",

View File

@ -1,9 +1,10 @@
[package] [package]
name = "chorus-macros" name = "chorus-macros"
version = "0.2.0" version = "0.4.1"
edition = "2021" edition = "2021"
license = "AGPL-3.0" license = "MPL-2.0"
description = "Macros for the chorus crate." description = "Macros for the chorus crate."
repository = "https://github.com/polyphony-chat/chorus"
[lib] [lib]
proc-macro = true proc-macro = true

View File

@ -6,6 +6,18 @@ use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed}; use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed};
#[proc_macro_derive(WebSocketEvent)]
pub fn websocket_event_macro_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
quote! {
impl WebSocketEvent for #name {}
}
.into()
}
#[proc_macro_derive(Updateable)] #[proc_macro_derive(Updateable)]
pub fn updateable_macro_derive(input: TokenStream) -> TokenStream { pub fn updateable_macro_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap(); let ast: syn::DeriveInput = syn::parse(input).unwrap();
@ -143,3 +155,70 @@ pub fn composite_derive(input: TokenStream) -> TokenStream {
_ => panic!("Composite derive macro only supports structs"), _ => panic!("Composite derive macro only supports structs"),
} }
} }
#[proc_macro_derive(SqlxBitFlags)]
pub fn sqlx_bitflag_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
quote!{
#[cfg(feature = "sqlx")]
impl sqlx::Type<sqlx::MySql> for #name {
fn type_info() -> sqlx::mysql::MySqlTypeInfo {
u64::type_info()
}
}
#[cfg(feature = "sqlx")]
impl<'q> sqlx::Encode<'q, sqlx::MySql> for #name {
fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
u64::encode_by_ref(&self.bits(), buf)
}
}
#[cfg(feature = "sqlx")]
impl<'q> sqlx::Decode<'q, sqlx::MySql> for #name {
fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'q>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
u64::decode(value).map(|d| #name::from_bits(d).unwrap())
}
}
}
.into()
}
#[proc_macro_derive(SerdeBitFlags)]
pub fn serde_bitflag_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
quote! {
impl std::str::FromStr for #name {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<#name, Self::Err> {
s.parse::<u64>().map(#name::from_bits).map(|f| f.unwrap_or(#name::empty()))
}
}
impl serde::Serialize for #name {
fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.bits().to_string())
}
}
impl<'de> serde::Deserialize<'de> for #name {
fn deserialize<D>(deserializer: D) -> Result<#name, D::Error> where D: serde::de::Deserializer<'de> + Sized {
// let s = String::deserialize(deserializer)?.parse::<u64>().map_err(serde::de::Error::custom)?;
let s = crate::types::serde::string_or_u64(deserializer)?;
// Note: while truncating may not be ideal, it's better than a panic if there are
// extra flags
Ok(Self::from_bits_truncate(s))
}
}
}
.into()
}

View File

@ -3,6 +3,8 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This example showcase how to properly use gateway observers. // This example showcase how to properly use gateway observers.
// (This assumes you have a manually created gateway, if you created
// a ChorusUser by e.g. logging in, you can access the gateway with user.gateway)
// //
// To properly run it, you will need to change the token below. // To properly run it, you will need to change the token below.
@ -12,7 +14,7 @@ const TOKEN: &str = "";
const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/"; const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/";
use async_trait::async_trait; use async_trait::async_trait;
use chorus::gateway::Gateway; use chorus::gateway::{Gateway, GatewayOptions};
use chorus::{ use chorus::{
self, self,
gateway::Observer, gateway::Observer,
@ -47,8 +49,14 @@ impl Observer<GatewayReady> for ExampleObserver {
async fn main() { async fn main() {
let gateway_websocket_url = GATEWAY_URL.to_string(); let gateway_websocket_url = GATEWAY_URL.to_string();
// These options specify the encoding format, compression, etc
//
// For most cases the defaults should work, though some implementations
// might only support some formats or not support compression
let options = GatewayOptions::default();
// Initiate the gateway connection // Initiate the gateway connection
let gateway = Gateway::spawn(gateway_websocket_url).await.unwrap(); let gateway = Gateway::spawn(gateway_websocket_url, options).await.unwrap();
// Create an instance of our observer // Create an instance of our observer
let observer = ExampleObserver {}; let observer = ExampleObserver {};

View File

@ -3,7 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This example showcases how to initiate a gateway connection manually // This example showcases how to initiate a gateway connection manually
// (e. g. not through ChorusUser) // (e. g. not through ChorusUser or Instance)
// //
// To properly run it, you will need to modify the token below. // To properly run it, you will need to modify the token below.
@ -14,7 +14,7 @@ const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/";
use std::time::Duration; use std::time::Duration;
use chorus::gateway::Gateway; use chorus::gateway::{Gateway, GatewayOptions};
use chorus::{self, types::GatewayIdentifyPayload}; use chorus::{self, types::GatewayIdentifyPayload};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -27,8 +27,14 @@ use wasmtimer::tokio::sleep;
async fn main() { async fn main() {
let gateway_websocket_url = GATEWAY_URL.to_string(); let gateway_websocket_url = GATEWAY_URL.to_string();
// These options specify the encoding format, compression, etc
//
// For most cases the defaults should work, though some implementations
// might only support some formats or not support compression
let options = GatewayOptions::default();
// Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another // Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another
let gateway = Gateway::spawn(gateway_websocket_url).await.unwrap(); let gateway = Gateway::spawn(gateway_websocket_url, options).await.unwrap();
// At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated // At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated

18
semver_release_checks.yml Normal file
View File

@ -0,0 +1,18 @@
name: Semver release checks
on:
pull_request:
branches: ["main"]
env:
CARGO_TERM_COLOR: always
jobs:
semver-checks:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: obi1kenobi/cargo-semver-checks-action@v2

View File

@ -11,7 +11,7 @@ use crate::errors::ChorusResult;
use crate::gateway::Gateway; use crate::gateway::Gateway;
use crate::instance::{ChorusUser, Instance}; use crate::instance::{ChorusUser, Instance};
use crate::ratelimiter::ChorusRequest; use crate::ratelimiter::ChorusRequest;
use crate::types::{GatewayIdentifyPayload, LimitType, LoginResult, LoginSchema}; use crate::types::{GatewayIdentifyPayload, LimitType, LoginResult, LoginSchema, User};
impl Instance { impl Instance {
/// Logs into an existing account on the spacebar server. /// Logs into an existing account on the spacebar server.
@ -30,27 +30,22 @@ impl Instance {
// We do not have a user yet, and the UserRateLimits will not be affected by a login // We do not have a user yet, and the UserRateLimits will not be affected by a login
// request (since login is an instance wide limit), which is why we are just cloning the // request (since login is an instance wide limit), which is why we are just cloning the
// instances' limits to pass them on as user_rate_limits later. // instances' limits to pass them on as user_rate_limits later.
let mut shell = let mut user =
ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None".to_string()).await; ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None".to_string()).await;
let login_result = chorus_request let login_result = chorus_request
.deserialize_response::<LoginResult>(&mut shell) .deserialize_response::<LoginResult>(&mut user)
.await?; .await?;
let object = self.get_user(login_result.token.clone(), None).await?; user.set_token(login_result.token);
if self.limits_information.is_some() { user.settings = login_result.settings;
self.limits_information.as_mut().unwrap().ratelimits = shell.limits.clone().unwrap();
} let object = User::get(&mut user, None).await?;
*user.object.write().unwrap() = object;
let mut identify = GatewayIdentifyPayload::common(); let mut identify = GatewayIdentifyPayload::common();
let gateway = Gateway::spawn(self.urls.wss.clone()).await.unwrap(); identify.token = user.token();
identify.token = login_result.token.clone(); user.gateway.send_identify(identify).await;
gateway.send_identify(identify).await;
let user = ChorusUser::new(
Arc::new(RwLock::new(self.clone())),
login_result.token,
self.clone_limits_if_some(),
login_result.settings,
Arc::new(RwLock::new(object)),
gateway,
);
Ok(user) Ok(user)
} }
} }

View File

@ -23,26 +23,19 @@ pub mod register;
impl Instance { impl Instance {
/// Logs into an existing account on the spacebar server, using only a token. /// Logs into an existing account on the spacebar server, using only a token.
pub async fn login_with_token(&mut self, token: String) -> ChorusResult<ChorusUser> { pub async fn login_with_token(&mut self, token: String) -> ChorusResult<ChorusUser> {
let object_result = self.get_user(token.clone(), None).await; let mut user =
if let Err(e) = object_result { ChorusUser::shell(Arc::new(RwLock::new(self.clone())), token).await;
return Result::Err(e);
} let object = User::get(&mut user, None).await?;
let settings = User::get_settings(&mut user).await?;
*user.object.write().unwrap() = object;
*user.settings.write().unwrap() = settings;
let user_settings = User::get_settings(&token, &self.urls.api, &mut self.clone())
.await
.unwrap();
let mut identify = GatewayIdentifyPayload::common(); let mut identify = GatewayIdentifyPayload::common();
let gateway = Gateway::spawn(self.urls.wss.clone()).await.unwrap(); identify.token = user.token();
identify.token = token.clone(); user.gateway.send_identify(identify).await;
gateway.send_identify(identify).await;
let user = ChorusUser::new(
Arc::new(RwLock::new(self.clone())),
token.clone(),
self.clone_limits_if_some(),
Arc::new(RwLock::new(user_settings)),
Arc::new(RwLock::new(object_result.unwrap())),
gateway,
);
Ok(user) Ok(user)
} }
} }

View File

@ -8,7 +8,7 @@ use reqwest::Client;
use serde_json::to_string; use serde_json::to_string;
use crate::gateway::{Gateway, GatewayHandle}; use crate::gateway::{Gateway, GatewayHandle};
use crate::types::GatewayIdentifyPayload; use crate::types::{GatewayIdentifyPayload, User};
use crate::{ use crate::{
errors::ChorusResult, errors::ChorusResult,
instance::{ChorusUser, Instance, Token}, instance::{ChorusUser, Instance, Token},
@ -37,29 +37,25 @@ impl Instance {
// We do not have a user yet, and the UserRateLimits will not be affected by a login // We do not have a user yet, and the UserRateLimits will not be affected by a login
// request (since register is an instance wide limit), which is why we are just cloning // request (since register is an instance wide limit), which is why we are just cloning
// the instances' limits to pass them on as user_rate_limits later. // the instances' limits to pass them on as user_rate_limits later.
let mut shell = let mut user =
ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None".to_string()).await; ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None".to_string()).await;
let token = chorus_request let token = chorus_request
.deserialize_response::<Token>(&mut shell) .deserialize_response::<Token>(&mut user)
.await? .await?
.token; .token;
if self.limits_information.is_some() { user.set_token(token);
self.limits_information.as_mut().unwrap().ratelimits = shell.limits.unwrap();
} let object = User::get(&mut user, None).await?;
let user_object = self.get_user(token.clone(), None).await.unwrap(); let settings = User::get_settings(&mut user).await?;
let settings = ChorusUser::get_settings(&token, &self.urls.api.clone(), self).await?;
*user.object.write().unwrap() = object;
*user.settings.write().unwrap() = settings;
let mut identify = GatewayIdentifyPayload::common(); let mut identify = GatewayIdentifyPayload::common();
let gateway: GatewayHandle = Gateway::spawn(self.urls.wss.clone()).await.unwrap(); identify.token = user.token();
identify.token = token.clone(); user.gateway.send_identify(identify).await;
gateway.send_identify(identify).await;
let user = ChorusUser::new(
Arc::new(RwLock::new(self.clone())),
token.clone(),
self.clone_limits_if_some(),
Arc::new(RwLock::new(settings)),
Arc::new(RwLock::new(user_object)),
gateway,
);
Ok(user) Ok(user)
} }
} }

View File

@ -44,6 +44,16 @@ impl ChorusUser {
&mut self, &mut self,
query: Option<GetUserGuildSchema>, query: Option<GetUserGuildSchema>,
) -> ChorusResult<Vec<Guild>> { ) -> ChorusResult<Vec<Guild>> {
let query_parameters = {
if let Some(query_some) = query {
query_some.to_query()
}
else {
Vec::new()
}
};
let url = format!( let url = format!(
"{}/users/@me/guilds", "{}/users/@me/guilds",
self.belongs_to.read().unwrap().urls.api, self.belongs_to.read().unwrap().urls.api,
@ -53,7 +63,7 @@ impl ChorusUser {
.get(url) .get(url)
.header("Authorization", self.token()) .header("Authorization", self.token())
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body(to_string(&query).unwrap()), .query(&query_parameters),
limit_type: LimitType::Global, limit_type: LimitType::Global,
}; };

View File

@ -30,13 +30,9 @@ impl ChorusUser {
/// Gets the user's settings. /// Gets the user's settings.
/// ///
/// # Notes /// # Notes
/// This functions is a wrapper around [`User::get_settings`]. /// This function is a wrapper around [`User::get_settings`].
pub async fn get_settings( pub async fn get_settings(&mut self) -> ChorusResult<UserSettings> {
token: &String, User::get_settings(self).await
url_api: &String,
instance: &mut Instance,
) -> ChorusResult<UserSettings> {
User::get_settings(token, url_api, instance).await
} }
/// Modifies the current user's representation. (See [`User`]) /// Modifies the current user's representation. (See [`User`])
@ -44,12 +40,18 @@ impl ChorusUser {
/// # Reference /// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/user#modify-current-user> /// See <https://discord-userdoccers.vercel.app/resources/user#modify-current-user>
pub async fn modify(&mut self, modify_schema: UserModifySchema) -> ChorusResult<User> { pub async fn modify(&mut self, modify_schema: UserModifySchema) -> ChorusResult<User> {
if modify_schema.new_password.is_some()
// See <https://docs.discord.sex/resources/user#json-params>, note 1
let requires_current_password = modify_schema.username.is_some()
|| modify_schema.discriminator.is_some()
|| modify_schema.email.is_some() || modify_schema.email.is_some()
|| modify_schema.code.is_some() || modify_schema.date_of_birth.is_some()
{ || modify_schema.new_password.is_some();
if requires_current_password && modify_schema.current_password.is_none() {
return Err(ChorusError::PasswordRequired); return Err(ChorusError::PasswordRequired);
} }
let request = Client::new() let request = Client::new()
.patch(format!( .patch(format!(
"{}/users/@me", "{}/users/@me",
@ -118,56 +120,21 @@ impl User {
/// ///
/// # Reference /// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/user_settings.html#get-usersmesettings> /// See <https://luna.gitlab.io/discord-unofficial-docs/docs/user_settings.html#get-usersmesettings>
pub async fn get_settings( pub async fn get_settings(user: &mut ChorusUser) -> ChorusResult<UserSettings> {
token: &String, let url_api = user.belongs_to.read().unwrap().urls.api.clone();
url_api: &String,
instance: &mut Instance,
) -> ChorusResult<UserSettings> {
let request: reqwest::RequestBuilder = Client::new() let request: reqwest::RequestBuilder = Client::new()
.get(format!("{}/users/@me/settings", url_api)) .get(format!("{}/users/@me/settings", url_api))
.header("Authorization", token); .header("Authorization", user.token());
let mut user =
ChorusUser::shell(Arc::new(RwLock::new(instance.clone())), token.clone()).await;
let chorus_request = ChorusRequest { let chorus_request = ChorusRequest {
request, request,
limit_type: LimitType::Global, limit_type: LimitType::Global,
}; };
let result = match chorus_request.send_request(&mut user).await { match chorus_request.send_request(user).await {
Ok(result) => Ok(serde_json::from_str(&result.text().await.unwrap()).unwrap()), Ok(result) => {
let result_text = result.text().await.unwrap();
Ok(serde_json::from_str(&result_text).unwrap())
}
Err(e) => Err(e), Err(e) => Err(e),
};
if instance.limits_information.is_some() {
instance.limits_information.as_mut().unwrap().ratelimits = user
.belongs_to
.read()
.unwrap()
.clone_limits_if_some()
.unwrap();
} }
result
}
}
impl Instance {
/// Gets a user by id, or if the id is None, gets the current user.
///
/// # Notes
/// This function is a wrapper around [`User::get`].
///
/// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/user#get-user> and
/// <https://discord-userdoccers.vercel.app/resources/user#get-current-user>
pub async fn get_user(&mut self, token: String, id: Option<&String>) -> ChorusResult<User> {
let mut user = ChorusUser::shell(Arc::new(RwLock::new(self.clone())), token).await;
let result = User::get(&mut user, id).await;
if self.limits_information.is_some() {
self.limits_information.as_mut().unwrap().ratelimits = user
.belongs_to
.read()
.unwrap()
.clone_limits_if_some()
.unwrap();
}
result
} }
} }

View File

@ -6,6 +6,7 @@
use custom_error::custom_error; use custom_error::custom_error;
use crate::types::WebSocketEvent; use crate::types::WebSocketEvent;
use chorus_macros::WebSocketEvent;
custom_error! { custom_error! {
#[derive(PartialEq, Eq, Clone, Hash)] #[derive(PartialEq, Eq, Clone, Hash)]
@ -72,7 +73,7 @@ custom_error! {
/// Supposed to be sent as numbers, though they are sent as string most of the time? /// Supposed to be sent as numbers, though they are sent as string most of the time?
/// ///
/// Also includes errors when initiating a connection and unexpected opcodes /// Also includes errors when initiating a connection and unexpected opcodes
#[derive(PartialEq, Eq, Default, Clone)] #[derive(PartialEq, Eq, Default, Clone, WebSocketEvent)]
pub GatewayError pub GatewayError
// Errors we have received from the gateway // Errors we have received from the gateway
#[default] #[default]
@ -92,22 +93,20 @@ custom_error! {
DisallowedIntents = "You sent a disallowed intent. You may have tried to specify an intent that you have not enabled or are not approved for", DisallowedIntents = "You sent a disallowed intent. You may have tried to specify an intent that you have not enabled or are not approved for",
// Errors when initiating a gateway connection // Errors when initiating a gateway connection
CannotConnect{error: String} = "Cannot connect due to a tungstenite error: {error}", CannotConnect{error: String} = "Cannot connect due to a websocket error: {error}",
NonHelloOnInitiate{opcode: u8} = "Received non hello on initial gateway connection ({opcode}), something is definitely wrong", NonHelloOnInitiate{opcode: u8} = "Received non hello on initial gateway connection ({opcode}), something is definitely wrong",
// Other misc errors // Other misc errors
UnexpectedOpcodeReceived{opcode: u8} = "Received an opcode we weren't expecting to receive: {opcode}", UnexpectedOpcodeReceived{opcode: u8} = "Received an opcode we weren't expecting to receive: {opcode}",
} }
impl WebSocketEvent for GatewayError {}
custom_error! { custom_error! {
/// Voice Gateway errors /// Voice Gateway errors
/// ///
/// Similar to [GatewayError]. /// Similar to [GatewayError].
/// ///
/// See <https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-close-event-codes>; /// See <https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-close-event-codes>;
#[derive(Clone, Default, PartialEq, Eq)] #[derive(Clone, Default, PartialEq, Eq, WebSocketEvent)]
pub VoiceGatewayError pub VoiceGatewayError
// Errors we receive // Errors we receive
#[default] #[default]
@ -125,18 +124,16 @@ custom_error! {
UnknownEncryptionMode = "Server failed to decrypt data", UnknownEncryptionMode = "Server failed to decrypt data",
// Errors when initiating a gateway connection // Errors when initiating a gateway connection
CannotConnect{error: String} = "Cannot connect due to a tungstenite error: {error}", CannotConnect{error: String} = "Cannot connect due to a websocket error: {error}",
NonHelloOnInitiate{opcode: u8} = "Received non hello on initial gateway connection ({opcode}), something is definitely wrong", NonHelloOnInitiate{opcode: u8} = "Received non hello on initial gateway connection ({opcode}), something is definitely wrong",
// Other misc errors // Other misc errors
UnexpectedOpcodeReceived{opcode: u8} = "Received an opcode we weren't expecting to receive: {opcode}", UnexpectedOpcodeReceived{opcode: u8} = "Received an opcode we weren't expecting to receive: {opcode}",
} }
impl WebSocketEvent for VoiceGatewayError {}
custom_error! { custom_error! {
/// Voice UDP errors. /// Voice UDP errors.
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq, WebSocketEvent)]
pub VoiceUdpError pub VoiceUdpError
// General errors // General errors
@ -155,4 +152,3 @@ custom_error! {
CannotConnect{error: String} = "Cannot connect due to a UDP error: {error}", CannotConnect{error: String} = "Cannot connect due to a UDP error: {error}",
} }
impl WebSocketEvent for VoiceUdpError {}

View File

@ -2,6 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use custom_error::custom_error;
use futures_util::{ use futures_util::{
stream::{SplitSink, SplitStream}, stream::{SplitSink, SplitStream},
StreamExt, StreamExt,
@ -11,8 +12,7 @@ use tokio_tungstenite::{
connect_async_tls_with_config, tungstenite, Connector, MaybeTlsStream, WebSocketStream, connect_async_tls_with_config, tungstenite, Connector, MaybeTlsStream, WebSocketStream,
}; };
use crate::errors::GatewayError; use crate::gateway::{GatewayMessage, RawGatewayMessage};
use crate::gateway::GatewayMessage;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TungsteniteBackend; pub struct TungsteniteBackend;
@ -22,18 +22,22 @@ pub type TungsteniteSink =
SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, tungstenite::Message>; SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, tungstenite::Message>;
pub type TungsteniteStream = SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>; pub type TungsteniteStream = SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>;
custom_error! {
pub TungsteniteBackendError
FailedToLoadCerts{error: std::io::Error} = "failed to load platform native certs: {error}",
TungsteniteError{error: tungstenite::error::Error} = "encountered a tungstenite error: {error}",
}
impl TungsteniteBackend { impl TungsteniteBackend {
pub async fn connect( pub async fn connect(
websocket_url: &str, websocket_url: &str,
) -> Result<(TungsteniteSink, TungsteniteStream), crate::errors::GatewayError> { ) -> Result<(TungsteniteSink, TungsteniteStream), TungsteniteBackendError> {
let mut roots = rustls::RootCertStore::empty(); let mut roots = rustls::RootCertStore::empty();
let certs = rustls_native_certs::load_native_certs(); let certs = rustls_native_certs::load_native_certs();
if let Err(e) = certs { if let Err(e) = certs {
log::error!("Failed to load platform native certs! {:?}", e); log::error!("Failed to load platform native certs! {:?}", e);
return Err(GatewayError::CannotConnect { return Err(TungsteniteBackendError::FailedToLoadCerts { error: e });
error: format!("{:?}", e),
});
} }
for cert in certs.unwrap() { for cert in certs.unwrap() {
@ -55,8 +59,8 @@ impl TungsteniteBackend {
{ {
Ok(websocket_stream) => websocket_stream, Ok(websocket_stream) => websocket_stream,
Err(e) => { Err(e) => {
return Err(GatewayError::CannotConnect { return Err(TungsteniteBackendError::TungsteniteError {
error: e.to_string(), error: e,
}) })
} }
}; };
@ -76,3 +80,22 @@ impl From<tungstenite::Message> for GatewayMessage {
Self(value.to_string()) Self(value.to_string())
} }
} }
impl From<RawGatewayMessage> for tungstenite::Message {
fn from(message: RawGatewayMessage) -> Self {
match message {
RawGatewayMessage::Text(text) => tungstenite::Message::Text(text),
RawGatewayMessage::Bytes(bytes) => tungstenite::Message::Binary(bytes),
}
}
}
impl From<tungstenite::Message> for RawGatewayMessage {
fn from(value: tungstenite::Message) -> Self {
match value {
tungstenite::Message::Binary(bytes) => RawGatewayMessage::Bytes(bytes),
tungstenite::Message::Text(text) => RawGatewayMessage::Text(text),
_ => RawGatewayMessage::Text(value.to_string()),
}
}
}

View File

@ -9,8 +9,7 @@ use futures_util::{
use ws_stream_wasm::*; use ws_stream_wasm::*;
use crate::errors::GatewayError; use crate::gateway::{GatewayMessage, RawGatewayMessage};
use crate::gateway::GatewayMessage;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct WasmBackend; pub struct WasmBackend;
@ -22,13 +21,8 @@ pub type WasmStream = SplitStream<WsStream>;
impl WasmBackend { impl WasmBackend {
pub async fn connect( pub async fn connect(
websocket_url: &str, websocket_url: &str,
) -> Result<(WasmSink, WasmStream), crate::errors::GatewayError> { ) -> Result<(WasmSink, WasmStream), ws_stream_wasm::WsErr> {
let (_, websocket_stream) = match WsMeta::connect(websocket_url, None).await { let (_, websocket_stream) = WsMeta::connect(websocket_url, None).await?;
Ok(stream) => Ok(stream),
Err(e) => Err(GatewayError::CannotConnect {
error: e.to_string(),
}),
}?;
Ok(websocket_stream.split()) Ok(websocket_stream.split())
} }
@ -52,3 +46,21 @@ impl From<WsMessage> for GatewayMessage {
} }
} }
} }
impl From<RawGatewayMessage> for WsMessage {
fn from(message: RawGatewayMessage) -> Self {
match message {
RawGatewayMessage::Text(text) => WsMessage::Text(text),
RawGatewayMessage::Bytes(bytes) => WsMessage::Binary(bytes),
}
}
}
impl From<WsMessage> for RawGatewayMessage {
fn from(value: WsMessage) -> Self {
match value {
WsMessage::Binary(bytes) => RawGatewayMessage::Bytes(bytes),
WsMessage::Text(text) => RawGatewayMessage::Text(text),
}
}
}

View File

@ -4,6 +4,7 @@
use std::time::Duration; use std::time::Duration;
use flate2::Decompress;
use futures_util::{SinkExt, StreamExt}; use futures_util::{SinkExt, StreamExt};
use log::*; use log::*;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -19,6 +20,9 @@ use crate::types::{
WebSocketEvent, WebSocketEvent,
}; };
/// Tells us we have received enough of the buffer to decompress it
const ZLIB_SUFFIX: [u8; 4] = [0, 0, 255, 255];
#[derive(Debug)] #[derive(Debug)]
pub struct Gateway { pub struct Gateway {
events: Arc<Mutex<Events>>, events: Arc<Mutex<Events>>,
@ -28,14 +32,36 @@ pub struct Gateway {
kill_send: tokio::sync::broadcast::Sender<()>, kill_send: tokio::sync::broadcast::Sender<()>,
kill_receive: tokio::sync::broadcast::Receiver<()>, kill_receive: tokio::sync::broadcast::Receiver<()>,
store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>, store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
/// Url which was used to initialize the gateway
url: String, url: String,
/// Options which were used to initialize the gateway
options: GatewayOptions,
zlib_inflate: Option<flate2::Decompress>,
zlib_buffer: Option<Vec<u8>>,
} }
impl Gateway { impl Gateway {
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
pub async fn spawn(websocket_url: String) -> Result<GatewayHandle, GatewayError> { /// Creates / opens a new gateway connection.
let (websocket_send, mut websocket_receive) = ///
WebSocketBackend::connect(&websocket_url).await?; /// # Note
/// The websocket url should begin with the prefix wss:// or ws:// (for unsecure connections)
pub async fn spawn(
websocket_url: String,
options: GatewayOptions,
) -> Result<GatewayHandle, GatewayError> {
let url = options.add_to_url(websocket_url);
debug!("GW: Connecting to {}", url);
let (websocket_send, mut websocket_receive) = match WebSocketBackend::connect(&url).await {
Ok(streams) => streams,
Err(e) => {
return Err(GatewayError::CannotConnect {
error: format!("{:?}", e),
});
}
};
let shared_websocket_send = Arc::new(Mutex::new(websocket_send)); let shared_websocket_send = Arc::new(Mutex::new(websocket_send));
@ -45,10 +71,34 @@ impl Gateway {
// Wait for the first hello and then spawn both tasks so we avoid nested tasks // Wait for the first hello and then spawn both tasks so we avoid nested tasks
// This automatically spawns the heartbeat task, but from the main thread // This automatically spawns the heartbeat task, but from the main thread
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
let msg: GatewayMessage = websocket_receive.next().await.unwrap().unwrap().into(); let received: RawGatewayMessage = websocket_receive.next().await.unwrap().unwrap().into();
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
let msg: GatewayMessage = websocket_receive.next().await.unwrap().into(); let received: RawGatewayMessage = websocket_receive.next().await.unwrap().into();
let gateway_payload: types::GatewayReceivePayload = serde_json::from_str(&msg.0).unwrap();
let message: GatewayMessage;
let zlib_buffer;
let zlib_inflate;
match options.transport_compression {
GatewayTransportCompression::None => {
zlib_buffer = None;
zlib_inflate = None;
message = GatewayMessage::from_raw_json_message(received).unwrap();
}
GatewayTransportCompression::ZLibStream => {
zlib_buffer = Some(Vec::new());
let mut inflate = Decompress::new(true);
message =
GatewayMessage::from_zlib_stream_json_message(received, &mut inflate).unwrap();
zlib_inflate = Some(inflate);
}
}
let gateway_payload: types::GatewayReceivePayload =
serde_json::from_str(&message.0).unwrap();
if gateway_payload.op_code != GATEWAY_HELLO { if gateway_payload.op_code != GATEWAY_HELLO {
return Err(GatewayError::NonHelloOnInitiate { return Err(GatewayError::NonHelloOnInitiate {
@ -78,7 +128,10 @@ impl Gateway {
kill_send: kill_send.clone(), kill_send: kill_send.clone(),
kill_receive: kill_send.subscribe(), kill_receive: kill_send.subscribe(),
store: store.clone(), store: store.clone(),
url: websocket_url.clone(), url: url.clone(),
options,
zlib_inflate,
zlib_buffer,
}; };
// Now we can continuously check for messages in a different task, since we aren't going to receive another hello // Now we can continuously check for messages in a different task, since we aren't going to receive another hello
@ -92,7 +145,7 @@ impl Gateway {
}); });
Ok(GatewayHandle { Ok(GatewayHandle {
url: websocket_url.clone(), url: url.clone(),
events: shared_events, events: shared_events,
websocket_send: shared_websocket_send.clone(), websocket_send: shared_websocket_send.clone(),
kill_send: kill_send.clone(), kill_send: kill_send.clone(),
@ -101,7 +154,7 @@ impl Gateway {
} }
/// The main gateway listener task; /// The main gateway listener task;
pub async fn gateway_listen_task(&mut self) { async fn gateway_listen_task(&mut self) {
loop { loop {
let msg; let msg;
@ -118,12 +171,12 @@ impl Gateway {
// PRETTYFYME: Remove inline conditional compiling // PRETTYFYME: Remove inline conditional compiling
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
if let Some(Ok(message)) = msg { if let Some(Ok(message)) = msg {
self.handle_message(message.into()).await; self.handle_raw_message(message.into()).await;
continue; continue;
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
if let Some(message) = msg { if let Some(message) = msg {
self.handle_message(message.into()).await; self.handle_raw_message(message.into()).await;
continue; continue;
} }
@ -156,8 +209,42 @@ impl Gateway {
Ok(()) Ok(())
} }
/// Takes a [RawGatewayMessage], converts it to [GatewayMessage] based
/// of connection options and calls handle_message
async fn handle_raw_message(&mut self, raw_message: RawGatewayMessage) {
let message;
match self.options.transport_compression {
GatewayTransportCompression::None => {
message = GatewayMessage::from_raw_json_message(raw_message).unwrap()
}
GatewayTransportCompression::ZLibStream => {
let message_bytes = raw_message.into_bytes();
let can_decompress = message_bytes.len() > 4
&& message_bytes[message_bytes.len() - 4..] == ZLIB_SUFFIX;
let zlib_buffer = self.zlib_buffer.as_mut().unwrap();
zlib_buffer.extend(message_bytes.clone());
if !can_decompress {
return;
}
let zlib_buffer = self.zlib_buffer.as_ref().unwrap();
let inflate = self.zlib_inflate.as_mut().unwrap();
message =
GatewayMessage::from_zlib_stream_json_bytes(zlib_buffer, inflate).unwrap();
self.zlib_buffer = Some(Vec::new());
}
};
self.handle_message(message).await;
}
/// This handles a message as a websocket event and updates its events along with the events' observers /// This handles a message as a websocket event and updates its events along with the events' observers
pub async fn handle_message(&mut self, msg: GatewayMessage) { async fn handle_message(&mut self, msg: GatewayMessage) {
if msg.0.is_empty() { if msg.0.is_empty() {
return; return;
} }
@ -194,7 +281,10 @@ impl Gateway {
let event = &mut self.events.lock().await.$($path).+; let event = &mut self.events.lock().await.$($path).+;
let json = gateway_payload.event_data.unwrap().get(); let json = gateway_payload.event_data.unwrap().get();
match serde_json::from_str(json) { match serde_json::from_str(json) {
Err(err) => warn!("Failed to parse gateway event {event_name} ({err})"), Err(err) => {
warn!("Failed to parse gateway event {event_name} ({err})");
trace!("Event data: {json}");
},
Ok(message) => { Ok(message) => {
$( $(
let mut message: $message_type = message; let mut message: $message_type = message;
@ -230,15 +320,12 @@ impl Gateway {
},)* },)*
"RESUMED" => (), "RESUMED" => (),
"SESSIONS_REPLACE" => { "SESSIONS_REPLACE" => {
let result: Result<Vec<types::Session>, serde_json::Error> = let json = gateway_payload.event_data.unwrap().get();
serde_json::from_str(gateway_payload.event_data.unwrap().get()); let result: Result<Vec<types::Session>, serde_json::Error> = serde_json::from_str(json);
match result { match result {
Err(err) => { Err(err) => {
warn!( warn!("Failed to parse gateway event {event_name} ({err})");
"Failed to parse gateway event {} ({})", trace!("Event data: {json}");
event_name,
err
);
return; return;
} }
Ok(sessions) => { Ok(sessions) => {
@ -250,6 +337,7 @@ impl Gateway {
}, },
_ => { _ => {
warn!("Received unrecognized gateway event ({event_name})! Please open an issue on the chorus github so we can implement it"); warn!("Received unrecognized gateway event ({event_name})! Please open an issue on the chorus github so we can implement it");
trace!("Event data: {}", gateway_payload.event_data.unwrap().get());
} }
} }
}; };

View File

@ -8,7 +8,7 @@ use log::*;
use std::fmt::Debug; use std::fmt::Debug;
use super::{events::Events, *}; use super::{events::Events, *};
use crate::types::{self, Composite}; use crate::types::{self, Composite, Shared};
/// Represents a handle to a Gateway connection. A Gateway connection will create observable /// Represents a handle to a Gateway connection. A Gateway connection will create observable
/// [`GatewayEvents`](GatewayEvent), which you can subscribe to. Gateway events include all currently /// [`GatewayEvents`](GatewayEvent), which you can subscribe to. Gateway events include all currently

View File

@ -2,11 +2,41 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::string::FromUtf8Error;
use crate::types; use crate::types;
use super::*; use super::*;
/// Represents a message received from the gateway. This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError]. /// Defines a raw gateway message, being either string json or bytes
///
/// This is used as an intermediary type between types from different websocket implementations
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum RawGatewayMessage {
Text(String),
Bytes(Vec<u8>),
}
impl RawGatewayMessage {
/// Attempt to consume the message into a String, will try to convert binary to utf8
pub fn into_text(self) -> Result<String, FromUtf8Error> {
match self {
RawGatewayMessage::Text(text) => Ok(text),
RawGatewayMessage::Bytes(bytes) => String::from_utf8(bytes),
}
}
/// Consume the message into bytes, will convert text to binary
pub fn into_bytes(self) -> Vec<u8> {
match self {
RawGatewayMessage::Text(text) => text.as_bytes().to_vec(),
RawGatewayMessage::Bytes(bytes) => bytes,
}
}
}
/// Represents a json message received from the gateway.
/// This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError].
/// This struct is used internally when handling messages. /// This struct is used internally when handling messages.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct GatewayMessage(pub String); pub struct GatewayMessage(pub String);
@ -44,4 +74,48 @@ impl GatewayMessage {
pub fn payload(&self) -> Result<types::GatewayReceivePayload, serde_json::Error> { pub fn payload(&self) -> Result<types::GatewayReceivePayload, serde_json::Error> {
serde_json::from_str(&self.0) serde_json::from_str(&self.0)
} }
/// Create self from an uncompressed json [RawGatewayMessage]
pub(crate) fn from_raw_json_message(
message: RawGatewayMessage,
) -> Result<GatewayMessage, FromUtf8Error> {
let text = message.into_text()?;
Ok(GatewayMessage(text))
}
/// Attempt to create self by decompressing zlib-stream bytes
// Thanks to <https://github.com/ByteAlex/zlib-stream-rs>, their
// code helped a lot with the stream implementation
pub(crate) fn from_zlib_stream_json_bytes(
bytes: &[u8],
inflate: &mut flate2::Decompress,
) -> Result<GatewayMessage, std::io::Error> {
// Note: is there a better way to handle the size of this output buffer?
//
// This used to be 10, I measured it at 11.5, so a safe bet feels like 20
//
// ^ - This dude is naive. apparently not even 20x is okay. Measured at 47.9x!!!!
// If it is >100x ever, I will literally explode
//
// About an hour later, you ^ will literally explode.
// 133 vs 13994 -- 105.21805x ratio
// Let's hope it doesn't go above 200??
let mut output = Vec::with_capacity(bytes.len() * 200);
let _status = inflate.decompress_vec(bytes, &mut output, flate2::FlushDecompress::Sync)?;
output.shrink_to_fit();
let string = String::from_utf8(output).unwrap();
Ok(GatewayMessage(string))
}
/// Attempt to create self by decompressing a zlib-stream bytes raw message
pub(crate) fn from_zlib_stream_json_message(
message: RawGatewayMessage,
inflate: &mut flate2::Decompress,
) -> Result<GatewayMessage, std::io::Error> {
Self::from_zlib_stream_json_bytes(&message.into_bytes(), inflate)
}
} }

View File

@ -10,12 +10,14 @@ pub mod gateway;
pub mod handle; pub mod handle;
pub mod heartbeat; pub mod heartbeat;
pub mod message; pub mod message;
pub mod options;
pub use backends::*; pub use backends::*;
pub use gateway::*; pub use gateway::*;
pub use handle::*; pub use handle::*;
use heartbeat::*; use heartbeat::*;
pub use message::*; pub use message::*;
pub use options::*;
use crate::errors::GatewayError; use crate::errors::GatewayError;
use crate::types::{Snowflake, WebSocketEvent}; use crate::types::{Snowflake, WebSocketEvent};
@ -133,10 +135,3 @@ impl<T: WebSocketEvent> GatewayEvent<T> {
} }
} }
/// A type alias for [`Arc<RwLock<T>>`], used to make the public facing API concerned with
/// Composite structs more ergonomic.
/// ## Note
///
/// While `T` does not have to implement `Composite` to be used with `Shared`,
/// the primary use of `Shared` is with types that implement `Composite`.
pub type Shared<T> = Arc<RwLock<T>>;

118
src/gateway/options.rs Normal file
View File

@ -0,0 +1,118 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Debug, Default)]
/// Options passed when initializing the gateway connection.
///
/// E.g. compression
///
/// # Note
///
/// Discord allows specifying the api version (v10, v9, ...) as well, but chorus is built upon one
/// main version (v9).
///
/// Similarly, discord also supports etf encoding, while chorus does not (yet).
/// We are looking into supporting it as an option, since it is faster and more lightweight.
///
/// See <https://docs.discord.sex/topics/gateway#connections>
pub struct GatewayOptions {
pub encoding: GatewayEncoding,
pub transport_compression: GatewayTransportCompression,
}
impl GatewayOptions {
/// Adds the options to an existing gateway url
///
/// Returns the new url
pub(crate) fn add_to_url(&self, url: String) -> String {
let mut url = url;
let mut parameters = Vec::with_capacity(2);
let encoding = self.encoding.to_url_parameter();
parameters.push(encoding);
let compression = self.transport_compression.to_url_parameter();
if let Some(some_compression) = compression {
parameters.push(some_compression);
}
let mut has_parameters = url.contains('?') && url.contains('=');
if !has_parameters {
// Insure it ends in a /, so we don't get a 400 error
if !url.ends_with('/') {
url.push('/');
}
// Lets hope that if it already has parameters the person knew to add '/'
}
for parameter in parameters {
if !has_parameters {
url = format!("{}?{}", url, parameter);
has_parameters = true;
}
else {
url = format!("{}&{}", url, parameter);
}
}
url
}
}
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug, Default)]
/// Possible transport compression options for the gateway.
///
/// See <https://docs.discord.sex/topics/gateway#transport-compression>
pub enum GatewayTransportCompression {
/// Do not transport compress packets
None,
/// Transport compress using zlib stream
#[default]
ZLibStream,
}
impl GatewayTransportCompression {
/// Returns the option as a url parameter.
///
/// If set to [GatewayTransportCompression::None] returns [None].
///
/// If set to anything else, returns a string like "compress=zlib-stream"
pub(crate) fn to_url_parameter(self) -> Option<String> {
match self {
Self::None => None,
Self::ZLibStream => Some(String::from("compress=zlib-stream"))
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug, Default)]
/// See <https://docs.discord.sex/topics/gateway#encoding-and-compression>
pub enum GatewayEncoding {
/// Javascript object notation, a standard for websocket connections,
/// but contains a lot of overhead
#[default]
Json,
/// A binary format originating from Erlang
///
/// Should be lighter and faster than json.
///
/// !! Chorus does not implement ETF yet !!
ETF
}
impl GatewayEncoding {
/// Returns the option as a url parameter.
///
/// Returns a string like "encoding=json"
pub(crate) fn to_url_parameter(self) -> String {
match self {
Self::Json => String::from("encoding=json"),
Self::ETF => String::from("encoding=etf")
}
}
}

View File

@ -13,11 +13,11 @@ use reqwest::Client;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::errors::ChorusResult; use crate::errors::ChorusResult;
use crate::gateway::{Gateway, GatewayHandle, Shared}; use crate::gateway::{Gateway, GatewayHandle, GatewayOptions};
use crate::ratelimiter::ChorusRequest; use crate::ratelimiter::ChorusRequest;
use crate::types::types::subconfigs::limits::rates::RateLimits; use crate::types::types::subconfigs::limits::rates::RateLimits;
use crate::types::{ use crate::types::{
GeneralConfiguration, Limit, LimitType, LimitsConfiguration, User, UserSettings, GeneralConfiguration, Limit, LimitType, LimitsConfiguration, Shared, User, UserSettings,
}; };
use crate::UrlBundle; use crate::UrlBundle;
@ -31,6 +31,8 @@ pub struct Instance {
pub limits_information: Option<LimitsInformation>, pub limits_information: Option<LimitsInformation>,
#[serde(skip)] #[serde(skip)]
pub client: Client, pub client: Client,
#[serde(skip)]
pub gateway_options: GatewayOptions,
} }
impl PartialEq for Instance { impl PartialEq for Instance {
@ -104,6 +106,7 @@ impl Instance {
instance_info: GeneralConfiguration::default(), instance_info: GeneralConfiguration::default(),
limits_information: limit_information, limits_information: limit_information,
client: Client::new(), client: Client::new(),
gateway_options: GatewayOptions::default(),
}; };
instance.instance_info = match instance.general_configuration_schema().await { instance.instance_info = match instance.general_configuration_schema().await {
Ok(schema) => schema, Ok(schema) => schema,
@ -139,6 +142,13 @@ impl Instance {
Err(_) => Ok(None), Err(_) => Ok(None),
} }
} }
/// Sets the [`GatewayOptions`] the instance will use when spawning new connections.
///
/// These options are used on the gateways created when logging in and registering.
pub fn set_gateway_options(&mut self, options: GatewayOptions) {
self.gateway_options = options;
}
} }
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
@ -215,7 +225,9 @@ impl ChorusUser {
let object = Arc::new(RwLock::new(User::default())); let object = Arc::new(RwLock::new(User::default()));
let wss_url = instance.read().unwrap().urls.wss.clone(); let wss_url = instance.read().unwrap().urls.wss.clone();
// Dummy gateway object // Dummy gateway object
let gateway = Gateway::spawn(wss_url).await.unwrap(); let gateway = Gateway::spawn(wss_url, GatewayOptions::default())
.await
.unwrap();
ChorusUser { ChorusUser {
token, token,
belongs_to: instance.clone(), belongs_to: instance.clone(),

View File

@ -88,7 +88,7 @@ impl ChorusRequest {
let client = user.belongs_to.read().unwrap().client.clone(); let client = user.belongs_to.read().unwrap().client.clone();
let result = match client.execute(self.request.build().unwrap()).await { let result = match client.execute(self.request.build().unwrap()).await {
Ok(result) => { Ok(result) => {
debug!("Request successful: {:?}", result); log::trace!("Request successful: {:?}", result);
result result
} }
Err(error) => { Err(error) => {
@ -494,7 +494,7 @@ impl ChorusRequest {
user: &mut ChorusUser, user: &mut ChorusUser,
) -> ChorusResult<T> { ) -> ChorusResult<T> {
let response = self.send_request(user).await?; let response = self.send_request(user).await?;
debug!("Got response: {:?}", response); log::trace!("Got response: {:?}", response);
let response_text = match response.text().await { let response_text = match response.text().await {
Ok(string) => string, Ok(string) => string,
Err(e) => { Err(e) => {

View File

@ -3,19 +3,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[cfg(feature = "sqlx")]
use std::io::Write;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::str::FromStr; use std::str::FromStr;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "sqlx")]
use sqlx::{
database::{HasArguments, HasValueRef},
encode::IsNull,
error::BoxDynError,
Decode, MySql,
};
use crate::types::config::types::subconfigs::guild::{ use crate::types::config::types::subconfigs::guild::{
autojoin::AutoJoinConfiguration, discovery::DiscoverConfiguration, autojoin::AutoJoinConfiguration, discovery::DiscoverConfiguration,
@ -172,8 +163,8 @@ impl Display for GuildFeaturesList {
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
impl<'r> sqlx::Decode<'r, sqlx::MySql> for GuildFeaturesList { impl<'r> sqlx::Decode<'r, sqlx::MySql> for GuildFeaturesList {
fn decode(value: <MySql as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> { fn decode(value: <sqlx::MySql as sqlx::database::HasValueRef<'r>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
let v = <&str as Decode<sqlx::MySql>>::decode(value)?; let v = <String as sqlx::Decode<sqlx::MySql>>::decode(value)?;
Ok(Self( Ok(Self(
v.split(',') v.split(',')
.filter(|f| !f.is_empty()) .filter(|f| !f.is_empty())
@ -185,9 +176,9 @@ impl<'r> sqlx::Decode<'r, sqlx::MySql> for GuildFeaturesList {
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
impl<'q> sqlx::Encode<'q, sqlx::MySql> for GuildFeaturesList { impl<'q> sqlx::Encode<'q, sqlx::MySql> for GuildFeaturesList {
fn encode_by_ref(&self, buf: &mut <MySql as HasArguments<'q>>::ArgumentBuffer) -> IsNull { fn encode_by_ref(&self, buf: &mut <sqlx::MySql as sqlx::database::HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
if self.is_empty() { if self.is_empty() {
return IsNull::Yes; return sqlx::encode::IsNull::Yes;
} }
let features = self let features = self
.iter() .iter()
@ -195,30 +186,18 @@ impl<'q> sqlx::Encode<'q, sqlx::MySql> for GuildFeaturesList {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(","); .join(",");
let _ = buf.write(features.as_bytes()); <String as sqlx::Encode<sqlx::MySql>>::encode_by_ref(&features, buf)
IsNull::No
} }
} }
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
impl sqlx::Type<sqlx::MySql> for GuildFeaturesList { impl sqlx::Type<sqlx::MySql> for GuildFeaturesList {
fn type_info() -> sqlx::mysql::MySqlTypeInfo { fn type_info() -> sqlx::mysql::MySqlTypeInfo {
<&str as sqlx::Type<sqlx::MySql>>::type_info() <String as sqlx::Type<sqlx::MySql>>::type_info()
} }
fn compatible(ty: &sqlx::mysql::MySqlTypeInfo) -> bool { fn compatible(ty: &sqlx::mysql::MySqlTypeInfo) -> bool {
<&str as sqlx::Type<sqlx::MySql>>::compatible(ty) <String as sqlx::Type<sqlx::MySql>>::compatible(ty)
}
}
#[cfg(feature = "sqlx")]
impl sqlx::TypeInfo for GuildFeaturesList {
fn is_null(&self) -> bool {
false
}
fn name(&self) -> &str {
"TEXT"
} }
} }
@ -376,6 +355,12 @@ impl FromStr for GuildFeatures {
} }
} }
impl From<Vec<GuildFeatures>> for GuildFeaturesList {
fn from(features: Vec<GuildFeatures>) -> GuildFeaturesList {
Self(features)
}
}
impl GuildFeatures { impl GuildFeatures {
pub fn to_str(&self) -> &'static str { pub fn to_str(&self) -> &'static str {
match *self { match *self {

View File

@ -3,10 +3,11 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_aux::prelude::deserialize_number_from_string;
use crate::types::config::types::subconfigs::register::{ use crate::types::{config::types::subconfigs::register::{
DateOfBirthConfiguration, PasswordConfiguration, RegistrationEmailConfiguration, DateOfBirthConfiguration, PasswordConfiguration, RegistrationEmailConfiguration,
}; }, Rights};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -22,7 +23,8 @@ pub struct RegisterConfiguration {
pub allow_multiple_accounts: bool, pub allow_multiple_accounts: bool,
pub block_proxies: bool, pub block_proxies: bool,
pub incrementing_discriminators: bool, pub incrementing_discriminators: bool,
pub default_rights: String, #[serde(deserialize_with = "deserialize_number_from_string")]
pub default_rights: Rights,
} }
impl Default for RegisterConfiguration { impl Default for RegisterConfiguration {
@ -39,7 +41,7 @@ impl Default for RegisterConfiguration {
allow_multiple_accounts: true, allow_multiple_accounts: true,
block_proxies: true, block_proxies: true,
incrementing_discriminators: false, incrementing_discriminators: false,
default_rights: String::from("875069521787904"), default_rights: Rights::from_bits(648540060672).expect("failed to parse default_rights"),
} }
} }
} }

View File

@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::gateway::Shared; use crate::types::Shared;
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;
use crate::types::{Team, User}; use crate::types::{Team, User};
@ -31,7 +31,7 @@ pub struct Application {
pub verify_key: String, pub verify_key: String,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub owner: Shared<User>, pub owner: Shared<User>,
pub flags: u64, pub flags: ApplicationFlags,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>, pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
@ -73,7 +73,7 @@ impl Default for Application {
bot_require_code_grant: false, bot_require_code_grant: false,
verify_key: "".to_string(), verify_key: "".to_string(),
owner: Default::default(), owner: Default::default(),
flags: 0, flags: ApplicationFlags::empty(),
redirect_uris: None, redirect_uris: None,
rpc_application_state: 0, rpc_application_state: 0,
store_application_state: 1, store_application_state: 1,
@ -93,12 +93,6 @@ impl Default for Application {
} }
} }
impl Application {
pub fn flags(&self) -> ApplicationFlags {
ApplicationFlags::from_bits(self.flags.to_owned()).unwrap()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/resources/application#install-params-object> /// See <https://discord.com/developers/docs/resources/application#install-params-object>
@ -108,7 +102,8 @@ pub struct InstallParams {
} }
bitflags! { bitflags! {
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/resources/application#application-object-application-flags> /// See <https://discord.com/developers/docs/resources/application#application-object-application-flags>
pub struct ApplicationFlags: u64 { pub struct ApplicationFlags: u64 {

View File

@ -3,21 +3,27 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::gateway::Shared; use crate::types::{AutoModerationRuleTriggerType, IntegrationType, PermissionOverwriteType, Shared};
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;
#[derive(Serialize, Deserialize, Debug, Default, Clone)] #[derive(Serialize, Deserialize, Debug, Default, Clone)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
/// See <https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object> /// See <https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object>
pub struct AuditLogEntry { pub struct AuditLogEntry {
pub target_id: Option<String>, pub target_id: Option<String>,
#[cfg(feature = "sqlx")]
pub changes: sqlx::types::Json<Option<Vec<Shared<AuditLogChange>>>>,
#[cfg(not(feature = "sqlx"))]
pub changes: Option<Vec<Shared<AuditLogChange>>>, pub changes: Option<Vec<Shared<AuditLogChange>>>,
pub user_id: Option<Snowflake>, pub user_id: Option<Snowflake>,
pub id: Snowflake, pub id: Snowflake,
// to:do implement an enum for these types pub action_type: AuditLogActionType,
pub action_type: u8, #[cfg(feature = "sqlx")]
// to:do add better options type pub options: Option<sqlx::types::Json<AuditEntryInfo>>,
pub options: Option<serde_json::Value>, #[cfg(not(feature = "sqlx"))]
pub options: Option<AuditEntryInfo>,
pub reason: Option<String>, pub reason: Option<String>,
} }
@ -28,3 +34,164 @@ pub struct AuditLogChange {
pub old_value: Option<serde_json::Value>, pub old_value: Option<serde_json::Value>,
pub key: String, pub key: String,
} }
#[derive(Default, Serialize_repr, Deserialize_repr, Debug, Clone, Copy)]
#[repr(u8)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
/// # Reference:
/// See <https://docs.discord.sex/resources/audit-log#audit-log-events>
pub enum AuditLogActionType {
#[default]
/// Guild settings were updated
GuildUpdate = 1,
/// Channel was created
ChannelCreate = 10,
/// Channel settings were updated
ChannelUpdate = 11,
/// Channel was deleted
ChannelDelete = 12,
/// Permission overwrite was added to a channel
ChannelOverwriteCreate = 13,
/// Permission overwrite was updated for a channel
ChannelOverwriteUpdate = 14,
/// Permission overwrite was deleted from a channel
ChannelOverwriteDelete = 15,
/// Member was removed from guild
MemberKick = 20,
/// Members were pruned from guild
MemberPrune = 21,
/// Member was banned from guild
MemberBanAdd = 22,
/// Member was unbanned from guild
MemberBanRemove = 23,
/// Member was updated in guild
MemberUpdate = 24,
/// Member was added or removed from a role
MemberRoleUpdate = 25,
/// Member was moved to a different voice channel
MemberMove = 26,
/// Member was disconnected from a voice channel
MemberDisconnect = 27,
/// Bot user was added to guild
BotAdd = 28,
/// Role was created
RoleCreate = 30,
/// Role was edited
RoleUpdate = 31,
/// Role was deleted
RoleDelete = 32,
/// Guild invite was created
InviteCreate = 40,
/// Guild invite was updated
InviteUpdate = 41,
/// Guild invite was deleted
InviteDelete = 42,
/// Webhook was created
WebhookCreate = 50,
/// Webhook properties or channel were updated
WebhookUpdate = 51,
/// Webhook was deleted
WebhookDelete = 52,
/// Emoji was created
EmojiCreate = 60,
/// Emoji name was updated
EmojiUpdate = 61,
/// Emoji was deleted
EmojiDelete = 62,
/// Single message was deleted
MessageDelete = 72,
/// Multiple messages were deleted
MessageBulkDelete = 73,
/// Message was pinned to a channel
MessagePin = 74,
/// Message was unpinned from a channel
MessageUnpin = 75,
/// Interaction was added to guild
IntegrationCreate = 80,
/// Integration was updated (e.g. its scopes were updated)
IntegrationUpdate = 81,
/// Integration was removed from guild
IntegrationDelete = 82,
/// Stage instance was created (stage channel becomes live)
StageInstanceCreate = 83,
/// Stage instance details were updated
StageInstanceUpdate = 84,
/// Stage instance was deleted (stage channel no longer live)
StageInstanceDelete = 85,
/// Sticker was created
StickerCreate = 90,
/// Sticker details were updated
StickerUpdate = 91,
/// Sticker was deleted
StickerDelete = 92,
/// Event was created
GuildScheduledEventCreate = 100,
/// Event was updated
GuildScheduledEventUpdate = 101,
/// Event was cancelled
GuildScheduledEventDelete = 102,
/// Thread was created in a channel
ThreadCreate = 110,
/// Thread was updated
ThreadUpdate = 111,
/// Thread was deleted
ThreadDelete = 112,
/// Permissions were updated for a command
ApplicationCommandPermissionUpdate = 121,
/// AutoMod rule created
AutoModerationRuleCreate = 140,
/// AutoMod rule was updated
AutoModerationRuleUpdate = 141,
/// AutoMod rule was deleted
AutoModerationRuleDelete = 142,
/// Message was blocked by AutoMod
AutoModerationBlockMessage = 143,
/// Message was flagged by AutoMod
AutoModerationFlagToChannel = 144,
/// Member was timed out by AutoMod
AutoModerationUserCommunicationDisabled = 145,
/// Member was quarantined by AutoMod
AutoModerationQuarantineUser = 146,
/// Creator monetization request was created
CreatorMonetizationRequestCreated = 150,
/// Creator monetization terms were accepted
CreatorMonetizationTermsAccepted = 151,
/// Onboarding prompt was created
OnboardingPromptCreate = 163,
/// Onboarding prompt was updated
OnboardingPromptUpdate = 164,
/// Onboarding prompt was deleted
OnboardingPromptDelete = 165,
/// Onboarding was created
OnboardingCreate = 166,
/// Onboarding was updated
OnboardingUpdate = 167,
/// Voice channel status was updated
VoiceChannelStatusUpdate = 192,
/// Voice channel status was deleted
VoiceChannelStatusDelete = 193
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct AuditEntryInfo {
pub application_id: Option<Snowflake>,
pub auto_moderation_rule_name: Option<String>,
pub auto_moderation_rule_trigger_type: Option<AutoModerationRuleTriggerType>,
pub channel_id: Option<Snowflake>,
// #[serde(option_string)]
pub count: Option<u64>,
// #[serde(option_string)]
pub delete_member_days: Option<u64>,
/// The ID of the overwritten entity
pub id: Option<Snowflake>,
pub integration_type: Option<IntegrationType>,
// #[serde(option_string)]
pub members_removed: Option<u64>,
// #[serde(option_string)]
pub message_id: Option<u64>,
pub role_name: Option<String>,
#[serde(rename = "type")]
pub overwrite_type: Option<PermissionOverwriteType>,
pub status: Option<String>
}

View File

@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::gateway::Shared; use crate::types::Shared;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::gateway::Updateable; use crate::gateway::Updateable;

View File

@ -3,15 +3,16 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use serde_aux::prelude::deserialize_string_from_number;
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use std::fmt::Debug; use std::fmt::{Debug, Formatter};
use std::str::FromStr;
use crate::gateway::Shared;
use crate::types::{ use crate::types::{
PermissionFlags, Shared,
entities::{GuildMember, User}, entities::{GuildMember, User},
utils::Snowflake, utils::Snowflake,
serde::string_or_u64
}; };
#[cfg(feature = "client")] #[cfg(feature = "client")]
@ -25,6 +26,8 @@ use crate::gateway::Updateable;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use chorus_macros::{observe_option_vec, Composite, Updateable}; use chorus_macros::{observe_option_vec, Composite, Updateable};
use serde::de::{Error, Visitor};
#[derive(Default, Debug, Serialize, Deserialize, Clone)] #[derive(Default, Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
@ -64,7 +67,9 @@ pub struct Channel {
pub managed: Option<bool>, pub managed: Option<bool>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member: Option<ThreadMember>, pub member: Option<ThreadMember>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member_count: Option<i32>, pub member_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub message_count: Option<i32>, pub message_count: Option<i32>,
pub name: Option<String>, pub name: Option<String>,
pub nsfw: Option<bool>, pub nsfw: Option<bool>,
@ -75,6 +80,7 @@ pub struct Channel {
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_option_vec)]
pub permission_overwrites: Option<Vec<Shared<PermissionOverwrite>>>, pub permission_overwrites: Option<Vec<Shared<PermissionOverwrite>>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub permissions: Option<String>, pub permissions: Option<String>,
pub position: Option<i32>, pub position: Option<i32>,
pub rate_limit_per_user: Option<i32>, pub rate_limit_per_user: Option<i32>,
@ -85,6 +91,7 @@ pub struct Channel {
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub thread_metadata: Option<ThreadMetadata>, pub thread_metadata: Option<ThreadMetadata>,
pub topic: Option<String>, pub topic: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub total_message_sent: Option<i32>, pub total_message_sent: Option<i32>,
pub user_limit: Option<i32>, pub user_limit: Option<i32>,
pub video_quality_mode: Option<i32>, pub video_quality_mode: Option<i32>,
@ -144,14 +151,79 @@ pub struct Tag {
pub struct PermissionOverwrite { pub struct PermissionOverwrite {
pub id: Snowflake, pub id: Snowflake,
#[serde(rename = "type")] #[serde(rename = "type")]
#[serde(deserialize_with = "deserialize_string_from_number")] pub overwrite_type: PermissionOverwriteType,
pub overwrite_type: String,
#[serde(default)] #[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")] pub allow: PermissionFlags,
pub allow: String,
#[serde(default)] #[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")] pub deny: PermissionFlags,
pub deny: String, }
#[derive(Debug, Serialize_repr, Clone, PartialEq, Eq, PartialOrd)]
#[repr(u8)]
/// # Reference
///
/// See <https://docs.discord.sex/resources/channel#permission-overwrite-type>
pub enum PermissionOverwriteType {
Role = 0,
Member = 1,
}
impl From<u8> for PermissionOverwriteType {
fn from(v: u8) -> Self {
match v {
0 => PermissionOverwriteType::Role,
1 => PermissionOverwriteType::Member,
_ => unreachable!(),
}
}
}
impl FromStr for PermissionOverwriteType {
type Err = serde::de::value::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"role" => Ok(PermissionOverwriteType::Role),
"member" => Ok(PermissionOverwriteType::Member),
_ => Err(Self::Err::custom("invalid permission overwrite type")),
}
}
}
struct PermissionOverwriteTypeVisitor;
impl<'de> Visitor<'de> for PermissionOverwriteTypeVisitor {
type Value = PermissionOverwriteType;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("a valid permission overwrite type")
}
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E> where E: Error {
Ok(PermissionOverwriteType::from(v))
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where E: Error {
self.visit_u8(v as u8)
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: Error {
PermissionOverwriteType::from_str(v)
.map_err(E::custom)
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E> where E: Error {
self.visit_str(v.as_str())
}
}
impl<'de> Deserialize<'de> for PermissionOverwriteType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
let val = deserializer.deserialize_any(PermissionOverwriteTypeVisitor)?;
Ok(val)
}
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
@ -160,10 +232,10 @@ pub struct PermissionOverwrite {
pub struct ThreadMetadata { pub struct ThreadMetadata {
pub archived: bool, pub archived: bool,
pub auto_archive_duration: i32, pub auto_archive_duration: i32,
pub archive_timestamp: String, pub archive_timestamp: DateTime<Utc>,
pub locked: bool, pub locked: bool,
pub invitable: Option<bool>, pub invitable: Option<bool>,
pub create_timestamp: Option<String>, pub create_timestamp: Option<DateTime<Utc>>,
} }
#[derive(Default, Debug, Deserialize, Serialize, Clone)] #[derive(Default, Debug, Deserialize, Serialize, Clone)]
@ -172,12 +244,12 @@ pub struct ThreadMetadata {
pub struct ThreadMember { pub struct ThreadMember {
pub id: Option<Snowflake>, pub id: Option<Snowflake>,
pub user_id: Option<Snowflake>, pub user_id: Option<Snowflake>,
pub join_timestamp: Option<String>, pub join_timestamp: Option<DateTime<Utc>>,
pub flags: Option<u64>, pub flags: Option<u64>,
pub member: Option<Shared<GuildMember>>, pub member: Option<Shared<GuildMember>>,
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd)]
/// Specifies the emoji to use as the default way to react to a [ChannelType::GuildForum] or [ChannelType::GuildMedia] channel post. /// Specifies the emoji to use as the default way to react to a [ChannelType::GuildForum] or [ChannelType::GuildMedia] channel post.
/// ///
/// # Reference /// # Reference
@ -256,3 +328,12 @@ pub enum ChannelType {
// TODO: Couldn't find reference // TODO: Couldn't find reference
Unhandled = 255, Unhandled = 255,
} }
/// # Reference
/// See <https://docs.discord.sex/resources/message#followed-channel-object>
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct FollowedChannel {
pub channel_id: Snowflake,
pub webhook_id: Snowflake
}

View File

@ -6,7 +6,7 @@ use std::fmt::Debug;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared; use crate::types::{PartialEmoji, Shared};
use crate::types::entities::User; use crate::types::entities::User;
use crate::types::Snowflake; use crate::types::Snowflake;
@ -66,3 +66,18 @@ impl PartialEq for Emoji {
|| self.available != other.available) || self.available != other.available)
} }
} }
impl From<PartialEmoji> for Emoji {
fn from(value: PartialEmoji) -> Self {
Self {
id: value.id.unwrap_or_default(), // TODO: this should be handled differently
name: Some(value.name),
roles: None,
user: None,
require_colons: Some(value.animated),
managed: None,
animated: Some(value.animated),
available: None,
}
}
}

View File

@ -9,7 +9,7 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::gateway::Shared; use crate::types::Shared;
use crate::types::types::guild_configuration::GuildFeaturesList; use crate::types::types::guild_configuration::GuildFeaturesList;
use crate::types::{ use crate::types::{
entities::{Channel, Emoji, RoleObject, Sticker, User, VoiceState, Webhook}, entities::{Channel, Emoji, RoleObject, Sticker, User, VoiceState, Webhook},
@ -23,7 +23,7 @@ use super::PublicUser;
use crate::gateway::Updateable; use crate::gateway::Updateable;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use chorus_macros::{observe_option_vec, observe_vec, Composite, Updateable}; use chorus_macros::{observe_vec, Composite, Updateable};
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::types::Composite; use crate::types::Composite;
@ -46,10 +46,12 @@ pub struct Guild {
pub approximate_presence_count: Option<i32>, pub approximate_presence_count: Option<i32>,
pub banner: Option<String>, pub banner: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub bans: Option<Vec<GuildBan>>, #[serde(default)]
pub bans: Vec<GuildBan>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_vec)]
pub channels: Option<Vec<Shared<Channel>>>, #[serde(default)]
pub channels: Vec<Shared<Channel>>,
pub default_message_notifications: Option<MessageNotificationLevel>, pub default_message_notifications: Option<MessageNotificationLevel>,
pub description: Option<String>, pub description: Option<String>,
pub discovery_splash: Option<String>, pub discovery_splash: Option<String>,
@ -57,17 +59,19 @@ pub struct Guild {
#[cfg_attr(feature = "client", observe_vec)] #[cfg_attr(feature = "client", observe_vec)]
#[serde(default)] #[serde(default)]
pub emojis: Vec<Shared<Emoji>>, pub emojis: Vec<Shared<Emoji>>,
pub explicit_content_filter: Option<i32>, pub explicit_content_filter: Option<ExplicitContentFilterLevel>,
//#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))] //#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))]
pub features: Option<GuildFeaturesList>, #[serde(default)]
pub features: GuildFeaturesList,
pub icon: Option<String>, pub icon: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub icon_hash: Option<String>, pub icon_hash: Option<String>,
pub id: Snowflake, pub id: Snowflake,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub invites: Option<Vec<GuildInvite>>, #[serde(default)]
pub invites: Vec<GuildInvite>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub joined_at: Option<String>, pub joined_at: Option<DateTime<Utc>>,
pub large: Option<bool>, pub large: Option<bool>,
pub max_members: Option<i32>, pub max_members: Option<i32>,
pub max_presences: Option<i32>, pub max_presences: Option<i32>,
@ -91,27 +95,31 @@ pub struct Guild {
pub public_updates_channel_id: Option<Snowflake>, pub public_updates_channel_id: Option<Snowflake>,
pub region: Option<String>, pub region: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_vec)]
pub roles: Option<Vec<Shared<RoleObject>>>, #[serde(default)]
pub roles: Vec<Shared<RoleObject>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub rules_channel: Option<String>, pub rules_channel: Option<String>,
pub rules_channel_id: Option<Snowflake>, pub rules_channel_id: Option<Snowflake>,
pub splash: Option<String>, pub splash: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub stickers: Option<Vec<Sticker>>, #[serde(default)]
pub system_channel_flags: Option<u64>, pub stickers: Vec<Sticker>,
pub system_channel_flags: Option<SystemChannelFlags>,
pub system_channel_id: Option<Snowflake>, pub system_channel_id: Option<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub vanity_url_code: Option<String>, pub vanity_url_code: Option<String>,
pub verification_level: Option<VerificationLevel>, pub verification_level: Option<VerificationLevel>,
#[serde(default)]
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_vec)]
pub voice_states: Option<Vec<Shared<VoiceState>>>, pub voice_states: Vec<Shared<VoiceState>>,
#[serde(default)]
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
#[cfg_attr(feature = "client", observe_option_vec)] #[cfg_attr(feature = "client", observe_vec)]
pub webhooks: Option<Vec<Shared<Webhook>>>, pub webhooks: Vec<Shared<Webhook>>,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub welcome_screen: Option<sqlx::types::Json<WelcomeScreenObject>>, pub welcome_screen: sqlx::types::Json<Option<WelcomeScreenObject>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
pub welcome_screen: Option<WelcomeScreenObject>, pub welcome_screen: Option<WelcomeScreenObject>,
pub widget_channel_id: Option<Snowflake>, pub widget_channel_id: Option<Snowflake>,
@ -225,6 +233,7 @@ impl std::cmp::PartialEq for Guild {
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)] #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct GuildBan { pub struct GuildBan {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: PublicUser, pub user: PublicUser,
pub reason: Option<String>, pub reason: Option<String>,
} }
@ -422,7 +431,8 @@ pub enum PremiumTier {
} }
bitflags! { bitflags! {
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// # Reference /// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/guild#system-channel-flags> /// See <https://discord-userdoccers.vercel.app/resources/guild#system-channel-flags>
pub struct SystemChannelFlags: u64 { pub struct SystemChannelFlags: u64 {

View File

@ -2,27 +2,32 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared; use crate::types::{GuildMemberFlags, PermissionFlags, Shared};
use crate::types::{entities::PublicUser, Snowflake}; use crate::types::{entities::PublicUser, Snowflake};
#[derive(Debug, Deserialize, Default, Serialize, Clone)] #[derive(Debug, Deserialize, Default, Serialize, Clone)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
/// Represents a participating user in a guild. /// Represents a participating user in a guild.
/// ///
/// # Reference /// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/guild#guild-member-object> /// See <https://discord-userdoccers.vercel.app/resources/guild#guild-member-object>
pub struct GuildMember { pub struct GuildMember {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: Option<Shared<PublicUser>>, pub user: Option<Shared<PublicUser>>,
pub nick: Option<String>, pub nick: Option<String>,
pub avatar: Option<String>, pub avatar: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub roles: Vec<Snowflake>, pub roles: Vec<Snowflake>,
pub joined_at: String, pub joined_at: DateTime<Utc>,
pub premium_since: Option<String>, pub premium_since: Option<DateTime<Utc>>,
pub deaf: bool, pub deaf: bool,
pub mute: bool, pub mute: bool,
pub flags: Option<i32>, pub flags: Option<GuildMemberFlags>,
pub pending: Option<bool>, pub pending: Option<bool>,
pub permissions: Option<String>, #[serde(default)]
pub communication_disabled_until: Option<String>, pub permissions: PermissionFlags,
pub communication_disabled_until: Option<DateTime<Utc>>,
} }

View File

@ -5,8 +5,8 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::{ use crate::types::{
Shared,
entities::{Application, User}, entities::{Application, User},
utils::Snowflake, utils::Snowflake,
}; };
@ -18,7 +18,7 @@ pub struct Integration {
pub id: Snowflake, pub id: Snowflake,
pub name: String, pub name: String,
#[serde(rename = "type")] #[serde(rename = "type")]
pub integration_type: String, pub integration_type: IntegrationType,
pub enabled: bool, pub enabled: bool,
pub syncing: Option<bool>, pub syncing: Option<bool>,
pub role_id: Option<String>, pub role_id: Option<String>,
@ -43,3 +43,15 @@ pub struct IntegrationAccount {
pub id: String, pub id: String,
pub name: String, pub name: String,
} }
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[cfg_attr(feature = "sqlx", sqlx(rename_all = "snake_case"))]
pub enum IntegrationType {
#[default]
Twitch,
Youtube,
Discord,
GuildSubscription,
}

View File

@ -5,37 +5,49 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared; use crate::types::{Snowflake, WelcomeScreenObject, Shared, InviteFlags, InviteType, InviteTargetType, Guild, VerificationLevel};
use crate::types::{Snowflake, WelcomeScreenObject}; use crate::types::types::guild_configuration::GuildFeaturesList;
use super::guild::GuildScheduledEvent; use super::guild::GuildScheduledEvent;
use super::{Application, Channel, GuildMember, NSFWLevel, User}; use super::{Application, Channel, GuildMember, NSFWLevel, User};
/// Represents a code that when used, adds a user to a guild or group DM channel, or creates a relationship between two users. /// Represents a code that when used, adds a user to a guild or group DM channel, or creates a relationship between two users.
/// See <https://discord-userdoccers.vercel.app/resources/invite#invite-object> /// See <https://discord-userdoccers.vercel.app/resources/invite#invite-object>
#[derive(Debug, Serialize, Deserialize)] #[derive(Default, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct Invite { pub struct Invite {
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub approximate_member_count: Option<i32>, pub approximate_member_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub approximate_presence_count: Option<i32>, pub approximate_presence_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub channel: Option<Channel>, pub channel: Option<Channel>,
pub code: String, pub code: String,
pub created_at: Option<DateTime<Utc>>, pub created_at: Option<DateTime<Utc>>,
pub expires_at: Option<DateTime<Utc>>, pub expires_at: Option<DateTime<Utc>>,
pub flags: Option<i32>, pub flags: Option<InviteFlags>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub guild: Option<InviteGuild>, pub guild: Option<InviteGuild>,
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub guild_scheduled_event: Option<Shared<GuildScheduledEvent>>, pub guild_scheduled_event: Option<Shared<GuildScheduledEvent>>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub invite_type: Option<i32>, #[cfg_attr(feature = "sqlx", sqlx(rename = "type"))]
pub invite_type: Option<InviteType>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub inviter: Option<User>, pub inviter: Option<User>,
pub max_age: Option<i32>, pub max_age: Option<u32>,
pub max_uses: Option<i32>, pub max_uses: Option<u8>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub stage_instance: Option<InviteStageInstance>, pub stage_instance: Option<InviteStageInstance>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub target_application: Option<Application>, pub target_application: Option<Application>,
pub target_type: Option<i32>, #[cfg_attr(feature = "sqlx", sqlx(rename = "target_user_type"))]
pub target_type: Option<InviteTargetType>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub target_user: Option<User>, pub target_user: Option<User>,
pub temporary: Option<bool>, pub temporary: Option<bool>,
pub uses: Option<i32>, pub uses: Option<u32>,
} }
/// The guild an invite is for. /// The guild an invite is for.
@ -46,8 +58,8 @@ pub struct InviteGuild {
pub name: String, pub name: String,
pub icon: Option<String>, pub icon: Option<String>,
pub splash: Option<String>, pub splash: Option<String>,
pub verification_level: i32, pub verification_level: VerificationLevel,
pub features: Vec<String>, pub features: GuildFeaturesList,
pub vanity_url_code: Option<String>, pub vanity_url_code: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub banner: Option<String>, pub banner: Option<String>,
@ -59,6 +71,29 @@ pub struct InviteGuild {
pub welcome_screen: Option<WelcomeScreenObject>, pub welcome_screen: Option<WelcomeScreenObject>,
} }
impl From<Guild> for InviteGuild {
fn from(value: Guild) -> Self {
Self {
id: value.id,
name: value.name.unwrap_or_default(),
icon: value.icon,
splash: value.splash,
verification_level: value.verification_level.unwrap_or_default(),
features: value.features,
vanity_url_code: value.vanity_url_code,
description: value.description,
banner: value.banner,
premium_subscription_count: value.premium_subscription_count,
nsfw_deprecated: None,
nsfw_level: value.nsfw_level.unwrap_or_default(),
#[cfg(feature = "sqlx")]
welcome_screen: value.welcome_screen.0,
#[cfg(not(feature = "sqlx"))]
welcome_screen: value.welcome_screen,
}
}
}
/// See <https://discord-userdoccers.vercel.app/resources/invite#invite-stage-instance-object> /// See <https://discord-userdoccers.vercel.app/resources/invite#invite-stage-instance-object>
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct InviteStageInstance { pub struct InviteStageInstance {

View File

@ -2,10 +2,13 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use bitflags::bitflags;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::gateway::Shared;
use crate::types::{ use crate::types::{
Shared,
entities::{ entities::{
Application, Attachment, Channel, Emoji, GuildMember, PublicUser, RoleSubscriptionData, Application, Attachment, Channel, Emoji, GuildMember, PublicUser, RoleSubscriptionData,
Sticker, StickerItem, User, Sticker, StickerItem, User,
@ -25,8 +28,8 @@ pub struct Message {
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub author: Option<PublicUser>, pub author: Option<PublicUser>,
pub content: Option<String>, pub content: Option<String>,
pub timestamp: String, pub timestamp: DateTime<Utc>,
pub edited_timestamp: Option<String>, pub edited_timestamp: Option<DateTime<Utc>>,
pub tts: Option<bool>, pub tts: Option<bool>,
pub mention_everyone: bool, pub mention_everyone: bool,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
@ -38,7 +41,7 @@ pub struct Message {
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub attachments: Option<Vec<Attachment>>, pub attachments: Option<Vec<Attachment>>,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub embeds: Vec<sqlx::types::Json<Embed>>, pub embeds: sqlx::types::Json<Vec<Embed>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
pub embeds: Option<Vec<Embed>>, pub embeds: Option<Vec<Embed>>,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
@ -49,7 +52,7 @@ pub struct Message {
pub pinned: bool, pub pinned: bool,
pub webhook_id: Option<Snowflake>, pub webhook_id: Option<Snowflake>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub message_type: i32, pub message_type: MessageType,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub activity: Option<sqlx::types::Json<MessageActivity>>, pub activity: Option<sqlx::types::Json<MessageActivity>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
@ -61,14 +64,22 @@ pub struct Message {
pub message_reference: Option<sqlx::types::Json<MessageReference>>, pub message_reference: Option<sqlx::types::Json<MessageReference>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
pub message_reference: Option<MessageReference>, pub message_reference: Option<MessageReference>,
pub flags: Option<u64>, pub flags: Option<MessageFlags>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub referenced_message: Option<Box<Message>>, pub referenced_message: Option<Box<Message>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub interaction: Option<MessageInteraction>, pub interaction: Option<MessageInteraction>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub thread: Option<Channel>, pub thread: Option<Channel>,
#[cfg(feature = "sqlx")]
pub components: Option<sqlx::types::Json<Vec<Component>>>,
#[cfg(not(feature = "sqlx"))]
pub components: Option<Vec<Component>>, pub components: Option<Vec<Component>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub sticker_items: Option<Vec<StickerItem>>, pub sticker_items: Option<Vec<StickerItem>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub stickers: Option<Vec<Sticker>>, pub stickers: Option<Vec<Sticker>>,
pub position: Option<i32>, #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub role_subscription_data: Option<RoleSubscriptionData>, pub role_subscription_data: Option<RoleSubscriptionData>,
} }
@ -102,7 +113,7 @@ impl PartialEq for Message {
&& self.thread == other.thread && self.thread == other.thread
&& self.components == other.components && self.components == other.components
&& self.sticker_items == other.sticker_items && self.sticker_items == other.sticker_items
&& self.position == other.position // && self.position == other.position
&& self.role_subscription_data == other.role_subscription_data && self.role_subscription_data == other.role_subscription_data
} }
} }
@ -111,12 +122,22 @@ impl PartialEq for Message {
/// # Reference /// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/message#message-reference-object> /// See <https://discord-userdoccers.vercel.app/resources/message#message-reference-object>
pub struct MessageReference { pub struct MessageReference {
#[serde(rename = "type")]
pub reference_type: MessageReferenceType,
pub message_id: Snowflake, pub message_id: Snowflake,
pub channel_id: Snowflake, pub channel_id: Snowflake,
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
pub fail_if_not_exists: Option<bool>, pub fail_if_not_exists: Option<bool>,
} }
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Ord, PartialOrd)]
pub enum MessageReferenceType {
/// A standard reference used by replies and system messages
Default = 0,
/// A reference used to point to a message at a point in time
Forward = 1,
}
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MessageInteraction { pub struct MessageInteraction {
pub id: Snowflake, pub id: Snowflake,
@ -156,7 +177,7 @@ pub struct ChannelMention {
pub struct Embed { pub struct Embed {
title: Option<String>, title: Option<String>,
#[serde(rename = "type")] #[serde(rename = "type")]
embed_type: Option<String>, embed_type: Option<EmbedType>,
description: Option<String>, description: Option<String>,
url: Option<String>, url: Option<String>,
timestamp: Option<String>, timestamp: Option<String>,
@ -170,6 +191,24 @@ pub struct Embed {
fields: Option<Vec<EmbedField>>, fields: Option<Vec<EmbedField>>,
} }
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum EmbedType {
#[deprecated]
ApplicationNews,
Article,
AutoModerationMessage,
AutoModerationNotification,
Gift,
#[serde(rename = "gifv")]
GifVideo,
Image,
Link,
PostPreview,
Rich,
Video
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct EmbedFooter { pub struct EmbedFooter {
text: String, text: String,
@ -226,10 +265,15 @@ pub struct EmbedField {
pub struct Reaction { pub struct Reaction {
pub count: u32, pub count: u32,
pub burst_count: u32, pub burst_count: u32,
#[serde(default)]
pub me: bool, pub me: bool,
#[serde(default)]
pub burst_me: bool, pub burst_me: bool,
pub burst_colors: Vec<String>, pub burst_colors: Vec<String>,
pub emoji: Emoji, pub emoji: Emoji,
#[cfg(feature = "sqlx")]
#[serde(skip)]
pub user_ids: Vec<Snowflake>
} }
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)]
@ -252,3 +296,155 @@ pub struct MessageActivity {
pub activity_type: i64, pub activity_type: i64,
pub party_id: Option<String>, pub party_id: Option<String>,
} }
#[derive(Debug, Default, PartialEq, Clone, Copy, Serialize_repr, Deserialize_repr, Eq, PartialOrd, Ord)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[repr(u8)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
/// # Reference
/// See <https://docs.discord.sex/resources/message#message-type>
pub enum MessageType {
/// A default message
#[default]
Default = 0,
/// A message sent when a user is added to a group DM or thread
RecipientAdd = 1,
/// A message sent when a user is removed from a group DM or thread
RecipientRemove = 2,
/// A message sent when a user creates a call in a private channel
Call = 3,
/// A message sent when a group DM or thread's name is changed
ChannelNameChange = 4,
/// A message sent when a group DM's icon is changed
ChannelIconChange = 5,
/// A message sent when a message is pinned in a channel
ChannelPinnedMessage = 6,
/// A message sent when a user joins a guild
GuildMemberJoin = 7,
/// A message sent when a user subscribes to (boosts) a guild
UserPremiumGuildSubscription = 8,
/// A message sent when a user subscribes to (boosts) a guild to tier 1
UserPremiumGuildSubscriptionTier1 = 9,
/// A message sent when a user subscribes to (boosts) a guild to tier 2
UserPremiumGuildSubscriptionTier2 = 10,
/// A message sent when a user subscribes to (boosts) a guild to tier 3
UserPremiumGuildSubscriptionTier3 = 11,
/// A message sent when a news channel is followed
ChannelFollowAdd = 12,
/// A message sent when a user starts streaming in a guild (deprecated)
#[deprecated]
GuildStream = 13,
/// A message sent when a guild is disqualified from discovery
GuildDiscoveryDisqualified = 14,
/// A message sent when a guild requalifies for discovery
GuildDiscoveryRequalified = 15,
/// A message sent when a guild has failed discovery requirements for a week
GuildDiscoveryGracePeriodInitial = 16,
/// A message sent when a guild has failed discovery requirements for 3 weeks
GuildDiscoveryGracePeriodFinal = 17,
/// A message sent when a thread is created
ThreadCreated = 18,
/// A message sent when a user replies to a message
Reply = 19,
/// A message sent when a user uses a slash command
#[serde(rename = "CHAT_INPUT_COMMAND")]
ApplicationCommand = 20,
/// A message sent when a thread starter message is added to a thread
ThreadStarterMessage = 21,
/// A message sent to remind users to invite friends to a guild
GuildInviteReminder = 22,
/// A message sent when a user uses a context menu command
ContextMenuCommand = 23,
/// A message sent when auto moderation takes an action
AutoModerationAction = 24,
/// A message sent when a user purchases or renews a role subscription
RoleSubscriptionPurchase = 25,
/// A message sent when a user is upsold to a premium interaction
InteractionPremiumUpsell = 26,
/// A message sent when a stage channel starts
StageStart = 27,
/// A message sent when a stage channel ends
StageEnd = 28,
/// A message sent when a user starts speaking in a stage channel
StageSpeaker = 29,
/// A message sent when a user raises their hand in a stage channel
StageRaiseHand = 30,
/// A message sent when a stage channel's topic is changed
StageTopic = 31,
/// A message sent when a user purchases an application premium subscription
GuildApplicationPremiumSubscription = 32,
/// A message sent when a user adds an application to group DM
PrivateChannelIntegrationAdded = 33,
/// A message sent when a user removed an application from a group DM
PrivateChannelIntegrationRemoved = 34,
/// A message sent when a user gifts a premium (Nitro) referral
PremiumReferral = 35,
/// A message sent when a user enabled lockdown for the guild
GuildIncidentAlertModeEnabled = 36,
/// A message sent when a user disables lockdown for the guild
GuildIncidentAlertModeDisabled = 37,
/// A message sent when a user reports a raid for the guild
GuildIncidentReportRaid = 38,
/// A message sent when a user reports a false alarm for the guild
GuildIncidentReportFalseAlarm = 39,
/// A message sent when no one sends a message in the current channel for 1 hour
GuildDeadchatRevivePrompt = 40,
/// A message sent when a user buys another user a gift
CustomGift = 41,
GuildGamingStatsPrompt = 42,
/// A message sent when a user purchases a guild product
PurchaseNotification = 44
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// # Reference
/// See <https://docs.discord.sex/resources/message#message-type>
pub struct MessageFlags: u64 {
/// This message has been published to subscribed channels (via Channel Following)
const CROSSPOSTED = 1 << 0;
/// This message originated from a message in another channel (via Channel Following)
const IS_CROSSPOST = 1 << 1;
/// Embeds will not be included when serializing this message
const SUPPRESS_EMBEDS = 1 << 2;
/// The source message for this crosspost has been deleted (via Channel Following)
const SOURCE_MESSAGE_DELETED = 1 << 3;
/// This message came from the urgent message system
const URGENT = 1 << 4;
/// This message has an associated thread, with the same ID as the message
const HAS_THREAD = 1 << 5;
/// This message is only visible to the user who invoked the interaction
const EPHEMERAL = 1 << 6;
/// This message is an interaction response and the bot is "thinking"
const LOADING = 1 << 7;
/// Some roles were not mentioned and added to the thread
const FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8;
/// This message contains a link that impersonates Discord
const SHOULD_SHOW_LINK_NOT_DISCORD_WARNING = 1 << 10;
/// This message will not trigger push and desktop notifications
const SUPPRESS_NOTIFICATIONS = 1 << 12;
/// This message's audio attachments are rendered as voice messages
const VOICE_MESSAGE = 1 << 13;
/// This message has a forwarded message snapshot attached
const HAS_SNAPSHOT = 1 << 14;
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct PartialEmoji {
#[serde(default)]
pub id: Option<Snowflake>,
pub name: String,
#[serde(default)]
pub animated: bool
}
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, PartialOrd)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[repr(u8)]
pub enum ReactionType {
Normal = 0,
Burst = 1, // The dreaded super reactions
}

View File

@ -27,7 +27,10 @@ pub use user_settings::*;
pub use voice_state::*; pub use voice_state::*;
pub use webhook::*; pub use webhook::*;
use crate::gateway::Shared; use crate::types::Shared;
#[cfg(feature = "client")]
use std::sync::{Arc, RwLock};
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::gateway::Updateable; use crate::gateway::Updateable;
@ -39,8 +42,6 @@ use async_trait::async_trait;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use std::fmt::Debug; use std::fmt::Debug;
#[cfg(feature = "client")]
use std::sync::{Arc, RwLock};
mod application; mod application;
mod attachment; mod attachment;
@ -134,6 +135,7 @@ pub trait IntoShared {
fn into_shared(self) -> Shared<Self>; fn into_shared(self) -> Shared<Self>;
} }
#[cfg(feature = "client")]
impl<T: Sized> IntoShared for T { impl<T: Sized> IntoShared for T {
fn into_shared(self) -> Shared<Self> { fn into_shared(self) -> Shared<Self> {
Arc::new(RwLock::new(self)) Arc::new(RwLock::new(self))

View File

@ -6,8 +6,7 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::gateway::Shared; use crate::types::{Shared, Snowflake};
use crate::types::Snowflake;
use super::PublicUser; use super::PublicUser;

View File

@ -4,7 +4,7 @@
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_aux::prelude::{deserialize_option_number_from_string, deserialize_string_from_number}; use serde_aux::prelude::deserialize_option_number_from_string;
use std::fmt::Debug; use std::fmt::Debug;
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;
@ -34,8 +34,7 @@ pub struct RoleObject {
pub unicode_emoji: Option<String>, pub unicode_emoji: Option<String>,
pub position: u16, pub position: u16,
#[serde(default)] #[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")] pub permissions: PermissionFlags,
pub permissions: String,
pub managed: bool, pub managed: bool,
pub mentionable: bool, pub mentionable: bool,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
@ -71,7 +70,8 @@ pub struct RoleTags {
} }
bitflags! { bitflags! {
#[derive(Debug, Default, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Default, Clone, Hash, PartialEq, Eq, PartialOrd, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// Permissions limit what users of certain roles can do on a Guild to Guild basis. /// Permissions limit what users of certain roles can do on a Guild to Guild basis.
/// ///
/// # Reference: /// # Reference:

View File

@ -3,9 +3,9 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::gateway::Shared; use crate::types::{entities::User, utils::Snowflake, Shared};
use crate::types::{entities::User, utils::Snowflake};
#[derive(Debug, Serialize, Deserialize, Clone, Default)] #[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
@ -19,15 +19,17 @@ pub struct Sticker {
pub pack_id: Option<Snowflake>, pub pack_id: Option<Snowflake>,
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
pub tags: String, pub tags: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub asset: Option<String>, pub asset: Option<String>,
#[serde(rename = "type")] #[serde(rename = "type")]
pub sticker_type: u8, pub sticker_type: StickerType,
pub format_type: u8, pub format_type: StickerFormatType,
pub available: Option<bool>, pub available: Option<bool>,
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: Option<Shared<User>>, pub user: Option<Shared<User>>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub sort_value: Option<u8>, pub sort_value: Option<u8>,
} }
@ -109,6 +111,18 @@ impl PartialOrd for Sticker {
} }
} }
impl Sticker {
pub fn tags(&self) -> Vec<String> {
self.tags
.as_ref()
.map_or(vec![], |s|
s.split(',')
.map(|tag| tag.trim().to_string())
.collect()
)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
/// A partial sticker object. /// A partial sticker object.
/// ///
@ -119,5 +133,61 @@ impl PartialOrd for Sticker {
pub struct StickerItem { pub struct StickerItem {
pub id: Snowflake, pub id: Snowflake,
pub name: String, pub name: String,
pub format_type: u8, pub format_type: StickerFormatType,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[serde(rename = "SCREAMING_SNAKE_CASE")]
/// # Reference
/// See <https://docs.discord.sex/resources/sticker#sticker-types>
pub enum StickerType {
/// An official sticker in a current or legacy purchasable pack
Standard = 1,
#[default]
/// A sticker uploaded to a guild for the guild's members
Guild = 2,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
/// # Reference
/// See <https://docs.discord.sex/resources/sticker#sticker-format-types>
pub enum StickerFormatType {
#[default]
/// A PNG image
PNG = 1,
/// An animated PNG image, using the APNG format - uses CDN
APNG = 2,
/// A lottie animation; requires the VERIFIED and/or PARTNERED guild feature - uses CDN
LOTTIE = 3,
/// An animated GIF image - does not use CDN
GIF = 4,
}
impl StickerFormatType {
pub fn is_animated(&self) -> bool {
matches!(self, StickerFormatType::APNG | StickerFormatType::LOTTIE | StickerFormatType::GIF)
}
pub const fn to_mime(&self) -> &'static str {
match self {
StickerFormatType::PNG => "image/png",
StickerFormatType::APNG => "image/apng",
StickerFormatType::LOTTIE => "application/json",
StickerFormatType::GIF => "image/gif",
}
}
pub fn from_mime(mime: &str) -> Option<Self> {
match mime {
"image/png" => Some(StickerFormatType::PNG),
"image/apng" => Some(StickerFormatType::APNG),
"application/json" => Some(StickerFormatType::LOTTIE),
"image/gif" => Some(StickerFormatType::GIF),
_ => None,
}
}
} }

View File

@ -4,9 +4,9 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::entities::User; use crate::types::entities::User;
use crate::types::Snowflake; use crate::types::Snowflake;
use crate::types::Shared;
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]

View File

@ -5,8 +5,8 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared;
use crate::types::{ use crate::types::{
Shared,
entities::{Guild, User}, entities::{Guild, User},
utils::Snowflake, utils::Snowflake,
}; };

View File

@ -45,7 +45,7 @@ pub struct User {
pub bot: Option<bool>, pub bot: Option<bool>,
pub system: Option<bool>, pub system: Option<bool>,
pub mfa_enabled: Option<bool>, pub mfa_enabled: Option<bool>,
pub accent_color: Option<u8>, pub accent_color: Option<u32>,
#[cfg_attr(feature = "sqlx", sqlx(default))] #[cfg_attr(feature = "sqlx", sqlx(default))]
pub locale: Option<String>, pub locale: Option<String>,
pub verified: Option<bool>, pub verified: Option<bool>,
@ -54,14 +54,14 @@ pub struct User {
/// So we need to account for that /// So we need to account for that
#[serde(default)] #[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")] #[serde(deserialize_with = "deserialize_option_number_from_string")]
pub flags: Option<i32>, pub flags: Option<UserFlags>,
pub premium_since: Option<DateTime<Utc>>, pub premium_since: Option<DateTime<Utc>>,
pub premium_type: Option<u8>, pub premium_type: Option<u8>,
pub pronouns: Option<String>, pub pronouns: Option<String>,
pub public_flags: Option<u32>, pub public_flags: Option<UserFlags>,
pub banner: Option<String>, pub banner: Option<String>,
pub bio: Option<String>, pub bio: Option<String>,
pub theme_colors: Option<Vec<u8>>, pub theme_colors: Option<Vec<u32>>,
pub phone: Option<String>, pub phone: Option<String>,
pub nsfw_allowed: Option<bool>, pub nsfw_allowed: Option<bool>,
pub premium: Option<bool>, pub premium: Option<bool>,
@ -76,15 +76,15 @@ pub struct PublicUser {
pub username: Option<String>, pub username: Option<String>,
pub discriminator: Option<String>, pub discriminator: Option<String>,
pub avatar: Option<String>, pub avatar: Option<String>,
pub accent_color: Option<u8>, pub accent_color: Option<u32>,
pub banner: Option<String>, pub banner: Option<String>,
pub theme_colors: Option<Vec<u8>>, pub theme_colors: Option<Vec<u32>>,
pub pronouns: Option<String>, pub pronouns: Option<String>,
pub bot: Option<bool>, pub bot: Option<bool>,
pub bio: Option<String>, pub bio: Option<String>,
pub premium_type: Option<u8>, pub premium_type: Option<u8>,
pub premium_since: Option<DateTime<Utc>>, pub premium_since: Option<DateTime<Utc>>,
pub public_flags: Option<u32>, pub public_flags: Option<UserFlags>,
} }
impl From<User> for PublicUser { impl From<User> for PublicUser {
@ -111,8 +111,8 @@ impl From<User> for PublicUser {
const CUSTOM_USER_FLAG_OFFSET: u64 = 1 << 32; const CUSTOM_USER_FLAG_OFFSET: u64 = 1 << 32;
bitflags::bitflags! { bitflags::bitflags! {
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
pub struct UserFlags: u64 { pub struct UserFlags: u64 {
const DISCORD_EMPLOYEE = 1 << 0; const DISCORD_EMPLOYEE = 1 << 0;
const PARTNERED_SERVER_OWNER = 1 << 1; const PARTNERED_SERVER_OWNER = 1 << 1;

View File

@ -2,12 +2,11 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::sync::{Arc, RwLock};
use chrono::{serde::ts_milliseconds_option, Utc}; use chrono::{serde::ts_milliseconds_option, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared; use crate::types::Shared;
use serde_aux::field_attributes::deserialize_option_number_from_string;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
@ -39,7 +38,7 @@ pub enum UserTheme {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct UserSettings { pub struct UserSettings {
pub afk_timeout: u16, pub afk_timeout: Option<u16>,
pub allow_accessibility_detection: bool, pub allow_accessibility_detection: bool,
pub animate_emoji: bool, pub animate_emoji: bool,
pub animate_stickers: u8, pub animate_stickers: u8,
@ -92,7 +91,7 @@ pub struct UserSettings {
impl Default for UserSettings { impl Default for UserSettings {
fn default() -> Self { fn default() -> Self {
Self { Self {
afk_timeout: 3600, afk_timeout: Some(3600),
allow_accessibility_detection: true, allow_accessibility_detection: true,
animate_emoji: true, animate_emoji: true,
animate_stickers: 0, animate_stickers: 0,
@ -119,7 +118,7 @@ impl Default for UserSettings {
render_reactions: true, render_reactions: true,
restricted_guilds: Default::default(), restricted_guilds: Default::default(),
show_current_game: true, show_current_game: true,
status: Arc::new(RwLock::new(UserStatus::Online)), status: Default::default(),
stream_notifications_enabled: false, stream_notifications_enabled: false,
theme: UserTheme::Dark, theme: UserTheme::Dark,
timezone_offset: 0, timezone_offset: 0,
@ -150,10 +149,17 @@ impl Default for FriendSourceFlags {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GuildFolder { pub struct GuildFolder {
pub color: u32, pub color: Option<u32>,
pub guild_ids: Vec<String>, pub guild_ids: Vec<String>,
pub id: u16, // FIXME: What is this thing?
pub name: String, // It's not a snowflake, and it's sometimes a string and sometimes an integer.
//
// Ex: 1249181105
//
// It can also be negative somehow? Ex: -1176643795
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub id: Option<i64>,
pub name: Option<String>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]

View File

@ -5,7 +5,8 @@
#[cfg(feature = "client")] #[cfg(feature = "client")]
use chorus_macros::Composite; use chorus_macros::Composite;
use crate::gateway::Shared; use crate::types::Shared;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::types::Composite; use crate::types::Composite;
@ -33,9 +34,11 @@ use crate::types::{
#[cfg_attr(feature = "client", derive(Composite))] #[cfg_attr(feature = "client", derive(Composite))]
pub struct VoiceState { pub struct VoiceState {
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub guild: Option<Guild>, pub guild: Option<Guild>,
pub channel_id: Option<Snowflake>, pub channel_id: Option<Snowflake>,
pub user_id: Snowflake, pub user_id: Snowflake,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member: Option<Shared<GuildMember>>, pub member: Option<Shared<GuildMember>>,
/// Includes alphanumeric characters, not a snowflake /// Includes alphanumeric characters, not a snowflake
pub session_id: String, pub session_id: String,
@ -51,6 +54,7 @@ pub struct VoiceState {
pub id: Option<Snowflake>, // Only exists on Spacebar pub id: Option<Snowflake>, // Only exists on Spacebar
} }
#[cfg(feature = "client")]
impl Updateable for VoiceState { impl Updateable for VoiceState {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
fn id(&self) -> Snowflake { fn id(&self) -> Snowflake {

View File

@ -6,7 +6,8 @@ use std::fmt::Debug;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gateway::Shared; use crate::types::Shared;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::gateway::Updateable; use crate::gateway::Updateable;
@ -31,13 +32,13 @@ use crate::types::{
pub struct Webhook { pub struct Webhook {
pub id: Snowflake, pub id: Snowflake,
#[serde(rename = "type")] #[serde(rename = "type")]
pub webhook_type: i32, pub webhook_type: WebhookType,
pub name: String, pub name: String,
pub avatar: String, pub avatar: String,
pub token: String, pub token: String,
pub guild_id: Snowflake, pub guild_id: Snowflake,
pub channel_id: Snowflake, pub channel_id: Snowflake,
pub application_id: Snowflake, pub application_id: Option<Snowflake>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "sqlx", sqlx(skip))] #[cfg_attr(feature = "sqlx", sqlx(skip))]
pub user: Option<Shared<User>>, pub user: Option<Shared<User>>,
@ -47,3 +48,13 @@ pub struct Webhook {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>, pub url: Option<String>,
} }
#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy)]
#[repr(u8)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
pub enum WebhookType {
#[default]
Incoming = 1,
ChannelFollower = 2,
Application = 3,
}

View File

@ -21,6 +21,9 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
Guild(#[from] GuildError), Guild(#[from] GuildError),
#[error("Invalid flags value: {0}")]
InvalidFlags(u64)
} }
#[derive(Debug, PartialEq, Eq, thiserror::Error)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]

View File

@ -5,12 +5,11 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{GuildApplicationCommandPermissions, WebSocketEvent}; use crate::types::{GuildApplicationCommandPermissions, WebSocketEvent};
use chorus_macros::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#application-command-permissions-update> /// See <https://discord.com/developers/docs/topics/gateway-events#application-command-permissions-update>
pub struct ApplicationCommandPermissionsUpdate { pub struct ApplicationCommandPermissionsUpdate {
#[serde(flatten)] #[serde(flatten)]
pub permissions: GuildApplicationCommandPermissions, pub permissions: GuildApplicationCommandPermissions,
} }
impl WebSocketEvent for ApplicationCommandPermissionsUpdate {}

View File

@ -2,28 +2,27 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::{JsonField, SourceUrlField}; use crate::types::{JsonField, SourceUrlField, WebSocketEvent};
use chorus_macros::{JsonField, SourceUrlField}; use chorus_macros::{JsonField, SourceUrlField, WebSocketEvent};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{ use crate::types::{
AutoModerationAction, AutoModerationRule, AutoModerationRuleTriggerType, Snowflake, AutoModerationAction, AutoModerationRule, AutoModerationRuleTriggerType, Snowflake,
WebSocketEvent,
}; };
#[cfg(feature = "client")] #[cfg(feature = "client")]
use super::UpdateMessage; use super::UpdateMessage;
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-create> /// See <https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-create>
pub struct AutoModerationRuleCreate { pub struct AutoModerationRuleCreate {
#[serde(flatten)] #[serde(flatten)]
pub rule: AutoModerationRule, pub rule: AutoModerationRule,
} }
impl WebSocketEvent for AutoModerationRuleCreate {} #[derive(
Debug, Deserialize, Serialize, Default, Clone, JsonField, SourceUrlField, WebSocketEvent,
#[derive(Debug, Deserialize, Serialize, Default, Clone, JsonField, SourceUrlField)] )]
/// See <https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-update> /// See <https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-update>
pub struct AutoModerationRuleUpdate { pub struct AutoModerationRuleUpdate {
#[serde(flatten)] #[serde(flatten)]
@ -43,18 +42,14 @@ impl UpdateMessage<AutoModerationRule> for AutoModerationRuleUpdate {
} }
} }
impl WebSocketEvent for AutoModerationRuleUpdate {} #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-delete> /// See <https://discord.com/developers/docs/topics/gateway-events#auto-moderation-rule-delete>
pub struct AutoModerationRuleDelete { pub struct AutoModerationRuleDelete {
#[serde(flatten)] #[serde(flatten)]
pub rule: AutoModerationRule, pub rule: AutoModerationRule,
} }
impl WebSocketEvent for AutoModerationRuleDelete {} #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#auto-moderation-action-execution> /// See <https://discord.com/developers/docs/topics/gateway-events#auto-moderation-action-execution>
pub struct AutoModerationActionExecution { pub struct AutoModerationActionExecution {
pub guild_id: Snowflake, pub guild_id: Snowflake,
@ -69,5 +64,3 @@ pub struct AutoModerationActionExecution {
pub matched_keyword: Option<String>, pub matched_keyword: Option<String>,
pub matched_content: Option<String>, pub matched_content: Option<String>,
} }
impl WebSocketEvent for AutoModerationActionExecution {}

View File

@ -5,8 +5,9 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{Snowflake, VoiceState, WebSocketEvent}; use crate::types::{Snowflake, VoiceState, WebSocketEvent};
use chorus_macros::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// Officially Undocumented; /// Officially Undocumented;
/// Is sent to a client by the server to signify a new call being created; /// Is sent to a client by the server to signify a new call being created;
/// ///
@ -23,9 +24,7 @@ pub struct CallCreate {
pub channel_id: Snowflake, pub channel_id: Snowflake,
} }
impl WebSocketEvent for CallCreate {} #[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
/// Officially Undocumented; /// Officially Undocumented;
/// Updates the client on which calls are ringing, along with a specific call?; /// Updates the client on which calls are ringing, along with a specific call?;
/// ///
@ -40,9 +39,7 @@ pub struct CallUpdate {
pub channel_id: Snowflake, pub channel_id: Snowflake,
} }
impl WebSocketEvent for CallUpdate {} #[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
/// Officially Undocumented; /// Officially Undocumented;
/// Deletes a ringing call; /// Deletes a ringing call;
/// Ex: {"t":"CALL_DELETE","s":8,"op":0,"d":{"channel_id":"837609115475771392"}} /// Ex: {"t":"CALL_DELETE","s":8,"op":0,"d":{"channel_id":"837609115475771392"}}
@ -50,9 +47,7 @@ pub struct CallDelete {
pub channel_id: Snowflake, pub channel_id: Snowflake,
} }
impl WebSocketEvent for CallDelete {} #[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
/// Officially Undocumented; /// Officially Undocumented;
/// See <https://unofficial-discord-docs.vercel.app/gateway/op13>; /// See <https://unofficial-discord-docs.vercel.app/gateway/op13>;
/// ///
@ -61,4 +56,3 @@ pub struct CallSync {
pub channel_id: Snowflake, pub channel_id: Snowflake,
} }
impl WebSocketEvent for CallSync {}

View File

@ -3,7 +3,6 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::events::WebSocketEvent; use crate::types::events::WebSocketEvent;
use crate::types::IntoShared;
use crate::types::{entities::Channel, JsonField, Snowflake, SourceUrlField}; use crate::types::{entities::Channel, JsonField, Snowflake, SourceUrlField};
use chorus_macros::{JsonField, SourceUrlField}; use chorus_macros::{JsonField, SourceUrlField};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -13,12 +12,15 @@ use serde::{Deserialize, Serialize};
use super::UpdateMessage; use super::UpdateMessage;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::gateway::Shared; use crate::types::Shared;
#[cfg(feature = "client")]
use crate::types::IntoShared;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::types::Guild; use crate::types::Guild;
#[derive(Debug, Default, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#channel-pins-update> /// See <https://discord.com/developers/docs/topics/gateway-events#channel-pins-update>
pub struct ChannelPinsUpdate { pub struct ChannelPinsUpdate {
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
@ -26,9 +28,7 @@ pub struct ChannelPinsUpdate {
pub last_pin_timestamp: Option<DateTime<Utc>>, pub last_pin_timestamp: Option<DateTime<Utc>>,
} }
impl WebSocketEvent for ChannelPinsUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)]
/// See <https://discord.com/developers/docs/topics/gateway-events#channel-create> /// See <https://discord.com/developers/docs/topics/gateway-events#channel-create>
pub struct ChannelCreate { pub struct ChannelCreate {
#[serde(flatten)] #[serde(flatten)]
@ -39,8 +39,6 @@ pub struct ChannelCreate {
pub source_url: String, pub source_url: String,
} }
impl WebSocketEvent for ChannelCreate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Guild> for ChannelCreate { impl UpdateMessage<Guild> for ChannelCreate {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
@ -51,15 +49,11 @@ impl UpdateMessage<Guild> for ChannelCreate {
fn update(&mut self, object_to_update: Shared<Guild>) { fn update(&mut self, object_to_update: Shared<Guild>) {
let mut write = object_to_update.write().unwrap(); let mut write = object_to_update.write().unwrap();
let update = self.channel.clone().into_shared(); let update = self.channel.clone().into_shared();
if write.channels.is_some() { write.channels.push(update);
write.channels.as_mut().unwrap().push(update);
} else {
write.channels = Some(Vec::from([update]));
}
} }
} }
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)] #[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#channel-update> /// See <https://discord.com/developers/docs/topics/gateway-events#channel-update>
pub struct ChannelUpdate { pub struct ChannelUpdate {
#[serde(flatten)] #[serde(flatten)]
@ -70,8 +64,6 @@ pub struct ChannelUpdate {
pub source_url: String, pub source_url: String,
} }
impl WebSocketEvent for ChannelUpdate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Channel> for ChannelUpdate { impl UpdateMessage<Channel> for ChannelUpdate {
fn update(&mut self, object_to_update: Shared<Channel>) { fn update(&mut self, object_to_update: Shared<Channel>) {
@ -85,7 +77,7 @@ impl UpdateMessage<Channel> for ChannelUpdate {
} }
} }
#[derive(Debug, Default, Deserialize, Serialize, Clone)] #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
/// Officially undocumented. /// Officially undocumented.
/// Sends updates to client about a new message with its id /// Sends updates to client about a new message with its id
/// {"channel_unread_updates": [{"id": "816412869766938648", "last_message_id": "1085892012085104680"}} /// {"channel_unread_updates": [{"id": "816412869766938648", "last_message_id": "1085892012085104680"}}
@ -100,12 +92,10 @@ pub struct ChannelUnreadUpdate {
pub struct ChannelUnreadUpdateObject { pub struct ChannelUnreadUpdateObject {
pub id: Snowflake, pub id: Snowflake,
pub last_message_id: Snowflake, pub last_message_id: Snowflake,
pub last_pin_timestamp: Option<String>, pub last_pin_timestamp: Option<DateTime<Utc>>,
} }
impl WebSocketEvent for ChannelUnreadUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)]
/// See <https://discord.com/developers/docs/topics/gateway-events#channel-delete> /// See <https://discord.com/developers/docs/topics/gateway-events#channel-delete>
pub struct ChannelDelete { pub struct ChannelDelete {
#[serde(flatten)] #[serde(flatten)]
@ -128,16 +118,15 @@ impl UpdateMessage<Guild> for ChannelDelete {
return; return;
} }
let mut write = object_to_update.write().unwrap(); let mut write = object_to_update.write().unwrap();
if write.channels.is_none() { if write.channels.is_empty() {
return; return;
} }
for (iteration, item) in (0_u32..).zip(write.channels.as_mut().unwrap().iter()) { for (iteration, item) in (0_u32..).zip(write.channels.iter()) {
if item.read().unwrap().id == self.id().unwrap() { if item.read().unwrap().id == self.id().unwrap() {
write.channels.as_mut().unwrap().remove(iteration as usize); write.channels.remove(iteration as usize);
return; return;
} }
} }
} }
} }
impl WebSocketEvent for ChannelDelete {}

View File

@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
use crate::types::entities::{Guild, PublicUser, UnavailableGuild}; use crate::types::entities::{Guild, PublicUser, UnavailableGuild};
use crate::types::events::WebSocketEvent; use crate::types::events::WebSocketEvent;
use crate::types::{ use crate::types::{
AuditLogEntry, Emoji, GuildMember, GuildScheduledEvent, IntoShared, JsonField, RoleObject, AuditLogEntry, Emoji, GuildMember, GuildScheduledEvent, JsonField, RoleObject,
Snowflake, SourceUrlField, Sticker, Snowflake, SourceUrlField, Sticker,
}; };
@ -18,9 +18,11 @@ use super::PresenceUpdate;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use super::UpdateMessage; use super::UpdateMessage;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::gateway::Shared; use crate::types::Shared;
#[cfg(feature = "client")]
use crate::types::IntoShared;
#[derive(Debug, Deserialize, Serialize, Default, Clone, SourceUrlField, JsonField)] #[derive(Debug, Deserialize, Serialize, Default, Clone, SourceUrlField, JsonField, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-create>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-create>;
/// Received to give data about a guild; /// Received to give data about a guild;
// This one is particularly painful, it can be a Guild object with an extra field or an unavailable guild object // This one is particularly painful, it can be a Guild object with an extra field or an unavailable guild object
@ -60,9 +62,7 @@ impl Default for GuildCreateDataOption {
} }
} }
impl WebSocketEvent for GuildCreate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-ban-add-guild-ban-add-event-fields>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-ban-add-guild-ban-add-event-fields>;
/// Received to give info about a user being banned from a guild; /// Received to give info about a user being banned from a guild;
pub struct GuildBanAdd { pub struct GuildBanAdd {
@ -70,9 +70,7 @@ pub struct GuildBanAdd {
pub user: PublicUser, pub user: PublicUser,
} }
impl WebSocketEvent for GuildBanAdd {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-ban-remove>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-ban-remove>;
/// Received to give info about a user being unbanned from a guild; /// Received to give info about a user being unbanned from a guild;
pub struct GuildBanRemove { pub struct GuildBanRemove {
@ -80,9 +78,7 @@ pub struct GuildBanRemove {
pub user: PublicUser, pub user: PublicUser,
} }
impl WebSocketEvent for GuildBanRemove {} #[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-update>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-update>;
/// Received to give info about a guild being updated; /// Received to give info about a guild being updated;
pub struct GuildUpdate { pub struct GuildUpdate {
@ -94,8 +90,6 @@ pub struct GuildUpdate {
pub json: String, pub json: String,
} }
impl WebSocketEvent for GuildUpdate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Guild> for GuildUpdate { impl UpdateMessage<Guild> for GuildUpdate {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
@ -104,7 +98,7 @@ impl UpdateMessage<Guild> for GuildUpdate {
} }
} }
#[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField)] #[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-delete>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-delete>;
/// Received to tell the client about a guild being deleted; /// Received to tell the client about a guild being deleted;
pub struct GuildDelete { pub struct GuildDelete {
@ -125,9 +119,7 @@ impl UpdateMessage<Guild> for GuildDelete {
fn update(&mut self, _: Shared<Guild>) {} fn update(&mut self, _: Shared<Guild>) {}
} }
impl WebSocketEvent for GuildDelete {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-audit-log-entry-create>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-audit-log-entry-create>;
/// Received to the client about an audit log entry being added; /// Received to the client about an audit log entry being added;
pub struct GuildAuditLogEntryCreate { pub struct GuildAuditLogEntryCreate {
@ -135,9 +127,7 @@ pub struct GuildAuditLogEntryCreate {
pub entry: AuditLogEntry, pub entry: AuditLogEntry,
} }
impl WebSocketEvent for GuildAuditLogEntryCreate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-emojis-update>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-emojis-update>;
/// Received to tell the client about a change to a guild's emoji list; /// Received to tell the client about a change to a guild's emoji list;
pub struct GuildEmojisUpdate { pub struct GuildEmojisUpdate {
@ -145,9 +135,7 @@ pub struct GuildEmojisUpdate {
pub emojis: Vec<Emoji>, pub emojis: Vec<Emoji>,
} }
impl WebSocketEvent for GuildEmojisUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-stickers-update>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-stickers-update>;
/// Received to tell the client about a change to a guild's sticker list; /// Received to tell the client about a change to a guild's sticker list;
pub struct GuildStickersUpdate { pub struct GuildStickersUpdate {
@ -155,17 +143,13 @@ pub struct GuildStickersUpdate {
pub stickers: Vec<Sticker>, pub stickers: Vec<Sticker>,
} }
impl WebSocketEvent for GuildStickersUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-integrations-update> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-integrations-update>
pub struct GuildIntegrationsUpdate { pub struct GuildIntegrationsUpdate {
pub guild_id: Snowflake, pub guild_id: Snowflake,
} }
impl WebSocketEvent for GuildIntegrationsUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-member-add>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-member-add>;
/// Received to tell the client about a user joining a guild; /// Received to tell the client about a user joining a guild;
pub struct GuildMemberAdd { pub struct GuildMemberAdd {
@ -174,9 +158,7 @@ pub struct GuildMemberAdd {
pub guild_id: Snowflake, pub guild_id: Snowflake,
} }
impl WebSocketEvent for GuildMemberAdd {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-member-remove>; /// See <https://discord.com/developers/docs/topics/gateway-events#guild-member-remove>;
/// Received to tell the client about a user leaving a guild; /// Received to tell the client about a user leaving a guild;
pub struct GuildMemberRemove { pub struct GuildMemberRemove {
@ -184,9 +166,7 @@ pub struct GuildMemberRemove {
pub user: PublicUser, pub user: PublicUser,
} }
impl WebSocketEvent for GuildMemberRemove {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-member-update> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-member-update>
pub struct GuildMemberUpdate { pub struct GuildMemberUpdate {
pub guild_id: Snowflake, pub guild_id: Snowflake,
@ -202,9 +182,7 @@ pub struct GuildMemberUpdate {
pub communication_disabled_until: Option<DateTime<Utc>>, pub communication_disabled_until: Option<DateTime<Utc>>,
} }
impl WebSocketEvent for GuildMemberUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-members-chunk> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-members-chunk>
pub struct GuildMembersChunk { pub struct GuildMembersChunk {
pub guild_id: Snowflake, pub guild_id: Snowflake,
@ -216,9 +194,7 @@ pub struct GuildMembersChunk {
pub nonce: Option<String>, pub nonce: Option<String>,
} }
impl WebSocketEvent for GuildMembersChunk {} #[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-role-create> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-role-create>
pub struct GuildRoleCreate { pub struct GuildRoleCreate {
pub guild_id: Snowflake, pub guild_id: Snowflake,
@ -229,8 +205,6 @@ pub struct GuildRoleCreate {
pub source_url: String, pub source_url: String,
} }
impl WebSocketEvent for GuildRoleCreate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Guild> for GuildRoleCreate { impl UpdateMessage<Guild> for GuildRoleCreate {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
@ -240,19 +214,13 @@ impl UpdateMessage<Guild> for GuildRoleCreate {
fn update(&mut self, object_to_update: Shared<Guild>) { fn update(&mut self, object_to_update: Shared<Guild>) {
let mut object_to_update = object_to_update.write().unwrap(); let mut object_to_update = object_to_update.write().unwrap();
if object_to_update.roles.is_some() { object_to_update
object_to_update .roles
.roles .push(self.role.clone().into_shared());
.as_mut()
.unwrap()
.push(self.role.clone().into_shared());
} else {
object_to_update.roles = Some(Vec::from([self.role.clone().into_shared()]));
}
} }
} }
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)] #[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-role-update> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-role-update>
pub struct GuildRoleUpdate { pub struct GuildRoleUpdate {
pub guild_id: Snowflake, pub guild_id: Snowflake,
@ -263,8 +231,6 @@ pub struct GuildRoleUpdate {
pub source_url: String, pub source_url: String,
} }
impl WebSocketEvent for GuildRoleUpdate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<RoleObject> for GuildRoleUpdate { impl UpdateMessage<RoleObject> for GuildRoleUpdate {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
@ -278,43 +244,35 @@ impl UpdateMessage<RoleObject> for GuildRoleUpdate {
} }
} }
#[derive(Debug, Default, Deserialize, Serialize, Clone)] #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-role-delete> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-role-delete>
pub struct GuildRoleDelete { pub struct GuildRoleDelete {
pub guild_id: Snowflake, pub guild_id: Snowflake,
pub role_id: Snowflake, pub role_id: Snowflake,
} }
impl WebSocketEvent for GuildRoleDelete {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-create> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-create>
pub struct GuildScheduledEventCreate { pub struct GuildScheduledEventCreate {
#[serde(flatten)] #[serde(flatten)]
pub event: GuildScheduledEvent, pub event: GuildScheduledEvent,
} }
impl WebSocketEvent for GuildScheduledEventCreate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-update> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-update>
pub struct GuildScheduledEventUpdate { pub struct GuildScheduledEventUpdate {
#[serde(flatten)] #[serde(flatten)]
pub event: GuildScheduledEvent, pub event: GuildScheduledEvent,
} }
impl WebSocketEvent for GuildScheduledEventUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-delete> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-delete>
pub struct GuildScheduledEventDelete { pub struct GuildScheduledEventDelete {
#[serde(flatten)] #[serde(flatten)]
pub event: GuildScheduledEvent, pub event: GuildScheduledEvent,
} }
impl WebSocketEvent for GuildScheduledEventDelete {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-add> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-add>
pub struct GuildScheduledEventUserAdd { pub struct GuildScheduledEventUserAdd {
pub guild_scheduled_event_id: Snowflake, pub guild_scheduled_event_id: Snowflake,
@ -322,14 +280,10 @@ pub struct GuildScheduledEventUserAdd {
pub guild_id: Snowflake, pub guild_id: Snowflake,
} }
impl WebSocketEvent for GuildScheduledEventUserAdd {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-remove> /// See <https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-remove>
pub struct GuildScheduledEventUserRemove { pub struct GuildScheduledEventUserRemove {
pub guild_scheduled_event_id: Snowflake, pub guild_scheduled_event_id: Snowflake,
pub user_id: Snowflake, pub user_id: Snowflake,
pub guild_id: Snowflake, pub guild_id: Snowflake,
} }
impl WebSocketEvent for GuildScheduledEventUserRemove {}

View File

@ -5,17 +5,14 @@
use crate::types::events::WebSocketEvent; use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize, WebSocketEvent)]
pub struct GatewayHeartbeat { pub struct GatewayHeartbeat {
pub op: u8, pub op: u8,
pub d: Option<u64>, pub d: Option<u64>,
} }
impl WebSocketEvent for GatewayHeartbeat {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
pub struct GatewayHeartbeatAck { pub struct GatewayHeartbeatAck {
pub op: i32, pub op: i32,
} }
impl WebSocketEvent for GatewayHeartbeatAck {}

View File

@ -3,22 +3,20 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::WebSocketEvent; use crate::types::WebSocketEvent;
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Received on gateway init, tells the client how often to send heartbeats; /// Received on gateway init, tells the client how often to send heartbeats;
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, WebSocketEvent)]
pub struct GatewayHello { pub struct GatewayHello {
pub op: i32, pub op: i32,
pub d: HelloData, pub d: HelloData,
} }
impl WebSocketEvent for GatewayHello {} #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, Copy, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, Copy)]
/// Contains info on how often the client should send heartbeats to the server; /// Contains info on how often the client should send heartbeats to the server;
pub struct HelloData { pub struct HelloData {
/// How often a client should send heartbeats, in milliseconds /// How often a client should send heartbeats, in milliseconds
pub heartbeat_interval: u64, pub heartbeat_interval: u64,
} }
impl WebSocketEvent for HelloData {}

View File

@ -6,7 +6,7 @@ use crate::types::events::{PresenceUpdate, WebSocketEvent};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::serde_as; use serde_with::serde_as;
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, WebSocketEvent)]
pub struct GatewayIdentifyPayload { pub struct GatewayIdentifyPayload {
pub token: String, pub token: String,
pub properties: GatewayIdentifyConnectionProps, pub properties: GatewayIdentifyConnectionProps,
@ -70,9 +70,7 @@ impl GatewayIdentifyPayload {
} }
} }
impl WebSocketEvent for GatewayIdentifyPayload {} #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[serde_as] #[serde_as]
pub struct GatewayIdentifyConnectionProps { pub struct GatewayIdentifyConnectionProps {
/// Almost always sent /// Almost always sent

View File

@ -5,8 +5,9 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{Integration, Snowflake, WebSocketEvent}; use crate::types::{Integration, Snowflake, WebSocketEvent};
use chorus_macros::WebSocketEvent;
#[derive(Debug, Default, Deserialize, Serialize, Clone)] #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#integration-create> /// See <https://discord.com/developers/docs/topics/gateway-events#integration-create>
pub struct IntegrationCreate { pub struct IntegrationCreate {
#[serde(flatten)] #[serde(flatten)]
@ -14,9 +15,7 @@ pub struct IntegrationCreate {
pub guild_id: Snowflake, pub guild_id: Snowflake,
} }
impl WebSocketEvent for IntegrationCreate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#integration-update> /// See <https://discord.com/developers/docs/topics/gateway-events#integration-update>
pub struct IntegrationUpdate { pub struct IntegrationUpdate {
#[serde(flatten)] #[serde(flatten)]
@ -24,9 +23,7 @@ pub struct IntegrationUpdate {
pub guild_id: Snowflake, pub guild_id: Snowflake,
} }
impl WebSocketEvent for IntegrationUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#integration-delete> /// See <https://discord.com/developers/docs/topics/gateway-events#integration-delete>
pub struct IntegrationDelete { pub struct IntegrationDelete {
pub id: Snowflake, pub id: Snowflake,
@ -34,4 +31,3 @@ pub struct IntegrationDelete {
pub application_id: Option<Snowflake>, pub application_id: Option<Snowflake>,
} }
impl WebSocketEvent for IntegrationDelete {}

View File

@ -5,12 +5,12 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{Interaction, WebSocketEvent}; use crate::types::{Interaction, WebSocketEvent};
use chorus_macros::WebSocketEvent;
#[derive(Debug, Default, Deserialize, Serialize, Clone)] #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#interaction-create> /// See <https://discord.com/developers/docs/topics/gateway-events#interaction-create>
pub struct InteractionCreate { pub struct InteractionCreate {
#[serde(flatten)] #[serde(flatten)]
pub interaction: Interaction, pub interaction: Interaction,
} }
impl WebSocketEvent for InteractionCreate {}

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use super::WebSocketEvent; use super::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// Your session is now invalid. /// Your session is now invalid.
/// ///
/// Either reauthenticate and reidentify or resume if possible. /// Either reauthenticate and reidentify or resume if possible.
@ -14,4 +14,3 @@ pub struct GatewayInvalidSession {
pub resumable: bool, pub resumable: bool,
} }
impl WebSocketEvent for GatewayInvalidSession {}

View File

@ -5,17 +5,16 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{GuildInvite, Snowflake, WebSocketEvent}; use crate::types::{GuildInvite, Snowflake, WebSocketEvent};
use chorus_macros::WebSocketEvent;
#[derive(Debug, Default, Deserialize, Serialize, Clone)] #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#invite-create> /// See <https://discord.com/developers/docs/topics/gateway-events#invite-create>
pub struct InviteCreate { pub struct InviteCreate {
#[serde(flatten)] #[serde(flatten)]
pub invite: GuildInvite, pub invite: GuildInvite,
} }
impl WebSocketEvent for InviteCreate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#invite-delete> /// See <https://discord.com/developers/docs/topics/gateway-events#invite-delete>
pub struct InviteDelete { pub struct InviteDelete {
pub channel_id: Snowflake, pub channel_id: Snowflake,
@ -23,4 +22,3 @@ pub struct InviteDelete {
pub code: String, pub code: String,
} }
impl WebSocketEvent for InviteDelete {}

View File

@ -7,10 +7,9 @@ use std::collections::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::Snowflake; use crate::types::Snowflake;
use super::WebSocketEvent; use super::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// Officially Undocumented /// Officially Undocumented
/// ///
/// Sent to the server to signify lazy loading of a guild; /// Sent to the server to signify lazy loading of a guild;
@ -31,4 +30,3 @@ pub struct LazyRequest {
pub channels: Option<HashMap<String, Vec<Vec<u64>>>>, pub channels: Option<HashMap<String, Vec<Vec<u64>>>>,
} }
impl WebSocketEvent for LazyRequest {}

View File

@ -6,12 +6,12 @@ use serde::{Deserialize, Serialize};
use crate::types::{ use crate::types::{
entities::{Emoji, GuildMember, Message, PublicUser}, entities::{Emoji, GuildMember, Message, PublicUser},
Snowflake, Snowflake, WebSocketEvent
}; };
use super::WebSocketEvent; use chorus_macros::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/topics/gateway-events#typing-start> /// See <https://discord.com/developers/docs/topics/gateway-events#typing-start>
pub struct TypingStartEvent { pub struct TypingStartEvent {
@ -22,9 +22,7 @@ pub struct TypingStartEvent {
pub member: Option<GuildMember>, pub member: Option<GuildMember>,
} }
impl WebSocketEvent for TypingStartEvent {} #[derive(Debug, Serialize, Deserialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#message-create> /// See <https://discord.com/developers/docs/topics/gateway-events#message-create>
pub struct MessageCreate { pub struct MessageCreate {
#[serde(flatten)] #[serde(flatten)]
@ -34,7 +32,7 @@ pub struct MessageCreate {
pub mentions: Option<Vec<MessageCreateUser>>, pub mentions: Option<Vec<MessageCreateUser>>,
} }
#[derive(Debug, Serialize, Deserialize, Default, Clone)] #[derive(Debug, Serialize, Deserialize, Default, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#message-create-message-create-extra-fields> /// See <https://discord.com/developers/docs/topics/gateway-events#message-create-message-create-extra-fields>
pub struct MessageCreateUser { pub struct MessageCreateUser {
#[serde(flatten)] #[serde(flatten)]
@ -42,9 +40,7 @@ pub struct MessageCreateUser {
pub member: Option<GuildMember>, pub member: Option<GuildMember>,
} }
impl WebSocketEvent for MessageCreate {} #[derive(Debug, Serialize, Deserialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/topics/gateway-events#message-update> /// See <https://discord.com/developers/docs/topics/gateway-events#message-update>
pub struct MessageUpdate { pub struct MessageUpdate {
@ -55,9 +51,7 @@ pub struct MessageUpdate {
pub mentions: Option<Vec<MessageCreateUser>>, pub mentions: Option<Vec<MessageCreateUser>>,
} }
impl WebSocketEvent for MessageUpdate {} #[derive(Debug, Serialize, Deserialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/topics/gateway-events#message-delete> /// See <https://discord.com/developers/docs/topics/gateway-events#message-delete>
pub struct MessageDelete { pub struct MessageDelete {
@ -66,9 +60,7 @@ pub struct MessageDelete {
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
} }
impl WebSocketEvent for MessageDelete {} #[derive(Debug, Serialize, Deserialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/topics/gateway-events#message-delete-bulk> /// See <https://discord.com/developers/docs/topics/gateway-events#message-delete-bulk>
pub struct MessageDeleteBulk { pub struct MessageDeleteBulk {
@ -77,9 +69,7 @@ pub struct MessageDeleteBulk {
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
} }
impl WebSocketEvent for MessageDeleteBulk {} #[derive(Debug, Serialize, Deserialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-add> /// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-add>
pub struct MessageReactionAdd { pub struct MessageReactionAdd {
@ -91,9 +81,7 @@ pub struct MessageReactionAdd {
pub emoji: Emoji, pub emoji: Emoji,
} }
impl WebSocketEvent for MessageReactionAdd {} #[derive(Debug, Serialize, Deserialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove> /// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove>
pub struct MessageReactionRemove { pub struct MessageReactionRemove {
@ -104,9 +92,7 @@ pub struct MessageReactionRemove {
pub emoji: Emoji, pub emoji: Emoji,
} }
impl WebSocketEvent for MessageReactionRemove {} #[derive(Debug, Serialize, Deserialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-all> /// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-all>
pub struct MessageReactionRemoveAll { pub struct MessageReactionRemoveAll {
@ -115,9 +101,7 @@ pub struct MessageReactionRemoveAll {
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
} }
impl WebSocketEvent for MessageReactionRemoveAll {} #[derive(Debug, Serialize, Deserialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
/// # Reference /// # Reference
/// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-emoji> /// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-emoji>
pub struct MessageReactionRemoveEmoji { pub struct MessageReactionRemoveEmoji {
@ -127,9 +111,7 @@ pub struct MessageReactionRemoveEmoji {
pub emoji: Emoji, pub emoji: Emoji,
} }
impl WebSocketEvent for MessageReactionRemoveEmoji {} #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// Officially Undocumented /// Officially Undocumented
/// ///
/// Not documented anywhere unofficially /// Not documented anywhere unofficially
@ -139,8 +121,8 @@ impl WebSocketEvent for MessageReactionRemoveEmoji {}
/// ///
/// {"t":"MESSAGE_ACK","s":3,"op":0,"d":{"version":52,"message_id":"1107236673638633472","last_viewed":null,"flags":null,"channel_id":"967363950217936897"}} /// {"t":"MESSAGE_ACK","s":3,"op":0,"d":{"version":52,"message_id":"1107236673638633472","last_viewed":null,"flags":null,"channel_id":"967363950217936897"}}
pub struct MessageACK { pub struct MessageACK {
/// ? // No ideas. See 206933
pub version: u16, pub version: u32,
pub message_id: Snowflake, pub message_id: Snowflake,
/// This is an integer??? /// This is an integer???
/// Not even unix, see '3070'??? /// Not even unix, see '3070'???
@ -150,4 +132,3 @@ pub struct MessageACK {
pub channel_id: Snowflake, pub channel_id: Snowflake,
} }
impl WebSocketEvent for MessageACK {}

View File

@ -33,6 +33,8 @@ pub use voice::*;
pub use voice_gateway::*; pub use voice_gateway::*;
pub use webhooks::*; pub use webhooks::*;
use chorus_macros::WebSocketEvent;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use super::Snowflake; use super::Snowflake;
@ -46,7 +48,7 @@ use serde_json::{from_str, from_value, to_value, Value};
use std::collections::HashMap; use std::collections::HashMap;
#[cfg(feature = "client")] #[cfg(feature = "client")]
use crate::gateway::Shared; use crate::types::Shared;
use std::fmt::Debug; use std::fmt::Debug;
#[cfg(feature = "client")] #[cfg(feature = "client")]
@ -84,7 +86,7 @@ mod voice_gateway;
pub trait WebSocketEvent: Send + Sync + Debug {} pub trait WebSocketEvent: Send + Sync + Debug {}
#[derive(Debug, Default, Serialize, Clone)] #[derive(Debug, Default, Serialize, Clone, WebSocketEvent)]
/// The payload used for sending events to the gateway /// The payload used for sending events to the gateway
/// ///
/// Similar to [GatewayReceivePayload], except we send a [serde_json::value::Value] for d whilst we receive a [serde_json::value::RawValue] /// Similar to [GatewayReceivePayload], except we send a [serde_json::value::Value] for d whilst we receive a [serde_json::value::RawValue]
@ -102,8 +104,6 @@ pub struct GatewaySendPayload {
pub sequence_number: Option<u64>, pub sequence_number: Option<u64>,
} }
impl WebSocketEvent for GatewaySendPayload {}
#[derive(Debug, Default, Deserialize, Clone)] #[derive(Debug, Default, Deserialize, Clone)]
/// The payload used for receiving events from the gateway /// The payload used for receiving events from the gateway
pub struct GatewayReceivePayload<'a> { pub struct GatewayReceivePayload<'a> {

View File

@ -7,15 +7,17 @@ use serde::{Deserialize, Serialize};
use super::{ChannelUnreadUpdateObject, WebSocketEvent}; use super::{ChannelUnreadUpdateObject, WebSocketEvent};
use crate::types::{GuildMember, Snowflake, VoiceState}; use crate::types::{GuildMember, Snowflake, VoiceState};
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize, Default, WebSocketEvent)]
/// Officially Undocumented /// Officially Undocumented
/// ///
/// Seems to be passively set to update the client on guild details (though, why not just send the update events?) /// Seems to be passively set to update the client on guild details (though, why not just send the update events?)
pub struct PassiveUpdateV1 { pub struct PassiveUpdateV1 {
#[serde(default)]
pub voice_states: Vec<VoiceState>, pub voice_states: Vec<VoiceState>,
pub members: Option<Vec<GuildMember>>, #[serde(default)]
pub members: Vec<GuildMember>,
pub guild_id: Snowflake, pub guild_id: Snowflake,
#[serde(default)]
pub channels: Vec<ChannelUnreadUpdateObject>, pub channels: Vec<ChannelUnreadUpdateObject>,
} }
impl WebSocketEvent for PassiveUpdateV1 {}

View File

@ -6,7 +6,7 @@ use crate::types::{events::WebSocketEvent, UserStatus};
use crate::types::{Activity, ClientStatusObject, PublicUser, Snowflake}; use crate::types::{Activity, ClientStatusObject, PublicUser, Snowflake};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// Sent by the client to update its status and presence; /// Sent by the client to update its status and presence;
/// See <https://discord.com/developers/docs/topics/gateway-events#update-presence> /// See <https://discord.com/developers/docs/topics/gateway-events#update-presence>
pub struct UpdatePresence { pub struct UpdatePresence {
@ -18,16 +18,18 @@ pub struct UpdatePresence {
pub afk: bool, pub afk: bool,
} }
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq)] #[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, WebSocketEvent)]
/// Received to tell the client that a user updated their presence / status /// Received to tell the client that a user updated their presence / status
///
/// See <https://discord.com/developers/docs/topics/gateway-events#presence-update-presence-update-event-fields> /// See <https://discord.com/developers/docs/topics/gateway-events#presence-update-presence-update-event-fields>
/// (Same structure as <https://docs.discord.sex/resources/presence#presence-object>)
pub struct PresenceUpdate { pub struct PresenceUpdate {
pub user: PublicUser, pub user: PublicUser,
#[serde(default)] #[serde(default)]
pub guild_id: Option<Snowflake>, pub guild_id: Option<Snowflake>,
pub status: UserStatus, pub status: UserStatus,
#[serde(default)]
pub activities: Vec<Activity>, pub activities: Vec<Activity>,
pub client_status: ClientStatusObject, pub client_status: ClientStatusObject,
} }
impl WebSocketEvent for PresenceUpdate {}

View File

@ -6,13 +6,14 @@ use serde::{Deserialize, Serialize};
use crate::types::entities::{Guild, User}; use crate::types::entities::{Guild, User};
use crate::types::events::{Session, WebSocketEvent}; use crate::types::events::{Session, WebSocketEvent};
use crate::types::interfaces::ClientStatusObject; use crate::types::{Activity, Channel, ClientStatusObject, GuildMember, PresenceUpdate, Snowflake, VoiceState};
use crate::types::{Activity, GuildMember, PresenceUpdate, VoiceState};
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// 1/2 half documented; /// 1/2 officially documented;
/// Received after identifying, provides initial user info; /// Received after identifying, provides initial user info;
/// See <https://discord.com/developers/docs/topics/gateway-events#ready;> ///
/// See <https://docs.discord.sex/topics/gateway-events#ready> and <https://discord.com/developers/docs/topics/gateway-events#ready>
// TODO: There are a LOT of fields missing here
pub struct GatewayReady { pub struct GatewayReady {
pub analytics_token: Option<String>, pub analytics_token: Option<String>,
pub auth_session_id_hash: Option<String>, pub auth_session_id_hash: Option<String>,
@ -30,42 +31,49 @@ pub struct GatewayReady {
pub shard: Option<(u64, u64)>, pub shard: Option<(u64, u64)>,
} }
impl WebSocketEvent for GatewayReady {} #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// Officially Undocumented; /// Officially Undocumented;
/// Sent after the READY event when a client is a user, seems to somehow add onto the ready event; /// Sent after the READY event when a client is a user,
/// seems to somehow add onto the ready event;
///
/// See <https://docs.discord.sex/topics/gateway-events#ready-supplemental>
pub struct GatewayReadySupplemental { pub struct GatewayReadySupplemental {
/// The presences of the user's relationships and guild presences sent at startup
pub merged_presences: MergedPresences, pub merged_presences: MergedPresences,
pub merged_members: Vec<Vec<GuildMember>>, pub merged_members: Vec<Vec<GuildMember>>,
// ? pub lazy_private_channels: Vec<Channel>,
pub lazy_private_channels: Vec<serde_json::Value>,
pub guilds: Vec<SupplementalGuild>, pub guilds: Vec<SupplementalGuild>,
// ? pomelo // "Upcoming changes that the client should disclose to the user" (discord.sex)
pub disclose: Vec<String>, pub disclose: Vec<String>,
} }
impl WebSocketEvent for GatewayReadySupplemental {}
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See <https://docs.discord.sex/topics/gateway-events#merged-presences-structure>
pub struct MergedPresences { pub struct MergedPresences {
/// "Presences of the user's guilds in the same order as the guilds array in ready"
/// (discord.sex)
pub guilds: Vec<Vec<MergedPresenceGuild>>, pub guilds: Vec<Vec<MergedPresenceGuild>>,
/// "Presences of the user's friends and implicit relationships" (discord.sex)
pub friends: Vec<MergedPresenceFriend>, pub friends: Vec<MergedPresenceFriend>,
} }
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// Not documented even unofficially
pub struct MergedPresenceFriend { pub struct MergedPresenceFriend {
pub user_id: String, pub user_id: Snowflake,
pub status: String, pub status: String,
/// Looks like ms?? // Looks like ms??
pub last_modified: u128, //
// Not always sent
pub last_modified: Option<u128>,
pub client_status: ClientStatusObject, pub client_status: ClientStatusObject,
pub activities: Vec<Activity>, pub activities: Vec<Activity>,
} }
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// Not documented even unofficially
pub struct MergedPresenceGuild { pub struct MergedPresenceGuild {
pub user_id: String, pub user_id: Snowflake,
pub status: String, pub status: String,
// ? // ?
pub game: Option<serde_json::Value>, pub game: Option<serde_json::Value>,
@ -74,8 +82,10 @@ pub struct MergedPresenceGuild {
} }
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See <https://docs.discord.sex/topics/gateway-events#supplemental-guild-structure>
pub struct SupplementalGuild { pub struct SupplementalGuild {
pub id: Snowflake,
pub voice_states: Option<Vec<VoiceState>>, pub voice_states: Option<Vec<VoiceState>>,
pub id: String, /// Field not documented even unofficially
pub embedded_activities: Vec<serde_json::Value>, pub embedded_activities: Vec<serde_json::Value>,
} }

View File

@ -2,11 +2,10 @@ use serde::{Deserialize, Serialize};
use super::WebSocketEvent; use super::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// "The reconnect event is dispatched when a client should reconnect to the Gateway (and resume their existing session, if they have one). This event usually occurs during deploys to migrate sessions gracefully off old hosts" /// "The reconnect event is dispatched when a client should reconnect to the Gateway (and resume their existing session, if they have one). This event usually occurs during deploys to migrate sessions gracefully off old hosts"
/// ///
/// # Reference /// # Reference
/// See <https://docs.discord.sex/topics/gateway-events#reconnect> /// See <https://docs.discord.sex/topics/gateway-events#reconnect>
pub struct GatewayReconnect {} pub struct GatewayReconnect {}
impl WebSocketEvent for GatewayReconnect {}

View File

@ -5,7 +5,7 @@
use crate::types::{events::WebSocketEvent, Relationship, RelationshipType, Snowflake}; use crate::types::{events::WebSocketEvent, Relationship, RelationshipType, Snowflake};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize, Default, WebSocketEvent)]
/// See <https://github.com/spacebarchat/server/issues/204> /// See <https://github.com/spacebarchat/server/issues/204>
pub struct RelationshipAdd { pub struct RelationshipAdd {
#[serde(flatten)] #[serde(flatten)]
@ -13,9 +13,7 @@ pub struct RelationshipAdd {
pub should_notify: bool, pub should_notify: bool,
} }
impl WebSocketEvent for RelationshipAdd {} #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See <https://github.com/spacebarchat/server/issues/203> /// See <https://github.com/spacebarchat/server/issues/203>
pub struct RelationshipRemove { pub struct RelationshipRemove {
pub id: Snowflake, pub id: Snowflake,
@ -23,4 +21,3 @@ pub struct RelationshipRemove {
pub relationship_type: RelationshipType, pub relationship_type: RelationshipType,
} }
impl WebSocketEvent for RelationshipRemove {}

View File

@ -5,7 +5,7 @@
use crate::types::{events::WebSocketEvent, Snowflake}; use crate::types::{events::WebSocketEvent, Snowflake};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)] #[derive(Debug, Deserialize, Serialize, Default, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#request-guild-members-request-guild-members-structure> /// See <https://discord.com/developers/docs/topics/gateway-events#request-guild-members-request-guild-members-structure>
pub struct GatewayRequestGuildMembers { pub struct GatewayRequestGuildMembers {
pub guild_id: Snowflake, pub guild_id: Snowflake,
@ -17,4 +17,3 @@ pub struct GatewayRequestGuildMembers {
pub nonce: Option<String>, pub nonce: Option<String>,
} }
impl WebSocketEvent for GatewayRequestGuildMembers {}

View File

@ -5,11 +5,10 @@
use crate::types::events::WebSocketEvent; use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize, Default)] #[derive(Debug, Clone, Deserialize, Serialize, Default, WebSocketEvent)]
pub struct GatewayResume { pub struct GatewayResume {
pub token: String, pub token: String,
pub session_id: String, pub session_id: String,
pub seq: String, pub seq: String,
} }
impl WebSocketEvent for GatewayResume {}

View File

@ -2,11 +2,12 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{Activity, WebSocketEvent}; use crate::types::{Activity, WebSocketEvent};
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// Officially Undocumented /// Officially Undocumented
/// Seems like it sends active session info to users on connect /// Seems like it sends active session info to users on connect
/// [{"activities":[],"client_info":{"client":"web","os":"other","version":0},"session_id":"ab5941b50d818b1f8d93b4b1b581b192","status":"online"}] /// [{"activities":[],"client_info":{"client":"web","os":"other","version":0},"session_id":"ab5941b50d818b1f8d93b4b1b581b192","status":"online"}]
@ -33,4 +34,3 @@ pub struct ClientInfo {
pub version: u8, pub version: u8,
} }
impl WebSocketEvent for SessionsReplace {}

View File

@ -5,30 +5,26 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{StageInstance, WebSocketEvent}; use crate::types::{StageInstance, WebSocketEvent};
use chorus_macros::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default, Clone)] #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#stage-instance-create> /// See <https://discord.com/developers/docs/topics/gateway-events#stage-instance-create>
pub struct StageInstanceCreate { pub struct StageInstanceCreate {
#[serde(flatten)] #[serde(flatten)]
pub stage_instance: StageInstance, pub stage_instance: StageInstance,
} }
impl WebSocketEvent for StageInstanceCreate {} #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#stage-instance-update> /// See <https://discord.com/developers/docs/topics/gateway-events#stage-instance-update>
pub struct StageInstanceUpdate { pub struct StageInstanceUpdate {
#[serde(flatten)] #[serde(flatten)]
pub stage_instance: StageInstance, pub stage_instance: StageInstance,
} }
impl WebSocketEvent for StageInstanceUpdate {} #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#stage-instance-delete> /// See <https://discord.com/developers/docs/topics/gateway-events#stage-instance-delete>
pub struct StageInstanceDelete { pub struct StageInstanceDelete {
#[serde(flatten)] #[serde(flatten)]
pub stage_instance: StageInstance, pub stage_instance: StageInstance,
} }
impl WebSocketEvent for StageInstanceDelete {}

View File

@ -12,16 +12,14 @@ use crate::types::{JsonField, Snowflake, SourceUrlField};
#[cfg(feature = "client")] #[cfg(feature = "client")]
use super::UpdateMessage; use super::UpdateMessage;
#[derive(Debug, Default, Deserialize, Serialize, Clone)] #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-create> /// See <https://discord.com/developers/docs/topics/gateway-events#thread-create>
pub struct ThreadCreate { pub struct ThreadCreate {
#[serde(flatten)] #[serde(flatten)]
pub thread: Channel, pub thread: Channel,
} }
impl WebSocketEvent for ThreadCreate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)]
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-update> /// See <https://discord.com/developers/docs/topics/gateway-events#thread-update>
pub struct ThreadUpdate { pub struct ThreadUpdate {
#[serde(flatten)] #[serde(flatten)]
@ -32,8 +30,6 @@ pub struct ThreadUpdate {
pub source_url: String, pub source_url: String,
} }
impl WebSocketEvent for ThreadUpdate {}
#[cfg(feature = "client")] #[cfg(feature = "client")]
impl UpdateMessage<Channel> for ThreadUpdate { impl UpdateMessage<Channel> for ThreadUpdate {
#[cfg(not(tarpaulin_include))] #[cfg(not(tarpaulin_include))]
@ -42,16 +38,14 @@ impl UpdateMessage<Channel> for ThreadUpdate {
} }
} }
#[derive(Debug, Default, Deserialize, Serialize, Clone)] #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-delete> /// See <https://discord.com/developers/docs/topics/gateway-events#thread-delete>
pub struct ThreadDelete { pub struct ThreadDelete {
#[serde(flatten)] #[serde(flatten)]
pub thread: Channel, pub thread: Channel,
} }
impl WebSocketEvent for ThreadDelete {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-list-sync> /// See <https://discord.com/developers/docs/topics/gateway-events#thread-list-sync>
pub struct ThreadListSync { pub struct ThreadListSync {
pub guild_id: Snowflake, pub guild_id: Snowflake,
@ -60,9 +54,7 @@ pub struct ThreadListSync {
pub members: Option<Vec<ThreadMember>>, pub members: Option<Vec<ThreadMember>>,
} }
impl WebSocketEvent for ThreadListSync {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-member-update> /// See <https://discord.com/developers/docs/topics/gateway-events#thread-member-update>
/// The inner payload is a thread member object with an extra field. /// The inner payload is a thread member object with an extra field.
pub struct ThreadMemberUpdate { pub struct ThreadMemberUpdate {
@ -71,9 +63,7 @@ pub struct ThreadMemberUpdate {
pub guild_id: Snowflake, pub guild_id: Snowflake,
} }
impl WebSocketEvent for ThreadMemberUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-members-update> /// See <https://discord.com/developers/docs/topics/gateway-events#thread-members-update>
pub struct ThreadMembersUpdate { pub struct ThreadMembersUpdate {
pub id: Snowflake, pub id: Snowflake,
@ -84,4 +74,3 @@ pub struct ThreadMembersUpdate {
pub removed_members: Option<Vec<Snowflake>>, pub removed_members: Option<Vec<Snowflake>>,
} }
impl WebSocketEvent for ThreadMembersUpdate {}

View File

@ -8,7 +8,7 @@ use crate::types::entities::PublicUser;
use crate::types::events::WebSocketEvent; use crate::types::events::WebSocketEvent;
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, WebSocketEvent)]
/// See <https://discord.com/developers/docs/topics/gateway-events#user-update>; /// See <https://discord.com/developers/docs/topics/gateway-events#user-update>;
/// Sent to indicate updates to a user object; (name changes, discriminator changes, etc); /// Sent to indicate updates to a user object; (name changes, discriminator changes, etc);
pub struct UserUpdate { pub struct UserUpdate {
@ -16,9 +16,7 @@ pub struct UserUpdate {
pub user: PublicUser, pub user: PublicUser,
} }
impl WebSocketEvent for UserUpdate {} #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
/// Undocumented; /// Undocumented;
/// ///
/// Possibly an update for muted guild / channel settings for the current user; /// Possibly an update for muted guild / channel settings for the current user;
@ -41,8 +39,6 @@ pub struct UserGuildSettingsUpdate {
pub channel_overrides: Vec<UserGuildSettingsChannelOverride>, pub channel_overrides: Vec<UserGuildSettingsChannelOverride>,
} }
impl WebSocketEvent for UserGuildSettingsUpdate {}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
/// Undocumented; /// Undocumented;
/// ///

View File

@ -5,7 +5,7 @@
use crate::types::{events::WebSocketEvent, Snowflake, VoiceState}; use crate::types::{events::WebSocketEvent, Snowflake, VoiceState};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Deserialize, Serialize, Default, Clone, Copy, PartialEq, Eq, WebSocketEvent)]
/// ///
/// Sent to the server to indicate an update of the voice state (leave voice channel, join voice channel, mute, deafen); /// Sent to the server to indicate an update of the voice state (leave voice channel, join voice channel, mute, deafen);
/// ///
@ -17,9 +17,7 @@ pub struct UpdateVoiceState {
pub self_deaf: bool, pub self_deaf: bool,
} }
impl WebSocketEvent for UpdateVoiceState {} #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#voice-state-update>; /// See <https://discord.com/developers/docs/topics/gateway-events#voice-state-update>;
/// ///
/// Received from the server to indicate an update in a user's voice state (leave voice channel, join voice channel, mute, deafen, etc); /// Received from the server to indicate an update in a user's voice state (leave voice channel, join voice channel, mute, deafen, etc);
@ -30,9 +28,7 @@ pub struct VoiceStateUpdate {
pub state: VoiceState, pub state: VoiceState,
} }
impl WebSocketEvent for VoiceStateUpdate {} #[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
/// See <https://discord.com/developers/docs/topics/gateway-events#voice-server-update>; /// See <https://discord.com/developers/docs/topics/gateway-events#voice-server-update>;
/// ///
/// Received to indicate which voice endpoint, token and guild_id to use; /// Received to indicate which voice endpoint, token and guild_id to use;
@ -45,4 +41,3 @@ pub struct VoiceServerUpdate {
pub endpoint: Option<String>, pub endpoint: Option<String>,
} }
impl WebSocketEvent for VoiceServerUpdate {}

View File

@ -3,9 +3,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::{Snowflake, WebSocketEvent}; use crate::types::{Snowflake, WebSocketEvent};
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy, WebSocketEvent)]
/// Sent when another user connects to the voice server. /// Sent when another user connects to the voice server.
/// ///
/// Contains the user id and "flags". /// Contains the user id and "flags".
@ -21,9 +22,7 @@ pub struct VoiceClientConnectFlags {
pub flags: Option<u8>, pub flags: Option<u8>,
} }
impl WebSocketEvent for VoiceClientConnectFlags {} #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy, WebSocketEvent)]
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy)]
/// Sent when another user connects to the voice server. /// Sent when another user connects to the voice server.
/// ///
/// Contains the user id and "platform". /// Contains the user id and "platform".
@ -37,4 +36,3 @@ pub struct VoiceClientConnectPlatform {
pub platform: u8, pub platform: u8,
} }
impl WebSocketEvent for VoiceClientConnectPlatform {}

View File

@ -3,9 +3,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::{Snowflake, WebSocketEvent}; use crate::types::{Snowflake, WebSocketEvent};
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy, WebSocketEvent)]
/// Sent when another user disconnects from the voice server. /// Sent when another user disconnects from the voice server.
/// ///
/// When received, the SSRC of the user should be discarded. /// When received, the SSRC of the user should be discarded.
@ -15,4 +16,3 @@ pub struct VoiceClientDisconnection {
pub user_id: Snowflake, pub user_id: Snowflake,
} }
impl WebSocketEvent for VoiceClientDisconnection {}

View File

@ -3,9 +3,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::WebSocketEvent; use crate::types::WebSocketEvent;
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy, WebSocketEvent)]
/// Contains info on how often the client should send heartbeats to the server; /// Contains info on how often the client should send heartbeats to the server;
/// ///
/// Differs from the normal hello data in that discord sends heartbeat interval as a float. /// Differs from the normal hello data in that discord sends heartbeat interval as a float.
@ -21,4 +22,3 @@ pub struct VoiceHelloData {
pub heartbeat_interval: f64, pub heartbeat_interval: f64,
} }
impl WebSocketEvent for VoiceHelloData {}

View File

@ -3,9 +3,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::{Snowflake, WebSocketEvent}; use crate::types::{Snowflake, WebSocketEvent};
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)] #[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq, WebSocketEvent)]
/// The identify payload for the voice gateway connection; /// The identify payload for the voice gateway connection;
/// ///
/// Contains authentication info and context to authenticate to the voice gateway. /// Contains authentication info and context to authenticate to the voice gateway.
@ -22,4 +23,3 @@ pub struct VoiceIdentify {
// TODO: Add video streams // TODO: Add video streams
} }
impl WebSocketEvent for VoiceIdentify {}

View File

@ -3,9 +3,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::WebSocketEvent; use crate::types::WebSocketEvent;
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Copy, WebSocketEvent)]
/// What does this do? /// What does this do?
/// ///
/// {"op":15,"d":{"any":100}} /// {"op":15,"d":{"any":100}}
@ -15,4 +16,3 @@ pub struct VoiceMediaSinkWants {
pub any: u16, pub any: u16,
} }
impl WebSocketEvent for VoiceMediaSinkWants {}

View File

@ -30,7 +30,7 @@ mod speaking;
mod ssrc_definition; mod ssrc_definition;
mod voice_backend_version; mod voice_backend_version;
#[derive(Debug, Default, Serialize, Clone)] #[derive(Debug, Default, Serialize, Clone, WebSocketEvent)]
/// The payload used for sending events to the voice gateway. /// The payload used for sending events to the voice gateway.
/// ///
/// Similar to [VoiceGatewayReceivePayload], except we send a [Value] for d whilst we receive a [serde_json::value::RawValue] /// Similar to [VoiceGatewayReceivePayload], except we send a [Value] for d whilst we receive a [serde_json::value::RawValue]
@ -42,8 +42,6 @@ pub struct VoiceGatewaySendPayload {
pub data: Value, pub data: Value,
} }
impl WebSocketEvent for VoiceGatewaySendPayload {}
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
/// The payload used for receiving events from the voice gateway. /// The payload used for receiving events from the voice gateway.
/// ///

View File

@ -5,11 +5,12 @@
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use crate::types::WebSocketEvent; use crate::types::WebSocketEvent;
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::VoiceEncryptionMode; use super::VoiceEncryptionMode;
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, WebSocketEvent)]
/// The voice gateway's ready event; /// The voice gateway's ready event;
/// ///
/// Gives the user info about the UDP connection IP and port, srrc to use, /// Gives the user info about the UDP connection IP and port, srrc to use,
@ -43,4 +44,3 @@ impl Default for VoiceReady {
} }
} }
impl WebSocketEvent for VoiceReady {}

View File

@ -1,8 +1,9 @@
use super::{AudioCodec, VideoCodec, VoiceEncryptionMode}; use super::{AudioCodec, VideoCodec, VoiceEncryptionMode};
use crate::types::WebSocketEvent; use crate::types::WebSocketEvent;
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone, Default)] #[derive(Debug, Deserialize, Serialize, Clone, Default, WebSocketEvent)]
/// Event that describes our encryption mode and secret key for encryption /// Event that describes our encryption mode and secret key for encryption
/// ///
/// See <https://discord-userdoccers.vercel.app/topics/voice-connections#session-description-structure> /// See <https://discord-userdoccers.vercel.app/topics/voice-connections#session-description-structure>
@ -19,9 +20,7 @@ pub struct SessionDescription {
pub keyframe_interval: Option<u64>, pub keyframe_interval: Option<u64>,
} }
impl WebSocketEvent for SessionDescription {} #[derive(Debug, Deserialize, Serialize, Clone, Default, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
/// Event that might be sent to update session parameters /// Event that might be sent to update session parameters
/// ///
/// See <https://discord-userdoccers.vercel.app/topics/voice-connections#session-update-structure> /// See <https://discord-userdoccers.vercel.app/topics/voice-connections#session-update-structure>
@ -36,4 +35,3 @@ pub struct SessionUpdate {
pub new_media_session_id: Option<String>, pub new_media_session_id: Option<String>,
} }
impl WebSocketEvent for SessionUpdate {}

View File

@ -6,13 +6,14 @@ use bitflags::bitflags;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{Snowflake, WebSocketEvent}; use crate::types::{Snowflake, WebSocketEvent};
use chorus_macros::WebSocketEvent;
/// Event that tells the server we are speaking; /// Event that tells the server we are speaking;
/// ///
/// Essentially, what allows us to send UDP data and lights up the green circle around your avatar. /// Essentially, what allows us to send UDP data and lights up the green circle around your avatar.
/// ///
/// See <https://discord-userdoccers.vercel.app/topics/voice-connections#speaking-structure> /// See <https://discord-userdoccers.vercel.app/topics/voice-connections#speaking-structure>
#[derive(Debug, Deserialize, Serialize, Clone, Default)] #[derive(Debug, Deserialize, Serialize, Clone, Default, WebSocketEvent)]
pub struct Speaking { pub struct Speaking {
/// Data about the audio we're transmitting. /// Data about the audio we're transmitting.
/// ///
@ -27,14 +28,12 @@ pub struct Speaking {
pub delay: u64, pub delay: u64,
} }
impl WebSocketEvent for Speaking {}
bitflags! { bitflags! {
/// Bitflags of speaking types; /// Bitflags of speaking types;
/// ///
/// See <https://discord.com/developers/docs/topics/voice-connections#speaking> /// See <https://discord.com/developers/docs/topics/voice-connections#speaking>
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, chorus_macros::SerdeBitFlags)]
pub struct SpeakingBitflags: u8 { pub struct SpeakingBitflags: u64 {
/// Whether we'll be transmitting normal voice audio /// Whether we'll be transmitting normal voice audio
const MICROPHONE = 1 << 0; const MICROPHONE = 1 << 0;
/// Whether we'll be transmitting context audio for video, no speaking indicator /// Whether we'll be transmitting context audio for video, no speaking indicator

View File

@ -3,6 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::{Snowflake, WebSocketEvent}; use crate::types::{Snowflake, WebSocketEvent};
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Defines an event which provides ssrcs for voice and video for a user id. /// Defines an event which provides ssrcs for voice and video for a user id.
@ -24,7 +25,7 @@ use serde::{Deserialize, Serialize};
/// ```json /// ```json
/// {"op":12,"d":{"audio_ssrc":2307250864,"video_ssrc":0,"rtx_ssrc":0,"streams":[{"type":"video","rid":"100","ssrc":26595,"active":false,"quality":100,"rtx_ssrc":26596,"max_bitrate":2500000,"max_framerate":30,"max_resolution":{"type":"fixed","width":1280,"height":720}}]}} /// {"op":12,"d":{"audio_ssrc":2307250864,"video_ssrc":0,"rtx_ssrc":0,"streams":[{"type":"video","rid":"100","ssrc":26595,"active":false,"quality":100,"rtx_ssrc":26596,"max_bitrate":2500000,"max_framerate":30,"max_resolution":{"type":"fixed","width":1280,"height":720}}]}}
/// ``` /// ```
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)] #[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq, WebSocketEvent)]
pub struct SsrcDefinition { pub struct SsrcDefinition {
/// The ssrc used for video communications. /// The ssrc used for video communications.
/// ///
@ -50,4 +51,3 @@ pub struct SsrcDefinition {
pub streams: Vec<String>, pub streams: Vec<String>,
} }
impl WebSocketEvent for SsrcDefinition {}

View File

@ -3,9 +3,10 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types::WebSocketEvent; use crate::types::WebSocketEvent;
use chorus_macros::WebSocketEvent;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, WebSocketEvent)]
/// Received from the voice gateway server to describe the backend version. /// Received from the voice gateway server to describe the backend version.
/// ///
/// See <https://discord-userdoccers.vercel.app/topics/voice-connections#voice-backend-version> /// See <https://discord-userdoccers.vercel.app/topics/voice-connections#voice-backend-version>
@ -18,4 +19,3 @@ pub struct VoiceBackendVersion {
pub rtc_worker_version: String, pub rtc_worker_version: String,
} }
impl WebSocketEvent for VoiceBackendVersion {}

View File

@ -4,15 +4,13 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::Snowflake; use crate::types::{Snowflake, WebSocketEvent};
use chorus_macros::WebSocketEvent;
use super::WebSocketEvent; #[derive(Debug, Deserialize, Serialize, Default, Clone, WebSocketEvent)]
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See <https://discord.com/developers/docs/topics/gateway-events#webhooks-update> /// See <https://discord.com/developers/docs/topics/gateway-events#webhooks-update>
pub struct WebhooksUpdate { pub struct WebhooksUpdate {
pub guild_id: Snowflake, pub guild_id: Snowflake,
pub channel_id: Snowflake, pub channel_id: Snowflake,
} }
impl WebSocketEvent for WebhooksUpdate {}

View File

@ -4,6 +4,9 @@
//! All the types, entities, events and interfaces of the Spacebar API. //! All the types, entities, events and interfaces of the Spacebar API.
#[cfg(feature = "client")]
use std::sync::{Arc, RwLock};
pub use config::*; pub use config::*;
pub use entities::*; pub use entities::*;
pub use errors::*; pub use errors::*;
@ -19,3 +22,17 @@ mod events;
mod interfaces; mod interfaces;
mod schema; mod schema;
mod utils; mod utils;
/// A type alias for [`Arc<RwLock<T>>`], used to make the public facing API concerned with
/// Composite structs more ergonomic.
/// ## Note
///
/// While `T` does not have to implement `Composite` to be used with `Shared`,
/// the primary use of `Shared` is with types that implement `Composite`.
///
/// When the `client` feature is disabled, this does nothing (same as just `T`),
/// since `Composite` structures are disabled.
#[cfg(feature = "client")]
pub type Shared<T> = Arc<RwLock<T>>;
#[cfg(not(feature = "client"))]
pub type Shared<T> = T;

View File

@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};
use crate::types::{ApplicationCommand, AuditLogActionType, AuditLogEntry, AutoModerationRule, Channel, GuildScheduledEvent, Integration, Snowflake, User, Webhook};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AuditLogObject {
pub audit_log_entries: Vec<AuditLogEntry>,
pub application_commands: Vec<ApplicationCommand>,
pub auto_moderation_rules: Vec<AutoModerationRule>,
pub guild_scheduled_events: Vec<GuildScheduledEvent>,
pub integrations: Vec<Integration>,
pub threads: Vec<Channel>,
pub users: Vec<User>,
pub webhooks: Vec<Webhook>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetAuditLogsQuery {
pub before: Option<Snowflake>,
pub after: Option<Snowflake>,
pub limit: Option<u8>,
pub user_id: Option<Snowflake>,
pub action_type: Option<AuditLogActionType>
}

View File

@ -2,6 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use chrono::NaiveDate;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
@ -13,7 +14,8 @@ pub struct RegisterSchema {
pub email: Option<String>, pub email: Option<String>,
pub fingerprint: Option<String>, pub fingerprint: Option<String>,
pub invite: Option<String>, pub invite: Option<String>,
pub date_of_birth: Option<String>, /// The user's date of birth, serialized as an ISO8601 date
pub date_of_birth: Option<NaiveDate>,
pub gift_code_sku_id: Option<String>, pub gift_code_sku_id: Option<String>,
pub captcha_key: Option<String>, pub captcha_key: Option<String>,
pub promotional_email_opt_in: Option<bool>, pub promotional_email_opt_in: Option<bool>,

View File

@ -5,8 +5,7 @@
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::ChannelType; use crate::types::{ChannelType, DefaultReaction, entities::PermissionOverwrite, Snowflake};
use crate::types::{entities::PermissionOverwrite, Snowflake};
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, PartialOrd)] #[derive(Debug, Deserialize, Serialize, Default, PartialEq, PartialOrd)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@ -36,7 +35,7 @@ pub struct ChannelCreateSchema {
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct ChannelModifySchema { pub struct ChannelModifySchema {
pub name: Option<String>, pub name: Option<String>,
pub channel_type: Option<u8>, pub channel_type: Option<ChannelType>,
pub topic: Option<String>, pub topic: Option<String>,
pub icon: Option<String>, pub icon: Option<String>,
pub bitrate: Option<i32>, pub bitrate: Option<i32>,
@ -48,7 +47,7 @@ pub struct ChannelModifySchema {
pub nsfw: Option<bool>, pub nsfw: Option<bool>,
pub rtc_region: Option<String>, pub rtc_region: Option<String>,
pub default_auto_archive_duration: Option<i32>, pub default_auto_archive_duration: Option<i32>,
pub default_reaction_emoji: Option<String>, pub default_reaction_emoji: Option<DefaultReaction>,
pub flags: Option<i32>, pub flags: Option<i32>,
pub default_thread_rate_limit_per_user: Option<i32>, pub default_thread_rate_limit_per_user: Option<i32>,
pub video_quality_mode: Option<i32>, pub video_quality_mode: Option<i32>,
@ -59,7 +58,7 @@ pub struct GetChannelMessagesSchema {
/// Between 1 and 100, defaults to 50. /// Between 1 and 100, defaults to 50.
pub limit: Option<i32>, pub limit: Option<i32>,
#[serde(flatten)] #[serde(flatten)]
pub anchor: ChannelMessagesAnchor, pub anchor: Option<ChannelMessagesAnchor>,
} }
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] #[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
@ -74,21 +73,21 @@ impl GetChannelMessagesSchema {
pub fn before(anchor: Snowflake) -> Self { pub fn before(anchor: Snowflake) -> Self {
Self { Self {
limit: None, limit: None,
anchor: ChannelMessagesAnchor::Before(anchor), anchor: Some(ChannelMessagesAnchor::Before(anchor)),
} }
} }
pub fn around(anchor: Snowflake) -> Self { pub fn around(anchor: Snowflake) -> Self {
Self { Self {
limit: None, limit: None,
anchor: ChannelMessagesAnchor::Around(anchor), anchor: Some(ChannelMessagesAnchor::Around(anchor)),
} }
} }
pub fn after(anchor: Snowflake) -> Self { pub fn after(anchor: Snowflake) -> Self {
Self { Self {
limit: None, limit: None,
anchor: ChannelMessagesAnchor::After(anchor), anchor: Some(ChannelMessagesAnchor::After(anchor)),
} }
} }
@ -109,7 +108,7 @@ pub struct CreateChannelInviteSchema {
pub temporary: Option<bool>, pub temporary: Option<bool>,
pub unique: Option<bool>, pub unique: Option<bool>,
pub validate: Option<String>, pub validate: Option<String>,
pub target_type: Option<InviteType>, pub target_type: Option<InviteTargetType>,
pub target_user_id: Option<Snowflake>, pub target_user_id: Option<Snowflake>,
pub target_application_id: Option<Snowflake>, pub target_application_id: Option<Snowflake>,
} }
@ -131,15 +130,30 @@ impl Default for CreateChannelInviteSchema {
} }
bitflags! { bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
pub struct InviteFlags: u64 { pub struct InviteFlags: u64 {
const GUEST = 1 << 0; const GUEST = 1 << 0;
const VIEWED = 1 << 1;
} }
} }
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)] #[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[repr(u8)]
pub enum InviteType { pub enum InviteType {
#[default]
Guild = 0,
GroupDm = 1,
Friend = 2,
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[repr(u8)]
pub enum InviteTargetType {
#[default] #[default]
Stream = 1, Stream = 1,
EmbeddedApplication = 2, EmbeddedApplication = 2,
@ -162,3 +176,15 @@ pub struct ModifyChannelPositionsSchema {
pub lock_permissions: Option<bool>, pub lock_permissions: Option<bool>,
pub parent_id: Option<Snowflake>, pub parent_id: Option<Snowflake>,
} }
/// See <https://docs.discord.sex/resources/channel#follow-channel>
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
pub struct AddFollowingChannelSchema {
pub webhook_channel_id: Snowflake,
}
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
pub struct CreateWebhookSchema {
pub name: String,
pub avatar: Option<String>,
}

View File

@ -2,16 +2,14 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::collections::HashMap;
use bitflags::bitflags; use bitflags::bitflags;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::entities::Channel; use crate::types::entities::Channel;
use crate::types::types::guild_configuration::GuildFeatures; use crate::types::types::guild_configuration::GuildFeatures;
use crate::types::{ use crate::types::{Emoji, ExplicitContentFilterLevel, GenericSearchQueryWithLimit, MessageNotificationLevel, Snowflake, Sticker, StickerFormatType, SystemChannelFlags, VerificationLevel, WelcomeScreenChannel};
Emoji, ExplicitContentFilterLevel, MessageNotificationLevel, Snowflake, Sticker,
SystemChannelFlags, VerificationLevel,
};
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@ -32,10 +30,20 @@ pub struct GuildCreateSchema {
/// Represents the schema which needs to be sent to create a Guild Ban. /// Represents the schema which needs to be sent to create a Guild Ban.
/// See: <https://discord-userdoccers.vercel.app/resources/guild#create-guild-ban> /// See: <https://discord-userdoccers.vercel.app/resources/guild#create-guild-ban>
pub struct GuildBanCreateSchema { pub struct GuildBanCreateSchema {
/// Deprecated
pub delete_message_days: Option<u8>, pub delete_message_days: Option<u8>,
pub delete_message_seconds: Option<u32>, pub delete_message_seconds: Option<u32>,
} }
#[derive(Debug, Deserialize, Serialize, Default, Clone, Eq, PartialEq)]
#[serde(rename_all = "snake_case")]
/// Represents the schema which needs to be sent to create a Guild Ban.
/// See: <https://discord-userdoccers.vercel.app/resources/guild#create-guild-ban>
pub struct GuildBanBulkCreateSchema {
pub user_ids: Vec<Snowflake>,
pub delete_message_seconds: Option<u32>,
}
#[derive(Debug, Deserialize, Serialize, Default, Clone, Eq, PartialEq)] #[derive(Debug, Deserialize, Serialize, Default, Clone, Eq, PartialEq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
/// Represents the schema used to modify a guild. /// Represents the schema used to modify a guild.
@ -77,6 +85,31 @@ pub struct GetUserGuildSchema {
pub with_counts: Option<bool>, pub with_counts: Option<bool>,
} }
impl GetUserGuildSchema {
/// Converts self to query string parameters
pub fn to_query(self) -> Vec<(&'static str, String)> {
let mut query = Vec::with_capacity(4);
if let Some(before) = self.before {
query.push(("before", before.to_string()));
}
if let Some(after) = self.after {
query.push(("after", after.to_string()));
}
if let Some(limit) = self.limit {
query.push(("limit", limit.to_string()));
}
if let Some(with_counts) = self.with_counts {
query.push(("with_counts", with_counts.to_string()));
}
query
}
}
impl std::default::Default for GetUserGuildSchema { impl std::default::Default for GetUserGuildSchema {
fn default() -> Self { fn default() -> Self {
Self { Self {
@ -119,6 +152,12 @@ impl Default for GuildMemberSearchSchema {
} }
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct GuildGetMembersQuery {
pub limit: Option<u16>,
pub after: Option<Snowflake>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct ModifyGuildMemberSchema { pub struct ModifyGuildMemberSchema {
pub nick: Option<String>, pub nick: Option<String>,
@ -131,7 +170,8 @@ pub struct ModifyGuildMemberSchema {
} }
bitflags! { bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
/// Represents the flags of a Guild Member. /// Represents the flags of a Guild Member.
/// ///
/// # Reference: /// # Reference:
@ -173,3 +213,200 @@ pub struct GuildBansQuery {
pub after: Option<Snowflake>, pub after: Option<Snowflake>,
pub limit: Option<u16>, pub limit: Option<u16>,
} }
/// Max query length is 32 characters.
/// The limit argument is a number between 1 and 10, defaults to 10.
pub type GuildBansSearchQuery = GenericSearchQueryWithLimit;
/// Query is partial or full, username or nickname.
/// Limit argument is a number between 1 and 1000, defaults to 1.
pub type GuildMembersSearchQuery = GenericSearchQueryWithLimit;
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// A guild's progress on meeting the requirements of joining discovery.
///
/// Certain guilds, such as those that are verified, are exempt from discovery requirements. These guilds will not have a fully populated discovery requirements object, and are guaranteed to receive only sufficient and sufficient_without_grace_period.
///
/// # Reference:
/// See <https://docs.discord.sex/resources/discovery#discovery-requirements-object>
pub struct GuildDiscoveryRequirements {
pub guild_id: Option<Snowflake>,
pub safe_environment: Option<bool>,
pub healthy: Option<bool>,
pub health_score_pending: Option<bool>,
pub size: Option<bool>,
pub nsfw_properties: Option<GuildDiscoveryNsfwProperties>,
pub protected: Option<bool>,
pub sufficient: Option<bool>,
pub sufficient_without_grace_period: Option<bool>,
pub valid_rules_channel: Option<bool>,
pub retention_healthy: Option<bool>,
pub engagement_healthy: Option<bool>,
pub age: Option<bool>,
pub minimum_age: Option<u16>,
pub health_score: Option<GuildDiscoveryHealthScore>,
pub minimum_size: Option<u64>,
pub grace_period_end_date: Option<DateTime<Utc>>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/discovery#discovery-nsfw-properties-structure>
pub struct GuildDiscoveryNsfwProperties {
pub channels: Vec<Snowflake>,
pub channel_banned_keywords: HashMap<Snowflake, Vec<String>>,
pub name: Option<String>,
pub name_banned_keywords: Vec<String>,
pub description: Option<String>,
pub description_banned_keywords: Vec<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// Activity metrics are recalculated weekly, as an 8-week rolling average. If they are not yet eligible to be calculated, all fields will be null.
///
/// # Reference:
/// See <https://docs.discord.sex/resources/discovery#discovery-health-score-structure>
pub struct GuildDiscoveryHealthScore {
pub avg_nonnew_communicators: u64,
pub avg_nonnew_participators: u64,
pub num_intentful_joiners: u64,
pub perc_ret_w1_intentful: f64,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/emoji#create-guild-emoji>
pub struct EmojiCreateSchema {
pub name: Option<String>,
/// # Reference:
/// See <https://docs.discord.sex/reference#cdn-data>
pub image: String,
#[serde(default)]
pub roles: Vec<Snowflake>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/emoji#modify-guild-emoji>
pub struct EmojiModifySchema {
pub name: Option<String>,
pub roles: Option<Vec<Snowflake>>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#get-guild-prune>
pub struct GuildPruneQuerySchema {
pub days: u8,
/// Only used on POST
#[serde(default, skip_serializing_if = "Option::is_none")]
pub compute_prune_count: Option<bool>,
#[serde(default)]
pub include_roles: Vec<Snowflake>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#get-guild-prune>
pub struct GuildPruneResult {
/// Null if compute_prune_count is false
pub pruned: Option<usize>,
}
#[derive(Default, Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/sticker#create-guild-sticker>
pub struct GuildCreateStickerSchema {
pub name: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub tags: Option<String>,
pub file_data: Vec<u8>,
#[serde(skip)]
pub sticker_format_type: StickerFormatType
}
impl GuildCreateStickerSchema {
#[cfg(feature = "poem")]
pub async fn from_multipart(mut multipart: poem::web::Multipart) -> Result<Self, poem::Error> {
let mut _self = GuildCreateStickerSchema::default();
while let Some(field) = multipart.next_field().await? {
let name = field.name().ok_or(poem::Error::from_string("All fields must be named", poem::http::StatusCode::BAD_REQUEST))?;
match name {
"name" => {
_self.name = field.text().await?;
}
"description" => {
_self.description = Some(field.text().await?);
}
"tags" => {
_self.tags = Some(field.text().await?);
}
"file_data" => {
if _self.name.is_empty() {
_self.name = field.file_name().map(String::from).ok_or(poem::Error::from_string("File name must be set", poem::http::StatusCode::BAD_REQUEST))?;
}
_self.sticker_format_type = StickerFormatType::from_mime(field.content_type().ok_or(poem::Error::from_string("Content type must be set", poem::http::StatusCode::BAD_REQUEST))?).ok_or(poem::Error::from_string("Unknown sticker format", poem::http::StatusCode::BAD_REQUEST))?;
_self.file_data = field.bytes().await?;
}
_ => {}
}
}
if _self.name.is_empty() || _self.file_data.is_empty() {
return Err(poem::Error::from_string("At least the name and file_data are required", poem::http::StatusCode::BAD_REQUEST));
}
Ok(_self)
}
// #[cfg(feature = "client")]
pub fn to_multipart(&self) -> reqwest::multipart::Form {
let mut form = reqwest::multipart::Form::new()
.text("name", self.name.clone())
.part("file_data", reqwest::multipart::Part::bytes(self.file_data.clone()).mime_str(self.sticker_format_type.to_mime()).unwrap());
if let Some(description) = &self.description {
form = form.text("description", description.to_owned());
}
if let Some(tags) = &self.tags {
form = form.text("tags", tags.to_owned())
}
form
}
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/sticker#modify-guild-sticker>
pub struct GuildModifyStickerSchema {
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub tags: Option<String>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#modify-guild-welcome-screen>
pub struct GuildModifyWelcomeScreenSchema {
pub enabled: Option<bool>,
pub description: Option<String>,
/// Max of 5
pub welcome_channels: Option<Vec<WelcomeScreenChannel>>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild-template#create-guild-template>
pub struct GuildTemplateCreateSchema {
/// Name of the template (1-100 characters)
pub name: String,
/// Description of the template (max 120 characters)
pub description: Option<String>
}

View File

@ -0,0 +1,26 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
/// Query parameters for the `Get Invite` route.
///
/// # Reference:
/// Read: <https://docs.discord.sex/resources/invite#query-string-params>
pub struct GetInvitesSchema {
pub with_counts: Option<bool>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#get-guild-vanity-invite>
pub struct GuildVanityInviteResponse {
pub code: String,
#[serde(default)]
pub uses: Option<u32>
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
/// # Reference:
/// See <https://docs.discord.sex/resources/guild#modify-guild-vanity-invite>
pub struct GuildCreateVanitySchema {
pub code: String,
}

View File

@ -7,13 +7,13 @@ use serde::{Deserialize, Serialize};
use crate::types::entities::{ use crate::types::entities::{
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment, AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
}; };
use crate::types::{Attachment, Snowflake}; use crate::types::{Attachment, EmbedType, Message, MessageFlags, MessageType, ReactionType, Snowflake};
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct MessageSendSchema { pub struct MessageSendSchema {
#[serde(rename = "type")] #[serde(rename = "type")]
pub message_type: Option<i32>, pub message_type: Option<MessageType>,
pub content: Option<String>, pub content: Option<String>,
pub nonce: Option<String>, pub nonce: Option<String>,
pub tts: Option<bool>, pub tts: Option<bool>,
@ -54,13 +54,13 @@ pub struct MessageSearchQuery {
pub attachment_extension: Option<Vec<String>>, pub attachment_extension: Option<Vec<String>>,
pub attachment_filename: Option<Vec<String>>, pub attachment_filename: Option<Vec<String>>,
pub author_id: Option<Vec<Snowflake>>, pub author_id: Option<Vec<Snowflake>>,
pub author_type: Option<Vec<String>>, pub author_type: Option<Vec<AuthorType>>,
pub channel_id: Option<Vec<Snowflake>>, pub channel_id: Option<Vec<Snowflake>>,
pub command_id: Option<Vec<Snowflake>>, pub command_id: Option<Vec<Snowflake>>,
pub content: Option<String>, pub content: Option<String>,
pub embed_provider: Option<Vec<String>>, pub embed_provider: Option<Vec<String>>,
pub embed_type: Option<Vec<String>>, pub embed_type: Option<Vec<EmbedType>>,
pub has: Option<Vec<String>>, pub has: Option<Vec<HasType>>,
pub include_nsfw: Option<bool>, pub include_nsfw: Option<bool>,
pub limit: Option<i32>, pub limit: Option<i32>,
pub link_hostname: Option<Vec<String>>, pub link_hostname: Option<Vec<String>>,
@ -70,8 +70,8 @@ pub struct MessageSearchQuery {
pub min_id: Option<String>, pub min_id: Option<String>,
pub offset: Option<i32>, pub offset: Option<i32>,
pub pinned: Option<bool>, pub pinned: Option<bool>,
pub sort_by: Option<String>, pub sort_by: Option<SortType>,
pub sort_order: Option<String>, pub sort_order: Option<SortOrder>,
} }
impl std::default::Default for MessageSearchQuery { impl std::default::Default for MessageSearchQuery {
@ -102,6 +102,75 @@ impl std::default::Default for MessageSearchQuery {
} }
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum AuthorType {
User,
#[serde(rename = "-user")]
NotUser,
Bot,
#[serde(rename = "-bot")]
NotBot,
Webhook,
#[serde(rename = "-webhook")]
NotWebhook,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum HasType {
Image,
#[serde(rename = "-image")]
NotImage,
Sound,
#[serde(rename = "-sound")]
NotSound,
Video,
#[serde(rename = "-video")]
NotVideo,
File,
#[serde(rename = "-file")]
NotFile,
Sticker,
#[serde(rename = "-sticker")]
NotSticker,
Embed,
#[serde(rename = "-embed")]
NotEmbed,
Link,
#[serde(rename = "-link")]
NotLink,
Poll,
#[serde(rename = "-poll")]
NotPoll,
Snapshot,
#[serde(rename = "-snapshot")]
NotSnapshot,
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum SortType {
#[default]
Timestamp,
Relevance
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum SortOrder {
#[default]
#[serde(rename = "desc")]
Descending,
#[serde(rename = "asc")]
Ascending,
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
pub struct MessageSearchResponse {
pub messages: Vec<Message>,
pub total_results: u64,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct CreateGreetMessage { pub struct CreateGreetMessage {
pub sticker_ids: Vec<Snowflake>, pub sticker_ids: Vec<Snowflake>,
@ -118,13 +187,21 @@ pub struct MessageAck {
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
pub struct MessageModifySchema { pub struct MessageModifySchema {
content: Option<String>, pub content: Option<String>,
embeds: Option<Vec<Embed>>, pub embeds: Option<Vec<Embed>>,
embed: Option<Embed>, pub embed: Option<Embed>,
allowed_mentions: Option<AllowedMention>, pub allowed_mentions: Option<AllowedMention>,
components: Option<Vec<Component>>, pub components: Option<Vec<Component>>,
flags: Option<i32>, pub flags: Option<MessageFlags>,
files: Option<Vec<u8>>, pub files: Option<Vec<u8>>,
payload_json: Option<String>, pub payload_json: Option<String>,
attachments: Option<Vec<Attachment>>, pub attachments: Option<Vec<Attachment>>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
pub struct ReactionQuerySchema {
pub after: Option<Snowflake>,
pub limit: Option<u32>,
#[serde(rename = "type")]
pub reaction_type: Option<ReactionType>
} }

View File

@ -3,6 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
pub use apierror::*; pub use apierror::*;
pub use audit_log::*;
pub use auth::*; pub use auth::*;
pub use channel::*; pub use channel::*;
pub use guild::*; pub use guild::*;
@ -10,8 +11,11 @@ pub use message::*;
pub use relationship::*; pub use relationship::*;
pub use role::*; pub use role::*;
pub use user::*; pub use user::*;
pub use invites::*;
pub use voice_state::*;
mod apierror; mod apierror;
mod audit_log;
mod auth; mod auth;
mod channel; mod channel;
mod guild; mod guild;
@ -19,3 +23,11 @@ mod message;
mod relationship; mod relationship;
mod role; mod role;
mod user; mod user;
mod invites;
mod voice_state;
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct GenericSearchQueryWithLimit {
pub query: String,
pub limit: Option<u16>,
}

View File

@ -3,6 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{PermissionFlags, Snowflake};
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
@ -10,8 +11,8 @@ use serde::{Deserialize, Serialize};
/// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema) /// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema)
pub struct RoleCreateModifySchema { pub struct RoleCreateModifySchema {
pub name: Option<String>, pub name: Option<String>,
pub permissions: Option<String>, pub permissions: Option<PermissionFlags>,
pub color: Option<u32>, pub color: Option<f64>,
pub hoist: Option<bool>, pub hoist: Option<bool>,
pub icon: Option<Vec<u8>>, pub icon: Option<Vec<u8>>,
pub unicode_emoji: Option<String>, pub unicode_emoji: Option<String>,
@ -24,6 +25,6 @@ pub struct RoleCreateModifySchema {
/// Represents the schema which needs to be sent to update a roles' position. /// Represents the schema which needs to be sent to update a roles' position.
/// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolepositionupdateschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolepositionupdateschema) /// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolepositionupdateschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolepositionupdateschema)
pub struct RolePositionUpdateSchema { pub struct RolePositionUpdateSchema {
pub id: String, pub id: Snowflake,
pub position: u16, pub position: u16,
} }

View File

@ -4,24 +4,91 @@
use std::collections::HashMap; use std::collections::HashMap;
use chrono::NaiveDate;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::Snowflake; use crate::types::Snowflake;
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
/// A schema used to modify a user. /// A schema used to modify a user.
///
/// See <https://docs.discord.sex/resources/user#json-params>
pub struct UserModifySchema { pub struct UserModifySchema {
/// The user's new username (2-32 characters)
///
/// Requires that `current_password` is set.
pub username: Option<String>, pub username: Option<String>,
// TODO: Maybe add a special discriminator type?
/// Requires that `current_password` is set.
pub discriminator: Option<String>,
/// The user's display name (1-32 characters)
///
/// # Note
///
/// This is not yet implemented on Spacebar
pub global_name: Option<String>,
// TODO: Add a CDN data type
pub avatar: Option<String>, pub avatar: Option<String>,
pub bio: Option<String>, /// Note: This is not yet implemented on Spacebar
pub accent_color: Option<u64>, pub avatar_decoration_id: Option<Snowflake>,
pub banner: Option<String>, /// Note: This is not yet implemented on Spacebar
pub current_password: Option<String>, pub avatar_decoration_sku_id: Option<Snowflake>,
pub new_password: Option<String>, /// The user's email address; if changing from a verified email, email_token must be provided
pub code: Option<String>, ///
/// Requires that `current_password` is set.
// TODO: Is ^ up to date? One would think this may not be the case, since email_token exists
pub email: Option<String>, pub email: Option<String>,
pub discriminator: Option<i16>, /// The user's email token from their previous email, required if a new email is set.
///
/// See <https://docs.discord.sex/resources/user#modify-user-email> and <https://docs.discord.sex/resources/user#verify-user-email-change>
/// for changing the user's email.
///
/// # Note
///
/// This is not yet implemented on Spacebar
pub email_token: Option<String>,
/// The user's pronouns (max 40 characters)
///
/// # Note
///
/// This is not yet implemented on Spacebar
pub pronouns: Option<String>,
/// The user's banner.
///
/// Can only be changed for premium users
pub banner: Option<String>,
/// The user's bio (max 190 characters)
pub bio: Option<String>,
/// The user's accent color, as a hex integer
pub accent_color: Option<u64>,
/// The user's [UserFlags].
///
/// Only [UserFlags::PREMIUM_PROMO_DISMISSED], [UserFlags::HAS_UNREAD_URGENT_MESSAGES]
/// and DISABLE_PREMIUM can be set.
///
/// # Note
///
/// This is not yet implemented on Spacebar
pub flags: Option<u64>,
/// The user's date of birth, can only be set once
///
/// Requires that `current_password` is set.
pub date_of_birth: Option<NaiveDate>,
/// The user's current password (if the account does not have a password, this sets it)
///
/// Required for updating `username`, `discriminator`, `email`, `date_of_birth` and
/// `new_password`
#[serde(rename = "password")]
pub current_password: Option<String>,
/// The user's new password (8-72 characters)
///
/// Requires that `current_password` is set.
///
/// Regenerates the user's token
pub new_password: Option<String>,
/// Spacebar only field, potentially same as `email_token`
pub code: Option<String>,
} }
/// A schema used to create a private channel. /// A schema used to create a private channel.
@ -33,7 +100,7 @@ pub struct UserModifySchema {
/// ///
/// # Reference: /// # Reference:
/// Read: <https://discord-userdoccers.vercel.app/resources/channel#json-params> /// Read: <https://discord-userdoccers.vercel.app/resources/channel#json-params>
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PrivateChannelCreateSchema { pub struct PrivateChannelCreateSchema {
pub recipients: Option<Vec<Snowflake>>, pub recipients: Option<Vec<Snowflake>>,
pub access_tokens: Option<Vec<String>>, pub access_tokens: Option<Vec<String>>,

View File

@ -0,0 +1,15 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::Snowflake;
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
/// # Reference:
/// See <https://docs.discord.sex/resources/voice#json-params>
pub struct VoiceStateUpdateSchema {
/// The ID of the channel the user is currently in
pub channel_id: Option<Snowflake>,
/// Whether to suppress the user
pub suppress: Option<bool>,
/// The time at which the user requested to speak
pub request_to_speak_timestamp: Option<DateTime<Utc>>,
}

View File

@ -19,7 +19,7 @@ pub struct Claims {
/// When the token was issued /// When the token was issued
pub iat: i64, pub iat: i64,
pub email: String, pub email: String,
pub id: String, pub id: Snowflake,
} }
impl Claims { impl Claims {
@ -27,7 +27,7 @@ impl Claims {
let unix = chrono::Utc::now().timestamp(); let unix = chrono::Utc::now().timestamp();
Self { Self {
exp: unix + (60 * 60 * 24), exp: unix + (60 * 60 * 24),
id: id.to_string(), id: *id,
iat: unix, iat: unix,
email: user.to_string(), email: user.to_string(),
} }

View File

@ -11,3 +11,5 @@ pub mod jwt;
mod regexes; mod regexes;
mod rights; mod rights;
mod snowflake; mod snowflake;
pub mod serde;

View File

@ -2,7 +2,11 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::num::ParseIntError;
use std::str::FromStr;
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::types::UserFlags;
bitflags! { bitflags! {
/// Rights are instance-wide, per-user permissions for everything you may perform on the instance, /// Rights are instance-wide, per-user permissions for everything you may perform on the instance,
@ -14,6 +18,8 @@ bitflags! {
/// ///
/// # Reference /// # Reference
/// See <https://docs.spacebar.chat/setup/server/security/rights/> /// See <https://docs.spacebar.chat/setup/server/security/rights/>
#[derive(Debug, Clone, Copy, Eq, PartialEq, chorus_macros::SerdeBitFlags)]
#[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))]
pub struct Rights: u64 { pub struct Rights: u64 {
/// All rights /// All rights
const OPERATOR = 1 << 0; const OPERATOR = 1 << 0;
@ -151,6 +157,12 @@ impl Rights {
} }
} }
impl Default for Rights {
fn default() -> Self {
Self::empty()
}
}
#[allow(dead_code)] // FIXME: Remove this when we use this #[allow(dead_code)] // FIXME: Remove this when we use this
fn all_rights() -> Rights { fn all_rights() -> Rights {
Rights::OPERATOR Rights::OPERATOR

Some files were not shown because too many files have changed in this diff Show More