Merge 411db01786
into cb3551dcd4
This commit is contained in:
commit
e6700cf906
|
@ -2,133 +2,133 @@ name: Build and Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: ["main"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main", "dev" ]
|
branches: ["main", "dev"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linux:
|
linux:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Clone spacebar server
|
- name: Clone spacebar server
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/bitfl0wer/server.git
|
git clone https://github.com/bitfl0wer/server.git
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
cache-dependency-path: server/package-lock.json
|
cache-dependency-path: server/package-lock.json
|
||||||
- name: Prepare and start Spacebar server
|
- name: Prepare and start Spacebar server
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
npm run setup
|
npm run setup
|
||||||
npm run start &
|
npm run start &
|
||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
cache-all-crates: "true"
|
cache-all-crates: "true"
|
||||||
prefix-key: "linux"
|
prefix-key: "linux"
|
||||||
- name: Build, Test and Publish Coverage
|
- uses: taiki-e/install-action@nextest
|
||||||
run: |
|
- name: Build, Test with nextest, Publish Coverage
|
||||||
if [ -n "${{ secrets.COVERALLS_REPO_TOKEN }}" ]; then
|
run: |
|
||||||
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
if [ -n "${{ secrets.COVERALLS_REPO_TOKEN }}" ]; then
|
||||||
cargo binstall --no-confirm cargo-tarpaulin --force
|
if [ "${{github.event.pull_request.head.ref}}" = "main" ]; then
|
||||||
cargo tarpaulin --all-features --avoid-cfg-tarpaulin --tests --verbose --skip-clean --coveralls ${{ secrets.COVERALLS_REPO_TOKEN }} --timeout 120
|
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
||||||
else
|
cargo binstall --no-confirm cargo-tarpaulin --force
|
||||||
echo "Code Coverage step is skipped on forks!"
|
cargo tarpaulin --all-features --avoid-cfg-tarpaulin --tests --verbose --skip-clean --coveralls ${{ secrets.COVERALLS_REPO_TOKEN }} --timeout 120
|
||||||
cargo build --verbose --all-features
|
else
|
||||||
cargo test --verbose --all-features
|
echo "Code Coverage step is skipped on non-main PRs and PRs from forks."
|
||||||
fi
|
cargo nextest run --verbose --all-features
|
||||||
# wasm-safari:
|
fi
|
||||||
# runs-on: macos-latest
|
else
|
||||||
# steps:
|
echo "Code Coverage step is skipped on non-main PRs and PRs from forks."
|
||||||
# - uses: actions/checkout@v4
|
cargo nextest run --verbose --all-features
|
||||||
# - name: Clone spacebar server
|
fi
|
||||||
# run: |
|
linux-non-default-features:
|
||||||
# git clone https://github.com/bitfl0wer/server.git
|
runs-on: ubuntu-latest
|
||||||
# - uses: actions/setup-node@v4
|
timeout-minutes: 10
|
||||||
# with:
|
steps:
|
||||||
# node-version: 18
|
- uses: actions/checkout@v4
|
||||||
# cache: 'npm'
|
- name: Check common non-default feature configurations
|
||||||
# cache-dependency-path: server/package-lock.json
|
run: |
|
||||||
# - name: Prepare and start Spacebar server
|
echo "No features:"
|
||||||
# run: |
|
cargo check --features="" --no-default-features
|
||||||
# npm install
|
echo "Only client:"
|
||||||
# npm run setup
|
cargo check --features="client" --no-default-features
|
||||||
# npm run start &
|
echo "Only backend:"
|
||||||
# working-directory: ./server
|
cargo check --features="backend" --no-default-features
|
||||||
# - uses: Swatinem/rust-cache@v2
|
echo "Only voice:"
|
||||||
# with:
|
cargo check --features="voice" --no-default-features
|
||||||
# cache-all-crates: "true"
|
echo "Only voice gateway:"
|
||||||
# prefix-key: "macos-safari"
|
cargo check --features="voice_gateway" --no-default-features
|
||||||
# - name: Run WASM tests with Safari, Firefox, Chrome
|
echo "Backend + client:"
|
||||||
# run: |
|
cargo check --features="backend, client" --no-default-features
|
||||||
# rustup target add wasm32-unknown-unknown
|
echo "Backend + voice:"
|
||||||
# curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
cargo check --features="backend, voice" --no-default-features
|
||||||
# cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force
|
echo "Backend + voice gateway:"
|
||||||
# SAFARIDRIVER=$(which safaridriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt" --no-fail-fast
|
cargo check --features="backend, voice_gateway" --no-default-features
|
||||||
|
echo "Client + voice gateway:"
|
||||||
|
cargo check --features="client, voice_gateway" --no-default-features
|
||||||
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
|
||||||
- name: Clone spacebar server
|
- name: Clone spacebar server
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/bitfl0wer/server.git
|
git clone https://github.com/bitfl0wer/server.git
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
cache-dependency-path: server/package-lock.json
|
cache-dependency-path: server/package-lock.json
|
||||||
- name: Prepare and start Spacebar server
|
- name: Prepare and start Spacebar server
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
npm run setup
|
npm run setup
|
||||||
npm run start &
|
npm run start &
|
||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
cache-all-crates: "true"
|
cache-all-crates: "true"
|
||||||
prefix-key: "macos"
|
prefix-key: "macos"
|
||||||
- name: Run WASM tests with Safari, Firefox, Chrome
|
- name: Run WASM tests with Safari, Firefox, Chrome
|
||||||
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.93" --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
|
||||||
- name: Clone spacebar server
|
- name: Clone spacebar server
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/bitfl0wer/server.git
|
git clone https://github.com/bitfl0wer/server.git
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
cache-dependency-path: server/package-lock.json
|
cache-dependency-path: server/package-lock.json
|
||||||
- name: Prepare and start Spacebar server
|
- name: Prepare and start Spacebar server
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
npm run setup
|
npm run setup
|
||||||
npm run start &
|
npm run start &
|
||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
cache-all-crates: "true"
|
cache-all-crates: "true"
|
||||||
prefix-key: "macos"
|
prefix-key: "macos"
|
||||||
- name: Run WASM tests with Safari, Firefox, Chrome
|
- name: Run WASM tests with Safari, Firefox, Chrome
|
||||||
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.93" --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"
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
name: cargo doc lints
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main", "preserve/*" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main", "dev" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cargo-doc-lints:
|
||||||
|
name: Run cargo doc for doc lints
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
|
- name: Install aditional components for sarif
|
||||||
|
run: cargo install clippy-sarif sarif-fmt
|
||||||
|
|
||||||
|
- name: Run cargo doc
|
||||||
|
run: cargo doc --no-deps --all-features --locked --message-format=json | clippy-sarif | sed 's/rust-lang.github.io\/rust-clippy/doc.rust-lang.org\/rustdoc\/lints.html/g' | sed 's/clippy/rustdoc/g' | tee cargo-doc-results.sarif | sarif-fmt
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Upload analysis results to GitHub
|
||||||
|
uses: github/codeql-action/upload-sarif@v3
|
||||||
|
with:
|
||||||
|
sarif_file: cargo-doc-results.sarif
|
||||||
|
wait-for-processing: true
|
|
@ -3,8 +3,10 @@
|
||||||
|
|
||||||
**Please refer to the [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) and [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md) before making a contribution.**
|
**Please refer to the [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) and [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md) before making a contribution.**
|
||||||
|
|
||||||
|
Contributions should always fork from and merge back into the `dev` branch.
|
||||||
|
|
||||||
Chorus is currently missing voice support and a lot of API endpoints, many of which should be trivial to implement,
|
Chorus is currently missing voice support and a lot of API endpoints, many of which should be trivial to implement,
|
||||||
ever since [we streamlined the process of doing so](https://github.com/polyphony-chat/chorus/discussions/401).
|
ever since [we streamlined the process of doing so](https://github.com/polyphony-chat/chorus/discussions/401).
|
||||||
|
|
||||||
If you'd like to contribute new functionality, check out [The 'Meta'-issues.](https://github.com/polyphony-chat/chorus/issues?q=is%3Aissue+label%3A%22Type%3A+Meta%22+) They contain a comprehensive list of all features which are yet missing for full Discord.com compatibility.
|
If you'd like to contribute new functionality, check out [The 'Meta'-issues.](https://github.com/polyphony-chat/chorus/issues?q=is%3Aissue+label%3A%22Type%3A+Meta%22+) They contain a comprehensive list of all features which are yet missing for full Discord.com compatibility.
|
||||||
Please feel free to open an Issue with the idea you have, or a Pull Request.
|
Please feel free to open an Issue with the idea you have, or a Pull Request.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
89
Cargo.toml
89
Cargo.toml
|
@ -8,73 +8,86 @@ repository = "https://github.com/polyphony-chat/chorus"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["spacebar", "discord", "polyphony"]
|
keywords = ["spacebar", "discord", "polyphony"]
|
||||||
website = ["https://discord.com/invite/m3FpcapGDD"]
|
website = ["https://discord.com/invite/m3FpcapGDD"]
|
||||||
rust-version = "1.67.1"
|
rust-version = "1.70.0"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["client", "rt-multi-thread"]
|
default = ["client", "rt-multi-thread"]
|
||||||
backend = ["dep:poem", "dep:sqlx"]
|
backend = ["poem", "sqlx", "sqlx-pg-uint"]
|
||||||
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 = []
|
||||||
|
sqlx-pg-uint = ["dep:sqlx-pg-uint", "sqlx-pg-uint/serde"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.35.1", features = ["macros", "sync"] }
|
tokio = { version = "1.39.3", features = ["macros", "sync"] }
|
||||||
serde = { version = "1.0.195", features = ["derive", "rc"] }
|
serde = { version = "1.0.209", features = ["derive", "rc"] }
|
||||||
serde_json = { version = "1.0.111", features = ["raw_value"] }
|
serde_json = { version = "1.0.127", features = ["raw_value"] }
|
||||||
serde-aux = "4.3.1"
|
serde-aux = "4.5.0"
|
||||||
serde_with = "3.4.0"
|
serde_with = "3.9.0"
|
||||||
serde_repr = "0.1.18"
|
serde_repr = "0.1.19"
|
||||||
reqwest = { features = ["multipart", "json"], version = "0.11.23" }
|
reqwest = { features = [
|
||||||
url = "2.5.0"
|
"multipart",
|
||||||
chrono = { version = "0.4.31", features = ["serde"] }
|
"json",
|
||||||
regex = "1.10.2"
|
"rustls-tls-webpki-roots",
|
||||||
|
], version = "=0.11.26", default-features = false }
|
||||||
|
url = "2.5.2"
|
||||||
|
chrono = { version = "0.4.38", features = ["serde"] }
|
||||||
|
regex = "1.10.6"
|
||||||
custom_error = "1.9.2"
|
custom_error = "1.9.2"
|
||||||
futures-util = "0.3.30"
|
futures-util = "0.3.30"
|
||||||
http = "0.2.11"
|
http = "0.2.12"
|
||||||
base64 = "0.21.7"
|
base64 = "0.21.7"
|
||||||
bitflags = { version = "2.4.1", features = ["serde"] }
|
bitflags = { version = "2.6.0", features = ["serde"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.5.0"
|
||||||
poem = { version = "1.3.59", optional = true }
|
poem = { version = "3.0.1", features = ["multipart"], optional = true }
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.63"
|
||||||
jsonwebtoken = "8.3.0"
|
jsonwebtoken = "8.3.0"
|
||||||
log = "0.4.20"
|
log = "0.4.22"
|
||||||
async-trait = "0.1.77"
|
async-trait = "0.1.81"
|
||||||
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.8.1", features = [
|
||||||
"mysql",
|
|
||||||
"sqlite",
|
|
||||||
"json",
|
"json",
|
||||||
"chrono",
|
"chrono",
|
||||||
"ipnetwork",
|
"ipnetwork",
|
||||||
"runtime-tokio-native-tls",
|
"runtime-tokio-rustls",
|
||||||
"any",
|
"postgres",
|
||||||
|
"bigdecimal",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
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.33", optional = true }
|
||||||
|
webpki-roots = "0.26.3"
|
||||||
|
pubserve = { version = "1.1.0", features = ["async", "send"] }
|
||||||
|
sqlx-pg-uint = { version = "0.5.0", features = ["serde"], optional = true }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
rustls = "0.21.10"
|
rustls = "0.21.12"
|
||||||
rustls-native-certs = "0.6.3"
|
|
||||||
tokio-tungstenite = { version = "0.20.1", features = [
|
tokio-tungstenite = { version = "0.20.1", features = [
|
||||||
"rustls-tls-native-roots",
|
"rustls-tls-webpki-roots",
|
||||||
"rustls-native-certs",
|
|
||||||
] }
|
] }
|
||||||
native-tls = "0.2.11"
|
|
||||||
hostname = "0.3.1"
|
hostname = "0.3.1"
|
||||||
getrandom = { version = "0.2.12" }
|
getrandom = { version = "0.2.15" }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
getrandom = { version = "0.2.12", features = ["js"] }
|
getrandom = { version = "0.2.15", features = ["js"] }
|
||||||
ws_stream_wasm = "0.7.4"
|
ws_stream_wasm = "0.7.4"
|
||||||
wasm-bindgen-futures = "0.4.39"
|
wasm-bindgen-futures = "0.4.43"
|
||||||
wasmtimer = "0.2.0"
|
wasmtimer = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.5.0"
|
||||||
wasm-bindgen-test = "0.3.39"
|
wasm-bindgen-test = "0.3.43"
|
||||||
wasm-bindgen = "0.2.89"
|
wasm-bindgen = "0.2.93"
|
||||||
|
simple_logger = { version = "5.0.0", default-features = false }
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(tarpaulin_include)'] }
|
||||||
|
|
154
README.md
154
README.md
|
@ -28,14 +28,15 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Chorus is a Rust library which poses as an API wrapper for [Spacebar Chat](https://github.com/spacebarchat/)
|
Chorus is a Rust library which poses as an API wrapper for [Spacebar Chat](https://github.com/spacebarchat/),
|
||||||
and Discord. It is designed to be easy to use, and to be compatible with both Discord and Spacebar Chat.
|
Discord and our own Polyphony. Its high-level API is designed to be easy to use, while still providing the
|
||||||
|
flexibility one would expect from a library like this.
|
||||||
|
|
||||||
You can establish as many connections to as many servers as you want, and you can use them all at the same time.
|
You can establish as many connections to as many servers as you want, and you can use them all at the same time.
|
||||||
|
|
||||||
## A Tour of Chorus
|
## A Tour of Chorus
|
||||||
|
|
||||||
Chorus combines all the required functionalities of a user-centric Spacebar library into one package.
|
Chorus combines all the required functionalities of an API wrapper for chat services into one modular library.
|
||||||
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
|
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
|
||||||
a WebSocket connection to the Gateway. This means that you can focus on building your application,
|
a WebSocket connection to the Gateway. This means that you can focus on building your application,
|
||||||
instead of worrying about the underlying implementation details.
|
instead of worrying about the underlying implementation details.
|
||||||
|
@ -44,19 +45,19 @@ To get started with Chorus, import it into your project by adding the following
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chorus = "0.15.0"
|
chorus = "0.16.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Establishing a Connection
|
### Establishing a Connection
|
||||||
|
|
||||||
To connect to a Spacebar compatible server, you need to create an [`Instance`](https://docs.rs/chorus/latest/chorus/instance/struct.Instance.html) like this:
|
To connect to a Polyphony/Spacebar compatible server, you'll need to create an [`Instance`](https://docs.rs/chorus/latest/chorus/instance/struct.Instance.html) like this:
|
||||||
|
|
||||||
```rs
|
```rs
|
||||||
use chorus::instance::Instance;
|
use chorus::instance::Instance;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let instance = Instance::new("https://example.com")
|
let instance = Instance::new("https://example.com", None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to the Spacebar server");
|
.expect("Failed to connect to the Spacebar server");
|
||||||
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
|
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
|
||||||
|
@ -81,7 +82,7 @@ let login_schema = LoginSchema {
|
||||||
password: "Correct-Horse-Battery-Staple".to_string(),
|
password: "Correct-Horse-Battery-Staple".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
// Each user connects to the Gateway. The Gateway connection lives on a separate thread. Depending on
|
// Each user connects to the Gateway. Each users' Gateway connection lives on a separate thread. Depending on
|
||||||
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
|
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
|
||||||
let user = instance
|
let user = instance
|
||||||
.login_account(login_schema)
|
.login_account(login_schema)
|
||||||
|
@ -97,15 +98,33 @@ All major desktop operating systems (Windows, macOS (aarch64/x86_64), Linux (aar
|
||||||
`wasm32-unknown-unknown` is a supported compilation target on versions `0.12.0` and up. This allows you to use
|
`wasm32-unknown-unknown` is a supported compilation target on versions `0.12.0` and up. This allows you to use
|
||||||
Chorus in your browser, or in any other environment that supports WebAssembly.
|
Chorus in your browser, or in any other environment that supports WebAssembly.
|
||||||
|
|
||||||
We recommend checking out the examples directory, as well as the documentation for more information.
|
To compile for `wasm32-unknown-unknown`, execute the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --target=wasm32-unknown-unknown --no-default-features
|
||||||
|
```
|
||||||
|
|
||||||
|
The following features are supported on `wasm32-unknown-unknown`:
|
||||||
|
|
||||||
|
| Feature | WASM Support |
|
||||||
|
| ----------------- | ------------ |
|
||||||
|
| `client` | ✅ |
|
||||||
|
| `rt` | ✅ |
|
||||||
|
| `rt-multi-thread` | ❌ |
|
||||||
|
| `backend` | ❌ |
|
||||||
|
| `voice` | ❌ |
|
||||||
|
| `voice_udp` | ❌ |
|
||||||
|
| `voice_gateway` | ✅ |
|
||||||
|
|
||||||
|
We recommend checking out the "examples" directory, as well as the documentation for more information.
|
||||||
|
|
||||||
## MSRV (Minimum Supported Rust Version)
|
## MSRV (Minimum Supported Rust Version)
|
||||||
|
|
||||||
Rust **1.67.1**. This number might change at any point while Chorus is not yet at version 1.0.0.
|
Rust **1.70.0**. This number might change at any point while Chorus is not yet at version 1.0.0.
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
Make sure that you have at least Rust 1.67.1 installed. You can check your Rust version by running `cargo --version`
|
Make sure that you have at least Rust 1.70.0 installed. You can check your Rust version by running `cargo --version`
|
||||||
in your terminal. To compile for `wasm32-unknown-unknown`, you need to install the `wasm32-unknown-unknown` target.
|
in your terminal. To compile for `wasm32-unknown-unknown`, you need to install the `wasm32-unknown-unknown` target.
|
||||||
You can do this by running `rustup target add wasm32-unknown-unknown`.
|
You can do this by running `rustup target add wasm32-unknown-unknown`.
|
||||||
|
|
||||||
|
@ -130,98 +149,23 @@ This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read
|
||||||
|
|
||||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||||
|
|
||||||
<details>
|
[Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust
|
||||||
<summary>Progress Tracker/Roadmap</summary>
|
[Rust-url]: https://www.rust-lang.org/
|
||||||
|
[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/build_and_test.yml?style=flat
|
||||||
### Core Functionality
|
[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/build_and_test.yml
|
||||||
- [x] Rate Limiter (hint: couldn't be fully tested due to [an Issue with the Spacebar Server](https://github.com/spacebarchat/server/issues/1022))
|
[clippy-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/clippy.yml?style=flat
|
||||||
- [x] [Login (the conventional way)](https://github.com/polyphony-chat/chorus/issues/1)
|
[clippy-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/clippy.yml
|
||||||
- [ ] [2FA](https://github.com/polyphony-chat/chorus/issues/40)
|
[contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat
|
||||||
- [x] [Registration](https://github.com/polyphony-chat/chorus/issues/1)
|
[contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors
|
||||||
|
[coverage-shield]: https://coveralls.io/repos/github/polyphony-chat/chorus/badge.svg?branch=main
|
||||||
### Messaging
|
[coverage-url]: https://coveralls.io/github/polyphony-chat/chorus?branch=main
|
||||||
- [x] [Sending messages](https://github.com/polyphony-chat/chorus/issues/23)
|
[forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat
|
||||||
- [x] [Events (Message, User, Channel, etc.)](https://github.com/polyphony-chat/chorus/issues/51)
|
[forks-url]: https://github.com/polyphony-chat/chorus/network/members
|
||||||
- [x] Channel creation
|
[stars-shield]: https://img.shields.io/github/stars/polyphony-chat/chorus.svg?style=flat
|
||||||
- [x] Channel deletion
|
[stars-url]: https://github.com/polyphony-chat/chorus/stargazers
|
||||||
- [x] [Channel management (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/48)
|
[issues-shield]: https://img.shields.io/github/issues/polyphony-chat/chorus.svg?style=flat
|
||||||
- [x] [Join and Leave Guilds](https://github.com/polyphony-chat/chorus/issues/45)
|
[issues-url]: https://github.com/polyphony-chat/chorus/issues
|
||||||
- [x] [Start DMs](https://github.com/polyphony-chat/chorus/issues/45)
|
[license-shield]: https://img.shields.io/github/license/polyphony-chat/chorus.svg?style=f;at
|
||||||
- [x] [Group DM creation, deletion and member management](https://github.com/polyphony-chat/chorus/issues/89)
|
[license-url]: https://github.com/polyphony-chat/chorus/blob/master/LICENSE
|
||||||
- [ ] [Deleting messages](https://github.com/polyphony-chat/chorus/issues/91)
|
[Discord]: https://dcbadge.vercel.app/api/server/m3FpcapGDD?style=flat
|
||||||
- [ ] [Message threads](https://github.com/polyphony-chat/chorus/issues/90)
|
[Discord-invite]: https://discord.com/invite/m3FpcapGDD
|
||||||
- [x] [Reactions](https://github.com/polyphony-chat/chorus/issues/85)
|
|
||||||
- [ ] Message Search
|
|
||||||
- [ ] Message history
|
|
||||||
- [ ] Emoji
|
|
||||||
- [ ] Stickers
|
|
||||||
- [ ] [Forum channels](https://github.com/polyphony-chat/chorus/issues/90)
|
|
||||||
|
|
||||||
### User Management
|
|
||||||
- [ ] [User profile customization](https://github.com/polyphony-chat/chorus/issues/41)
|
|
||||||
- [x] Gettings users and user profiles
|
|
||||||
- [x] [Friend requests](https://github.com/polyphony-chat/chorus/issues/92)
|
|
||||||
- [x] [Blocking users](https://github.com/polyphony-chat/chorus/issues/92)
|
|
||||||
- [ ] User presence (online, offline, idle, etc.)
|
|
||||||
- [ ] User status (custom status, etc.)
|
|
||||||
- [x] Account deletion
|
|
||||||
|
|
||||||
### Additional Features
|
|
||||||
- [ ] Server discovery
|
|
||||||
- [ ] Server templates
|
|
||||||
|
|
||||||
### Voice and Video
|
|
||||||
- [ ] [Voice chat support](https://github.com/polyphony-chat/chorus/issues/49)
|
|
||||||
- [ ] [Video chat support](https://github.com/polyphony-chat/chorus/issues/49)
|
|
||||||
|
|
||||||
### Permissions and Roles
|
|
||||||
- [x] [Role management](https://github.com/polyphony-chat/chorus/issues/46) (creation, deletion, modification)
|
|
||||||
- [x] [Permission management](https://github.com/polyphony-chat/chorus/issues/46) (assigning and revoking permissions)
|
|
||||||
- [x] [Channel-specific permissions](https://github.com/polyphony-chat/chorus/issues/88)
|
|
||||||
- [x] Role-based access control
|
|
||||||
|
|
||||||
### Guild Management
|
|
||||||
- [x] Guild creation
|
|
||||||
- [x] Guild deletion
|
|
||||||
- [ ] [Guild settings (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/43)
|
|
||||||
- [ ] Guild invites
|
|
||||||
|
|
||||||
### Moderation
|
|
||||||
- [ ] Channel moderation (slow mode, etc.)
|
|
||||||
- [ ] User sanctions (mute, kick, ban)
|
|
||||||
- [ ] Audit logs
|
|
||||||
|
|
||||||
### Embeds and Rich Content
|
|
||||||
- [x] Sending rich content in messages (links, images, videos)
|
|
||||||
- [ ] Customizing embed appearance (title, description, color, fields)
|
|
||||||
|
|
||||||
### Webhooks
|
|
||||||
- [ ] Webhook creation and management
|
|
||||||
- [ ] Handling incoming webhook events
|
|
||||||
|
|
||||||
### Documentation and Examples
|
|
||||||
- [ ] Comprehensive documentation
|
|
||||||
- [ ] Example usage and code snippets
|
|
||||||
- [ ] Tutorials and guides
|
|
||||||
|
|
||||||
[Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust
|
|
||||||
[Rust-url]: https://www.rust-lang.org/
|
|
||||||
[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/build_and_test.yml?style=flat
|
|
||||||
[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/build_and_test.yml
|
|
||||||
[clippy-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/clippy.yml?style=flat
|
|
||||||
[clippy-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/clippy.yml
|
|
||||||
[contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat
|
|
||||||
[contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors
|
|
||||||
[coverage-shield]: https://coveralls.io/repos/github/polyphony-chat/chorus/badge.svg?branch=main
|
|
||||||
[coverage-url]: https://coveralls.io/github/polyphony-chat/chorus?branch=main
|
|
||||||
[forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat
|
|
||||||
[forks-url]: https://github.com/polyphony-chat/chorus/network/members
|
|
||||||
[stars-shield]: https://img.shields.io/github/stars/polyphony-chat/chorus.svg?style=flat
|
|
||||||
[stars-url]: https://github.com/polyphony-chat/chorus/stargazers
|
|
||||||
[issues-shield]: https://img.shields.io/github/issues/polyphony-chat/chorus.svg?style=flat
|
|
||||||
[issues-url]: https://github.com/polyphony-chat/chorus/issues
|
|
||||||
[license-shield]: https://img.shields.io/github/license/polyphony-chat/chorus.svg?style=f;at
|
|
||||||
[license-url]: https://github.com/polyphony-chat/chorus/blob/master/LICENSE
|
|
||||||
[Discord]: https://dcbadge.vercel.app/api/server/m3FpcapGDD?style=flat
|
|
||||||
[Discord-invite]: https://discord.com/invite/m3FpcapGDD
|
|
||||||
</details>
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# 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 https://mozilla.org/MPL/2.0/.
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
cargo build --no-default-features --target=wasm32-unknown-unknown "$@"
|
|
@ -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",
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chorus-macros"
|
name = "chorus-macros"
|
||||||
version = "0.2.0"
|
version = "0.5.0"
|
||||||
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
|
||||||
|
|
|
@ -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,89 @@ 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::Postgres> for #name {
|
||||||
|
fn type_info() -> sqlx::postgres::PgTypeInfo {
|
||||||
|
<sqlx_pg_uint::PgU64 as sqlx::Type<sqlx::Postgres>>::type_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl<'q> sqlx::Encode<'q, sqlx::Postgres> for #name {
|
||||||
|
fn encode_by_ref(&self, buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
|
||||||
|
<sqlx_pg_uint::PgU64 as sqlx::Encode<sqlx::Postgres>>::encode_by_ref(&self.bits().into(), buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl<'q> sqlx::Decode<'q, sqlx::Postgres> for #name {
|
||||||
|
fn decode(value: <sqlx::Postgres as sqlx::Database>::ValueRef<'q>) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
|
<sqlx_pg_uint::PgU64 as sqlx::Decode<sqlx::Postgres>>::decode(value).map(|v| Self::from_bits_truncate(v.to_uint()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a [Vec<u8>] to an unsigned, 64 bit integer. The [u64] is created using [u64::from_be_bytes].
|
||||||
|
///
|
||||||
|
/// Empty vectors will result in an output of `0_u64`. Only the first 8 values from the vector are
|
||||||
|
/// being processed. Any additional values will be skipped.
|
||||||
|
///
|
||||||
|
/// Vectors holding less than 8 values will be treated as a vector holding 8 values, where the
|
||||||
|
/// missing values are padded with `0_u8`.
|
||||||
|
fn vec_u8_to_u64(vec: Vec<u8>) -> u64 {
|
||||||
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
|
let mut position = 0;
|
||||||
|
for read in vec.iter() {
|
||||||
|
buf[position] = *read;
|
||||||
|
position += 1;
|
||||||
|
if position > 7 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u64::from_be_bytes(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.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()
|
||||||
|
}
|
||||||
|
|
|
@ -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,12 +14,12 @@ 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,
|
|
||||||
types::{GatewayIdentifyPayload, GatewayReady},
|
types::{GatewayIdentifyPayload, GatewayReady},
|
||||||
};
|
};
|
||||||
|
use pubserve::Subscriber;
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use tokio::{self};
|
use tokio::{self};
|
||||||
|
|
||||||
|
@ -32,11 +34,16 @@ use wasmtimer::tokio::sleep;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ExampleObserver {}
|
pub struct ExampleObserver {}
|
||||||
|
|
||||||
// This struct can observe GatewayReady events when subscribed, because it implements the trait Observer<GatewayReady>.
|
// This struct can observe GatewayReady events when subscribed, because it implements the trait Subscriber<GatewayReady>.
|
||||||
// The Observer trait can be implemented for a struct for a given websocketevent to handle observing it
|
// The Subscriber trait can be implemented for a struct for a given websocketevent to handle observing it
|
||||||
|
//
|
||||||
|
// Note that this trait is quite generic and can be use to observe any type.
|
||||||
|
//
|
||||||
|
// It is just used for WebSocketEvents in chorus.
|
||||||
|
//
|
||||||
// One struct can be an observer of multiple websocketevents, if needed
|
// One struct can be an observer of multiple websocketevents, if needed
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Observer<GatewayReady> for ExampleObserver {
|
impl Subscriber<GatewayReady> for ExampleObserver {
|
||||||
// After we subscribe to an event this function is called every time we receive it
|
// After we subscribe to an event this function is called every time we receive it
|
||||||
async fn update(&self, _data: &GatewayReady) {
|
async fn update(&self, _data: &GatewayReady) {
|
||||||
println!("Observed Ready!");
|
println!("Observed Ready!");
|
||||||
|
@ -45,10 +52,18 @@ impl Observer<GatewayReady> for ExampleObserver {
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let gateway_websocket_url = GATEWAY_URL.to_string();
|
let gateway_websocket_url = GATEWAY_URL;
|
||||||
|
|
||||||
|
// 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 {};
|
||||||
|
|
|
@ -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"))]
|
||||||
|
@ -25,10 +25,18 @@ use wasmtimer::tokio::sleep;
|
||||||
/// This example creates a simple gateway connection and a session with an Identify event
|
/// This example creates a simple gateway connection and a session with an Identify event
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let gateway_websocket_url = GATEWAY_URL.to_string();
|
let gateway_websocket_url = GATEWAY_URL;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use chorus::instance::Instance;
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let instance = Instance::new("https://example.com/")
|
let instance = Instance::new("https://example.com/", None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to the Spacebar server");
|
.expect("Failed to connect to the Spacebar server");
|
||||||
dbg!(instance.instance_info);
|
dbg!(instance.instance_info);
|
||||||
|
|
|
@ -7,7 +7,7 @@ use chorus::types::LoginSchema;
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let mut instance = Instance::new("https://example.com/")
|
let mut instance = Instance::new("https://example.com/", None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to the Spacebar server");
|
.expect("Failed to connect to the Spacebar server");
|
||||||
// Assume, you already have an account created on this instance. Registering an account works
|
// Assume, you already have an account created on this instance. Registering an account works
|
||||||
|
|
|
@ -22,11 +22,9 @@ use simplelog::{TermLogger, Config, WriteLogger};
|
||||||
use std::{net::SocketAddrV4, sync::Arc, fs::File, time::Duration};
|
use std::{net::SocketAddrV4, sync::Arc, fs::File, time::Duration};
|
||||||
|
|
||||||
use chorus::{
|
use chorus::{
|
||||||
gateway::{Observer, Gateway},
|
gateway::{Gateway, GatewayOptions, Observer},
|
||||||
types::{
|
types::{
|
||||||
GatewayReady, SelectProtocol, SelectProtocolData, SessionDescription, Snowflake, Speaking,
|
GatewayIdentifyPayload, GatewayReady, SelectProtocol, SelectProtocolData, SessionDescription, Snowflake, Speaking, SpeakingBitflags, SsrcDefinition, UpdateVoiceState, VoiceEncryptionMode, VoiceIdentify, VoiceProtocol, VoiceReady, VoiceServerUpdate
|
||||||
SpeakingBitflags, SsrcDefinition, VoiceEncryptionMode, VoiceIdentify, VoiceProtocol,
|
|
||||||
VoiceReady, VoiceServerUpdate, GatewayIdentifyPayload, UpdateVoiceState,
|
|
||||||
},
|
},
|
||||||
voice::{
|
voice::{
|
||||||
gateway::{VoiceGateway, VoiceGatewayHandle},
|
gateway::{VoiceGateway, VoiceGatewayHandle},
|
||||||
|
@ -219,7 +217,7 @@ impl Observer<Speaking> for VoiceHandler {
|
||||||
println!(
|
println!(
|
||||||
"Received Speaking! (SRRC: {}, flags: {:?})",
|
"Received Speaking! (SRRC: {}, flags: {:?})",
|
||||||
data.ssrc,
|
data.ssrc,
|
||||||
SpeakingBitflags::from_bits(data.speaking).unwrap()
|
SpeakingBitflags::from_bits(data.speaking.into()).unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,7 +251,7 @@ async fn main() {
|
||||||
])
|
])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let gateway = Gateway::spawn(GATEWAY_URL.to_string())
|
let gateway = Gateway::spawn(GATEWAY_URL.to_string(), GatewayOptions::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -281,7 +279,7 @@ async fn main() {
|
||||||
.session
|
.session
|
||||||
.ready
|
.ready
|
||||||
.subscribe(voice_handler.clone());
|
.subscribe(voice_handler.clone());
|
||||||
|
|
||||||
// Data which channel to update the local user to be joined into.
|
// Data which channel to update the local user to be joined into.
|
||||||
//
|
//
|
||||||
// guild_id and channel_id can be some to join guild voice channels
|
// guild_id and channel_id can be some to join guild voice channels
|
||||||
|
|
|
@ -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
|
|
@ -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,21 @@ 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").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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,27 +22,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: &str) -> ChorusResult<ChorusUser> {
|
||||||
let object_result = self.get_user(token.clone(), None).await;
|
let mut user = ChorusUser::shell(Arc::new(RwLock::new(self.clone())), token).await;
|
||||||
if let Err(e) = object_result {
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,24 @@ 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").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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use crate::types::{
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
|
#[allow(clippy::useless_conversion)]
|
||||||
/// Sends a message in the channel with the provided channel_id.
|
/// Sends a message in the channel with the provided channel_id.
|
||||||
/// Returns the sent message.
|
/// Returns the sent message.
|
||||||
///
|
///
|
||||||
|
@ -40,7 +41,7 @@ impl Message {
|
||||||
chorus_request.deserialize_response::<Message>(user).await
|
chorus_request.deserialize_response::<Message>(user).await
|
||||||
} else {
|
} else {
|
||||||
for (index, attachment) in message.attachments.iter_mut().enumerate() {
|
for (index, attachment) in message.attachments.iter_mut().enumerate() {
|
||||||
attachment.get_mut(index).unwrap().id = Some(index as i16);
|
attachment.get_mut(index).unwrap().id = Some((index as u64).into());
|
||||||
}
|
}
|
||||||
let mut form = reqwest::multipart::Form::new();
|
let mut form = reqwest::multipart::Form::new();
|
||||||
let payload_json = to_string(&message).unwrap();
|
let payload_json = to_string(&message).unwrap();
|
||||||
|
@ -111,7 +112,7 @@ impl Message {
|
||||||
let result = request.send_request(user).await?;
|
let result = request.send_request(user).await?;
|
||||||
let result_json = result.json::<Value>().await.unwrap();
|
let result_json = result.json::<Value>().await.unwrap();
|
||||||
if !result_json.is_object() {
|
if !result_json.is_object() {
|
||||||
return Err(search_error(result_json.to_string()));
|
return Err(search_error(result_json.to_string().as_str()));
|
||||||
}
|
}
|
||||||
let value_map = result_json.as_object().unwrap();
|
let value_map = result_json.as_object().unwrap();
|
||||||
if let Some(messages) = value_map.get("messages") {
|
if let Some(messages) = value_map.get("messages") {
|
||||||
|
@ -122,7 +123,7 @@ impl Message {
|
||||||
}
|
}
|
||||||
// The code below might be incorrect. We'll cross that bridge when we come to it
|
// The code below might be incorrect. We'll cross that bridge when we come to it
|
||||||
if !value_map.contains_key("code") || !value_map.contains_key("retry_after") {
|
if !value_map.contains_key("code") || !value_map.contains_key("retry_after") {
|
||||||
return Err(search_error(result_json.to_string()));
|
return Err(search_error(result_json.to_string().as_str()));
|
||||||
}
|
}
|
||||||
let code = value_map.get("code").unwrap().as_u64().unwrap();
|
let code = value_map.get("code").unwrap().as_u64().unwrap();
|
||||||
let retry_after = value_map.get("retry_after").unwrap().as_u64().unwrap();
|
let retry_after = value_map.get("retry_after").unwrap().as_u64().unwrap();
|
||||||
|
@ -481,7 +482,7 @@ impl Message {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_error(result_text: String) -> ChorusError {
|
fn search_error(result_text: &str) -> ChorusError {
|
||||||
ChorusError::InvalidResponse {
|
ChorusError::InvalidResponse {
|
||||||
error: format!(
|
error: format!(
|
||||||
"Got unexpected Response, or Response which is not valid JSON. Response: \n{}",
|
"Got unexpected Response, or Response which is not valid JSON. Response: \n{}",
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Useful metadata for working with [`types::Reaction`], bundled together nicely.
|
/// Useful metadata for working with [`types::Reaction`], bundled together nicely.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, PartialOrd, Ord)]
|
||||||
pub struct ReactionMeta {
|
pub struct ReactionMeta {
|
||||||
pub message_id: types::Snowflake,
|
pub message_id: types::Snowflake,
|
||||||
pub channel_id: types::Snowflake,
|
pub channel_id: types::Snowflake,
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {}
|
|
||||||
|
|
|
@ -2,19 +2,21 @@
|
||||||
// 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,
|
||||||
};
|
};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_tungstenite::{
|
use tokio_tungstenite::{
|
||||||
connect_async_tls_with_config, tungstenite, Connector, MaybeTlsStream, WebSocketStream,
|
connect_async_tls_with_config, connect_async_with_config, tungstenite, Connector,
|
||||||
|
MaybeTlsStream, WebSocketStream,
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use crate::errors::GatewayError;
|
use crate::gateway::{GatewayMessage, RawGatewayMessage};
|
||||||
use crate::gateway::GatewayMessage;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct TungsteniteBackend;
|
pub struct TungsteniteBackend;
|
||||||
|
|
||||||
// These could be made into inherent associated types when that's stabilized
|
// These could be made into inherent associated types when that's stabilized
|
||||||
|
@ -22,46 +24,70 @@ 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 websocket_url_parsed =
|
||||||
let certs = rustls_native_certs::load_native_certs();
|
Url::parse(websocket_url).map_err(|_| TungsteniteBackendError::TungsteniteError {
|
||||||
|
error: tungstenite::error::Error::Url(
|
||||||
|
tungstenite::error::UrlError::UnsupportedUrlScheme,
|
||||||
|
),
|
||||||
|
})?;
|
||||||
|
if websocket_url_parsed.scheme() == "ws" {
|
||||||
|
let (websocket_stream, _) =
|
||||||
|
match connect_async_with_config(websocket_url, None, false).await {
|
||||||
|
Ok(websocket_stream) => websocket_stream,
|
||||||
|
Err(e) => return Err(TungsteniteBackendError::TungsteniteError { error: e }),
|
||||||
|
};
|
||||||
|
|
||||||
if let Err(e) = certs {
|
Ok(websocket_stream.split())
|
||||||
log::error!("Failed to load platform native certs! {:?}", e);
|
} else if websocket_url_parsed.scheme() == "wss" {
|
||||||
return Err(GatewayError::CannotConnect {
|
let certs = webpki_roots::TLS_SERVER_ROOTS;
|
||||||
error: format!("{:?}", e),
|
let roots = rustls::RootCertStore {
|
||||||
});
|
roots: certs
|
||||||
|
.iter()
|
||||||
|
.map(|cert| {
|
||||||
|
rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||||
|
cert.subject.to_vec(),
|
||||||
|
cert.subject_public_key_info.to_vec(),
|
||||||
|
cert.name_constraints.as_ref().map(|der| der.to_vec()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let (websocket_stream, _) = match connect_async_tls_with_config(
|
||||||
|
websocket_url,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
Some(Connector::Rustls(
|
||||||
|
rustls::ClientConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_root_certificates(roots)
|
||||||
|
.with_no_client_auth()
|
||||||
|
.into(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(websocket_stream) => websocket_stream,
|
||||||
|
Err(e) => return Err(TungsteniteBackendError::TungsteniteError { error: e }),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(websocket_stream.split())
|
||||||
|
} else {
|
||||||
|
Err(TungsteniteBackendError::TungsteniteError {
|
||||||
|
error: tungstenite::error::Error::Url(
|
||||||
|
tungstenite::error::UrlError::UnsupportedUrlScheme,
|
||||||
|
),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for cert in certs.unwrap() {
|
|
||||||
roots.add(&rustls::Certificate(cert.0)).unwrap();
|
|
||||||
}
|
|
||||||
let (websocket_stream, _) = match connect_async_tls_with_config(
|
|
||||||
websocket_url,
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
Some(Connector::Rustls(
|
|
||||||
rustls::ClientConfig::builder()
|
|
||||||
.with_safe_defaults()
|
|
||||||
.with_root_certificates(roots)
|
|
||||||
.with_no_client_auth()
|
|
||||||
.into(),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(websocket_stream) => websocket_stream,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(GatewayError::CannotConnect {
|
|
||||||
error: e.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(websocket_stream.split())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,3 +102,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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// 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 pubserve::Publisher;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::types;
|
use crate::types;
|
||||||
|
|
||||||
|
@ -23,144 +25,144 @@ pub struct Events {
|
||||||
pub call: Call,
|
pub call: Call,
|
||||||
pub voice: Voice,
|
pub voice: Voice,
|
||||||
pub webhooks: Webhooks,
|
pub webhooks: Webhooks,
|
||||||
pub gateway_identify_payload: GatewayEvent<types::GatewayIdentifyPayload>,
|
pub gateway_identify_payload: Publisher<types::GatewayIdentifyPayload>,
|
||||||
pub gateway_resume: GatewayEvent<types::GatewayResume>,
|
pub gateway_resume: Publisher<types::GatewayResume>,
|
||||||
pub error: GatewayEvent<GatewayError>,
|
pub error: Publisher<GatewayError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Application {
|
pub struct Application {
|
||||||
pub command_permissions_update: GatewayEvent<types::ApplicationCommandPermissionsUpdate>,
|
pub command_permissions_update: Publisher<types::ApplicationCommandPermissionsUpdate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct AutoModeration {
|
pub struct AutoModeration {
|
||||||
pub rule_create: GatewayEvent<types::AutoModerationRuleCreate>,
|
pub rule_create: Publisher<types::AutoModerationRuleCreate>,
|
||||||
pub rule_update: GatewayEvent<types::AutoModerationRuleUpdate>,
|
pub rule_update: Publisher<types::AutoModerationRuleUpdate>,
|
||||||
pub rule_delete: GatewayEvent<types::AutoModerationRuleDelete>,
|
pub rule_delete: Publisher<types::AutoModerationRuleDelete>,
|
||||||
pub action_execution: GatewayEvent<types::AutoModerationActionExecution>,
|
pub action_execution: Publisher<types::AutoModerationActionExecution>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
pub ready: GatewayEvent<types::GatewayReady>,
|
pub ready: Publisher<types::GatewayReady>,
|
||||||
pub ready_supplemental: GatewayEvent<types::GatewayReadySupplemental>,
|
pub ready_supplemental: Publisher<types::GatewayReadySupplemental>,
|
||||||
pub replace: GatewayEvent<types::SessionsReplace>,
|
pub replace: Publisher<types::SessionsReplace>,
|
||||||
pub reconnect: GatewayEvent<types::GatewayReconnect>,
|
pub reconnect: Publisher<types::GatewayReconnect>,
|
||||||
pub invalid: GatewayEvent<types::GatewayInvalidSession>,
|
pub invalid: Publisher<types::GatewayInvalidSession>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct StageInstance {
|
pub struct StageInstance {
|
||||||
pub create: GatewayEvent<types::StageInstanceCreate>,
|
pub create: Publisher<types::StageInstanceCreate>,
|
||||||
pub update: GatewayEvent<types::StageInstanceUpdate>,
|
pub update: Publisher<types::StageInstanceUpdate>,
|
||||||
pub delete: GatewayEvent<types::StageInstanceDelete>,
|
pub delete: Publisher<types::StageInstanceDelete>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub create: GatewayEvent<types::MessageCreate>,
|
pub create: Publisher<types::MessageCreate>,
|
||||||
pub update: GatewayEvent<types::MessageUpdate>,
|
pub update: Publisher<types::MessageUpdate>,
|
||||||
pub delete: GatewayEvent<types::MessageDelete>,
|
pub delete: Publisher<types::MessageDelete>,
|
||||||
pub delete_bulk: GatewayEvent<types::MessageDeleteBulk>,
|
pub delete_bulk: Publisher<types::MessageDeleteBulk>,
|
||||||
pub reaction_add: GatewayEvent<types::MessageReactionAdd>,
|
pub reaction_add: Publisher<types::MessageReactionAdd>,
|
||||||
pub reaction_remove: GatewayEvent<types::MessageReactionRemove>,
|
pub reaction_remove: Publisher<types::MessageReactionRemove>,
|
||||||
pub reaction_remove_all: GatewayEvent<types::MessageReactionRemoveAll>,
|
pub reaction_remove_all: Publisher<types::MessageReactionRemoveAll>,
|
||||||
pub reaction_remove_emoji: GatewayEvent<types::MessageReactionRemoveEmoji>,
|
pub reaction_remove_emoji: Publisher<types::MessageReactionRemoveEmoji>,
|
||||||
pub ack: GatewayEvent<types::MessageACK>,
|
pub ack: Publisher<types::MessageACK>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub update: GatewayEvent<types::UserUpdate>,
|
pub update: Publisher<types::UserUpdate>,
|
||||||
pub guild_settings_update: GatewayEvent<types::UserGuildSettingsUpdate>,
|
pub guild_settings_update: Publisher<types::UserGuildSettingsUpdate>,
|
||||||
pub presence_update: GatewayEvent<types::PresenceUpdate>,
|
pub presence_update: Publisher<types::PresenceUpdate>,
|
||||||
pub typing_start: GatewayEvent<types::TypingStartEvent>,
|
pub typing_start: Publisher<types::TypingStartEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Relationship {
|
pub struct Relationship {
|
||||||
pub add: GatewayEvent<types::RelationshipAdd>,
|
pub add: Publisher<types::RelationshipAdd>,
|
||||||
pub remove: GatewayEvent<types::RelationshipRemove>,
|
pub remove: Publisher<types::RelationshipRemove>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Channel {
|
pub struct Channel {
|
||||||
pub create: GatewayEvent<types::ChannelCreate>,
|
pub create: Publisher<types::ChannelCreate>,
|
||||||
pub update: GatewayEvent<types::ChannelUpdate>,
|
pub update: Publisher<types::ChannelUpdate>,
|
||||||
pub unread_update: GatewayEvent<types::ChannelUnreadUpdate>,
|
pub unread_update: Publisher<types::ChannelUnreadUpdate>,
|
||||||
pub delete: GatewayEvent<types::ChannelDelete>,
|
pub delete: Publisher<types::ChannelDelete>,
|
||||||
pub pins_update: GatewayEvent<types::ChannelPinsUpdate>,
|
pub pins_update: Publisher<types::ChannelPinsUpdate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
pub create: GatewayEvent<types::ThreadCreate>,
|
pub create: Publisher<types::ThreadCreate>,
|
||||||
pub update: GatewayEvent<types::ThreadUpdate>,
|
pub update: Publisher<types::ThreadUpdate>,
|
||||||
pub delete: GatewayEvent<types::ThreadDelete>,
|
pub delete: Publisher<types::ThreadDelete>,
|
||||||
pub list_sync: GatewayEvent<types::ThreadListSync>,
|
pub list_sync: Publisher<types::ThreadListSync>,
|
||||||
pub member_update: GatewayEvent<types::ThreadMemberUpdate>,
|
pub member_update: Publisher<types::ThreadMemberUpdate>,
|
||||||
pub members_update: GatewayEvent<types::ThreadMembersUpdate>,
|
pub members_update: Publisher<types::ThreadMembersUpdate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Guild {
|
pub struct Guild {
|
||||||
pub create: GatewayEvent<types::GuildCreate>,
|
pub create: Publisher<types::GuildCreate>,
|
||||||
pub update: GatewayEvent<types::GuildUpdate>,
|
pub update: Publisher<types::GuildUpdate>,
|
||||||
pub delete: GatewayEvent<types::GuildDelete>,
|
pub delete: Publisher<types::GuildDelete>,
|
||||||
pub audit_log_entry_create: GatewayEvent<types::GuildAuditLogEntryCreate>,
|
pub audit_log_entry_create: Publisher<types::GuildAuditLogEntryCreate>,
|
||||||
pub ban_add: GatewayEvent<types::GuildBanAdd>,
|
pub ban_add: Publisher<types::GuildBanAdd>,
|
||||||
pub ban_remove: GatewayEvent<types::GuildBanRemove>,
|
pub ban_remove: Publisher<types::GuildBanRemove>,
|
||||||
pub emojis_update: GatewayEvent<types::GuildEmojisUpdate>,
|
pub emojis_update: Publisher<types::GuildEmojisUpdate>,
|
||||||
pub stickers_update: GatewayEvent<types::GuildStickersUpdate>,
|
pub stickers_update: Publisher<types::GuildStickersUpdate>,
|
||||||
pub integrations_update: GatewayEvent<types::GuildIntegrationsUpdate>,
|
pub integrations_update: Publisher<types::GuildIntegrationsUpdate>,
|
||||||
pub member_add: GatewayEvent<types::GuildMemberAdd>,
|
pub member_add: Publisher<types::GuildMemberAdd>,
|
||||||
pub member_remove: GatewayEvent<types::GuildMemberRemove>,
|
pub member_remove: Publisher<types::GuildMemberRemove>,
|
||||||
pub member_update: GatewayEvent<types::GuildMemberUpdate>,
|
pub member_update: Publisher<types::GuildMemberUpdate>,
|
||||||
pub members_chunk: GatewayEvent<types::GuildMembersChunk>,
|
pub members_chunk: Publisher<types::GuildMembersChunk>,
|
||||||
pub role_create: GatewayEvent<types::GuildRoleCreate>,
|
pub role_create: Publisher<types::GuildRoleCreate>,
|
||||||
pub role_update: GatewayEvent<types::GuildRoleUpdate>,
|
pub role_update: Publisher<types::GuildRoleUpdate>,
|
||||||
pub role_delete: GatewayEvent<types::GuildRoleDelete>,
|
pub role_delete: Publisher<types::GuildRoleDelete>,
|
||||||
pub role_scheduled_event_create: GatewayEvent<types::GuildScheduledEventCreate>,
|
pub role_scheduled_event_create: Publisher<types::GuildScheduledEventCreate>,
|
||||||
pub role_scheduled_event_update: GatewayEvent<types::GuildScheduledEventUpdate>,
|
pub role_scheduled_event_update: Publisher<types::GuildScheduledEventUpdate>,
|
||||||
pub role_scheduled_event_delete: GatewayEvent<types::GuildScheduledEventDelete>,
|
pub role_scheduled_event_delete: Publisher<types::GuildScheduledEventDelete>,
|
||||||
pub role_scheduled_event_user_add: GatewayEvent<types::GuildScheduledEventUserAdd>,
|
pub role_scheduled_event_user_add: Publisher<types::GuildScheduledEventUserAdd>,
|
||||||
pub role_scheduled_event_user_remove: GatewayEvent<types::GuildScheduledEventUserRemove>,
|
pub role_scheduled_event_user_remove: Publisher<types::GuildScheduledEventUserRemove>,
|
||||||
pub passive_update_v1: GatewayEvent<types::PassiveUpdateV1>,
|
pub passive_update_v1: Publisher<types::PassiveUpdateV1>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Invite {
|
pub struct Invite {
|
||||||
pub create: GatewayEvent<types::InviteCreate>,
|
pub create: Publisher<types::InviteCreate>,
|
||||||
pub delete: GatewayEvent<types::InviteDelete>,
|
pub delete: Publisher<types::InviteDelete>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Integration {
|
pub struct Integration {
|
||||||
pub create: GatewayEvent<types::IntegrationCreate>,
|
pub create: Publisher<types::IntegrationCreate>,
|
||||||
pub update: GatewayEvent<types::IntegrationUpdate>,
|
pub update: Publisher<types::IntegrationUpdate>,
|
||||||
pub delete: GatewayEvent<types::IntegrationDelete>,
|
pub delete: Publisher<types::IntegrationDelete>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Interaction {
|
pub struct Interaction {
|
||||||
pub create: GatewayEvent<types::InteractionCreate>,
|
pub create: Publisher<types::InteractionCreate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Call {
|
pub struct Call {
|
||||||
pub create: GatewayEvent<types::CallCreate>,
|
pub create: Publisher<types::CallCreate>,
|
||||||
pub update: GatewayEvent<types::CallUpdate>,
|
pub update: Publisher<types::CallUpdate>,
|
||||||
pub delete: GatewayEvent<types::CallDelete>,
|
pub delete: Publisher<types::CallDelete>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Voice {
|
pub struct Voice {
|
||||||
pub state_update: GatewayEvent<types::VoiceStateUpdate>,
|
pub state_update: Publisher<types::VoiceStateUpdate>,
|
||||||
pub server_update: GatewayEvent<types::VoiceServerUpdate>,
|
pub server_update: Publisher<types::VoiceServerUpdate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Webhooks {
|
pub struct Webhooks {
|
||||||
pub update: GatewayEvent<types::WebhooksUpdate>,
|
pub update: Publisher<types::WebhooksUpdate>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
|
|
||||||
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::*;
|
||||||
|
use pubserve::Publisher;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
|
@ -19,6 +21,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 +33,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: &str,
|
||||||
|
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 +72,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 +129,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 +146,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 +155,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 +172,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +198,7 @@ impl Gateway {
|
||||||
#[allow(dead_code)] // TODO: Remove this allow annotation
|
#[allow(dead_code)] // TODO: Remove this allow annotation
|
||||||
async fn handle_event<'a, T: WebSocketEvent + serde::Deserialize<'a>>(
|
async fn handle_event<'a, T: WebSocketEvent + serde::Deserialize<'a>>(
|
||||||
data: &'a str,
|
data: &'a str,
|
||||||
event: &mut GatewayEvent<T>,
|
event: &mut Publisher<T>,
|
||||||
) -> Result<(), serde_json::Error> {
|
) -> Result<(), serde_json::Error> {
|
||||||
let data_deserialize_result: Result<T, serde_json::Error> = serde_json::from_str(data);
|
let data_deserialize_result: Result<T, serde_json::Error> = serde_json::from_str(data);
|
||||||
|
|
||||||
|
@ -152,12 +206,46 @@ impl Gateway {
|
||||||
return Err(data_deserialize_result.err().unwrap());
|
return Err(data_deserialize_result.err().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
event.notify(data_deserialize_result.unwrap()).await;
|
event.publish(data_deserialize_result.unwrap()).await;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +254,7 @@ impl Gateway {
|
||||||
if let Some(error) = msg.error() {
|
if let Some(error) = msg.error() {
|
||||||
warn!("GW: Received error {:?}, connection will close..", error);
|
warn!("GW: Received error {:?}, connection will close..", error);
|
||||||
self.close().await;
|
self.close().await;
|
||||||
self.events.lock().await.error.notify(error).await;
|
self.events.lock().await.error.publish(error).await;
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
"Message unrecognised: {:?}, please open an issue on the chorus github",
|
"Message unrecognised: {:?}, please open an issue on the chorus github",
|
||||||
|
@ -194,7 +282,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;
|
||||||
|
@ -202,7 +293,7 @@ impl Gateway {
|
||||||
let id = if message.id().is_some() {
|
let id = if message.id().is_some() {
|
||||||
message.id().unwrap()
|
message.id().unwrap()
|
||||||
} else {
|
} else {
|
||||||
event.notify(message).await;
|
event.publish(message).await;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if let Some(to_update) = store.get(&id) {
|
if let Some(to_update) = store.get(&id) {
|
||||||
|
@ -224,25 +315,22 @@ impl Gateway {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)?
|
)?
|
||||||
event.notify(message).await;
|
event.publish(message).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},)*
|
},)*
|
||||||
"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) => {
|
||||||
self.events.lock().await.session.replace.notify(
|
self.events.lock().await.session.replace.publish(
|
||||||
types::SessionsReplace {sessions}
|
types::SessionsReplace {sessions}
|
||||||
).await;
|
).await;
|
||||||
}
|
}
|
||||||
|
@ -250,6 +338,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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -358,7 +447,7 @@ impl Gateway {
|
||||||
.await
|
.await
|
||||||
.session
|
.session
|
||||||
.reconnect
|
.reconnect
|
||||||
.notify(reconnect)
|
.publish(reconnect)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
GATEWAY_INVALID_SESSION => {
|
GATEWAY_INVALID_SESSION => {
|
||||||
|
@ -383,7 +472,7 @@ impl Gateway {
|
||||||
.await
|
.await
|
||||||
.session
|
.session
|
||||||
.invalid
|
.invalid
|
||||||
.notify(invalid_session)
|
.publish(invalid_session)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
// Starts our heartbeat
|
// Starts our heartbeat
|
||||||
|
|
|
@ -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
|
||||||
|
@ -154,7 +154,7 @@ impl GatewayHandle {
|
||||||
|
|
||||||
/// Sends a call sync to the server
|
/// Sends a call sync to the server
|
||||||
pub async fn send_call_sync(&self, to_send: types::CallSync) {
|
pub async fn send_call_sync(&self, to_send: types::CallSync) {
|
||||||
let to_send_value = serde_json::to_value(&to_send).unwrap();
|
let to_send_value = serde_json::to_value(to_send).unwrap();
|
||||||
|
|
||||||
trace!("GW: Sending Call Sync..");
|
trace!("GW: Sending Call Sync..");
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// 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 async_trait::async_trait;
|
|
||||||
|
|
||||||
pub mod backends;
|
pub mod backends;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
@ -10,15 +9,17 @@ 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;
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -76,67 +77,11 @@ const GATEWAY_LAZY_REQUEST: u8 = 14;
|
||||||
|
|
||||||
pub type ObservableObject = dyn Send + Sync + Any;
|
pub type ObservableObject = dyn Send + Sync + Any;
|
||||||
|
|
||||||
|
/// Note: this is a reexport of [pubserve::Subscriber],
|
||||||
|
/// exported not to break the public api and make development easier
|
||||||
|
pub use pubserve::Subscriber as Observer;
|
||||||
|
|
||||||
/// An entity type which is supposed to be updateable via the Gateway. This is implemented for all such types chorus supports, implementing it for your own types is likely a mistake.
|
/// An entity type which is supposed to be updateable via the Gateway. This is implemented for all such types chorus supports, implementing it for your own types is likely a mistake.
|
||||||
pub trait Updateable: 'static + Send + Sync {
|
pub trait Updateable: 'static + Send + Sync {
|
||||||
fn id(&self) -> Snowflake;
|
fn id(&self) -> Snowflake;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait which defines the behavior of an Observer. An Observer is an object which is subscribed to
|
|
||||||
/// an Observable. The Observer is notified when the Observable's data changes.
|
|
||||||
/// In this case, the Observable is a [`GatewayEvent`], which is a wrapper around a WebSocketEvent.
|
|
||||||
/// Note that `Debug` is used to tell `Observer`s apart when unsubscribing.
|
|
||||||
#[async_trait]
|
|
||||||
pub trait Observer<T>: Sync + Send + std::fmt::Debug {
|
|
||||||
async fn update(&self, data: &T);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// GatewayEvent is a wrapper around a WebSocketEvent. It is used to notify the observers of a
|
|
||||||
/// change in the WebSocketEvent. GatewayEvents are observable.
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct GatewayEvent<T: WebSocketEvent> {
|
|
||||||
observers: Vec<Arc<dyn Observer<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: WebSocketEvent> GatewayEvent<T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
observers: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the GatewayEvent is observed by at least one Observer.
|
|
||||||
pub fn is_observed(&self) -> bool {
|
|
||||||
!self.observers.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Subscribes an Observer to the GatewayEvent.
|
|
||||||
pub fn subscribe(&mut self, observable: Arc<dyn Observer<T>>) {
|
|
||||||
self.observers.push(observable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unsubscribes an Observer from the GatewayEvent.
|
|
||||||
pub fn unsubscribe(&mut self, observable: &dyn Observer<T>) {
|
|
||||||
// .retain()'s closure retains only those elements of the vector, which have a different
|
|
||||||
// pointer value than observable.
|
|
||||||
// The usage of the debug format to compare the generic T of observers is quite stupid, but the only thing to compare between them is T and if T == T they are the same
|
|
||||||
// anddd there is no way to do that without using format
|
|
||||||
let to_remove = format!("{:?}", observable);
|
|
||||||
self.observers
|
|
||||||
.retain(|obs| format!("{:?}", obs) != to_remove);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notifies the observers of the GatewayEvent.
|
|
||||||
pub(crate) async fn notify(&self, new_event_data: T) {
|
|
||||||
for observer in &self.observers {
|
|
||||||
observer.update(&new_event_data).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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>>;
|
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
// 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, Copy)]
|
||||||
|
/// 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: &str) -> String {
|
||||||
|
let mut url = url.to_string();
|
||||||
|
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,24 +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 {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.urls == other.urls
|
|
||||||
&& self.instance_info == other.instance_info
|
|
||||||
&& self.limits_information == other.limits_information
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::hash::Hash for Instance {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.urls.hash(state);
|
|
||||||
self.instance_info.hash(state);
|
|
||||||
if let Some(inf) = &self.limits_information {
|
|
||||||
inf.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq)]
|
||||||
|
@ -67,6 +51,7 @@ impl std::hash::Hash for LimitsInformation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
impl PartialEq for LimitsInformation {
|
impl PartialEq for LimitsInformation {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.ratelimits.iter().eq(other.ratelimits.iter())
|
self.ratelimits.iter().eq(other.ratelimits.iter())
|
||||||
|
@ -84,8 +69,13 @@ impl Instance {
|
||||||
|
|
||||||
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle).
|
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle).
|
||||||
///
|
///
|
||||||
|
/// If `options` is `None`, the default [`GatewayOptions`] will be used.
|
||||||
|
///
|
||||||
/// To create an Instance from one singular url, use [`Instance::new()`].
|
/// To create an Instance from one singular url, use [`Instance::new()`].
|
||||||
pub async fn from_url_bundle(urls: UrlBundle) -> ChorusResult<Instance> {
|
pub async fn from_url_bundle(
|
||||||
|
urls: UrlBundle,
|
||||||
|
options: Option<GatewayOptions>,
|
||||||
|
) -> ChorusResult<Instance> {
|
||||||
let is_limited: Option<LimitsConfiguration> = Instance::is_limited(&urls.api).await?;
|
let is_limited: Option<LimitsConfiguration> = Instance::is_limited(&urls.api).await?;
|
||||||
let limit_information;
|
let limit_information;
|
||||||
|
|
||||||
|
@ -104,6 +94,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: options.unwrap_or_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,
|
||||||
|
@ -117,14 +108,16 @@ impl Instance {
|
||||||
|
|
||||||
/// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url.
|
/// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url.
|
||||||
///
|
///
|
||||||
|
/// If `options` is `None`, the default [`GatewayOptions`] will be used.
|
||||||
|
///
|
||||||
/// Shorthand for `Instance::from_url_bundle(UrlBundle::from_root_domain(root_domain).await?)`.
|
/// Shorthand for `Instance::from_url_bundle(UrlBundle::from_root_domain(root_domain).await?)`.
|
||||||
pub async fn new(root_url: &str) -> ChorusResult<Instance> {
|
pub async fn new(root_url: &str, options: Option<GatewayOptions>) -> ChorusResult<Instance> {
|
||||||
let urls = UrlBundle::from_root_url(root_url).await?;
|
let urls = UrlBundle::from_root_url(root_url).await?;
|
||||||
Instance::from_url_bundle(urls).await
|
Instance::from_url_bundle(urls, options).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_limited(api_url: &str) -> ChorusResult<Option<LimitsConfiguration>> {
|
pub async fn is_limited(api_url: &str) -> ChorusResult<Option<LimitsConfiguration>> {
|
||||||
let api_url = UrlBundle::parse_url(api_url.to_string());
|
let api_url = UrlBundle::parse_url(api_url);
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let request = client
|
let request = client
|
||||||
.get(format!("{}/policies/instance/limits", &api_url))
|
.get(format!("{}/policies/instance/limits", &api_url))
|
||||||
|
@ -139,6 +132,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)]
|
||||||
|
@ -165,21 +165,13 @@ pub struct ChorusUser {
|
||||||
pub gateway: GatewayHandle,
|
pub gateway: GatewayHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for ChorusUser {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.token == other.token
|
|
||||||
&& self.limits == other.limits
|
|
||||||
&& self.gateway.url == other.gateway.url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChorusUser {
|
impl ChorusUser {
|
||||||
pub fn token(&self) -> String {
|
pub fn token(&self) -> String {
|
||||||
self.token.clone()
|
self.token.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_token(&mut self, token: String) {
|
pub fn set_token(&mut self, token: &str) {
|
||||||
self.token = token;
|
self.token = token.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [ChorusUser] from existing data.
|
/// Creates a new [ChorusUser] from existing data.
|
||||||
|
@ -210,14 +202,15 @@ impl ChorusUser {
|
||||||
/// registering or logging in to the Instance, where you do not yet have a User object, but still
|
/// registering or logging in to the Instance, where you do not yet have a User object, but still
|
||||||
/// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify
|
/// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify
|
||||||
/// first.
|
/// first.
|
||||||
pub(crate) async fn shell(instance: Shared<Instance>, token: String) -> ChorusUser {
|
pub(crate) async fn shell(instance: Shared<Instance>, token: &str) -> ChorusUser {
|
||||||
let settings = Arc::new(RwLock::new(UserSettings::default()));
|
let settings = Arc::new(RwLock::new(UserSettings::default()));
|
||||||
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();
|
||||||
|
let gateway_options = instance.read().unwrap().gateway_options;
|
||||||
// Dummy gateway object
|
// Dummy gateway object
|
||||||
let gateway = Gateway::spawn(wss_url).await.unwrap();
|
let gateway = Gateway::spawn(wss_url, gateway_options).await.unwrap();
|
||||||
ChorusUser {
|
ChorusUser {
|
||||||
token,
|
token: token.to_string(),
|
||||||
belongs_to: instance.clone(),
|
belongs_to: instance.clone(),
|
||||||
limits: instance
|
limits: instance
|
||||||
.read()
|
.read()
|
||||||
|
|
118
src/lib.rs
118
src/lib.rs
|
@ -3,27 +3,29 @@
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Chorus combines all the required functionalities of a user-centric Spacebar library into one package.
|
Chorus is a Rust library which poses as an API wrapper for [Spacebar Chat](https://github.com/spacebarchat/),
|
||||||
|
Discord and our own Polyphony. Its high-level API is designed to be easy to use, while still providing the
|
||||||
|
flexibility one would expect from a library like this.
|
||||||
|
|
||||||
|
You can establish as many connections to as many servers as you want, and you can use them all at the same time.
|
||||||
|
|
||||||
|
## A Tour of Chorus
|
||||||
|
|
||||||
|
Chorus combines all the required functionalities of an API wrapper for chat services into one modular library.
|
||||||
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
|
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
|
||||||
a WebSocket connection to the Gateway. This means that you can focus on building your application,
|
a WebSocket connection to the Gateway. This means that you can focus on building your application,
|
||||||
instead of worrying about the underlying implementation details.
|
instead of worrying about the underlying implementation details.
|
||||||
|
|
||||||
### Establishing a Connection
|
### Establishing a Connection
|
||||||
|
|
||||||
To connect to a Spacebar compatible server, you need to create an [`Instance`](https://docs.rs/chorus/latest/chorus/instance/struct.Instance.html) like this:
|
To connect to a Polyphony/Spacebar compatible server, you'll need to create an [`Instance`](https://docs.rs/chorus/latest/chorus/instance/struct.Instance.html) like this:
|
||||||
|
|
||||||
```rs
|
```rs
|
||||||
use chorus::instance::Instance;
|
use chorus::instance::Instance;
|
||||||
use chorus::UrlBundle;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let bundle = UrlBundle::new(
|
let instance = Instance::new("https://example.com")
|
||||||
"https://example.com/api".to_string(),
|
|
||||||
"wss://example.com/".to_string(),
|
|
||||||
"https://example.com/cdn".to_string(),
|
|
||||||
);
|
|
||||||
let instance = Instance::new(bundle)
|
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to the Spacebar server");
|
.expect("Failed to connect to the Spacebar server");
|
||||||
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
|
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
|
||||||
|
@ -36,7 +38,7 @@ This Instance can now be used to log in, register and from there on, interact wi
|
||||||
|
|
||||||
### Logging In
|
### Logging In
|
||||||
|
|
||||||
Logging in correctly provides you with an instance of [`ChorusUser`](https://docs.rs/chorus/latest/chorus/instance/struct.ChorusUser.html), with which you can interact with the server and
|
Logging in correctly provides you with an instance of `ChorusUser`, with which you can interact with the server and
|
||||||
manipulate the account. Assuming you already have an account on the server, you can log in like this:
|
manipulate the account. Assuming you already have an account on the server, you can log in like this:
|
||||||
|
|
||||||
```rs
|
```rs
|
||||||
|
@ -48,7 +50,7 @@ let login_schema = LoginSchema {
|
||||||
password: "Correct-Horse-Battery-Staple".to_string(),
|
password: "Correct-Horse-Battery-Staple".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
// Each user connects to the Gateway. The Gateway connection lives on a separate thread. Depending on
|
// Each user connects to the Gateway. Each users' Gateway connection lives on a separate thread. Depending on
|
||||||
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
|
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
|
||||||
let user = instance
|
let user = instance
|
||||||
.login_account(login_schema)
|
.login_account(login_schema)
|
||||||
|
@ -64,15 +66,33 @@ All major desktop operating systems (Windows, macOS (aarch64/x86_64), Linux (aar
|
||||||
`wasm32-unknown-unknown` is a supported compilation target on versions `0.12.0` and up. This allows you to use
|
`wasm32-unknown-unknown` is a supported compilation target on versions `0.12.0` and up. This allows you to use
|
||||||
Chorus in your browser, or in any other environment that supports WebAssembly.
|
Chorus in your browser, or in any other environment that supports WebAssembly.
|
||||||
|
|
||||||
We recommend checking out the examples directory, as well as the documentation for more information.
|
To compile for `wasm32-unknown-unknown`, execute the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --target=wasm32-unknown-unknown --no-default-features
|
||||||
|
```
|
||||||
|
|
||||||
|
The following features are supported on `wasm32-unknown-unknown`:
|
||||||
|
|
||||||
|
| Feature | WASM Support |
|
||||||
|
| ----------------- | ------------ |
|
||||||
|
| `client` | ✅ |
|
||||||
|
| `rt` | ✅ |
|
||||||
|
| `rt-multi-thread` | ❌ |
|
||||||
|
| `backend` | ❌ |
|
||||||
|
| `voice` | ❌ |
|
||||||
|
| `voice_udp` | ❌ |
|
||||||
|
| `voice_gateway` | ✅ |
|
||||||
|
|
||||||
|
We recommend checking out the "examples" directory, as well as the documentation for more information.
|
||||||
|
|
||||||
## MSRV (Minimum Supported Rust Version)
|
## MSRV (Minimum Supported Rust Version)
|
||||||
|
|
||||||
Rust **1.67.1**. This number might change at any point while Chorus is not yet at version 1.0.0.
|
Rust **1.70.0**. This number might change at any point while Chorus is not yet at version 1.0.0.
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
Make sure that you have at least Rust 1.67.1 installed. You can check your Rust version by running `cargo --version`
|
Make sure that you have at least Rust 1.70.0 installed. You can check your Rust version by running `cargo --version`
|
||||||
in your terminal. To compile for `wasm32-unknown-unknown`, you need to install the `wasm32-unknown-unknown` target.
|
in your terminal. To compile for `wasm32-unknown-unknown`, you need to install the `wasm32-unknown-unknown` target.
|
||||||
You can do this by running `rustup target add wasm32-unknown-unknown`.
|
You can do this by running `rustup target add wasm32-unknown-unknown`.
|
||||||
|
|
||||||
|
@ -86,31 +106,36 @@ like "proxy connection checking" are already disabled on this version, which oth
|
||||||
### wasm
|
### wasm
|
||||||
|
|
||||||
To test for wasm, you will need to `cargo install wasm-pack`. You can then run
|
To test for wasm, you will need to `cargo install wasm-pack`. You can then run
|
||||||
`wasm-pack test --<chrome/firefox/safari> --headless -- --target wasm32-unknown-unknown --features="rt, client" --no-default-features`
|
`wasm-pack test --<chrome/firefox/safari> --headless -- --target wasm32-unknown-unknown --features="rt, client, voice_gateway" --no-default-features`
|
||||||
to run the tests for wasm.
|
to run the tests for wasm.
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read the specification [here](https://semver.org/spec/v2.0.0.html).
|
This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read the specification [here](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||||
!*/
|
!*/
|
||||||
#![doc(
|
#![doc(
|
||||||
html_logo_url = "https://raw.githubusercontent.com/polyphony-chat/design/main/branding/polyphony-chorus-round-8bit.png"
|
html_logo_url = "https://raw.githubusercontent.com/polyphony-chat/design/main/branding/polyphony-chorus-round-8bit.png"
|
||||||
)]
|
)]
|
||||||
#![allow(clippy::module_inception)]
|
#![allow(clippy::module_inception)]
|
||||||
#![deny(
|
#![deny(
|
||||||
missing_debug_implementations,
|
|
||||||
clippy::extra_unused_lifetimes,
|
clippy::extra_unused_lifetimes,
|
||||||
clippy::from_over_into,
|
clippy::from_over_into,
|
||||||
clippy::needless_borrow,
|
clippy::needless_borrow,
|
||||||
clippy::new_without_default,
|
clippy::new_without_default
|
||||||
clippy::useless_conversion
|
|
||||||
)]
|
)]
|
||||||
#![warn(
|
#![warn(
|
||||||
clippy::todo,
|
clippy::todo,
|
||||||
clippy::unimplemented,
|
clippy::unimplemented,
|
||||||
clippy::dbg_macro,
|
clippy::dbg_macro,
|
||||||
clippy::print_stdout,
|
clippy::print_stdout,
|
||||||
clippy::print_stderr
|
clippy::print_stderr,
|
||||||
|
missing_debug_implementations,
|
||||||
|
missing_copy_implementations,
|
||||||
|
clippy::useless_conversion
|
||||||
)]
|
)]
|
||||||
#[cfg(all(feature = "rt", feature = "rt_multi_thread"))]
|
#[cfg(all(feature = "rt", feature = "rt_multi_thread"))]
|
||||||
compile_error!("feature \"rt\" and feature \"rt_multi_thread\" cannot be enabled at the same time");
|
compile_error!("feature \"rt\" and feature \"rt_multi_thread\" cannot be enabled at the same time");
|
||||||
|
@ -138,6 +163,27 @@ pub mod types;
|
||||||
))]
|
))]
|
||||||
pub mod voice;
|
pub mod voice;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt128 = u128;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt128 = sqlx_pg_uint::PgU128;
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt64 = u64;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt64 = sqlx_pg_uint::PgU64;
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt32 = u32;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt32 = sqlx_pg_uint::PgU32;
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt16 = u16;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt16 = sqlx_pg_uint::PgU16;
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt8 = u8;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt8 = sqlx_pg_uint::PgU8;
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
/// A URLBundle bundles together the API-, Gateway- and CDN-URLs of a Spacebar instance.
|
/// A URLBundle bundles together the API-, Gateway- and CDN-URLs of a Spacebar instance.
|
||||||
///
|
///
|
||||||
|
@ -164,7 +210,7 @@ pub struct UrlBundle {
|
||||||
|
|
||||||
impl UrlBundle {
|
impl UrlBundle {
|
||||||
/// Creates a new UrlBundle from the relevant urls.
|
/// Creates a new UrlBundle from the relevant urls.
|
||||||
pub fn new(root: String, api: String, wss: String, cdn: String) -> Self {
|
pub fn new(root: &str, api: &str, wss: &str, cdn: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
root: UrlBundle::parse_url(root),
|
root: UrlBundle::parse_url(root),
|
||||||
api: UrlBundle::parse_url(api),
|
api: UrlBundle::parse_url(api),
|
||||||
|
@ -181,17 +227,17 @@ impl UrlBundle {
|
||||||
/// let url = parse_url("localhost:3000");
|
/// let url = parse_url("localhost:3000");
|
||||||
/// ```
|
/// ```
|
||||||
/// `-> Outputs "http://localhost:3000".`
|
/// `-> Outputs "http://localhost:3000".`
|
||||||
pub fn parse_url(url: String) -> String {
|
pub fn parse_url(url: &str) -> String {
|
||||||
let url = match Url::parse(&url) {
|
let url = match Url::parse(url) {
|
||||||
Ok(url) => {
|
Ok(url) => {
|
||||||
if url.scheme() == "localhost" {
|
if url.scheme() == "localhost" {
|
||||||
return UrlBundle::parse_url(format!("http://{}", url));
|
return UrlBundle::parse_url(&format!("http://{}", url));
|
||||||
}
|
}
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
Err(ParseError::RelativeUrlWithoutBase) => {
|
Err(ParseError::RelativeUrlWithoutBase) => {
|
||||||
let url_fmt = format!("http://{}", url);
|
let url_fmt = format!("http://{}", url);
|
||||||
return UrlBundle::parse_url(url_fmt);
|
return UrlBundle::parse_url(&url_fmt);
|
||||||
}
|
}
|
||||||
Err(_) => panic!("Invalid URL"), // TODO: should not panic here
|
Err(_) => panic!("Invalid URL"), // TODO: should not panic here
|
||||||
};
|
};
|
||||||
|
@ -214,7 +260,7 @@ impl UrlBundle {
|
||||||
/// of the above approaches fail, it is very likely that the instance is misconfigured, unreachable, or that
|
/// of the above approaches fail, it is very likely that the instance is misconfigured, unreachable, or that
|
||||||
/// a wrong URL was provided.
|
/// a wrong URL was provided.
|
||||||
pub async fn from_root_url(url: &str) -> ChorusResult<UrlBundle> {
|
pub async fn from_root_url(url: &str) -> ChorusResult<UrlBundle> {
|
||||||
let parsed = UrlBundle::parse_url(url.to_string());
|
let parsed = UrlBundle::parse_url(url);
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let request_wellknown = client
|
let request_wellknown = client
|
||||||
.get(format!("{}/.well-known/spacebar", &parsed))
|
.get(format!("{}/.well-known/spacebar", &parsed))
|
||||||
|
@ -252,10 +298,10 @@ impl UrlBundle {
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(UrlBundle::new(
|
Ok(UrlBundle::new(
|
||||||
url.to_string(),
|
url,
|
||||||
body.api_endpoint,
|
&body.api_endpoint,
|
||||||
body.gateway,
|
&body.gateway,
|
||||||
body.cdn,
|
&body.cdn,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(ChorusError::RequestFailed {
|
Err(ChorusError::RequestFailed {
|
||||||
|
@ -272,13 +318,13 @@ mod lib {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_url() {
|
fn test_parse_url() {
|
||||||
let mut result = UrlBundle::parse_url(String::from("localhost:3000/"));
|
let mut result = UrlBundle::parse_url("localhost:3000/");
|
||||||
assert_eq!(result, String::from("http://localhost:3000"));
|
assert_eq!(result, "http://localhost:3000");
|
||||||
result = UrlBundle::parse_url(String::from("https://some.url.com/"));
|
result = UrlBundle::parse_url("https://some.url.com/");
|
||||||
assert_eq!(result, String::from("https://some.url.com"));
|
|
||||||
result = UrlBundle::parse_url(String::from("https://some.url.com/"));
|
|
||||||
assert_eq!(result, String::from("https://some.url.com"));
|
|
||||||
result = UrlBundle::parse_url(String::from("https://some.url.com"));
|
|
||||||
assert_eq!(result, String::from("https://some.url.com"));
|
assert_eq!(result, String::from("https://some.url.com"));
|
||||||
|
result = UrlBundle::parse_url("https://some.url.com/");
|
||||||
|
assert_eq!(result, "https://some.url.com");
|
||||||
|
result = UrlBundle::parse_url("https://some.url.com");
|
||||||
|
assert_eq!(result, "https://some.url.com");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use log::{self, debug};
|
|
||||||
use reqwest::{Client, RequestBuilder, Response};
|
use reqwest::{Client, RequestBuilder, Response};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
|
@ -88,7 +87,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 +493,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) => {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::defaults::{guild::GuildDefaults, user::UserDefaults};
|
use crate::types::config::types::subconfigs::defaults::{guild::GuildDefaults, user::UserDefaults};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Copy)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DefaultsConfiguration {
|
pub struct DefaultsConfiguration {
|
||||||
pub guild: GuildDefaults,
|
pub guild: GuildDefaults,
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -171,9 +162,11 @@ 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::Postgres> for GuildFeaturesList {
|
||||||
fn decode(value: <MySql as HasValueRef<'r>>::ValueRef) -> Result<Self, BoxDynError> {
|
fn decode(
|
||||||
let v = <&str as Decode<sqlx::MySql>>::decode(value)?;
|
value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>,
|
||||||
|
) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
|
let v = <String as sqlx::Decode<sqlx::Postgres>>::decode(value)?;
|
||||||
Ok(Self(
|
Ok(Self(
|
||||||
v.split(',')
|
v.split(',')
|
||||||
.filter(|f| !f.is_empty())
|
.filter(|f| !f.is_empty())
|
||||||
|
@ -184,10 +177,13 @@ 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::Postgres> for GuildFeaturesList {
|
||||||
fn encode_by_ref(&self, buf: &mut <MySql as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
fn encode_by_ref(
|
||||||
|
&self,
|
||||||
|
buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
|
||||||
|
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return IsNull::Yes;
|
return Ok(sqlx::encode::IsNull::Yes);
|
||||||
}
|
}
|
||||||
let features = self
|
let features = self
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -195,30 +191,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::Postgres>>::encode_by_ref(&features, buf)
|
||||||
IsNull::No
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl sqlx::Type<sqlx::MySql> for GuildFeaturesList {
|
impl sqlx::Type<sqlx::Postgres> for GuildFeaturesList {
|
||||||
fn type_info() -> sqlx::mysql::MySqlTypeInfo {
|
fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
|
||||||
<&str as sqlx::Type<sqlx::MySql>>::type_info()
|
<String as sqlx::Type<sqlx::Postgres>>::type_info()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compatible(ty: &sqlx::mysql::MySqlTypeInfo) -> bool {
|
fn compatible(ty: &<sqlx::Postgres as sqlx::Database>::TypeInfo) -> bool {
|
||||||
<&str as sqlx::Type<sqlx::MySql>>::compatible(ty)
|
<String as sqlx::Type<sqlx::Postgres>>::compatible(ty)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
impl sqlx::TypeInfo for GuildFeaturesList {
|
|
||||||
fn is_null(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"TEXT"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,6 +360,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 {
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Copy, Hash, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct LoginConfiguration {
|
pub struct LoginConfiguration {
|
||||||
pub require_captcha: bool,
|
pub require_captcha: bool,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Ord, PartialOrd, Copy, Hash)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MetricsConfiguration {
|
pub struct MetricsConfiguration {
|
||||||
pub timeout: u64,
|
pub timeout: u64,
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Copy, Hash, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PasswordResetConfiguration {
|
pub struct PasswordResetConfiguration {
|
||||||
pub require_captcha: bool,
|
pub require_captcha: bool,
|
||||||
|
|
|
@ -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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{ExplicitContentFilterLevel, MessageNotificationLevel};
|
use crate::types::{ExplicitContentFilterLevel, MessageNotificationLevel};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, Hash, PartialOrd, Ord)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GuildDefaults {
|
pub struct GuildDefaults {
|
||||||
pub max_presences: u64,
|
pub max_presences: u64,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, Hash, PartialOrd, Ord)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UserDefaults {
|
pub struct UserDefaults {
|
||||||
pub premium: bool,
|
pub premium: bool,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DiscoverConfiguration {
|
pub struct DiscoverConfiguration {
|
||||||
pub show_all_guilds: bool,
|
pub show_all_guilds: bool,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ChannelLimits {
|
pub struct ChannelLimits {
|
||||||
pub max_pins: u16,
|
pub max_pins: u16,
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
pub struct GlobalRateLimit {
|
pub struct GlobalRateLimit {
|
||||||
pub limit: u16,
|
pub limit: u64,
|
||||||
pub window: u64,
|
pub window: u64,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ impl Default for GlobalRateLimit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GlobalRateLimits {
|
pub struct GlobalRateLimits {
|
||||||
pub register: GlobalRateLimit,
|
pub register: GlobalRateLimit,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, Hash, PartialOrd, Ord)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GuildLimits {
|
pub struct GuildLimits {
|
||||||
pub max_roles: u16,
|
pub max_roles: u16,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MessageLimits {
|
pub struct MessageLimits {
|
||||||
pub max_characters: u32,
|
pub max_characters: u32,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::limits::ratelimits::RateLimitOptions;
|
use crate::types::config::types::subconfigs::limits::ratelimits::RateLimitOptions;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
pub struct AuthRateLimit {
|
pub struct AuthRateLimit {
|
||||||
pub login: RateLimitOptions,
|
pub login: RateLimitOptions,
|
||||||
pub register: RateLimitOptions,
|
pub register: RateLimitOptions,
|
||||||
|
|
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod route;
|
pub mod route;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, PartialOrd, Ord, Copy)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RateLimitOptions {
|
pub struct RateLimitOptions {
|
||||||
pub bot: Option<u64>,
|
pub bot: Option<u64>,
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::types::config::types::subconfigs::limits::ratelimits::{
|
||||||
auth::AuthRateLimit, RateLimitOptions,
|
auth::AuthRateLimit, RateLimitOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Copy, PartialOrd, Ord)]
|
||||||
pub struct RouteRateLimit {
|
pub struct RouteRateLimit {
|
||||||
pub guild: RateLimitOptions,
|
pub guild: RateLimitOptions,
|
||||||
pub webhook: RateLimitOptions,
|
pub webhook: RateLimitOptions,
|
||||||
|
|
|
@ -50,14 +50,14 @@ impl Default for RateLimits {
|
||||||
impl RateLimits {
|
impl RateLimits {
|
||||||
pub fn to_hash_map(&self) -> HashMap<LimitType, RateLimitOptions> {
|
pub fn to_hash_map(&self) -> HashMap<LimitType, RateLimitOptions> {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert(LimitType::AuthLogin, self.routes.auth.login.clone());
|
map.insert(LimitType::AuthLogin, self.routes.auth.login);
|
||||||
map.insert(LimitType::AuthRegister, self.routes.auth.register.clone());
|
map.insert(LimitType::AuthRegister, self.routes.auth.register);
|
||||||
map.insert(LimitType::ChannelBaseline, self.routes.channel.clone());
|
map.insert(LimitType::ChannelBaseline, self.routes.channel);
|
||||||
map.insert(LimitType::Error, self.error.clone());
|
map.insert(LimitType::Error, self.error);
|
||||||
map.insert(LimitType::Global, self.global.clone());
|
map.insert(LimitType::Global, self.global);
|
||||||
map.insert(LimitType::Ip, self.ip.clone());
|
map.insert(LimitType::Ip, self.ip);
|
||||||
map.insert(LimitType::WebhookBaseline, self.routes.webhook.clone());
|
map.insert(LimitType::WebhookBaseline, self.routes.webhook);
|
||||||
map.insert(LimitType::GuildBaseline, self.routes.guild.clone());
|
map.insert(LimitType::GuildBaseline, self.routes.guild);
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Copy, Hash)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct UserLimits {
|
pub struct UserLimits {
|
||||||
pub max_guilds: u64,
|
pub max_guilds: u64,
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, PartialOrd, Copy)]
|
||||||
pub struct LatLong {
|
pub struct LatLong {
|
||||||
pub latitude: f64,
|
pub latitude: f64,
|
||||||
pub longitude: f64,
|
pub longitude: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, PartialOrd)]
|
||||||
pub struct Region {
|
pub struct Region {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
pub struct DateOfBirthConfiguration {
|
pub struct DateOfBirthConfiguration {
|
||||||
pub required: bool,
|
pub required: bool,
|
||||||
pub minimum: u8,
|
pub minimum: u8,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PasswordConfiguration {
|
pub struct PasswordConfiguration {
|
||||||
pub required: bool,
|
pub required: bool,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, Hash, PartialOrd, Ord)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct TwoFactorConfiguration {
|
pub struct TwoFactorConfiguration {
|
||||||
pub generate_backup_codes: bool,
|
pub generate_backup_codes: bool,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct TemplateConfiguration {
|
pub struct TemplateConfiguration {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
|
|
|
@ -7,10 +7,13 @@ 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::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
use crate::types::Shared;
|
||||||
use crate::types::{Team, User};
|
use crate::types::{Team, User};
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use super::{arc_rwlock_ptr_eq, option_arc_rwlock_ptr_eq};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
|
@ -31,7 +34,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"))]
|
||||||
|
@ -59,6 +62,64 @@ pub struct Application {
|
||||||
pub team: Option<Team>,
|
pub team: Option<Team>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
impl PartialEq for Application {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
&& self.name == other.name
|
||||||
|
&& self.icon == other.icon
|
||||||
|
&& self.description == other.description
|
||||||
|
&& self.summary == other.summary
|
||||||
|
&& self.r#type == other.r#type
|
||||||
|
&& self.hook == other.hook
|
||||||
|
&& self.bot_public == other.bot_public
|
||||||
|
&& self.bot_require_code_grant == other.bot_require_code_grant
|
||||||
|
&& self.verify_key == other.verify_key
|
||||||
|
&& arc_rwlock_ptr_eq(&self.owner, &other.owner)
|
||||||
|
&& self.flags == other.flags
|
||||||
|
&& self.redirect_uris == other.redirect_uris
|
||||||
|
&& self.rpc_application_state == other.rpc_application_state
|
||||||
|
&& self.store_application_state == other.store_application_state
|
||||||
|
&& self.verification_state == other.verification_state
|
||||||
|
&& self.interactions_endpoint_url == other.interactions_endpoint_url
|
||||||
|
&& self.integration_public == other.integration_public
|
||||||
|
&& self.integration_require_code_grant == other.integration_require_code_grant
|
||||||
|
&& self.discoverability_state == other.discoverability_state
|
||||||
|
&& self.discovery_eligibility_flags == other.discovery_eligibility_flags
|
||||||
|
&& self.tags == other.tags
|
||||||
|
&& self.cover_image == other.cover_image
|
||||||
|
&& compare_install_params(&self.install_params, &other.install_params)
|
||||||
|
&& self.terms_of_service_url == other.terms_of_service_url
|
||||||
|
&& self.privacy_policy_url == other.privacy_policy_url
|
||||||
|
&& self.team == other.team
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
fn compare_install_params(
|
||||||
|
a: &Option<sqlx::types::Json<InstallParams>>,
|
||||||
|
b: &Option<sqlx::types::Json<InstallParams>>,
|
||||||
|
) -> bool {
|
||||||
|
match (a, b) {
|
||||||
|
(Some(a), Some(b)) => match (a.encode_to_string(), b.encode_to_string()) {
|
||||||
|
(Ok(a), Ok(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
(None, None) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
fn compare_install_params(
|
||||||
|
a: &Option<Shared<InstallParams>>,
|
||||||
|
b: &Option<Shared<InstallParams>>,
|
||||||
|
) -> bool {
|
||||||
|
option_arc_rwlock_ptr_eq(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Application {
|
impl Default for Application {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -73,7 +134,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 +154,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 +163,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 {
|
||||||
|
@ -168,7 +224,8 @@ pub struct ApplicationCommandOptionChoice {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(i32)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-types>
|
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-types>
|
||||||
pub enum ApplicationCommandOptionType {
|
pub enum ApplicationCommandOptionType {
|
||||||
|
@ -212,7 +269,9 @@ pub struct GuildApplicationCommandPermissions {
|
||||||
pub permissions: Vec<Shared<ApplicationCommandPermission>>,
|
pub permissions: Vec<Shared<ApplicationCommandPermission>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Debug, Default, Clone, PartialEq, Serialize, Deserialize, Copy, Eq, Hash, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure>
|
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure>
|
||||||
pub struct ApplicationCommandPermission {
|
pub struct ApplicationCommandPermission {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
|
@ -222,9 +281,22 @@ pub struct ApplicationCommandPermission {
|
||||||
pub permission: bool,
|
pub permission: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq, Eq, Hash)]
|
#[derive(
|
||||||
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
Copy,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permission-type>
|
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permission-type>
|
||||||
pub enum ApplicationCommandPermissionType {
|
pub enum ApplicationCommandPermissionType {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, PartialOrd)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -16,11 +17,11 @@ pub struct Attachment {
|
||||||
/// Max 1024 characters
|
/// Max 1024 characters
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub content_type: Option<String>,
|
pub content_type: Option<String>,
|
||||||
pub size: u64,
|
pub size: UInt64,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub proxy_url: String,
|
pub proxy_url: String,
|
||||||
pub height: Option<u64>,
|
pub height: Option<UInt64>,
|
||||||
pub width: Option<u64>,
|
pub width: Option<UInt64>,
|
||||||
pub ephemeral: Option<bool>,
|
pub ephemeral: Option<bool>,
|
||||||
/// The duration of the audio file (only for voice messages)
|
/// The duration of the audio file (only for voice messages)
|
||||||
pub duration_secs: Option<f32>,
|
pub duration_secs: Option<f32>,
|
||||||
|
@ -37,12 +38,12 @@ pub struct Attachment {
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct PartialDiscordFileAttachment {
|
pub struct PartialDiscordFileAttachment {
|
||||||
pub id: Option<i16>,
|
pub id: Option<UInt64>,
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
/// Max 1024 characters
|
/// Max 1024 characters
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub content_type: Option<String>,
|
pub content_type: Option<String>,
|
||||||
pub size: Option<i64>,
|
pub size: Option<UInt64>,
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub proxy_url: Option<String>,
|
pub proxy_url: Option<String>,
|
||||||
pub height: Option<i32>,
|
pub height: Option<i32>,
|
||||||
|
|
|
@ -2,25 +2,92 @@
|
||||||
// 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 serde::{Deserialize, Serialize};
|
#[allow(unused_imports)]
|
||||||
|
use super::option_vec_arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
use crate::gateway::Shared;
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
use crate::types::{
|
||||||
|
AutoModerationRuleTriggerType, IntegrationType, PermissionOverwriteType, Shared,
|
||||||
|
};
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
#[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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for AuditLogEntry {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.target_id == other.target_id
|
||||||
|
&& self.user_id == other.user_id
|
||||||
|
&& self.id == other.id
|
||||||
|
&& self.action_type == other.action_type
|
||||||
|
&& compare_options(&self.options, &other.options)
|
||||||
|
&& self.reason == other.reason
|
||||||
|
&& compare_changes(&self.changes, &other.changes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
fn compare_options(
|
||||||
|
a: &Option<sqlx::types::Json<AuditEntryInfo>>,
|
||||||
|
b: &Option<sqlx::types::Json<AuditEntryInfo>>,
|
||||||
|
) -> bool {
|
||||||
|
match (a, b) {
|
||||||
|
(Some(a), Some(b)) => match (a.encode_to_string(), b.encode_to_string()) {
|
||||||
|
(Ok(a), Ok(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
(None, None) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
fn compare_options(a: &Option<AuditEntryInfo>, b: &Option<AuditEntryInfo>) -> bool {
|
||||||
|
a == b
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
fn compare_changes(
|
||||||
|
a: &sqlx::types::Json<Option<Vec<Shared<AuditLogChange>>>>,
|
||||||
|
b: &sqlx::types::Json<Option<Vec<Shared<AuditLogChange>>>>,
|
||||||
|
) -> bool {
|
||||||
|
match (a.encode_to_string(), b.encode_to_string()) {
|
||||||
|
(Ok(a), Ok(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
fn compare_changes(
|
||||||
|
a: &Option<Vec<Shared<AuditLogChange>>>,
|
||||||
|
b: &Option<Vec<Shared<AuditLogChange>>>,
|
||||||
|
) -> bool {
|
||||||
|
option_vec_arc_rwlock_ptr_eq(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
/// See <https://discord.com/developers/docs/resources/audit-log#audit-log-change-object>
|
/// See <https://discord.com/developers/docs/resources/audit-log#audit-log-change-object>
|
||||||
pub struct AuditLogChange {
|
pub struct AuditLogChange {
|
||||||
|
@ -28,3 +95,176 @@ 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,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
|
#[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, PartialEq)]
|
||||||
|
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<UInt64>,
|
||||||
|
// #[serde(option_string)]
|
||||||
|
pub delete_member_days: Option<UInt64>,
|
||||||
|
/// The ID of the overwritten entity
|
||||||
|
pub id: Option<Snowflake>,
|
||||||
|
pub integration_type: Option<IntegrationType>,
|
||||||
|
// #[serde(option_string)]
|
||||||
|
pub members_removed: Option<UInt64>,
|
||||||
|
// #[serde(option_string)]
|
||||||
|
pub message_id: Option<UInt64>,
|
||||||
|
pub role_name: Option<String>,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub overwrite_type: Option<PermissionOverwriteType>,
|
||||||
|
pub status: Option<String>,
|
||||||
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
// 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;
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use crate::gateway::Updateable;
|
use crate::gateway::Updateable;
|
||||||
|
use crate::types::Shared;
|
||||||
|
use crate::UInt8;
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use chorus_macros::Updateable;
|
use chorus_macros::Updateable;
|
||||||
|
@ -31,8 +32,9 @@ pub struct AutoModerationRule {
|
||||||
pub exempt_channels: Vec<Snowflake>,
|
pub exempt_channels: Vec<Snowflake>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default, Copy)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-event-types>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-event-types>
|
||||||
pub enum AutoModerationRuleEventType {
|
pub enum AutoModerationRuleEventType {
|
||||||
|
@ -40,8 +42,11 @@ pub enum AutoModerationRuleEventType {
|
||||||
MessageSend = 1,
|
MessageSend = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(
|
||||||
#[repr(u8)]
|
Serialize_repr, Deserialize_repr, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types>
|
||||||
pub enum AutoModerationRuleTriggerType {
|
pub enum AutoModerationRuleTriggerType {
|
||||||
|
@ -52,7 +57,7 @@ pub enum AutoModerationRuleTriggerType {
|
||||||
MentionSpam = 5,
|
MentionSpam = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
||||||
pub enum AutoModerationRuleTriggerMetadata {
|
pub enum AutoModerationRuleTriggerMetadata {
|
||||||
|
@ -63,7 +68,7 @@ pub enum AutoModerationRuleTriggerMetadata {
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
||||||
pub struct AutoModerationRuleTriggerMetadataForKeyword {
|
pub struct AutoModerationRuleTriggerMetadataForKeyword {
|
||||||
pub keyword_filter: Vec<String>,
|
pub keyword_filter: Vec<String>,
|
||||||
|
@ -71,23 +76,27 @@ pub struct AutoModerationRuleTriggerMetadataForKeyword {
|
||||||
pub allow_list: Vec<String>,
|
pub allow_list: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
||||||
pub struct AutoModerationRuleTriggerMetadataForKeywordPreset {
|
pub struct AutoModerationRuleTriggerMetadataForKeywordPreset {
|
||||||
pub presets: Vec<AutoModerationRuleKeywordPresetType>,
|
pub presets: Vec<AutoModerationRuleKeywordPresetType>,
|
||||||
pub allow_list: Vec<String>,
|
pub allow_list: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[allow(missing_copy_implementations)]
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
||||||
pub struct AutoModerationRuleTriggerMetadataForMentionSpam {
|
pub struct AutoModerationRuleTriggerMetadataForMentionSpam {
|
||||||
/// Max 50
|
/// Max 50
|
||||||
pub mention_total_limit: u8,
|
pub mention_total_limit: UInt8,
|
||||||
pub mention_raid_protection_enabled: bool,
|
pub mention_raid_protection_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(
|
||||||
#[repr(u8)]
|
Serialize_repr, Deserialize_repr, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-keyword-preset-types>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-keyword-preset-types>
|
||||||
pub enum AutoModerationRuleKeywordPresetType {
|
pub enum AutoModerationRuleKeywordPresetType {
|
||||||
|
@ -105,8 +114,21 @@ pub struct AutoModerationAction {
|
||||||
pub metadata: Option<Shared<AutoModerationActionMetadata>>,
|
pub metadata: Option<Shared<AutoModerationActionMetadata>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(
|
||||||
#[repr(u8)]
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Default,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Copy,
|
||||||
|
Hash,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-types>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-types>
|
||||||
pub enum AutoModerationActionType {
|
pub enum AutoModerationActionType {
|
||||||
|
@ -116,7 +138,7 @@ pub enum AutoModerationActionType {
|
||||||
Timeout = 3,
|
Timeout = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata>
|
||||||
pub enum AutoModerationActionMetadata {
|
pub enum AutoModerationActionMetadata {
|
||||||
|
@ -127,19 +149,19 @@ pub enum AutoModerationActionMetadata {
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata>
|
||||||
pub struct AutoModerationActionMetadataForBlockMessage {
|
pub struct AutoModerationActionMetadataForBlockMessage {
|
||||||
pub custom_message: Option<String>,
|
pub custom_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy)]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata>
|
||||||
pub struct AutoModerationActionMetadataForSendAlertMessage {
|
pub struct AutoModerationActionMetadataForSendAlertMessage {
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy)]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-metadata>
|
||||||
pub struct AutoModerationActionMetadataForTimeout {
|
pub struct AutoModerationActionMetadataForTimeout {
|
||||||
/// Max 2419200
|
/// Max 2419200
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
// 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::{
|
||||||
entities::{GuildMember, User},
|
entities::{GuildMember, User},
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
|
PermissionFlags, Shared,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
|
@ -22,9 +22,16 @@ use crate::gateway::GatewayHandle;
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use crate::gateway::Updateable;
|
use crate::gateway::Updateable;
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
#[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};
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
use sqlx::types::Json;
|
||||||
|
|
||||||
|
use super::{option_arc_rwlock_ptr_eq, option_vec_arc_rwlock_ptr_eq};
|
||||||
|
|
||||||
#[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))]
|
||||||
|
@ -35,13 +42,7 @@ use chorus_macros::{observe_option_vec, Composite, Updateable};
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#channels-resource>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#channels-resource>
|
||||||
pub struct Channel {
|
pub struct Channel {
|
||||||
pub application_id: Option<Snowflake>,
|
pub application_id: Option<Snowflake>,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub applied_tags: Option<sqlx::types::Json<Vec<String>>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub applied_tags: Option<Vec<String>>,
|
pub applied_tags: Option<Vec<String>>,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub available_tags: Option<sqlx::types::Json<Vec<Tag>>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub available_tags: Option<Vec<Tag>>,
|
pub available_tags: Option<Vec<Tag>>,
|
||||||
pub bitrate: Option<i32>,
|
pub bitrate: Option<i32>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -49,9 +50,7 @@ pub struct Channel {
|
||||||
pub created_at: Option<chrono::DateTime<Utc>>,
|
pub created_at: Option<chrono::DateTime<Utc>>,
|
||||||
pub default_auto_archive_duration: Option<i32>,
|
pub default_auto_archive_duration: Option<i32>,
|
||||||
pub default_forum_layout: Option<i32>,
|
pub default_forum_layout: Option<i32>,
|
||||||
#[cfg(feature = "sqlx")]
|
// DefaultReaction could be stored in a separate table. However, there are a lot of default emojis. How would we handle that?
|
||||||
pub default_reaction_emoji: Option<sqlx::types::Json<DefaultReaction>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub default_reaction_emoji: Option<DefaultReaction>,
|
pub default_reaction_emoji: Option<DefaultReaction>,
|
||||||
pub default_sort_order: Option<i32>,
|
pub default_sort_order: Option<i32>,
|
||||||
pub default_thread_rate_limit_per_user: Option<i32>,
|
pub default_thread_rate_limit_per_user: Option<i32>,
|
||||||
|
@ -64,7 +63,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 +76,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,19 +87,28 @@ 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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
#[allow(clippy::nonminimal_bool)]
|
||||||
impl PartialEq for Channel {
|
impl PartialEq for Channel {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.application_id == other.application_id
|
self.application_id == other.application_id
|
||||||
|
&& self.applied_tags == other.applied_tags
|
||||||
|
&& self.applied_tags == other.applied_tags
|
||||||
|
&& self.available_tags == other.available_tags
|
||||||
|
&& self.available_tags == other.available_tags
|
||||||
&& self.bitrate == other.bitrate
|
&& self.bitrate == other.bitrate
|
||||||
&& self.channel_type == other.channel_type
|
&& self.channel_type == other.channel_type
|
||||||
&& self.created_at == other.created_at
|
&& self.created_at == other.created_at
|
||||||
&& self.default_auto_archive_duration == other.default_auto_archive_duration
|
&& self.default_auto_archive_duration == other.default_auto_archive_duration
|
||||||
&& self.default_forum_layout == other.default_forum_layout
|
&& self.default_forum_layout == other.default_forum_layout
|
||||||
|
&& self.default_reaction_emoji == other.default_reaction_emoji
|
||||||
|
&& self.default_reaction_emoji == other.default_reaction_emoji
|
||||||
&& self.default_sort_order == other.default_sort_order
|
&& self.default_sort_order == other.default_sort_order
|
||||||
&& self.default_thread_rate_limit_per_user == other.default_thread_rate_limit_per_user
|
&& self.default_thread_rate_limit_per_user == other.default_thread_rate_limit_per_user
|
||||||
&& self.flags == other.flags
|
&& self.flags == other.flags
|
||||||
|
@ -107,16 +118,23 @@ impl PartialEq for Channel {
|
||||||
&& self.last_message_id == other.last_message_id
|
&& self.last_message_id == other.last_message_id
|
||||||
&& self.last_pin_timestamp == other.last_pin_timestamp
|
&& self.last_pin_timestamp == other.last_pin_timestamp
|
||||||
&& self.managed == other.managed
|
&& self.managed == other.managed
|
||||||
|
&& self.member == other.member
|
||||||
&& self.member_count == other.member_count
|
&& self.member_count == other.member_count
|
||||||
&& self.message_count == other.message_count
|
&& self.message_count == other.message_count
|
||||||
&& self.name == other.name
|
&& self.name == other.name
|
||||||
&& self.nsfw == other.nsfw
|
&& self.nsfw == other.nsfw
|
||||||
&& self.owner_id == other.owner_id
|
&& self.owner_id == other.owner_id
|
||||||
&& self.parent_id == other.parent_id
|
&& self.parent_id == other.parent_id
|
||||||
|
&& compare_permission_overwrites(
|
||||||
|
&self.permission_overwrites,
|
||||||
|
&other.permission_overwrites,
|
||||||
|
)
|
||||||
&& self.permissions == other.permissions
|
&& self.permissions == other.permissions
|
||||||
&& self.position == other.position
|
&& self.position == other.position
|
||||||
&& self.rate_limit_per_user == other.rate_limit_per_user
|
&& self.rate_limit_per_user == other.rate_limit_per_user
|
||||||
|
&& option_vec_arc_rwlock_ptr_eq(&self.recipients, &other.recipients)
|
||||||
&& self.rtc_region == other.rtc_region
|
&& self.rtc_region == other.rtc_region
|
||||||
|
&& self.thread_metadata == other.thread_metadata
|
||||||
&& self.topic == other.topic
|
&& self.topic == other.topic
|
||||||
&& self.total_message_sent == other.total_message_sent
|
&& self.total_message_sent == other.total_message_sent
|
||||||
&& self.user_limit == other.user_limit
|
&& self.user_limit == other.user_limit
|
||||||
|
@ -124,11 +142,38 @@ impl PartialEq for Channel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
fn compare_permission_overwrites(
|
||||||
|
a: &Option<Json<Vec<PermissionOverwrite>>>,
|
||||||
|
b: &Option<Json<Vec<PermissionOverwrite>>>,
|
||||||
|
) -> bool {
|
||||||
|
match (a, b) {
|
||||||
|
(Some(a), Some(b)) => match (a.encode_to_string(), b.encode_to_string()) {
|
||||||
|
(Ok(a), Ok(b)) => a == b,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
(None, None) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
fn compare_permission_overwrites(
|
||||||
|
a: &Option<Vec<Shared<PermissionOverwrite>>>,
|
||||||
|
b: &Option<Vec<Shared<PermissionOverwrite>>>,
|
||||||
|
) -> bool {
|
||||||
|
option_vec_arc_rwlock_ptr_eq(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
/// A tag that can be applied to a thread in a [ChannelType::GuildForum] or [ChannelType::GuildMedia] channel.
|
/// A tag that can be applied to a thread in a [ChannelType::GuildForum] or [ChannelType::GuildMedia] channel.
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#forum-tag-object>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#forum-tag-object>
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
|
#[cfg_attr(feature = "sqlx", sqlx(type_name = "interface_type"))]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
/// The name of the tag (max 20 characters)
|
/// The name of the tag (max 20 characters)
|
||||||
|
@ -144,26 +189,105 @@ 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, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize_repr, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
|
/// # 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, PartialOrd, Ord, Hash, Copy)]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#thread-metadata-object>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#thread-metadata-object>
|
||||||
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,16 +296,29 @@ 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<UInt64>,
|
||||||
pub member: Option<Shared<GuildMember>>,
|
pub member: Option<Shared<GuildMember>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
impl PartialEq for ThreadMember {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
&& self.user_id == other.user_id
|
||||||
|
&& self.join_timestamp == other.join_timestamp
|
||||||
|
&& self.flags == other.flags
|
||||||
|
&& option_arc_rwlock_ptr_eq(&self.member, &other.member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#default-reaction-object>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#default-reaction-object>
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
|
#[cfg_attr(feature = "sqlx", sqlx(type_name = "interface_type"))]
|
||||||
pub struct DefaultReaction {
|
pub struct DefaultReaction {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub emoji_id: Option<Snowflake>,
|
pub emoji_id: Option<Snowflake>,
|
||||||
|
@ -203,7 +340,7 @@ pub struct DefaultReaction {
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[repr(u32)]
|
#[repr(i32)]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#channel-type>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#channel-type>
|
||||||
pub enum ChannelType {
|
pub enum ChannelType {
|
||||||
|
@ -256,3 +393,11 @@ 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, Copy, Hash, PartialOrd, Ord)]
|
||||||
|
pub struct FollowedChannel {
|
||||||
|
pub channel_id: Snowflake,
|
||||||
|
pub webhook_id: Snowflake,
|
||||||
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
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::{PartialEmoji, Shared};
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use crate::gateway::GatewayHandle;
|
use crate::gateway::GatewayHandle;
|
||||||
|
@ -22,6 +22,8 @@ use crate::gateway::Updateable;
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use chorus_macros::{Composite, Updateable};
|
use chorus_macros::{Composite, Updateable};
|
||||||
|
|
||||||
|
use super::option_arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
||||||
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -30,9 +32,6 @@ use chorus_macros::{Composite, Updateable};
|
||||||
pub struct Emoji {
|
pub struct Emoji {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub roles: Option<sqlx::types::Json<Vec<Snowflake>>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub roles: Option<Vec<Snowflake>>,
|
pub roles: Option<Vec<Snowflake>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub user: Option<Shared<User>>,
|
pub user: Option<Shared<User>>,
|
||||||
|
@ -42,27 +41,33 @@ pub struct Emoji {
|
||||||
pub available: Option<bool>,
|
pub available: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::hash::Hash for Emoji {
|
#[cfg(not(tarpaulin_include))]
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
#[allow(clippy::nonminimal_bool)]
|
||||||
self.id.hash(state);
|
impl PartialEq for Emoji {
|
||||||
self.name.hash(state);
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.roles.hash(state);
|
self.id == other.id
|
||||||
self.roles.hash(state);
|
&& self.name == other.name
|
||||||
self.require_colons.hash(state);
|
&& self.roles == other.roles
|
||||||
self.managed.hash(state);
|
&& self.roles == other.roles
|
||||||
self.animated.hash(state);
|
&& option_arc_rwlock_ptr_eq(&self.user, &other.user)
|
||||||
self.available.hash(state);
|
&& self.require_colons == other.require_colons
|
||||||
|
&& self.managed == other.managed
|
||||||
|
&& self.animated == other.animated
|
||||||
|
&& self.available == other.available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Emoji {
|
impl From<PartialEmoji> for Emoji {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn from(value: PartialEmoji) -> Self {
|
||||||
!(self.id != other.id
|
Self {
|
||||||
|| self.name != other.name
|
id: value.id.unwrap_or_default(), // TODO: Make this method an impl to TryFrom<> instead
|
||||||
|| self.roles != other.roles
|
name: Some(value.name),
|
||||||
|| self.require_colons != other.require_colons
|
roles: None,
|
||||||
|| self.managed != other.managed
|
user: None,
|
||||||
|| self.animated != other.animated
|
require_colons: Some(value.animated),
|
||||||
|| self.available != other.available)
|
managed: None,
|
||||||
|
animated: Some(value.animated),
|
||||||
|
available: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,27 +3,29 @@
|
||||||
// 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::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use chrono::{DateTime, Utc};
|
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::types::guild_configuration::GuildFeaturesList;
|
use crate::types::types::guild_configuration::GuildFeaturesList;
|
||||||
|
use crate::types::Shared;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
entities::{Channel, Emoji, RoleObject, Sticker, User, VoiceState, Webhook},
|
entities::{Channel, Emoji, RoleObject, Sticker, User, VoiceState, Webhook},
|
||||||
interfaces::WelcomeScreenObject,
|
interfaces::WelcomeScreenObject,
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
use super::PublicUser;
|
use super::{option_arc_rwlock_ptr_eq, vec_arc_rwlock_ptr_eq, PublicUser};
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
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 +48,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 +61,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,86 +97,39 @@ 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>,
|
||||||
pub widget_enabled: Option<bool>,
|
pub widget_enabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::hash::Hash for Guild {
|
#[cfg(not(tarpaulin_include))]
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
impl PartialEq for Guild {
|
||||||
self.afk_channel_id.hash(state);
|
|
||||||
self.afk_timeout.hash(state);
|
|
||||||
self.application_id.hash(state);
|
|
||||||
self.approximate_member_count.hash(state);
|
|
||||||
self.approximate_presence_count.hash(state);
|
|
||||||
self.banner.hash(state);
|
|
||||||
self.bans.hash(state);
|
|
||||||
self.default_message_notifications.hash(state);
|
|
||||||
self.description.hash(state);
|
|
||||||
self.discovery_splash.hash(state);
|
|
||||||
self.explicit_content_filter.hash(state);
|
|
||||||
self.features.hash(state);
|
|
||||||
self.icon.hash(state);
|
|
||||||
self.icon_hash.hash(state);
|
|
||||||
self.id.hash(state);
|
|
||||||
self.invites.hash(state);
|
|
||||||
self.joined_at.hash(state);
|
|
||||||
self.large.hash(state);
|
|
||||||
self.max_members.hash(state);
|
|
||||||
self.max_presences.hash(state);
|
|
||||||
self.max_stage_video_channel_users.hash(state);
|
|
||||||
self.max_video_channel_users.hash(state);
|
|
||||||
self.mfa_level.hash(state);
|
|
||||||
self.name.hash(state);
|
|
||||||
self.nsfw_level.hash(state);
|
|
||||||
self.owner.hash(state);
|
|
||||||
self.owner_id.hash(state);
|
|
||||||
self.permissions.hash(state);
|
|
||||||
self.preferred_locale.hash(state);
|
|
||||||
self.premium_progress_bar_enabled.hash(state);
|
|
||||||
self.premium_subscription_count.hash(state);
|
|
||||||
self.premium_tier.hash(state);
|
|
||||||
self.primary_category_id.hash(state);
|
|
||||||
self.public_updates_channel_id.hash(state);
|
|
||||||
self.region.hash(state);
|
|
||||||
self.rules_channel.hash(state);
|
|
||||||
self.rules_channel_id.hash(state);
|
|
||||||
self.splash.hash(state);
|
|
||||||
self.stickers.hash(state);
|
|
||||||
self.system_channel_flags.hash(state);
|
|
||||||
self.system_channel_id.hash(state);
|
|
||||||
self.vanity_url_code.hash(state);
|
|
||||||
self.verification_level.hash(state);
|
|
||||||
self.welcome_screen.hash(state);
|
|
||||||
self.welcome_screen.hash(state);
|
|
||||||
self.widget_channel_id.hash(state);
|
|
||||||
self.widget_enabled.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::cmp::PartialEq for Guild {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.afk_channel_id == other.afk_channel_id
|
self.afk_channel_id == other.afk_channel_id
|
||||||
&& self.afk_timeout == other.afk_timeout
|
&& self.afk_timeout == other.afk_timeout
|
||||||
|
@ -179,14 +138,17 @@ impl std::cmp::PartialEq for Guild {
|
||||||
&& self.approximate_presence_count == other.approximate_presence_count
|
&& self.approximate_presence_count == other.approximate_presence_count
|
||||||
&& self.banner == other.banner
|
&& self.banner == other.banner
|
||||||
&& self.bans == other.bans
|
&& self.bans == other.bans
|
||||||
|
&& vec_arc_rwlock_ptr_eq(&self.channels, &other.channels)
|
||||||
&& self.default_message_notifications == other.default_message_notifications
|
&& self.default_message_notifications == other.default_message_notifications
|
||||||
&& self.description == other.description
|
&& self.description == other.description
|
||||||
&& self.discovery_splash == other.discovery_splash
|
&& self.discovery_splash == other.discovery_splash
|
||||||
|
&& vec_arc_rwlock_ptr_eq(&self.emojis, &other.emojis)
|
||||||
&& self.explicit_content_filter == other.explicit_content_filter
|
&& self.explicit_content_filter == other.explicit_content_filter
|
||||||
&& self.features == other.features
|
&& self.features == other.features
|
||||||
&& self.icon == other.icon
|
&& self.icon == other.icon
|
||||||
&& self.icon_hash == other.icon_hash
|
&& self.icon_hash == other.icon_hash
|
||||||
&& self.id == other.id
|
&& self.id == other.id
|
||||||
|
&& self.invites == other.invites
|
||||||
&& self.joined_at == other.joined_at
|
&& self.joined_at == other.joined_at
|
||||||
&& self.large == other.large
|
&& self.large == other.large
|
||||||
&& self.max_members == other.max_members
|
&& self.max_members == other.max_members
|
||||||
|
@ -206,6 +168,7 @@ impl std::cmp::PartialEq for Guild {
|
||||||
&& self.primary_category_id == other.primary_category_id
|
&& self.primary_category_id == other.primary_category_id
|
||||||
&& self.public_updates_channel_id == other.public_updates_channel_id
|
&& self.public_updates_channel_id == other.public_updates_channel_id
|
||||||
&& self.region == other.region
|
&& self.region == other.region
|
||||||
|
&& vec_arc_rwlock_ptr_eq(&self.roles, &other.roles)
|
||||||
&& self.rules_channel == other.rules_channel
|
&& self.rules_channel == other.rules_channel
|
||||||
&& self.rules_channel_id == other.rules_channel_id
|
&& self.rules_channel_id == other.rules_channel_id
|
||||||
&& self.splash == other.splash
|
&& self.splash == other.splash
|
||||||
|
@ -214,6 +177,8 @@ impl std::cmp::PartialEq for Guild {
|
||||||
&& self.system_channel_id == other.system_channel_id
|
&& self.system_channel_id == other.system_channel_id
|
||||||
&& self.vanity_url_code == other.vanity_url_code
|
&& self.vanity_url_code == other.vanity_url_code
|
||||||
&& self.verification_level == other.verification_level
|
&& self.verification_level == other.verification_level
|
||||||
|
&& vec_arc_rwlock_ptr_eq(&self.voice_states, &other.voice_states)
|
||||||
|
&& vec_arc_rwlock_ptr_eq(&self.webhooks, &other.webhooks)
|
||||||
&& self.welcome_screen == other.welcome_screen
|
&& self.welcome_screen == other.welcome_screen
|
||||||
&& self.welcome_screen == other.welcome_screen
|
&& self.welcome_screen == other.welcome_screen
|
||||||
&& self.widget_channel_id == other.widget_channel_id
|
&& self.widget_channel_id == other.widget_channel_id
|
||||||
|
@ -225,6 +190,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>,
|
||||||
}
|
}
|
||||||
|
@ -252,32 +218,41 @@ pub struct GuildInvite {
|
||||||
pub vanity_url: Option<bool>,
|
pub vanity_url: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::hash::Hash for GuildInvite {
|
#[cfg(not(tarpaulin_include))]
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
impl PartialEq for GuildInvite {
|
||||||
self.code.hash(state);
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.temporary.hash(state);
|
self.code == other.code
|
||||||
self.uses.hash(state);
|
&& self.temporary == other.temporary
|
||||||
self.max_uses.hash(state);
|
&& self.uses == other.uses
|
||||||
self.max_age.hash(state);
|
&& self.max_uses == other.max_uses
|
||||||
self.created_at.hash(state);
|
&& self.max_age == other.max_age
|
||||||
self.expires_at.hash(state);
|
&& self.created_at == other.created_at
|
||||||
self.guild_id.hash(state);
|
&& self.expires_at == other.expires_at
|
||||||
self.channel_id.hash(state);
|
&& self.guild_id == other.guild_id
|
||||||
self.inviter_id.hash(state);
|
&& option_arc_rwlock_ptr_eq(&self.guild, &other.guild)
|
||||||
self.target_user_id.hash(state);
|
&& self.channel_id == other.channel_id
|
||||||
self.target_user.hash(state);
|
&& option_arc_rwlock_ptr_eq(&self.channel, &other.channel)
|
||||||
self.target_user_type.hash(state);
|
&& self.inviter_id == other.inviter_id
|
||||||
self.vanity_url.hash(state);
|
&& option_arc_rwlock_ptr_eq(&self.inviter, &other.inviter)
|
||||||
|
&& self.target_user_id == other.target_user_id
|
||||||
|
&& self.target_user == other.target_user
|
||||||
|
&& self.target_user_type == other.target_user_type
|
||||||
|
&& self.vanity_url == other.vanity_url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Hash)]
|
#[derive(
|
||||||
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, Hash, Eq, PartialOrd, Ord, Copy,
|
||||||
|
)]
|
||||||
pub struct UnavailableGuild {
|
pub struct UnavailableGuild {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub unavailable: bool,
|
pub unavailable: Option<bool>,
|
||||||
|
pub geo_restricted: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(
|
||||||
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy,
|
||||||
|
)]
|
||||||
pub struct GuildCreateResponse {
|
pub struct GuildCreateResponse {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
}
|
}
|
||||||
|
@ -299,20 +274,44 @@ pub struct GuildScheduledEvent {
|
||||||
pub entity_id: Option<Snowflake>,
|
pub entity_id: Option<Snowflake>,
|
||||||
pub entity_metadata: Option<GuildScheduledEventEntityMetadata>,
|
pub entity_metadata: Option<GuildScheduledEventEntityMetadata>,
|
||||||
pub creator: Option<Shared<User>>,
|
pub creator: Option<Shared<User>>,
|
||||||
pub user_count: Option<u64>,
|
pub user_count: Option<UInt64>,
|
||||||
pub image: Option<String>,
|
pub image: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
|
#[cfg(not(tarpaulin_include))]
|
||||||
#[repr(u8)]
|
impl PartialEq for GuildScheduledEvent {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
&& self.guild_id == other.guild_id
|
||||||
|
&& self.channel_id == other.channel_id
|
||||||
|
&& self.creator_id == other.creator_id
|
||||||
|
&& self.name == other.name
|
||||||
|
&& self.description == other.description
|
||||||
|
&& self.scheduled_start_time == other.scheduled_start_time
|
||||||
|
&& self.scheduled_end_time == other.scheduled_end_time
|
||||||
|
&& self.privacy_level == other.privacy_level
|
||||||
|
&& self.status == other.status
|
||||||
|
&& self.entity_type == other.entity_type
|
||||||
|
&& self.entity_id == other.entity_id
|
||||||
|
&& self.entity_metadata == other.entity_metadata
|
||||||
|
&& option_arc_rwlock_ptr_eq(&self.creator, &other.creator)
|
||||||
|
&& self.user_count == other.user_count
|
||||||
|
&& self.image == other.image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq, Copy)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level>
|
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level>
|
||||||
pub enum GuildScheduledEventPrivacyLevel {
|
pub enum GuildScheduledEventPrivacyLevel {
|
||||||
#[default]
|
#[default]
|
||||||
GuildOnly = 2,
|
GuildOnly = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq, Copy)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status>
|
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status>
|
||||||
pub enum GuildScheduledEventStatus {
|
pub enum GuildScheduledEventStatus {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -322,8 +321,21 @@ pub enum GuildScheduledEventStatus {
|
||||||
Canceled = 4,
|
Canceled = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
|
#[derive(
|
||||||
#[repr(u8)]
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Copy,
|
||||||
|
Hash,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types>
|
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types>
|
||||||
pub enum GuildScheduledEventEntityType {
|
pub enum GuildScheduledEventEntityType {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -332,7 +344,7 @@ pub enum GuildScheduledEventEntityType {
|
||||||
External = 3,
|
External = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata>
|
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata>
|
||||||
pub struct GuildScheduledEventEntityMetadata {
|
pub struct GuildScheduledEventEntityMetadata {
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
|
@ -347,9 +359,22 @@ pub struct VoiceRegion {
|
||||||
custom: bool,
|
custom: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
#[derive(
|
||||||
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
Copy,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#message-notification-level>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#message-notification-level>
|
||||||
pub enum MessageNotificationLevel {
|
pub enum MessageNotificationLevel {
|
||||||
|
@ -358,9 +383,22 @@ pub enum MessageNotificationLevel {
|
||||||
OnlyMentions = 1,
|
OnlyMentions = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
#[derive(
|
||||||
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
Copy,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#explicit-content-filter-level>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#explicit-content-filter-level>
|
||||||
pub enum ExplicitContentFilterLevel {
|
pub enum ExplicitContentFilterLevel {
|
||||||
|
@ -370,9 +408,22 @@ pub enum ExplicitContentFilterLevel {
|
||||||
AllMembers = 2,
|
AllMembers = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
#[derive(
|
||||||
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
Copy,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
||||||
pub enum VerificationLevel {
|
pub enum VerificationLevel {
|
||||||
|
@ -384,9 +435,22 @@ pub enum VerificationLevel {
|
||||||
VeryHigh = 4,
|
VeryHigh = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
#[derive(
|
||||||
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
Copy,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
||||||
pub enum MFALevel {
|
pub enum MFALevel {
|
||||||
|
@ -395,9 +459,22 @@ pub enum MFALevel {
|
||||||
Elevated = 1,
|
Elevated = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
#[derive(
|
||||||
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
Copy,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
||||||
pub enum NSFWLevel {
|
pub enum NSFWLevel {
|
||||||
|
@ -408,9 +485,22 @@ pub enum NSFWLevel {
|
||||||
AgeRestricted = 3,
|
AgeRestricted = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
#[derive(
|
||||||
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
Copy,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
||||||
pub enum PremiumTier {
|
pub enum PremiumTier {
|
||||||
|
@ -422,7 +512,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 {
|
||||||
|
|
|
@ -2,27 +2,52 @@
|
||||||
// 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::{entities::PublicUser, Snowflake};
|
use crate::types::{entities::PublicUser, Snowflake};
|
||||||
|
use crate::types::{GuildMemberFlags, PermissionFlags, Shared};
|
||||||
|
|
||||||
|
use super::option_arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
#[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>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
impl PartialEq for GuildMember {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.nick == other.nick
|
||||||
|
&& self.avatar == other.avatar
|
||||||
|
&& self.roles == other.roles
|
||||||
|
&& self.joined_at == other.joined_at
|
||||||
|
&& self.premium_since == other.premium_since
|
||||||
|
&& self.deaf == other.deaf
|
||||||
|
&& self.mute == other.mute
|
||||||
|
&& self.flags == other.flags
|
||||||
|
&& self.pending == other.pending
|
||||||
|
&& self.permissions == other.permissions
|
||||||
|
&& self.communication_disabled_until == other.communication_disabled_until
|
||||||
|
&& option_arc_rwlock_ptr_eq(&self.user, &other.user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,12 @@
|
||||||
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::{
|
||||||
entities::{Application, User},
|
entities::{Application, User},
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
|
Shared,
|
||||||
};
|
};
|
||||||
|
use crate::{UInt16, UInt8};
|
||||||
|
|
||||||
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
|
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -18,13 +19,13 @@ 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>,
|
||||||
pub enabled_emoticons: Option<bool>,
|
pub enabled_emoticons: Option<bool>,
|
||||||
pub expire_behaviour: Option<u8>,
|
pub expire_behaviour: Option<UInt8>,
|
||||||
pub expire_grace_period: Option<u16>,
|
pub expire_grace_period: Option<UInt16>,
|
||||||
#[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))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
|
@ -43,3 +44,17 @@ pub struct IntegrationAccount {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Copy, Hash,
|
||||||
|
)]
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
|
|
@ -5,37 +5,53 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::gateway::Shared;
|
use crate::types::types::guild_configuration::GuildFeaturesList;
|
||||||
use crate::types::{Snowflake, WelcomeScreenObject};
|
use crate::types::{
|
||||||
|
Guild, InviteFlags, InviteTargetType, InviteType, Shared, Snowflake, VerificationLevel,
|
||||||
|
WelcomeScreenObject,
|
||||||
|
};
|
||||||
|
use crate::{UInt32, UInt8};
|
||||||
|
|
||||||
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<UInt32>,
|
||||||
pub max_uses: Option<i32>,
|
pub max_uses: Option<UInt8>,
|
||||||
|
#[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<UInt32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The guild an invite is for.
|
/// The guild an invite is for.
|
||||||
|
@ -46,8 +62,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 +75,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 {
|
||||||
|
|
|
@ -2,16 +2,22 @@
|
||||||
// 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::{
|
||||||
entities::{
|
entities::{
|
||||||
Application, Attachment, Channel, Emoji, GuildMember, PublicUser, RoleSubscriptionData,
|
Application, Attachment, Channel, Emoji, GuildMember, PublicUser, RoleSubscriptionData,
|
||||||
Sticker, StickerItem, User,
|
Sticker, StickerItem, User,
|
||||||
},
|
},
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
|
Shared,
|
||||||
};
|
};
|
||||||
|
use crate::{UInt32, UInt8};
|
||||||
|
|
||||||
|
use super::option_arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -25,8 +31,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 +44,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 +55,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,17 +67,26 @@ 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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
impl PartialEq for Message {
|
impl PartialEq for Message {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.id == other.id
|
self.id == other.id
|
||||||
|
@ -88,46 +103,72 @@ impl PartialEq for Message {
|
||||||
&& self.attachments == other.attachments
|
&& self.attachments == other.attachments
|
||||||
&& self.embeds == other.embeds
|
&& self.embeds == other.embeds
|
||||||
&& self.embeds == other.embeds
|
&& self.embeds == other.embeds
|
||||||
|
&& self.reactions == other.reactions
|
||||||
|
&& self.reactions == other.reactions
|
||||||
&& self.nonce == other.nonce
|
&& self.nonce == other.nonce
|
||||||
&& self.pinned == other.pinned
|
&& self.pinned == other.pinned
|
||||||
&& self.webhook_id == other.webhook_id
|
&& self.webhook_id == other.webhook_id
|
||||||
&& self.message_type == other.message_type
|
&& self.message_type == other.message_type
|
||||||
&& self.activity == other.activity
|
&& self.activity == other.activity
|
||||||
&& self.activity == other.activity
|
&& self.activity == other.activity
|
||||||
|
&& self.application == other.application
|
||||||
&& self.application_id == other.application_id
|
&& self.application_id == other.application_id
|
||||||
&& self.message_reference == other.message_reference
|
&& self.message_reference == other.message_reference
|
||||||
&& self.message_reference == other.message_reference
|
&& self.message_reference == other.message_reference
|
||||||
&& self.flags == other.flags
|
&& self.flags == other.flags
|
||||||
&& self.referenced_message == other.referenced_message
|
&& self.referenced_message == other.referenced_message
|
||||||
|
&& self.interaction == other.interaction
|
||||||
&& self.thread == other.thread
|
&& self.thread == other.thread
|
||||||
&& self.components == other.components
|
&& 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.stickers == other.stickers
|
||||||
&& self.role_subscription_data == other.role_subscription_data
|
&& self.role_subscription_data == other.role_subscription_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Ord, PartialOrd)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Ord, PartialOrd, Copy)]
|
||||||
/// # 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, Copy)]
|
||||||
|
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,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub interaction_type: u8,
|
pub interaction_type: UInt8,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub user: User,
|
pub user: User,
|
||||||
pub member: Option<Shared<GuildMember>>,
|
pub member: Option<Shared<GuildMember>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, Eq, PartialOrd, Ord)]
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
impl PartialEq for MessageInteraction {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
&& self.interaction_type == other.interaction_type
|
||||||
|
&& self.name == other.name
|
||||||
|
&& self.user == other.user
|
||||||
|
&& option_arc_rwlock_ptr_eq(&self.member, &other.member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct AllowedMention {
|
pub struct AllowedMention {
|
||||||
parse: Vec<AllowedMentionType>,
|
parse: Vec<AllowedMentionType>,
|
||||||
roles: Vec<Snowflake>,
|
roles: Vec<Snowflake>,
|
||||||
|
@ -135,7 +176,7 @@ pub struct AllowedMention {
|
||||||
replied_user: bool,
|
replied_user: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum AllowedMentionType {
|
pub enum AllowedMentionType {
|
||||||
Roles,
|
Roles,
|
||||||
|
@ -152,11 +193,11 @@ pub struct ChannelMention {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Ord)]
|
||||||
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,14 +211,32 @@ pub struct Embed {
|
||||||
fields: Option<Vec<EmbedField>>,
|
fields: Option<Vec<EmbedField>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[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, Hash)]
|
||||||
pub struct EmbedFooter {
|
pub struct EmbedFooter {
|
||||||
text: String,
|
text: String,
|
||||||
icon_url: Option<String>,
|
icon_url: Option<String>,
|
||||||
proxy_icon_url: Option<String>,
|
proxy_icon_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord, Hash)]
|
||||||
pub struct EmbedImage {
|
pub struct EmbedImage {
|
||||||
url: String,
|
url: String,
|
||||||
proxy_url: String,
|
proxy_url: String,
|
||||||
|
@ -185,7 +244,7 @@ pub struct EmbedImage {
|
||||||
width: Option<i32>,
|
width: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord, Hash)]
|
||||||
pub struct EmbedThumbnail {
|
pub struct EmbedThumbnail {
|
||||||
url: String,
|
url: String,
|
||||||
proxy_url: Option<String>,
|
proxy_url: Option<String>,
|
||||||
|
@ -193,7 +252,7 @@ pub struct EmbedThumbnail {
|
||||||
width: Option<i32>,
|
width: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord, Hash)]
|
||||||
struct EmbedVideo {
|
struct EmbedVideo {
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
proxy_url: Option<String>,
|
proxy_url: Option<String>,
|
||||||
|
@ -201,13 +260,13 @@ struct EmbedVideo {
|
||||||
width: Option<i32>,
|
width: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord, Hash)]
|
||||||
pub struct EmbedProvider {
|
pub struct EmbedProvider {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord, Hash)]
|
||||||
pub struct EmbedAuthor {
|
pub struct EmbedAuthor {
|
||||||
name: String,
|
name: String,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
|
@ -215,7 +274,7 @@ pub struct EmbedAuthor {
|
||||||
proxy_icon_url: Option<String>,
|
proxy_icon_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord, Hash)]
|
||||||
pub struct EmbedField {
|
pub struct EmbedField {
|
||||||
name: String,
|
name: String,
|
||||||
value: String,
|
value: String,
|
||||||
|
@ -224,15 +283,22 @@ pub struct EmbedField {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct Reaction {
|
pub struct Reaction {
|
||||||
pub count: u32,
|
pub count: UInt32,
|
||||||
pub burst_count: u32,
|
pub burst_count: UInt32,
|
||||||
|
#[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)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
pub enum Component {
|
pub enum Component {
|
||||||
ActionRow = 1,
|
ActionRow = 1,
|
||||||
Button = 2,
|
Button = 2,
|
||||||
|
@ -252,3 +318,159 @@ 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")]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
|
#[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, Ord, Eq, Hash)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
|
pub enum ReactionType {
|
||||||
|
Normal = 0,
|
||||||
|
Burst = 1, // The dreaded super reactions
|
||||||
|
}
|
||||||
|
|
|
@ -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,8 +135,53 @@ 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Internal function to compare two `Shared<T>`s by comparing their pointers.
|
||||||
|
#[cfg_attr(not(feature = "client"), allow(unused_variables))]
|
||||||
|
pub(crate) fn arc_rwlock_ptr_eq<T>(a: &Shared<T>, b: &Shared<T>) -> bool {
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
{
|
||||||
|
Shared::ptr_eq(a, b)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "client"))]
|
||||||
|
{
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal function to compare two `Vec<Shared<T>>`s by comparing their pointers.
|
||||||
|
pub(crate) fn vec_arc_rwlock_ptr_eq<T>(a: &[Shared<T>], b: &[Shared<T>]) -> bool {
|
||||||
|
for (a, b) in a.iter().zip(b.iter()) {
|
||||||
|
if !arc_rwlock_ptr_eq(a, b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal function to compare two `Option<Shared<T>>`s by comparing their pointers.
|
||||||
|
pub(crate) fn option_arc_rwlock_ptr_eq<T>(a: &Option<Shared<T>>, b: &Option<Shared<T>>) -> bool {
|
||||||
|
match (a, b) {
|
||||||
|
(Some(a), Some(b)) => arc_rwlock_ptr_eq(a, b),
|
||||||
|
(None, None) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal function to compare two `Option<Vec<Shared<T>>>`s by comparing their pointers.
|
||||||
|
pub(crate) fn option_vec_arc_rwlock_ptr_eq<T>(
|
||||||
|
a: &Option<Vec<Shared<T>>>,
|
||||||
|
b: &Option<Vec<Shared<T>>>,
|
||||||
|
) -> bool {
|
||||||
|
match (a, b) {
|
||||||
|
(Some(a), Some(b)) => vec_arc_rwlock_ptr_eq(a, b),
|
||||||
|
(None, None) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@ use crate::types::Snowflake;
|
||||||
/// The different types of ratelimits that can be applied to a request. Includes "Baseline"-variants
|
/// The different types of ratelimits that can be applied to a request. Includes "Baseline"-variants
|
||||||
/// for when the Snowflake is not yet known.
|
/// for when the Snowflake is not yet known.
|
||||||
/// See <https://discord.com/developers/docs/topics/rate-limits#rate-limits> for more information.
|
/// See <https://discord.com/developers/docs/topics/rate-limits#rate-limits> for more information.
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default, Hash, Serialize, Deserialize)]
|
#[derive(
|
||||||
|
Clone, Copy, Eq, PartialEq, Debug, Default, Hash, Serialize, Deserialize, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
pub enum LimitType {
|
pub enum LimitType {
|
||||||
AuthRegister,
|
AuthRegister,
|
||||||
AuthLogin,
|
AuthLogin,
|
||||||
|
@ -29,7 +31,7 @@ pub enum LimitType {
|
||||||
|
|
||||||
/// A struct that represents the current ratelimits, either instance-wide or user-wide.
|
/// A struct that represents the current ratelimits, either instance-wide or user-wide.
|
||||||
/// See <https://discord.com/developers/docs/topics/rate-limits#rate-limits> for more information.
|
/// See <https://discord.com/developers/docs/topics/rate-limits#rate-limits> for more information.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)]
|
||||||
pub struct Limit {
|
pub struct Limit {
|
||||||
pub bucket: LimitType,
|
pub bucket: LimitType,
|
||||||
pub limit: u64,
|
pub limit: u64,
|
||||||
|
|
|
@ -6,10 +6,9 @@ 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::{arc_rwlock_ptr_eq, PublicUser};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/user#relationship-structure>
|
/// See <https://discord-userdoccers.vercel.app/resources/user#relationship-structure>
|
||||||
|
@ -22,17 +21,32 @@ pub struct Relationship {
|
||||||
pub since: Option<DateTime<Utc>>,
|
pub since: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
impl PartialEq for Relationship {
|
impl PartialEq for Relationship {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.id == other.id
|
self.id == other.id
|
||||||
&& self.relationship_type == other.relationship_type
|
&& self.relationship_type == other.relationship_type
|
||||||
&& self.since == other.since
|
|
||||||
&& self.nickname == other.nickname
|
&& self.nickname == other.nickname
|
||||||
|
&& arc_rwlock_ptr_eq(&self.user, &other.user)
|
||||||
|
&& self.since == other.since
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default, Eq, PartialEq)]
|
#[derive(
|
||||||
#[repr(u8)]
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Default,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Copy,
|
||||||
|
Hash,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/user#relationship-type>
|
/// See <https://discord-userdoccers.vercel.app/resources/user#relationship-type>
|
||||||
pub enum RelationshipType {
|
pub enum RelationshipType {
|
||||||
Suggestion = 6,
|
Suggestion = 6,
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
|
|
||||||
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;
|
||||||
|
use crate::{UInt16, UInt32};
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use chorus_macros::{Composite, Updateable};
|
use chorus_macros::{Composite, Updateable};
|
||||||
|
@ -32,10 +33,9 @@ pub struct RoleObject {
|
||||||
pub hoist: bool,
|
pub hoist: bool,
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
pub unicode_emoji: Option<String>,
|
pub unicode_emoji: Option<String>,
|
||||||
pub position: u16,
|
pub position: UInt16,
|
||||||
#[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")]
|
||||||
|
@ -48,11 +48,13 @@ pub struct RoleObject {
|
||||||
pub struct RoleSubscriptionData {
|
pub struct RoleSubscriptionData {
|
||||||
pub role_subscription_listing_id: Snowflake,
|
pub role_subscription_listing_id: Snowflake,
|
||||||
pub tier_name: String,
|
pub tier_name: String,
|
||||||
pub total_months_subscribed: u32,
|
pub total_months_subscribed: UInt32,
|
||||||
pub is_renewal: bool,
|
pub is_renewal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)]
|
#[derive(
|
||||||
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
/// See <https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure>
|
/// See <https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure>
|
||||||
pub struct RoleTags {
|
pub struct RoleTags {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -71,7 +73,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:
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -13,7 +14,7 @@ pub struct SecurityKey {
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub key_id: String,
|
pub key_id: String,
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
pub counter: u64,
|
pub counter: UInt64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +25,8 @@ impl Default for SecurityKey {
|
||||||
user_id: String::new(),
|
user_id: String::new(),
|
||||||
key_id: String::new(),
|
key_id: String::new(),
|
||||||
public_key: String::new(),
|
public_key: String::new(),
|
||||||
counter: 0,
|
#[allow(clippy::useless_conversion)]
|
||||||
|
counter: 0u64.into(),
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,11 @@ pub struct StageInstance {
|
||||||
pub guild_scheduled_event_id: Option<Snowflake>,
|
pub guild_scheduled_event_id: Option<Snowflake>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(
|
||||||
#[repr(u8)]
|
Serialize_repr, Deserialize_repr, Debug, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/stage-instance#stage-instance-object-privacy-level>
|
/// See <https://discord.com/developers/docs/resources/stage-instance#stage-instance-object-privacy-level>
|
||||||
pub enum StageInstancePrivacyLevel {
|
pub enum StageInstancePrivacyLevel {
|
||||||
|
|
|
@ -3,9 +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_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
use crate::gateway::Shared;
|
use crate::types::{entities::User, utils::Snowflake, Shared};
|
||||||
use crate::types::{entities::User, utils::Snowflake};
|
|
||||||
|
use super::option_arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
#[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,34 +21,21 @@ 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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::hash::Hash for Sticker {
|
#[cfg(not(tarpaulin_include))]
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.id.hash(state);
|
|
||||||
self.pack_id.hash(state);
|
|
||||||
self.name.hash(state);
|
|
||||||
self.description.hash(state);
|
|
||||||
self.tags.hash(state);
|
|
||||||
self.asset.hash(state);
|
|
||||||
self.sticker_type.hash(state);
|
|
||||||
self.format_type.hash(state);
|
|
||||||
self.available.hash(state);
|
|
||||||
self.guild_id.hash(state);
|
|
||||||
self.sort_value.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Sticker {
|
impl PartialEq for Sticker {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.id == other.id
|
self.id == other.id
|
||||||
|
@ -59,53 +48,16 @@ impl PartialEq for Sticker {
|
||||||
&& self.format_type == other.format_type
|
&& self.format_type == other.format_type
|
||||||
&& self.available == other.available
|
&& self.available == other.available
|
||||||
&& self.guild_id == other.guild_id
|
&& self.guild_id == other.guild_id
|
||||||
|
&& option_arc_rwlock_ptr_eq(&self.user, &other.user)
|
||||||
&& self.sort_value == other.sort_value
|
&& self.sort_value == other.sort_value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Sticker {
|
impl Sticker {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
pub fn tags(&self) -> Vec<String> {
|
||||||
match self.id.partial_cmp(&other.id) {
|
self.tags.as_ref().map_or(vec![], |s| {
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
s.split(',').map(|tag| tag.trim().to_string()).collect()
|
||||||
ord => return ord,
|
})
|
||||||
}
|
|
||||||
match self.pack_id.partial_cmp(&other.pack_id) {
|
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
|
||||||
ord => return ord,
|
|
||||||
}
|
|
||||||
match self.name.partial_cmp(&other.name) {
|
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
|
||||||
ord => return ord,
|
|
||||||
}
|
|
||||||
match self.description.partial_cmp(&other.description) {
|
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
|
||||||
ord => return ord,
|
|
||||||
}
|
|
||||||
match self.tags.partial_cmp(&other.tags) {
|
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
|
||||||
ord => return ord,
|
|
||||||
}
|
|
||||||
match self.asset.partial_cmp(&other.asset) {
|
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
|
||||||
ord => return ord,
|
|
||||||
}
|
|
||||||
match self.sticker_type.partial_cmp(&other.sticker_type) {
|
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
|
||||||
ord => return ord,
|
|
||||||
}
|
|
||||||
match self.format_type.partial_cmp(&other.format_type) {
|
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
|
||||||
ord => return ord,
|
|
||||||
}
|
|
||||||
match self.available.partial_cmp(&other.available) {
|
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
|
||||||
ord => return ord,
|
|
||||||
}
|
|
||||||
match self.guild_id.partial_cmp(&other.guild_id) {
|
|
||||||
Some(core::cmp::Ordering::Equal) => {}
|
|
||||||
ord => return ord,
|
|
||||||
}
|
|
||||||
self.sort_value.partial_cmp(&other.sort_value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,5 +71,70 @@ 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,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
|
#[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,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,12 @@
|
||||||
|
|
||||||
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::Shared;
|
||||||
use crate::types::Snowflake;
|
use crate::types::Snowflake;
|
||||||
|
use crate::UInt8;
|
||||||
|
|
||||||
|
use super::arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -19,10 +22,31 @@ pub struct Team {
|
||||||
pub owner_user_id: Snowflake,
|
pub owner_user_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
impl PartialEq for Team {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.icon == other.icon
|
||||||
|
&& self.id == other.id
|
||||||
|
&& self.members == other.members
|
||||||
|
&& self.name == other.name
|
||||||
|
&& self.owner_user_id == other.owner_user_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct TeamMember {
|
pub struct TeamMember {
|
||||||
pub membership_state: u8,
|
pub membership_state: UInt8,
|
||||||
pub permissions: Vec<String>,
|
pub permissions: Vec<String>,
|
||||||
pub team_id: Snowflake,
|
pub team_id: Snowflake,
|
||||||
pub user: Shared<User>,
|
pub user: Shared<User>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
impl PartialEq for TeamMember {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.membership_state == other.membership_state
|
||||||
|
&& self.permissions == other.permissions
|
||||||
|
&& self.team_id == other.team_id
|
||||||
|
&& arc_rwlock_ptr_eq(&self.user, &other.user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,11 +5,12 @@
|
||||||
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::{
|
||||||
entities::{Guild, User},
|
entities::{Guild, User},
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
|
Shared,
|
||||||
};
|
};
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-template>
|
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-template>
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
|
@ -18,7 +19,7 @@ pub struct GuildTemplate {
|
||||||
pub code: String,
|
pub code: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub usage_count: Option<u64>,
|
pub usage_count: Option<UInt64>,
|
||||||
pub creator_id: Snowflake,
|
pub creator_id: Snowflake,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub creator: Shared<User>,
|
pub creator: Shared<User>,
|
||||||
|
|
|
@ -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 crate::errors::ChorusError;
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
use crate::{UInt32, UInt8};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_aux::prelude::deserialize_option_number_from_string;
|
use serde_aux::prelude::deserialize_option_number_from_string;
|
||||||
|
use std::array::TryFromSliceError;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
|
@ -45,7 +48,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<UInt32>,
|
||||||
#[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 +57,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<UInt8>,
|
||||||
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<ThemeColors>,
|
||||||
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>,
|
||||||
|
@ -70,21 +73,87 @@ pub struct User {
|
||||||
pub disabled: Option<bool>,
|
pub disabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)]
|
||||||
|
pub struct ThemeColors {
|
||||||
|
#[serde(flatten)]
|
||||||
|
inner: (u32, u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Vec<u8>> for ThemeColors {
|
||||||
|
type Error = ChorusError;
|
||||||
|
|
||||||
|
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||||
|
if value.len() % 4 != 0 || value.len() > 8 {
|
||||||
|
return Err(ChorusError::InvalidArguments {
|
||||||
|
error: "Value has incorrect length to be decodeable from Vec<u8>".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let first: Result<[u8; 4], TryFromSliceError> = value[0..3].try_into();
|
||||||
|
let second: Result<[u8; 4], TryFromSliceError> = {
|
||||||
|
if value.len() == 8 {
|
||||||
|
value[0..3].try_into()
|
||||||
|
} else {
|
||||||
|
[0; 4][0..3].try_into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match (first, second) {
|
||||||
|
(Ok(first), Ok(second)) => Ok(Self {
|
||||||
|
inner: (u32::from_be_bytes(first), u32::from_be_bytes(second)),
|
||||||
|
}),
|
||||||
|
_ => Err(ChorusError::InvalidArguments {
|
||||||
|
error: "ThemeColors cannot be built from this Vec<u8>".to_string(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
// TODO: Add tests for Encode and Decode.
|
||||||
|
impl<'q> sqlx::Encode<'q, sqlx::Postgres> for ThemeColors {
|
||||||
|
fn encode_by_ref(
|
||||||
|
&self,
|
||||||
|
buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
|
||||||
|
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut vec_u8 = Vec::new();
|
||||||
|
vec_u8.extend_from_slice(&self.inner.0.to_be_bytes());
|
||||||
|
vec_u8.extend_from_slice(&self.inner.1.to_be_bytes());
|
||||||
|
<Vec<u8> as sqlx::Encode<sqlx::Postgres>>::encode_by_ref(&vec_u8, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl<'d> sqlx::Decode<'d, sqlx::Postgres> for ThemeColors {
|
||||||
|
fn decode(
|
||||||
|
value: <sqlx::Postgres as sqlx::Database>::ValueRef<'d>,
|
||||||
|
) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
|
let value_vec = <Vec<u8> as sqlx::Decode<'d, sqlx::Postgres>>::decode(value)?;
|
||||||
|
value_vec.try_into().map_err(|e: ChorusError| e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl sqlx::Type<sqlx::Postgres> for ThemeColors {
|
||||||
|
fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
|
||||||
|
<String as sqlx::Type<sqlx::Postgres>>::type_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||||
pub struct PublicUser {
|
pub struct PublicUser {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
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<UInt32>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
pub theme_colors: Option<Vec<u8>>,
|
pub theme_colors: Option<ThemeColors>,
|
||||||
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<UInt8>,
|
||||||
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 +180,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;
|
||||||
|
@ -144,7 +213,7 @@ pub struct UserProfileMetadata {
|
||||||
pub bio: Option<String>,
|
pub bio: Option<String>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
pub accent_color: Option<i32>,
|
pub accent_color: Option<i32>,
|
||||||
pub theme_colors: Option<Vec<i32>>,
|
pub theme_colors: Option<ThemeColors>,
|
||||||
pub popout_animation_particle_type: Option<Snowflake>,
|
pub popout_animation_particle_type: Option<Snowflake>,
|
||||||
pub emoji: Option<Emoji>,
|
pub emoji: Option<Emoji>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,16 @@
|
||||||
// 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 crate::{UInt16, UInt32, UInt8};
|
||||||
|
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, Copy, PartialOrd, Ord, Hash,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum UserStatus {
|
pub enum UserStatus {
|
||||||
|
@ -27,7 +29,9 @@ impl std::fmt::Display for UserStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Copy, PartialOrd, Ord, Hash,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum UserTheme {
|
pub enum UserTheme {
|
||||||
|
@ -39,36 +43,23 @@ 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<UInt16>,
|
||||||
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: UInt8,
|
||||||
pub contact_sync_enabled: bool,
|
pub contact_sync_enabled: bool,
|
||||||
pub convert_emoticons: bool,
|
pub convert_emoticons: bool,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub custom_status: Option<sqlx::types::Json<CustomStatus>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub custom_status: Option<CustomStatus>,
|
pub custom_status: Option<CustomStatus>,
|
||||||
pub default_guilds_restricted: bool,
|
pub default_guilds_restricted: bool,
|
||||||
pub detect_platform_accounts: bool,
|
pub detect_platform_accounts: bool,
|
||||||
pub developer_mode: bool,
|
pub developer_mode: bool,
|
||||||
pub disable_games_tab: bool,
|
pub disable_games_tab: bool,
|
||||||
pub enable_tts_command: bool,
|
pub enable_tts_command: bool,
|
||||||
pub explicit_content_filter: u8,
|
pub explicit_content_filter: UInt8,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub friend_source_flags: sqlx::types::Json<FriendSourceFlags>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub friend_source_flags: FriendSourceFlags,
|
pub friend_source_flags: FriendSourceFlags,
|
||||||
pub gateway_connected: Option<bool>,
|
pub gateway_connected: Option<bool>,
|
||||||
pub gif_auto_play: bool,
|
pub gif_auto_play: bool,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub guild_folders: sqlx::types::Json<Vec<GuildFolder>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub guild_folders: Vec<GuildFolder>,
|
pub guild_folders: Vec<GuildFolder>,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub guild_positions: sqlx::types::Json<Vec<String>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub guild_positions: Vec<String>,
|
pub guild_positions: Vec<String>,
|
||||||
pub inline_attachment_media: bool,
|
pub inline_attachment_media: bool,
|
||||||
|
@ -78,9 +69,6 @@ pub struct UserSettings {
|
||||||
pub native_phone_integration_enabled: bool,
|
pub native_phone_integration_enabled: bool,
|
||||||
pub render_embeds: bool,
|
pub render_embeds: bool,
|
||||||
pub render_reactions: bool,
|
pub render_reactions: bool,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub restricted_guilds: sqlx::types::Json<Vec<String>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub restricted_guilds: Vec<String>,
|
pub restricted_guilds: Vec<String>,
|
||||||
pub show_current_game: bool,
|
pub show_current_game: bool,
|
||||||
pub status: Shared<UserStatus>,
|
pub status: Shared<UserStatus>,
|
||||||
|
@ -92,10 +80,14 @@ pub struct UserSettings {
|
||||||
impl Default for UserSettings {
|
impl Default for UserSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
afk_timeout: 3600,
|
#[allow(clippy::useless_conversion)]
|
||||||
|
afk_timeout: Some(3600u16.into()),
|
||||||
allow_accessibility_detection: true,
|
allow_accessibility_detection: true,
|
||||||
animate_emoji: true,
|
animate_emoji: true,
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
animate_stickers: 0,
|
animate_stickers: 0,
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
animate_stickers: 0.into(),
|
||||||
contact_sync_enabled: false,
|
contact_sync_enabled: false,
|
||||||
convert_emoticons: false,
|
convert_emoticons: false,
|
||||||
custom_status: None,
|
custom_status: None,
|
||||||
|
@ -104,7 +96,10 @@ impl Default for UserSettings {
|
||||||
developer_mode: true,
|
developer_mode: true,
|
||||||
disable_games_tab: true,
|
disable_games_tab: true,
|
||||||
enable_tts_command: false,
|
enable_tts_command: false,
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
explicit_content_filter: 0,
|
explicit_content_filter: 0,
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
explicit_content_filter: 0.into(),
|
||||||
friend_source_flags: Default::default(),
|
friend_source_flags: Default::default(),
|
||||||
gateway_connected: Some(false),
|
gateway_connected: Some(false),
|
||||||
gif_auto_play: false,
|
gif_auto_play: false,
|
||||||
|
@ -119,7 +114,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,
|
||||||
|
@ -128,7 +123,8 @@ impl Default for UserSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
|
#[cfg_attr(feature = "sqlx", sqlx(type_name = "interface_type"))]
|
||||||
pub struct CustomStatus {
|
pub struct CustomStatus {
|
||||||
pub emoji_id: Option<String>,
|
pub emoji_id: Option<String>,
|
||||||
pub emoji_name: Option<String>,
|
pub emoji_name: Option<String>,
|
||||||
|
@ -137,7 +133,8 @@ pub struct CustomStatus {
|
||||||
pub text: Option<String>,
|
pub text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
pub struct FriendSourceFlags {
|
pub struct FriendSourceFlags {
|
||||||
pub all: bool,
|
pub all: bool,
|
||||||
}
|
}
|
||||||
|
@ -149,11 +146,20 @@ impl Default for FriendSourceFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
|
#[cfg_attr(feature = "sqlx", sqlx(type_name = "interface_type"))]
|
||||||
pub struct GuildFolder {
|
pub struct GuildFolder {
|
||||||
pub color: u32,
|
pub color: Option<UInt32>,
|
||||||
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)]
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -24,6 +25,8 @@ use crate::types::{
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::option_arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
/// The VoiceState struct. Note, that Discord does not have an `id` field for this, whereas Spacebar
|
/// The VoiceState struct. Note, that Discord does not have an `id` field for this, whereas Spacebar
|
||||||
/// does.
|
/// does.
|
||||||
///
|
///
|
||||||
|
@ -33,9 +36,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 +56,29 @@ pub struct VoiceState {
|
||||||
pub id: Option<Snowflake>, // Only exists on Spacebar
|
pub id: Option<Snowflake>, // Only exists on Spacebar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
impl PartialEq for VoiceState {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.guild_id == other.guild_id
|
||||||
|
&& self.guild == other.guild
|
||||||
|
&& self.channel_id == other.channel_id
|
||||||
|
&& self.user_id == other.user_id
|
||||||
|
&& option_arc_rwlock_ptr_eq(&self.member, &other.member)
|
||||||
|
&& self.session_id == other.session_id
|
||||||
|
&& self.token == other.token
|
||||||
|
&& self.deaf == other.deaf
|
||||||
|
&& self.mute == other.mute
|
||||||
|
&& self.self_deaf == other.self_deaf
|
||||||
|
&& self.self_mute == other.self_mute
|
||||||
|
&& self.self_stream == other.self_stream
|
||||||
|
&& self.self_video == other.self_video
|
||||||
|
&& self.suppress == other.suppress
|
||||||
|
&& self.request_to_speak_timestamp == other.request_to_speak_timestamp
|
||||||
|
&& self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -24,6 +25,8 @@ use crate::types::{
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::option_arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-webhook>
|
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-webhook>
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
|
@ -31,13 +34,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 +50,33 @@ 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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
impl PartialEq for Webhook {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
&& self.webhook_type == other.webhook_type
|
||||||
|
&& self.name == other.name
|
||||||
|
&& self.avatar == other.avatar
|
||||||
|
&& self.token == other.token
|
||||||
|
&& self.guild_id == other.guild_id
|
||||||
|
&& self.channel_id == other.channel_id
|
||||||
|
&& self.application_id == other.application_id
|
||||||
|
&& option_arc_rwlock_ptr_eq(&self.user, &other.user)
|
||||||
|
&& option_arc_rwlock_ptr_eq(&self.source_guild, &other.source_guild)
|
||||||
|
&& self.url == other.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
|
pub enum WebhookType {
|
||||||
|
#[default]
|
||||||
|
Incoming = 1,
|
||||||
|
ChannelFollower = 2,
|
||||||
|
Application = 3,
|
||||||
|
}
|
||||||
|
|
|
@ -21,15 +21,18 @@ 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, Copy, Clone)]
|
||||||
pub enum GuildError {
|
pub enum GuildError {
|
||||||
#[error("Invalid Guild Feature")]
|
#[error("Invalid Guild Feature")]
|
||||||
InvalidGuildFeature,
|
InvalidGuildFeature,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error, Copy, Clone)]
|
||||||
pub enum FieldFormatError {
|
pub enum FieldFormatError {
|
||||||
#[error("Password must be between 1 and 72 characters.")]
|
#[error("Password must be between 1 and 72 characters.")]
|
||||||
PasswordError,
|
PasswordError,
|
||||||
|
|
|
@ -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 {}
|
|
||||||
|
|
|
@ -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 {}
|
|
||||||
|
|
|
@ -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,19 @@ pub struct CallUpdate {
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for CallUpdate {}
|
#[derive(
|
||||||
|
Debug,
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
WebSocketEvent,
|
||||||
|
Copy,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
/// 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 +59,19 @@ pub struct CallDelete {
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for CallDelete {}
|
#[derive(
|
||||||
|
Debug,
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
WebSocketEvent,
|
||||||
|
Copy,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
/// Officially Undocumented;
|
/// Officially Undocumented;
|
||||||
/// See <https://unofficial-discord-docs.vercel.app/gateway/op13>;
|
/// See <https://unofficial-discord-docs.vercel.app/gateway/op13>;
|
||||||
///
|
///
|
||||||
|
@ -60,5 +79,3 @@ impl WebSocketEvent for CallDelete {}
|
||||||
pub struct CallSync {
|
pub struct CallSync {
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for CallSync {}
|
|
||||||
|
|
|
@ -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, Copy, PartialEq, Clone, Eq, Hash, PartialOrd, Ord)]
|
||||||
/// 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"}}
|
||||||
|
@ -94,18 +86,16 @@ pub struct ChannelUnreadUpdate {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
/// Contains very few fields from [Channel]
|
/// Contains very few fields from [Channel]
|
||||||
/// See also [ChannelUnreadUpdate]
|
/// See also [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 {}
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ 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,
|
||||||
Snowflake, SourceUrlField, Sticker,
|
SourceUrlField, Sticker,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::PresenceUpdate;
|
use super::PresenceUpdate;
|
||||||
|
@ -18,9 +18,21 @@ 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::IntoShared;
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::types::Shared;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone, SourceUrlField, JsonField)]
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
SourceUrlField,
|
||||||
|
JsonField,
|
||||||
|
WebSocketEvent,
|
||||||
|
PartialEq,
|
||||||
|
)]
|
||||||
/// 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
|
||||||
|
@ -47,7 +59,7 @@ impl UpdateMessage<Guild> for GuildCreate {
|
||||||
fn update(&mut self, _: Shared<Guild>) {}
|
fn update(&mut self, _: Shared<Guild>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum GuildCreateDataOption {
|
pub enum GuildCreateDataOption {
|
||||||
UnavailableGuild(UnavailableGuild),
|
UnavailableGuild(UnavailableGuild),
|
||||||
|
@ -60,9 +72,31 @@ impl Default for GuildCreateDataOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildCreate {}
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum GuildEvents {
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
Create(GuildCreate),
|
||||||
|
Update(GuildUpdate),
|
||||||
|
Delete(GuildDelete),
|
||||||
|
BanAdd(GuildBanAdd),
|
||||||
|
BanRemove(GuildBanRemove),
|
||||||
|
EmojisUpdate(GuildEmojisUpdate),
|
||||||
|
StickersUpdate(GuildStickersUpdate),
|
||||||
|
IntegrationsUpdate(GuildIntegrationsUpdate),
|
||||||
|
MemberAdd(GuildMemberAdd),
|
||||||
|
MemberRemove(GuildMemberRemove),
|
||||||
|
MemberUpdate(GuildMemberUpdate),
|
||||||
|
MembersChunk(GuildMembersChunk),
|
||||||
|
RoleCreate(GuildRoleCreate),
|
||||||
|
RoleUpdate(GuildRoleUpdate),
|
||||||
|
RoleDelete(GuildRoleDelete),
|
||||||
|
ScheduledEventCreate(GuildScheduledEventCreate),
|
||||||
|
ScheduledEventUpdate(GuildScheduledEventUpdate),
|
||||||
|
ScheduledEventDelete(GuildScheduledEventDelete),
|
||||||
|
ScheduledEventUserAdd(GuildScheduledEventUserAdd),
|
||||||
|
ScheduledEventUserRemove(GuildScheduledEventUserRemove),
|
||||||
|
AuditLogEntryCreate(GuildAuditLogEntryCreate),
|
||||||
|
}
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent, PartialEq)]
|
||||||
/// 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 +104,7 @@ pub struct GuildBanAdd {
|
||||||
pub user: PublicUser,
|
pub user: PublicUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildBanAdd {}
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent, PartialEq)]
|
||||||
|
|
||||||
#[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 +112,17 @@ pub struct GuildBanRemove {
|
||||||
pub user: PublicUser,
|
pub user: PublicUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildBanRemove {}
|
#[derive(
|
||||||
|
Debug,
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField)]
|
Default,
|
||||||
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
Clone,
|
||||||
|
SourceUrlField,
|
||||||
|
JsonField,
|
||||||
|
WebSocketEvent,
|
||||||
|
PartialEq,
|
||||||
|
)]
|
||||||
/// 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 +134,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 +142,17 @@ impl UpdateMessage<Guild> for GuildUpdate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField)]
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
Default,
|
||||||
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
Clone,
|
||||||
|
SourceUrlField,
|
||||||
|
JsonField,
|
||||||
|
WebSocketEvent,
|
||||||
|
PartialEq,
|
||||||
|
)]
|
||||||
/// 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 +173,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, PartialEq)]
|
||||||
|
|
||||||
#[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 +181,7 @@ pub struct GuildAuditLogEntryCreate {
|
||||||
pub entry: AuditLogEntry,
|
pub entry: AuditLogEntry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildAuditLogEntryCreate {}
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent, PartialEq)]
|
||||||
|
|
||||||
#[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 +189,7 @@ pub struct GuildEmojisUpdate {
|
||||||
pub emojis: Vec<Emoji>,
|
pub emojis: Vec<Emoji>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildEmojisUpdate {}
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent, PartialEq)]
|
||||||
|
|
||||||
#[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 +197,13 @@ pub struct GuildStickersUpdate {
|
||||||
pub stickers: Vec<Sticker>,
|
pub stickers: Vec<Sticker>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildStickersUpdate {}
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent, PartialEq, Copy, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
||||||
#[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, PartialEq)]
|
||||||
|
|
||||||
#[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 +212,7 @@ pub struct GuildMemberAdd {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildMemberAdd {}
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent, PartialEq)]
|
||||||
|
|
||||||
#[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 +220,7 @@ pub struct GuildMemberRemove {
|
||||||
pub user: PublicUser,
|
pub user: PublicUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildMemberRemove {}
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent, PartialEq)]
|
||||||
|
|
||||||
#[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 +236,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, PartialEq)]
|
||||||
|
|
||||||
#[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 +248,17 @@ pub struct GuildMembersChunk {
|
||||||
pub nonce: Option<String>,
|
pub nonce: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildMembersChunk {}
|
#[derive(
|
||||||
|
Debug,
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)]
|
Default,
|
||||||
|
Deserialize,
|
||||||
|
Serialize,
|
||||||
|
Clone,
|
||||||
|
JsonField,
|
||||||
|
SourceUrlField,
|
||||||
|
WebSocketEvent,
|
||||||
|
PartialEq,
|
||||||
|
)]
|
||||||
/// 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 +269,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 +278,21 @@ 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.roles.push(self.role.clone().into_shared());
|
||||||
object_to_update
|
|
||||||
.roles
|
|
||||||
.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,
|
||||||
|
PartialEq,
|
||||||
|
)]
|
||||||
/// 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 +303,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 +316,35 @@ impl UpdateMessage<RoleObject> for GuildRoleUpdate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
|
||||||
/// 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, PartialEq)]
|
||||||
|
|
||||||
#[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, PartialEq)]
|
||||||
|
|
||||||
#[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, PartialEq)]
|
||||||
|
|
||||||
#[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, PartialEq, Copy, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
||||||
#[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 +352,10 @@ pub struct GuildScheduledEventUserAdd {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildScheduledEventUserAdd {}
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, WebSocketEvent, PartialEq, Copy, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
||||||
#[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 {}
|
|
||||||
|
|
|
@ -5,17 +5,52 @@
|
||||||
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, Deserialize, Serialize, WebSocketEvent, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
pub struct GatewayHeartbeat {
|
pub struct GatewayHeartbeat {
|
||||||
pub op: u8,
|
pub op: u8,
|
||||||
pub d: Option<u64>,
|
pub d: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GatewayHeartbeat {}
|
impl GatewayHeartbeat {
|
||||||
|
/// The Heartbeat packet a server would receive from a new or fresh Gateway connection.
|
||||||
|
pub fn first() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
/// Quickly create a [GatewayHeartbeat] with the correct `opcode` and the given `sequence_number`.
|
||||||
|
///
|
||||||
|
/// Shorthand for
|
||||||
|
/// ```rs
|
||||||
|
/// Self {
|
||||||
|
/// op: 1,
|
||||||
|
/// d: Some(sequence_number)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn new(sequence_number: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
op: 1,
|
||||||
|
d: Some(sequence_number),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for GatewayHeartbeat {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { op: 1, d: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Deserialize, Serialize, Clone, WebSocketEvent, Copy, PartialEq, Eq, Hash, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
pub struct GatewayHeartbeatAck {
|
pub struct GatewayHeartbeatAck {
|
||||||
pub op: i32,
|
pub op: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GatewayHeartbeatAck {}
|
impl std::default::Default for GatewayHeartbeatAck {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { op: 11 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,22 +3,42 @@
|
||||||
// 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, Deserialize, Serialize, Clone, PartialEq, Eq, WebSocketEvent, Copy, Hash, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
pub struct GatewayHello {
|
pub struct GatewayHello {
|
||||||
pub op: i32,
|
pub op: i32,
|
||||||
pub d: HelloData,
|
pub d: HelloData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GatewayHello {}
|
#[derive(
|
||||||
|
Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Copy, WebSocketEvent, Hash, PartialOrd, Ord,
|
||||||
#[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 {}
|
impl std::default::Default for GatewayHello {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
// "HELLO" opcode is 10
|
||||||
|
op: 10,
|
||||||
|
d: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for HelloData {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
// Discord docs mention 45000 seconds - discord.sex mentions 41250. Defaulting to 45s
|
||||||
|
heartbeat_interval: 45000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -159,7 +157,7 @@ impl GatewayIdentifyConnectionProps {
|
||||||
// 25% of the web
|
// 25% of the web
|
||||||
//default.browser_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36".to_string();
|
//default.browser_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36".to_string();
|
||||||
browser: String::from("Chrome"),
|
browser: String::from("Chrome"),
|
||||||
browser_version: String::from("113.0.0.0"),
|
browser_version: String::from("126.0.0.0"),
|
||||||
system_locale: String::from("en-US"),
|
system_locale: String::from("en-US"),
|
||||||
os: String::from("Windows"),
|
os: String::from("Windows"),
|
||||||
os_version: Some(String::from("10")),
|
os_version: Some(String::from("10")),
|
||||||
|
|
|
@ -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, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|
||||||
#[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 {}
|
|
||||||
|
|
|
@ -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 {}
|
|
||||||
|
|
|
@ -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, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
|
||||||
/// 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 {}
|
|
||||||
|
|
|
@ -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 {}
|
|
||||||
|
|
|
@ -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 {}
|
|
||||||
|
|
|
@ -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,20 @@ pub struct MessageUpdate {
|
||||||
pub mentions: Option<Vec<MessageCreateUser>>,
|
pub mentions: Option<Vec<MessageCreateUser>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageUpdate {}
|
#[derive(
|
||||||
|
Debug,
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
WebSocketEvent,
|
||||||
|
Copy,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
/// # 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 +73,19 @@ pub struct MessageDelete {
|
||||||
pub guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageDelete {}
|
#[derive(
|
||||||
|
Debug,
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
WebSocketEvent,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
/// # 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 +94,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 +106,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 +117,20 @@ pub struct MessageReactionRemove {
|
||||||
pub emoji: Emoji,
|
pub emoji: Emoji,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageReactionRemove {}
|
#[derive(
|
||||||
|
Debug,
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
WebSocketEvent,
|
||||||
|
Copy,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
/// # 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 +139,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 +149,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 +159,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'???
|
||||||
|
@ -149,5 +169,3 @@ pub struct MessageACK {
|
||||||
pub flags: Option<serde_json::Value>,
|
pub flags: Option<serde_json::Value>,
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageACK {}
|
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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 {}
|
|
||||||
|
|
|
@ -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 {}
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue