275 commits behind ono
This commit is contained in:
commit
06ea17875f
|
@ -2,7 +2,7 @@ name: Build and Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "main", "dev" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ jobs:
|
||||||
git clone https://github.com/bitfl0wer/server.git
|
git clone https://github.com/bitfl0wer/server.git
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
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
|
||||||
|
@ -31,7 +31,17 @@ jobs:
|
||||||
npm run start &
|
npm run start &
|
||||||
working-directory: ./server
|
working-directory: ./server
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Build
|
with:
|
||||||
run: cargo build --verbose
|
cache-all-crates: "true"
|
||||||
- name: Run tests
|
- name: Build, Test and Publish Coverage
|
||||||
run: cargo test --verbose
|
run: |
|
||||||
|
if [ -n "${{ secrets.COVERALLS_REPO_TOKEN }}" ]; then
|
||||||
|
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 cargo-tarpaulin --force
|
||||||
|
cargo tarpaulin --all-features --avoid-cfg-tarpaulin --tests --verbose --skip-clean --coveralls ${{ secrets.COVERALLS_REPO_TOKEN }} --timeout 120
|
||||||
|
else
|
||||||
|
echo "Code Coverage step is skipped on forks!"
|
||||||
|
cargo build --verbose --all-features
|
||||||
|
cargo test --verbose --all-features
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ name: rust-clippy analyze
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main", "preserve/*" ]
|
branches: [ "main", "preserve/*", "dev" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
/target/
|
/**/target/
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
56
Cargo.toml
56
Cargo.toml
|
@ -1,8 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chorus"
|
name = "chorus"
|
||||||
version = "0.1.0"
|
description = "A library for interacting with multiple Spacebar-compatible Instances at once."
|
||||||
license = "AGPL-3"
|
version = "0.9.0"
|
||||||
|
license = "AGPL-3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
repository = "https://github.com/polyphony-chat/chorus"
|
||||||
|
readme = "README.md"
|
||||||
|
keywords = ["spacebar", "discord", "polyphony"]
|
||||||
|
website = ["https://discord.com/invite/m3FpcapGDD"]
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["client"]
|
default = ["client"]
|
||||||
|
@ -10,35 +16,43 @@ backend = ["poem", "sqlx"]
|
||||||
client = []
|
client = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = {version = "1.29.1", features = ["macros"]}
|
tokio = { version = "1.29.1", features = ["macros"] }
|
||||||
serde = {version = "1.0.171", features = ["derive"]}
|
serde = { version = "1.0.188", features = ["derive", "rc"] }
|
||||||
serde_json = {version= "1.0.103", features = ["raw_value"]}
|
serde_json = { version = "1.0.105", features = ["raw_value"] }
|
||||||
serde-aux = "4.2.0"
|
serde-aux = "4.2.0"
|
||||||
serde_with = "3.0.0"
|
serde_with = "3.3.0"
|
||||||
serde_repr = "0.1.14"
|
serde_repr = "0.1.16"
|
||||||
reqwest = {version = "0.11.18", features = ["multipart"]}
|
reqwest = { version = "0.11.20", features = ["multipart", "json"] }
|
||||||
url = "2.4.0"
|
url = "2.4.0"
|
||||||
chrono = {version = "0.4.26", features = ["serde"]}
|
chrono = { version = "0.4.26", features = ["serde"] }
|
||||||
regex = "1.9.1"
|
regex = "1.9.4"
|
||||||
custom_error = "1.9.2"
|
custom_error = "1.9.2"
|
||||||
native-tls = "0.2.11"
|
native-tls = "0.2.11"
|
||||||
tokio-tungstenite = {version = "0.19.0", features = ["native-tls"]}
|
tokio-tungstenite = { version = "0.20.0", features = ["native-tls"] }
|
||||||
futures-util = "0.3.28"
|
futures-util = "0.3.28"
|
||||||
http = "0.2.9"
|
http = "0.2.9"
|
||||||
openssl = "0.10.55"
|
openssl = "0.10.56"
|
||||||
base64 = "0.21.2"
|
base64 = "0.21.3"
|
||||||
hostname = "0.3.1"
|
hostname = "0.3.1"
|
||||||
bitflags = { version = "2.3.3", features = ["serde"] }
|
bitflags = { version = "2.4.0", features = ["serde"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
poem = { version = "1.3.56", optional = true }
|
poem = { version = "1.3.57", optional = true }
|
||||||
sqlx = { git = "https://github.com/zert3x/sqlx", branch="feature/skip", features = ["mysql", "sqlite", "json", "chrono", "ipnetwork", "runtime-tokio-native-tls", "any"], optional = true }
|
sqlx = { version = "0.7.1", features = [
|
||||||
thiserror = "1.0.43"
|
"mysql",
|
||||||
|
"sqlite",
|
||||||
|
"json",
|
||||||
|
"chrono",
|
||||||
|
"ipnetwork",
|
||||||
|
"runtime-tokio-native-tls",
|
||||||
|
"any",
|
||||||
|
], optional = true }
|
||||||
|
thiserror = "1.0.47"
|
||||||
jsonwebtoken = "8.3.0"
|
jsonwebtoken = "8.3.0"
|
||||||
log = "0.4.19"
|
log = "0.4.20"
|
||||||
async-trait = "0.1.71"
|
async-trait = "0.1.73"
|
||||||
chorus-macros = {path = "chorus-macros"}
|
chorus-macros = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = {version = "1.29.1", features = ["full"]}
|
tokio = { version = "1.32.0", features = ["full"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
rusty-hook = "0.11.2"
|
rusty-hook = "0.11.2"
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
[![Discord]][Discord-invite]
|
[![Discord]][Discord-invite]
|
||||||
[![Build][build-shield]][build-url]
|
[![Build][build-shield]][build-url]
|
||||||
|
[![Coverage][coverage-shield]][coverage-url]
|
||||||
[![Contributors][contributors-shield]][contributors-url]
|
[![Contributors][contributors-shield]][contributors-url]
|
||||||
[![Forks][forks-shield]][forks-url]
|
[![Forks][forks-shield]][forks-url]
|
||||||
[![Issues][issues-shield]][issues-url]
|
[![Issues][issues-shield]][issues-url]
|
||||||
<img src="https://img.shields.io/static/v1?label=Status&message=Early%20Development&color=blue">
|
<img src="https://img.shields.io/static/v1?label=Status&message=Alpha&color=blue">
|
||||||
|
|
||||||
</br>
|
</br>
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
@ -37,6 +38,7 @@ Chorus is a Rust library that allows developers to interact with multiple Spaceb
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
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 would like to contribute, please feel free to open an Issue with the idea you have, or a
|
If you would like to contribute, please feel free to open an Issue with the idea you have, or a
|
||||||
Pull Request. Please keep our [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) in mind. Your contribution might not be
|
Pull Request. Please keep our [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) in mind. Your contribution might not be
|
||||||
accepted, if it violates these guidelines or [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md).
|
accepted, if it violates these guidelines or [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md).
|
||||||
|
@ -123,6 +125,8 @@ accepted, if it violates these guidelines or [our Code of Conduct](https://githu
|
||||||
[clippy-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/clippy.yml
|
[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-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat
|
||||||
[contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors
|
[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-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat
|
||||||
[forks-url]: https://github.com/polyphony-chat/chorus/network/members
|
[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-shield]: https://img.shields.io/github/stars/polyphony-chat/chorus.svg?style=flat
|
||||||
|
|
|
@ -2,10 +2,22 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chorus-macros"
|
name = "chorus-macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
@ -21,18 +33,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.31"
|
version = "1.0.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0"
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.27"
|
version = "2.0.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
|
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chorus-macros"
|
name = "chorus-macros"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
license = "AGPL-3.0"
|
||||||
|
description = "Macros for the chorus crate."
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "1"
|
quote = "1.0.33"
|
||||||
syn = "2"
|
syn = "2.0.29"
|
||||||
|
async-trait = "0.1.73"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed};
|
||||||
|
|
||||||
#[proc_macro_derive(Updateable)]
|
#[proc_macro_derive(Updateable)]
|
||||||
pub fn updateable_macro_derive(input: TokenStream) -> TokenStream {
|
pub fn updateable_macro_derive(input: TokenStream) -> TokenStream {
|
||||||
|
@ -16,3 +17,125 @@ pub fn updateable_macro_derive(input: TokenStream) -> TokenStream {
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(JsonField)]
|
||||||
|
pub fn jsonfield_macro_derive(input: TokenStream) -> TokenStream {
|
||||||
|
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||||
|
|
||||||
|
let name = &ast.ident;
|
||||||
|
// No need for macro hygiene, we're only using this in chorus
|
||||||
|
quote! {
|
||||||
|
impl JsonField for #name {
|
||||||
|
fn get_json(&self) -> String {
|
||||||
|
self.json.clone()
|
||||||
|
}
|
||||||
|
fn set_json(&mut self, json: String) {
|
||||||
|
self.json = json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(SourceUrlField)]
|
||||||
|
pub fn source_url_macro_derive(input: TokenStream) -> TokenStream {
|
||||||
|
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||||
|
|
||||||
|
let name = &ast.ident;
|
||||||
|
// No need for macro hygiene, we're only using this in chorus
|
||||||
|
quote! {
|
||||||
|
impl SourceUrlField for #name {
|
||||||
|
fn get_source_url(&self) -> String {
|
||||||
|
self.source_url.clone()
|
||||||
|
}
|
||||||
|
fn set_source_url(&mut self, url: String) {
|
||||||
|
self.source_url = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn observe_option(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn observe_option_vec(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn observe(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn observe_vec(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(
|
||||||
|
Composite,
|
||||||
|
attributes(observe_option_vec, observe_option, observe, observe_vec)
|
||||||
|
)]
|
||||||
|
pub fn composite_derive(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let process_field = |field: &Field| {
|
||||||
|
let field_name = &field.ident;
|
||||||
|
let attrs = &field.attrs;
|
||||||
|
|
||||||
|
let observe_option = attrs
|
||||||
|
.iter()
|
||||||
|
.any(|attr| attr.path().is_ident("observe_option"));
|
||||||
|
let observe_option_vec = attrs
|
||||||
|
.iter()
|
||||||
|
.any(|attr| attr.path().is_ident("observe_option_vec"));
|
||||||
|
let observe = attrs.iter().any(|attr| attr.path().is_ident("observe"));
|
||||||
|
let observe_vec = attrs.iter().any(|attr| attr.path().is_ident("observe_vec"));
|
||||||
|
|
||||||
|
match (observe_option, observe_option_vec, observe, observe_vec) {
|
||||||
|
(true, _, _, _) => quote! {
|
||||||
|
#field_name: Self::option_observe_fn(self.#field_name, gateway).await
|
||||||
|
},
|
||||||
|
(_, true, _, _) => quote! {
|
||||||
|
#field_name: Self::option_vec_observe_fn(self.#field_name, gateway).await
|
||||||
|
},
|
||||||
|
(_, _, true, _) => quote! {
|
||||||
|
#field_name: Self::value_observe_fn(self.#field_name, gateway).await
|
||||||
|
},
|
||||||
|
(_, _, _, true) => quote! {
|
||||||
|
#field_name: Self::vec_observe_fn(self.#field_name, gateway).await
|
||||||
|
},
|
||||||
|
_ => quote! {
|
||||||
|
#field_name: self.#field_name
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match &input.data {
|
||||||
|
Data::Struct(data) => match &data.fields {
|
||||||
|
Fields::Named(FieldsNamed { named, .. }) => {
|
||||||
|
let field_exprs = named.iter().map(process_field);
|
||||||
|
|
||||||
|
let ident = &input.ident;
|
||||||
|
let expanded = quote! {
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl<T: Updateable + Clone + Debug> Composite<T> for #ident {
|
||||||
|
async fn watch_whole(self, gateway: &GatewayHandle) -> Self {
|
||||||
|
Self {
|
||||||
|
#(#field_exprs,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
_ => panic!("Composite derive macro only supports named fields"),
|
||||||
|
},
|
||||||
|
_ => panic!("Composite derive macro only supports structs"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,30 +1,33 @@
|
||||||
use std::cell::RefCell;
|
use std::sync::{Arc, RwLock};
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::api::LimitType;
|
|
||||||
use crate::errors::ChorusResult;
|
use crate::errors::ChorusResult;
|
||||||
use crate::gateway::Gateway;
|
use crate::gateway::Gateway;
|
||||||
use crate::instance::{Instance, UserMeta};
|
use crate::instance::{ChorusUser, Instance};
|
||||||
use crate::ratelimiter::ChorusRequest;
|
use crate::ratelimiter::ChorusRequest;
|
||||||
use crate::types::{GatewayIdentifyPayload, LoginResult, LoginSchema};
|
use crate::types::{GatewayIdentifyPayload, LimitType, LoginResult, LoginSchema};
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
pub async fn login_account(&mut self, login_schema: &LoginSchema) -> ChorusResult<UserMeta> {
|
/// Logs into an existing account on the spacebar server.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.spacebar.chat/routes/#post-/auth/login/>
|
||||||
|
pub async fn login_account(mut self, login_schema: LoginSchema) -> ChorusResult<ChorusUser> {
|
||||||
let endpoint_url = self.urls.api.clone() + "/auth/login";
|
let endpoint_url = self.urls.api.clone() + "/auth/login";
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(endpoint_url)
|
.post(endpoint_url)
|
||||||
.body(to_string(login_schema).unwrap()),
|
.body(to_string(&login_schema).unwrap())
|
||||||
|
.header("Content-Type", "application/json"),
|
||||||
limit_type: LimitType::AuthLogin,
|
limit_type: LimitType::AuthLogin,
|
||||||
};
|
};
|
||||||
// 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 shell =
|
||||||
UserMeta::shell(Rc::new(RefCell::new(self.clone())), "None".to_string()).await;
|
ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None".to_string()).await;
|
||||||
let login_result = chorus_request
|
let login_result = chorus_request
|
||||||
.deserialize_response::<LoginResult>(&mut shell)
|
.deserialize_response::<LoginResult>(&mut shell)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -36,12 +39,12 @@ impl Instance {
|
||||||
let gateway = Gateway::new(self.urls.wss.clone()).await.unwrap();
|
let gateway = Gateway::new(self.urls.wss.clone()).await.unwrap();
|
||||||
identify.token = login_result.token.clone();
|
identify.token = login_result.token.clone();
|
||||||
gateway.send_identify(identify).await;
|
gateway.send_identify(identify).await;
|
||||||
let user = UserMeta::new(
|
let user = ChorusUser::new(
|
||||||
Rc::new(RefCell::new(self.clone())),
|
Arc::new(RwLock::new(self.clone())),
|
||||||
login_result.token,
|
login_result.token,
|
||||||
self.clone_limits_if_some(),
|
self.clone_limits_if_some(),
|
||||||
login_result.settings,
|
login_result.settings,
|
||||||
object,
|
Arc::new(RwLock::new(object)),
|
||||||
gateway,
|
gateway,
|
||||||
);
|
);
|
||||||
Ok(user)
|
Ok(user)
|
||||||
|
|
|
@ -1,5 +1,41 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
pub use login::*;
|
pub use login::*;
|
||||||
pub use register::*;
|
pub use register::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
errors::ChorusResult,
|
||||||
|
gateway::Gateway,
|
||||||
|
instance::{ChorusUser, Instance},
|
||||||
|
types::{GatewayIdentifyPayload, User},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod login;
|
pub mod login;
|
||||||
pub mod register;
|
pub mod register;
|
||||||
|
|
||||||
|
impl Instance {
|
||||||
|
/// 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> {
|
||||||
|
let object_result = self.get_user(token.clone(), None).await;
|
||||||
|
if let Err(e) = object_result {
|
||||||
|
return Result::Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
let user_settings = User::get_settings(&token, &self.urls.api, &mut self.clone())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut identify = GatewayIdentifyPayload::common();
|
||||||
|
let gateway = Gateway::new(self.urls.wss.clone()).await.unwrap();
|
||||||
|
identify.token = token.clone();
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
@ -6,39 +6,35 @@ use serde_json::to_string;
|
||||||
use crate::gateway::Gateway;
|
use crate::gateway::Gateway;
|
||||||
use crate::types::GatewayIdentifyPayload;
|
use crate::types::GatewayIdentifyPayload;
|
||||||
use crate::{
|
use crate::{
|
||||||
api::policies::instance::LimitType,
|
|
||||||
errors::ChorusResult,
|
errors::ChorusResult,
|
||||||
instance::{Instance, Token, UserMeta},
|
instance::{ChorusUser, Instance, Token},
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
|
types::LimitType,
|
||||||
types::RegisterSchema,
|
types::RegisterSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
/// Registers a new user on the Spacebar server.
|
/// Registers a new user on the server.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://docs.spacebar.chat/routes/#post-/auth/register/>
|
||||||
/// * `register_schema` - The [`RegisterSchema`] that contains all the information that is needed to register a new user.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// * [`ChorusLibError`] - If the server does not respond.
|
|
||||||
pub async fn register_account(
|
pub async fn register_account(
|
||||||
&mut self,
|
mut self,
|
||||||
register_schema: &RegisterSchema,
|
register_schema: RegisterSchema,
|
||||||
) -> ChorusResult<UserMeta> {
|
) -> ChorusResult<ChorusUser> {
|
||||||
let endpoint_url = self.urls.api.clone() + "/auth/register";
|
let endpoint_url = self.urls.api.clone() + "/auth/register";
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(endpoint_url)
|
.post(endpoint_url)
|
||||||
.body(to_string(register_schema).unwrap()),
|
.body(to_string(®ister_schema).unwrap())
|
||||||
|
.header("Content-Type", "application/json"),
|
||||||
limit_type: LimitType::AuthRegister,
|
limit_type: LimitType::AuthRegister,
|
||||||
};
|
};
|
||||||
// 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 shell =
|
||||||
UserMeta::shell(Rc::new(RefCell::new(self.clone())), "None".to_string()).await;
|
ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None".to_string()).await;
|
||||||
let token = chorus_request
|
let token = chorus_request
|
||||||
.deserialize_response::<Token>(&mut shell)
|
.deserialize_response::<Token>(&mut shell)
|
||||||
.await?
|
.await?
|
||||||
|
@ -47,17 +43,17 @@ impl Instance {
|
||||||
self.limits_information.as_mut().unwrap().ratelimits = shell.limits.unwrap();
|
self.limits_information.as_mut().unwrap().ratelimits = shell.limits.unwrap();
|
||||||
}
|
}
|
||||||
let user_object = self.get_user(token.clone(), None).await.unwrap();
|
let user_object = self.get_user(token.clone(), None).await.unwrap();
|
||||||
let settings = UserMeta::get_settings(&token, &self.urls.api.clone(), self).await?;
|
let settings = ChorusUser::get_settings(&token, &self.urls.api.clone(), &mut self).await?;
|
||||||
let mut identify = GatewayIdentifyPayload::common();
|
let mut identify = GatewayIdentifyPayload::common();
|
||||||
let gateway = Gateway::new(self.urls.wss.clone()).await.unwrap();
|
let gateway = Gateway::new(self.urls.wss.clone()).await.unwrap();
|
||||||
identify.token = token.clone();
|
identify.token = token.clone();
|
||||||
gateway.send_identify(identify).await;
|
gateway.send_identify(identify).await;
|
||||||
let user = UserMeta::new(
|
let user = ChorusUser::new(
|
||||||
Rc::new(RefCell::new(self.clone())),
|
Arc::new(RwLock::new(self.clone())),
|
||||||
token.clone(),
|
token.clone(),
|
||||||
self.clone_limits_if_some(),
|
self.clone_limits_if_some(),
|
||||||
settings,
|
Arc::new(RwLock::new(settings)),
|
||||||
user_object,
|
Arc::new(RwLock::new(user_object)),
|
||||||
gateway,
|
gateway,
|
||||||
);
|
);
|
||||||
Ok(user)
|
Ok(user)
|
||||||
|
|
|
@ -1,126 +1,165 @@
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::types::AddChannelRecipientSchema;
|
use crate::types::{AddChannelRecipientSchema, ModifyChannelPositionsSchema};
|
||||||
use crate::{
|
use crate::{
|
||||||
api::LimitType,
|
|
||||||
errors::{ChorusError, ChorusResult},
|
errors::{ChorusError, ChorusResult},
|
||||||
instance::UserMeta,
|
instance::ChorusUser,
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
types::{Channel, ChannelModifySchema, GetChannelMessagesSchema, Message, Snowflake},
|
types::{
|
||||||
|
Channel, ChannelModifySchema, GetChannelMessagesSchema, LimitType, Message, Snowflake,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Channel {
|
impl Channel {
|
||||||
pub async fn get(user: &mut UserMeta, channel_id: Snowflake) -> ChorusResult<Channel> {
|
/// Retrieves a channel from the server.
|
||||||
let url = user.belongs_to.borrow().urls.api.clone();
|
///
|
||||||
let chorus_request = ChorusRequest {
|
/// # Reference
|
||||||
request: Client::new()
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#get-channel>
|
||||||
.get(format!("{}/channels/{}/", url, channel_id))
|
pub async fn get(user: &mut ChorusUser, channel_id: Snowflake) -> ChorusResult<Channel> {
|
||||||
.bearer_auth(user.token()),
|
let chorus_request = ChorusRequest::new(
|
||||||
limit_type: LimitType::Channel(channel_id),
|
http::Method::GET,
|
||||||
};
|
&format!(
|
||||||
|
"{}/channels/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api.clone(),
|
||||||
|
channel_id
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
chorus_request.deserialize_response::<Channel>(user).await
|
chorus_request.deserialize_response::<Channel>(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes a channel.
|
/// Deletes self.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Requires the [`MANAGE_CHANNELS`](crate::types::PermissionFlags::MANAGE_CHANNELS) permission in a guild, or
|
||||||
|
/// the [`MANAGE_THREADS`](crate::types::PermissionFlags::MANAGE_THREADS) permission if the channel is a thread.
|
||||||
///
|
///
|
||||||
/// * `token` - A string slice that holds the authorization token.
|
/// # Reference
|
||||||
/// * `url_api` - A string slice that holds the URL of the API.
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#delete-channel>
|
||||||
/// * `channel` - A `Channel` object that represents the channel to be deleted.
|
pub async fn delete(
|
||||||
/// * `limits_user` - A mutable reference to a `Limits` object that represents the user's rate limits.
|
self,
|
||||||
/// * `limits_instance` - A mutable reference to a `Limits` object that represents the instance's rate limits.
|
audit_log_reason: Option<String>,
|
||||||
///
|
user: &mut ChorusUser,
|
||||||
/// # Returns
|
) -> ChorusResult<()> {
|
||||||
///
|
let url = format!(
|
||||||
/// A `Result` that contains a `ChorusLibError` if an error occurred during the request, or `()` if the request was successful.
|
"{}/channels/{}",
|
||||||
pub async fn delete(self, user: &mut UserMeta) -> ChorusResult<()> {
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
let chorus_request = ChorusRequest {
|
self.id,
|
||||||
request: Client::new()
|
);
|
||||||
.delete(format!(
|
|
||||||
"{}/channels/{}/",
|
let request = ChorusRequest::new(
|
||||||
user.belongs_to.borrow().urls.api,
|
http::Method::DELETE,
|
||||||
self.id
|
&url,
|
||||||
))
|
None,
|
||||||
.bearer_auth(user.token()),
|
audit_log_reason.as_deref(),
|
||||||
limit_type: LimitType::Channel(self.id),
|
None,
|
||||||
};
|
Some(user),
|
||||||
chorus_request.handle_request_as_result(user).await
|
LimitType::Channel(self.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modifies a channel.
|
/// Modifies a channel with the provided data.
|
||||||
|
/// Returns the new Channel.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Requires the [`MANAGE_CHANNELS`](crate::types::PermissionFlags::MANAGE_CHANNELS) permission in a guild.
|
||||||
///
|
///
|
||||||
/// * `modify_data` - A `ChannelModifySchema` object that represents the modifications to be made to the channel.
|
/// If modifying permission overwrites, the [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) permission is required.
|
||||||
/// * `token` - A string slice that holds the authorization token.
|
/// Only permissions you have in the guild or parent channel (if applicable) can be allowed/denied
|
||||||
/// * `url_api` - A string slice that holds the URL of the API.
|
/// (unless you have a [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) overwrite in the channel).
|
||||||
/// * `channel_id` - A string slice that holds the ID of the channel to be modified.
|
|
||||||
/// * `limits_user` - A mutable reference to a `Limits` object that represents the user's rate limits.
|
|
||||||
/// * `limits_instance` - A mutable reference to a `Limits` object that represents the instance's rate limits.
|
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// If modifying a thread and setting `archived` to `false`, when `locked` is also `false`, only the [`SEND_MESSAGES`](crate::types::PermissionFlags::SEND_MESSAGES) permission is required.
|
||||||
|
/// Otherwise, requires the [`MANAGE_THREADS`](crate::types::PermissionFlags::MANAGE_THREADS) permission. Requires the thread to have `archived` set to `false` or be set to `false` in the request.
|
||||||
///
|
///
|
||||||
/// A `Result` that contains a `Channel` object if the request was successful, or an `ChorusLibError` if an error occurred during the request.
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#modify-channel>
|
||||||
pub async fn modify(
|
pub async fn modify(
|
||||||
&self,
|
&self,
|
||||||
modify_data: ChannelModifySchema,
|
modify_data: ChannelModifySchema,
|
||||||
channel_id: Snowflake,
|
audit_log_reason: Option<String>,
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
) -> ChorusResult<Channel> {
|
) -> ChorusResult<Channel> {
|
||||||
let chorus_request = ChorusRequest {
|
let channel_id = self.id;
|
||||||
request: Client::new()
|
let url = format!(
|
||||||
.patch(format!(
|
"{}/channels/{}",
|
||||||
"{}/channels/{}/",
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
user.belongs_to.borrow().urls.api,
|
channel_id
|
||||||
channel_id
|
);
|
||||||
))
|
|
||||||
.bearer_auth(user.token())
|
let request = ChorusRequest::new(
|
||||||
.body(to_string(&modify_data).unwrap()),
|
http::Method::PATCH,
|
||||||
limit_type: LimitType::Channel(channel_id),
|
&url,
|
||||||
};
|
Some(to_string(&modify_data).unwrap()),
|
||||||
chorus_request.deserialize_response::<Channel>(user).await
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.deserialize_response::<Channel>(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetches recent messages from a channel.
|
||||||
|
///
|
||||||
|
/// If operating on a guild channel, this endpoint requires the [`VIEW_CHANNEL`](crate::types::PermissionFlags::VIEW_CHANNEL) permission.
|
||||||
|
///
|
||||||
|
/// If the user is missing the [`READ_MESSAGE_HISTORY`](crate::types::PermissionFlags::READ_MESSAGE_HISTORY) permission,
|
||||||
|
/// this method returns an empty list.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#get-messages>
|
||||||
pub async fn messages(
|
pub async fn messages(
|
||||||
range: GetChannelMessagesSchema,
|
range: GetChannelMessagesSchema,
|
||||||
channel_id: Snowflake,
|
channel_id: Snowflake,
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
) -> Result<Vec<Message>, ChorusError> {
|
) -> Result<Vec<Message>, ChorusError> {
|
||||||
let chorus_request = ChorusRequest {
|
let url = format!(
|
||||||
request: Client::new()
|
"{}/channels/{}/messages",
|
||||||
.get(format!(
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
"{}/channels/{}/messages",
|
channel_id
|
||||||
user.belongs_to.borrow().urls.api,
|
);
|
||||||
channel_id
|
|
||||||
))
|
let mut chorus_request = ChorusRequest::new(
|
||||||
.bearer_auth(user.token())
|
http::Method::GET,
|
||||||
.query(&range),
|
&url,
|
||||||
limit_type: Default::default(),
|
None,
|
||||||
};
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
Default::default(),
|
||||||
|
);
|
||||||
|
chorus_request.request = chorus_request.request.query(&range);
|
||||||
|
|
||||||
chorus_request
|
chorus_request
|
||||||
.deserialize_response::<Vec<Message>>(user)
|
.deserialize_response::<Vec<Message>>(user)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a recipient to a group DM.
|
||||||
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// Read: <https://discord-userdoccers.vercel.app/resources/channel#add-channel-recipient>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#add-channel-recipient>
|
||||||
pub async fn add_channel_recipient(
|
pub async fn add_channel_recipient(
|
||||||
&self,
|
&self,
|
||||||
recipient_id: Snowflake,
|
recipient_id: Snowflake,
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
add_channel_recipient_schema: Option<AddChannelRecipientSchema>,
|
add_channel_recipient_schema: Option<AddChannelRecipientSchema>,
|
||||||
) -> ChorusResult<()> {
|
) -> ChorusResult<()> {
|
||||||
let mut request = Client::new()
|
let mut request = Client::new()
|
||||||
.put(format!(
|
.put(format!(
|
||||||
"{}/channels/{}/recipients/{}/",
|
"{}/channels/{}/recipients/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
self.id,
|
self.id,
|
||||||
recipient_id
|
recipient_id
|
||||||
))
|
))
|
||||||
.bearer_auth(user.token());
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json");
|
||||||
if let Some(schema) = add_channel_recipient_schema {
|
if let Some(schema) = add_channel_recipient_schema {
|
||||||
request = request.body(to_string(&schema).unwrap());
|
request = request.body(to_string(&schema).unwrap());
|
||||||
}
|
}
|
||||||
|
@ -132,26 +171,61 @@ impl Channel {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes a recipient from a group DM.
|
||||||
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// Read: <https://discord-userdoccers.vercel.app/resources/channel#remove-channel-recipient>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#remove-channel-recipient>
|
||||||
pub async fn remove_channel_recipient(
|
pub async fn remove_channel_recipient(
|
||||||
&self,
|
&self,
|
||||||
recipient_id: Snowflake,
|
recipient_id: Snowflake,
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
) -> ChorusResult<()> {
|
) -> ChorusResult<()> {
|
||||||
let request = Client::new()
|
let url = format!(
|
||||||
.delete(format!(
|
"{}/channels/{}/recipients/{}",
|
||||||
"{}/channels/{}/recipients/{}/",
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
user.belongs_to.borrow().urls.api,
|
self.id,
|
||||||
self.id,
|
recipient_id
|
||||||
recipient_id
|
);
|
||||||
))
|
|
||||||
.bearer_auth(user.token());
|
let request = ChorusRequest::new(
|
||||||
ChorusRequest {
|
http::Method::DELETE,
|
||||||
request,
|
&url,
|
||||||
limit_type: LimitType::Channel(self.id),
|
None,
|
||||||
}
|
None,
|
||||||
.handle_request_as_result(user)
|
None,
|
||||||
.await
|
Some(user),
|
||||||
|
LimitType::Channel(self.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies the positions of a set of channel objects for the guild. Requires the `MANAGE_CHANNELS` permission.
|
||||||
|
/// Only channels to be modified are required.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#modify-guild-channel-positions>
|
||||||
|
pub async fn modify_positions(
|
||||||
|
schema: Vec<ModifyChannelPositionsSchema>,
|
||||||
|
guild_id: Snowflake,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/guilds/{}/channels",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::PATCH,
|
||||||
|
&url,
|
||||||
|
Some(to_string(&schema).unwrap()),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,36 @@
|
||||||
use http::header::CONTENT_DISPOSITION;
|
use http::header::CONTENT_DISPOSITION;
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
use reqwest::{multipart, Client};
|
use reqwest::{multipart, Client};
|
||||||
use serde_json::to_string;
|
use serde_json::{from_value, to_string, Value};
|
||||||
|
|
||||||
use crate::api::LimitType;
|
use crate::errors::{ChorusError, ChorusResult};
|
||||||
use crate::instance::UserMeta;
|
use crate::instance::ChorusUser;
|
||||||
use crate::ratelimiter::ChorusRequest;
|
use crate::ratelimiter::ChorusRequest;
|
||||||
use crate::types::{Message, MessageSendSchema, Snowflake};
|
use crate::types::{
|
||||||
|
Channel, CreateGreetMessage, LimitType, Message, MessageAck, MessageModifySchema,
|
||||||
|
MessageSearchEndpoint, MessageSearchQuery, MessageSendSchema, Snowflake,
|
||||||
|
};
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
|
/// Sends a message in the channel with the provided channel_id.
|
||||||
|
/// Returns the sent message.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#create-message>
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
channel_id: Snowflake,
|
channel_id: Snowflake,
|
||||||
mut message: MessageSendSchema,
|
mut message: MessageSendSchema,
|
||||||
) -> Result<Message, crate::errors::ChorusError> {
|
) -> ChorusResult<Message> {
|
||||||
let url_api = user.belongs_to.borrow().urls.api.clone();
|
let url_api = user.belongs_to.read().unwrap().urls.api.clone();
|
||||||
|
|
||||||
if message.attachments.is_none() {
|
if message.attachments.is_none() {
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(format!("{}/channels/{}/messages/", url_api, channel_id))
|
.post(format!("{}/channels/{}/messages", url_api, channel_id))
|
||||||
.bearer_auth(user.token())
|
.header("Authorization", user.token())
|
||||||
.body(to_string(&message).unwrap()),
|
.body(to_string(&message).unwrap())
|
||||||
|
.header("Content-Type", "application/json"),
|
||||||
limit_type: LimitType::Channel(channel_id),
|
limit_type: LimitType::Channel(channel_id),
|
||||||
};
|
};
|
||||||
chorus_request.deserialize_response::<Message>(user).await
|
chorus_request.deserialize_response::<Message>(user).await
|
||||||
|
@ -55,22 +64,462 @@ impl Message {
|
||||||
|
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(format!("{}/channels/{}/messages/", url_api, channel_id))
|
.post(format!("{}/channels/{}/messages", url_api, channel_id))
|
||||||
.bearer_auth(user.token())
|
.header("Authorization", user.token())
|
||||||
.multipart(form),
|
.multipart(form),
|
||||||
limit_type: LimitType::Channel(channel_id),
|
limit_type: LimitType::Channel(channel_id),
|
||||||
};
|
};
|
||||||
chorus_request.deserialize_response::<Message>(user).await
|
chorus_request.deserialize_response::<Message>(user).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns messages without the reactions key that match a search query in the guild or channel.
|
||||||
|
/// The messages that are direct results will have an extra hit key set to true.
|
||||||
|
/// If operating on a guild channel, this endpoint requires the `READ_MESSAGE_HISTORY`
|
||||||
|
/// permission to be present on the current user.
|
||||||
|
///
|
||||||
|
/// If the guild/channel you are searching is not yet indexed, the endpoint will return a 202 accepted response.
|
||||||
|
/// In this case, the method will return a [`ChorusError::InvalidResponse`] error.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#search-messages>
|
||||||
|
pub(crate) async fn search(
|
||||||
|
endpoint: MessageSearchEndpoint,
|
||||||
|
query: MessageSearchQuery,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Vec<Message>> {
|
||||||
|
let limit_type = match &endpoint {
|
||||||
|
MessageSearchEndpoint::Channel(id) => LimitType::Channel(*id),
|
||||||
|
MessageSearchEndpoint::GuildChannel(id) => LimitType::Guild(*id),
|
||||||
|
};
|
||||||
|
let request = ChorusRequest {
|
||||||
|
limit_type,
|
||||||
|
request: Client::new()
|
||||||
|
.get(format!(
|
||||||
|
"{}/{}/messages/search",
|
||||||
|
&user.belongs_to.read().unwrap().urls.api,
|
||||||
|
endpoint
|
||||||
|
))
|
||||||
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(to_string(&query).unwrap()),
|
||||||
|
};
|
||||||
|
let result = request.send_request(user).await?;
|
||||||
|
let result_json = result.json::<Value>().await.unwrap();
|
||||||
|
if !result_json.is_object() {
|
||||||
|
return Err(search_error(result_json.to_string()));
|
||||||
|
}
|
||||||
|
let value_map = result_json.as_object().unwrap();
|
||||||
|
if let Some(messages) = value_map.get("messages") {
|
||||||
|
if let Ok(response) = from_value::<Vec<Vec<Message>>>(messages.clone()) {
|
||||||
|
let result_messages: Vec<Message> = response.into_iter().flatten().collect();
|
||||||
|
return Ok(result_messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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") {
|
||||||
|
return Err(search_error(result_json.to_string()));
|
||||||
|
}
|
||||||
|
let code = value_map.get("code").unwrap().as_u64().unwrap();
|
||||||
|
let retry_after = value_map.get("retry_after").unwrap().as_u64().unwrap();
|
||||||
|
Err(ChorusError::NotFound {
|
||||||
|
error: format!(
|
||||||
|
"Index not yet available. Try again later. Code: {}. Retry after {}s",
|
||||||
|
code, retry_after
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all pinned messages in the channel as a Vector of message objects without the reactions key.
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/message#get-pinned-messages>
|
||||||
|
pub async fn get_sticky(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Vec<Message>> {
|
||||||
|
let chorus_request = ChorusRequest::new(
|
||||||
|
http::Method::GET,
|
||||||
|
format!(
|
||||||
|
"{}/channels/{}/pins",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
chorus_request
|
||||||
|
.deserialize_response::<Vec<Message>>(user)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pins a message in a channel. Requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success.
|
||||||
|
/// The max pinned messages is 50.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/message#pin-message>
|
||||||
|
pub async fn sticky(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
message_id: Snowflake,
|
||||||
|
audit_log_reason: Option<&str>,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::PUT,
|
||||||
|
format!(
|
||||||
|
"{}/channels/{}/pins/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
message_id
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
audit_log_reason,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpins a message in a channel. Requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success.
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/message#unpin-message>
|
||||||
|
pub async fn unsticky(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
message_id: Snowflake,
|
||||||
|
audit_log_reason: Option<&str>,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::DELETE,
|
||||||
|
format!(
|
||||||
|
"{}/channels/{}/pins/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
message_id
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
audit_log_reason,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a specific message object in the channel.
|
||||||
|
/// If operating on a guild channel, this endpoint requires the `READ_MESSAGE_HISTORY` permission to be present on the current user.
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/message#get-message>
|
||||||
|
pub async fn get(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
message_id: Snowflake,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Message> {
|
||||||
|
let chorus_request = ChorusRequest {
|
||||||
|
request: Client::new()
|
||||||
|
.get(format!(
|
||||||
|
"{}/channels/{}/messages/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
message_id
|
||||||
|
))
|
||||||
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json"),
|
||||||
|
limit_type: LimitType::Channel(channel_id),
|
||||||
|
};
|
||||||
|
chorus_request.deserialize_response::<Message>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Posts a greet message to a channel. This endpoint requires the channel is a DM channel or you reply to a system message.
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/message#create-greet-message>
|
||||||
|
pub async fn create_greet(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
schema: CreateGreetMessage,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Message> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::POST,
|
||||||
|
format!(
|
||||||
|
"{}/channels/{}/messages/greet",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
Some(to_string(&schema).unwrap()),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
request.deserialize_response::<Message>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the channel's latest acknowledged message (marks a message as read) for the current user.
|
||||||
|
/// The message ID parameter does not need to be a valid message ID, but it must be a valid snowflake.
|
||||||
|
/// If the message ID is being set to a message sent prior to the latest acknowledged one,
|
||||||
|
/// manual should be true or the resulting read state update should be ignored by clients (but is still saved), resulting in undefined behavior.
|
||||||
|
/// In this case, mention_count should also be set to the amount of mentions unacknowledged as it is not automatically calculated by Discord.
|
||||||
|
///
|
||||||
|
/// Returns an optional token, which can be used as the new `ack` token for following `ack`s.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/message#acknowledge-message>
|
||||||
|
pub async fn acknowledge(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
message_id: Snowflake,
|
||||||
|
schema: MessageAck,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Option<String>> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::POST,
|
||||||
|
format!(
|
||||||
|
"{}/channels/{}/messages/{}/ack",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
message_id
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
Some(to_string(&schema).unwrap()),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
request.deserialize_response::<Option<String>>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Crossposts a message in a News Channel to following channels.
|
||||||
|
/// This endpoint requires the `SEND_MESSAGES` permission, if the current user sent the message,
|
||||||
|
/// or additionally the `MANAGE_MESSAGES` permission, for all other messages, to be present for the current user.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#crosspost-message>
|
||||||
|
pub async fn crosspost(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
message_id: Snowflake,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Message> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::POST,
|
||||||
|
format!(
|
||||||
|
"{}/channels/{}/messages/{}/crosspost",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
message_id
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
request.deserialize_response::<Message>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hides a message from the feed of the guild the channel belongs to. Returns a 204 empty response on success.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#hide-message-from-guild-feed>
|
||||||
|
pub async fn hide_from_guild_feed(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
message_id: Snowflake,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/channels/{}/messages/{}/hide-guild-feed",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
message_id
|
||||||
|
);
|
||||||
|
let chorus_request = ChorusRequest::new(
|
||||||
|
http::Method::DELETE,
|
||||||
|
&url,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
chorus_request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Edits a previously sent message. All fields can be edited by the original message author.
|
||||||
|
/// Other users can only edit flags and only if they have the MANAGE_MESSAGES permission in the corresponding channel.
|
||||||
|
/// When specifying flags, ensure to include all previously set flags/bits in addition to ones that you are modifying.
|
||||||
|
/// When the content field is edited, the mentions array in the message object will be reconstructed from scratch based on the new content.
|
||||||
|
/// The allowed_mentions field of the edit request controls how this happens.
|
||||||
|
/// If there is no explicit allowed_mentions in the edit request, the content will be parsed with default allowances, that is,
|
||||||
|
/// without regard to whether or not an allowed_mentions was present in the request that originally created the message.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/message#edit-message>
|
||||||
|
pub async fn modify(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
message_id: Snowflake,
|
||||||
|
schema: MessageModifySchema,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Message> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/channels/{}/messages/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
message_id
|
||||||
|
);
|
||||||
|
let chorus_request = ChorusRequest::new(
|
||||||
|
http::Method::PATCH,
|
||||||
|
&url,
|
||||||
|
Some(to_string(&schema).unwrap()),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
chorus_request.deserialize_response::<Message>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes a message. If operating on a guild channel and trying to delete a message that was not sent by the current user,
|
||||||
|
/// this endpoint requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success.
|
||||||
|
pub async fn delete(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
message_id: Snowflake,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/channels/{}/messages/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
message_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let chorus_request = ChorusRequest::new(
|
||||||
|
http::Method::DELETE,
|
||||||
|
&url,
|
||||||
|
None,
|
||||||
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
chorus_request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes multiple messages in a single request. This endpoint can only be used on guild channels and requires the MANAGE_MESSAGES permission.
|
||||||
|
/// Returns a 204 empty response on success.
|
||||||
|
///
|
||||||
|
/// **This endpoint will not delete messages older than 2 weeks, and will fail if any message provided is older than that or if any duplicate message IDs are provided.**
|
||||||
|
///
|
||||||
|
/// **This endpoint is not usable by user accounts.** (At least according to Discord.com. Spacebar behaviour may differ.)
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/message#bulk-delete-messages>
|
||||||
|
pub async fn bulk_delete(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
messages: Vec<Snowflake>,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
if messages.len() < 2 {
|
||||||
|
return Err(ChorusError::InvalidArguments {
|
||||||
|
error: "`messages` must contain at least 2 entries.".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::POST,
|
||||||
|
format!(
|
||||||
|
"{}/channels/{}/messages/bulk-delete",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
Some(to_string(&messages).unwrap()),
|
||||||
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acknowledges the currently pinned messages in a channel. Returns a 204 empty response on success.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/message#acknowledge-pinned-messages>
|
||||||
|
pub async fn acknowledge_pinned(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
let chorus_request = ChorusRequest::new(
|
||||||
|
http::Method::POST,
|
||||||
|
format!(
|
||||||
|
"{}/channels/{}/pins/ack",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
channel_id,
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
chorus_request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserMeta {
|
fn search_error(result_text: String) -> ChorusError {
|
||||||
|
ChorusError::InvalidResponse {
|
||||||
|
error: format!(
|
||||||
|
"Got unexpected Response, or Response which is not valid JSON. Response: \n{}",
|
||||||
|
result_text
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChorusUser {
|
||||||
|
/// Sends a message in the channel with the provided channel_id.
|
||||||
|
/// Returns the sent message.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// Shorthand call for [`Message::send`]
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#create-message>
|
||||||
pub async fn send_message(
|
pub async fn send_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
message: MessageSendSchema,
|
message: MessageSendSchema,
|
||||||
channel_id: Snowflake,
|
channel_id: Snowflake,
|
||||||
) -> Result<Message, crate::errors::ChorusError> {
|
) -> ChorusResult<Message> {
|
||||||
Message::send(self, channel_id, message).await
|
Message::send(self, channel_id, message).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Channel {
|
||||||
|
/// Returns messages without the reactions key that match a search query in the channel.
|
||||||
|
/// The messages that are direct results will have an extra hit key set to true.
|
||||||
|
/// If operating on a guild channel, this endpoint requires the `READ_MESSAGE_HISTORY`
|
||||||
|
/// permission to be present on the current user.
|
||||||
|
///
|
||||||
|
/// If the guild/channel you are searching is not yet indexed, the endpoint will return a 202 accepted response.
|
||||||
|
/// In this case, the method will return a [`ChorusError::InvalidResponse`] error.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#search-messages>
|
||||||
|
pub async fn search_messages(
|
||||||
|
channel_id: Snowflake,
|
||||||
|
query: MessageSearchQuery,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Vec<Message>> {
|
||||||
|
Message::search(MessageSearchEndpoint::Channel(channel_id), query, user).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,33 +2,32 @@ use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::LimitType,
|
|
||||||
errors::{ChorusError, ChorusResult},
|
errors::{ChorusError, ChorusResult},
|
||||||
instance::UserMeta,
|
instance::ChorusUser,
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
types::{self, PermissionOverwrite, Snowflake},
|
types::{self, LimitType, PermissionOverwrite, Snowflake},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl types::Channel {
|
impl types::Channel {
|
||||||
/// Edits the permission overwrites for a channel.
|
/// Edits the permission overwrites for a user or role in a channel.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Only usable for guild channels.
|
||||||
///
|
///
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
/// Requires the [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) permission.
|
||||||
/// * `channel_id` - A string slice representing the ID of the channel.
|
/// Only permissions you have in the guild or parent channel (if applicable) can be allowed/denied
|
||||||
/// * `overwrite` - A [`PermissionOverwrite`] instance representing the new permission overwrites.
|
/// (unless you have a [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) overwrite in the channel).
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Reference
|
||||||
///
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#modify-channel-permissions>
|
||||||
/// This function returns a result that is either [`Ok(())`] if the request is successful, or an [`Err(ChorusLibError)`].
|
pub async fn modify_permissions(
|
||||||
pub async fn edit_permissions(
|
user: &mut ChorusUser,
|
||||||
user: &mut UserMeta,
|
|
||||||
channel_id: Snowflake,
|
channel_id: Snowflake,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
overwrite: PermissionOverwrite,
|
overwrite: PermissionOverwrite,
|
||||||
) -> ChorusResult<()> {
|
) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/permissions/{}",
|
"{}/channels/{}/permissions/{}",
|
||||||
user.belongs_to.borrow_mut().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
channel_id,
|
channel_id,
|
||||||
overwrite.id
|
overwrite.id
|
||||||
);
|
);
|
||||||
|
@ -40,39 +39,51 @@ impl types::Channel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let mut request = Client::new()
|
||||||
|
.put(url)
|
||||||
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(body);
|
||||||
|
if let Some(reason) = audit_log_reason {
|
||||||
|
request = request.header("X-Audit-Log-Reason", reason);
|
||||||
|
}
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().put(url).bearer_auth(user.token()).body(body),
|
request,
|
||||||
limit_type: LimitType::Channel(channel_id),
|
limit_type: LimitType::Channel(channel_id),
|
||||||
};
|
};
|
||||||
chorus_request.handle_request_as_result(user).await
|
chorus_request.handle_request_as_result(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes a permission overwrite for a channel.
|
/// Deletes a permission overwrite for a user or role in a channel.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Only usable for guild channels.
|
||||||
///
|
///
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
/// Requires the [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) permission.
|
||||||
/// * `channel_id` - A string slice representing the ID of the channel.
|
|
||||||
/// * `overwrite_id` - A string slice representing the ID of the permission overwrite to delete.
|
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Reference
|
||||||
///
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#delete-channel-permission>
|
||||||
/// This function returns a Result that is either [`Ok(())`] if the request is successfulm or an [`Err(ChorusLibError)`].
|
|
||||||
pub async fn delete_permission(
|
pub async fn delete_permission(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
channel_id: Snowflake,
|
channel_id: Snowflake,
|
||||||
overwrite_id: Snowflake,
|
overwrite_id: Snowflake,
|
||||||
) -> ChorusResult<()> {
|
) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/permissions/{}",
|
"{}/channels/{}/permissions/{}",
|
||||||
user.belongs_to.borrow_mut().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
channel_id,
|
channel_id,
|
||||||
overwrite_id
|
overwrite_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
|
||||||
request: Client::new().delete(url).bearer_auth(user.token()),
|
let request = ChorusRequest::new(
|
||||||
limit_type: LimitType::Channel(channel_id),
|
http::Method::DELETE,
|
||||||
};
|
&url,
|
||||||
chorus_request.handle_request_as_result(user).await
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
use reqwest::Client;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::LimitType,
|
|
||||||
errors::ChorusResult,
|
errors::ChorusResult,
|
||||||
instance::UserMeta,
|
instance::ChorusUser,
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
types::{self, PublicUser, Snowflake},
|
types::{self, LimitType, PublicUser, Snowflake},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/// 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)]
|
||||||
*/
|
|
||||||
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,
|
||||||
|
@ -18,169 +14,189 @@ pub struct ReactionMeta {
|
||||||
|
|
||||||
impl ReactionMeta {
|
impl ReactionMeta {
|
||||||
/// Deletes all reactions for a message.
|
/// Deletes all reactions for a message.
|
||||||
/// This endpoint requires the `MANAGE_MESSAGES` permission to be present on the current user.
|
///
|
||||||
/// # Arguments
|
/// This endpoint requires the [`MANAGE_MESSAGES`](crate::types::PermissionFlags::MANAGE_MESSAGES) permission.
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
///
|
||||||
/// # Returns
|
|
||||||
/// A `Result` [`()`] [`crate::errors::ChorusLibError`] if something went wrong.
|
|
||||||
/// Fires a `Message Reaction Remove All` Gateway event.
|
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See [https://discord.com/developers/docs/resources/channel#delete-all-reactions](https://discord.com/developers/docs/resources/channel#delete-all-reactions)
|
/// See <https://discord.com/developers/docs/resources/channel#delete-all-reactions>
|
||||||
pub async fn delete_all(&self, user: &mut UserMeta) -> ChorusResult<()> {
|
pub async fn delete_all(&self, user: &mut ChorusUser) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/",
|
"{}/channels/{}/messages/{}/reactions",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id
|
self.message_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
|
||||||
request: Client::new().delete(url).bearer_auth(user.token()),
|
let request = ChorusRequest::new(
|
||||||
limit_type: LimitType::Channel(self.channel_id),
|
http::Method::DELETE,
|
||||||
};
|
&url,
|
||||||
chorus_request.handle_request_as_result(user).await
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(self.channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a list of users that reacted with a specific emoji to a message.
|
/// Gets a list of users that reacted with a specific emoji to a message.
|
||||||
/// # Arguments
|
|
||||||
/// * `emoji` - A string slice containing the emoji to search for. The emoji must be URL Encoded or
|
|
||||||
/// the request will fail with 10014: Unknown Emoji. To use custom emoji, you must encode it in the
|
|
||||||
/// format name:id with the emoji name and emoji id.
|
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
|
||||||
/// # Returns
|
|
||||||
/// A Result that is [`Err(crate::errors::ChorusLibError)`] if something went wrong.
|
|
||||||
/// # Reference
|
|
||||||
/// See [https://discord.com/developers/docs/resources/channel#get-reactions](https://discord.com/developers/docs/resources/channel#get-reactions)
|
|
||||||
pub async fn get(&self, emoji: &str, user: &mut UserMeta) -> ChorusResult<Vec<PublicUser>> {
|
|
||||||
let url = format!(
|
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/",
|
|
||||||
user.belongs_to.borrow().urls.api,
|
|
||||||
self.channel_id,
|
|
||||||
self.message_id,
|
|
||||||
emoji
|
|
||||||
);
|
|
||||||
let chorus_request = ChorusRequest {
|
|
||||||
request: Client::new().get(url).bearer_auth(user.token()),
|
|
||||||
limit_type: LimitType::Channel(self.channel_id),
|
|
||||||
};
|
|
||||||
chorus_request
|
|
||||||
.deserialize_response::<Vec<PublicUser>>(user)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deletes all the reactions for a given `emoji` on a message. This endpoint requires the
|
|
||||||
/// MANAGE_MESSAGES permission to be present on the current user.
|
|
||||||
/// # Arguments
|
|
||||||
/// * `emoji` - A string slice containing the emoji to delete. The `emoji` must be URL Encoded or
|
|
||||||
/// the request will fail with 10014: Unknown Emoji. To use custom emoji, you must encode it in the
|
|
||||||
/// format name:id with the emoji name and emoji id.
|
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
|
||||||
/// # Returns
|
|
||||||
/// A Result that is [`Err(crate::errors::ChorusLibError)`] if something went wrong.
|
|
||||||
/// Fires a `Message Reaction Remove Emoji` Gateway event.
|
|
||||||
/// # Reference
|
|
||||||
/// See [https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji](https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji)
|
|
||||||
pub async fn delete_emoji(&self, emoji: &str, user: &mut UserMeta) -> ChorusResult<()> {
|
|
||||||
let url = format!(
|
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/",
|
|
||||||
user.belongs_to.borrow().urls.api,
|
|
||||||
self.channel_id,
|
|
||||||
self.message_id,
|
|
||||||
emoji
|
|
||||||
);
|
|
||||||
let chorus_request = ChorusRequest {
|
|
||||||
request: Client::new().delete(url).bearer_auth(user.token()),
|
|
||||||
limit_type: LimitType::Channel(self.channel_id),
|
|
||||||
};
|
|
||||||
chorus_request.handle_request_as_result(user).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a reaction for the message.
|
|
||||||
/// This endpoint requires the READ_MESSAGE_HISTORY permission
|
|
||||||
/// to be present on the current user. Additionally, if nobody else has reacted to the message using
|
|
||||||
/// this emoji, this endpoint requires the ADD_REACTIONS permission to be present on the current
|
|
||||||
/// user.
|
|
||||||
/// # Arguments
|
|
||||||
/// * `emoji` - A string slice containing the emoji to delete. The `emoji` must be URL Encoded or
|
|
||||||
/// the request will fail with 10014: Unknown Emoji. To use custom emoji, you must encode it in the
|
|
||||||
/// format name:id with the emoji name and emoji id.
|
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
|
||||||
/// # Returns
|
|
||||||
/// A `Result` containing [`()`] or a [`crate::errors::ChorusLibError`].
|
|
||||||
/// # Reference
|
|
||||||
/// See [https://discord.com/developers/docs/resources/channel#create-reaction](https://discord.com/developers/docs/resources/channel#create-reaction)
|
|
||||||
///
|
///
|
||||||
pub async fn create(&self, emoji: &str, user: &mut UserMeta) -> ChorusResult<()> {
|
/// The emoji must be URL Encoded or the request will fail with 10014: Unknown Emoji.
|
||||||
|
/// To use custom emoji, the format of the emoji string must be name:id.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/resources/channel#get-reactions>
|
||||||
|
pub async fn get(&self, emoji: &str, user: &mut ChorusUser) -> ChorusResult<Vec<PublicUser>> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/@me/",
|
"{}/channels/{}/messages/{}/reactions/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id,
|
self.message_id,
|
||||||
emoji
|
emoji
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
|
||||||
request: Client::new().put(url).bearer_auth(user.token()),
|
let request = ChorusRequest::new(
|
||||||
limit_type: LimitType::Channel(self.channel_id),
|
http::Method::GET,
|
||||||
};
|
&url,
|
||||||
chorus_request.handle_request_as_result(user).await
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(self.channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.deserialize_response::<Vec<PublicUser>>(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a reaction the current user has made for the message.
|
/// Deletes all the reactions for a given emoji on a message.
|
||||||
/// # Arguments
|
///
|
||||||
/// * `emoji` - A string slice containing the emoji to delete. The `emoji` must be URL Encoded or
|
/// This endpoint requires the [`MANAGE_MESSAGES`](crate::types::PermissionFlags::MANAGE_MESSAGES) permission.
|
||||||
/// the request will fail with 10014: Unknown Emoji. To use custom emoji, you must encode it in the
|
///
|
||||||
/// format name:id with the emoji name and emoji id.
|
/// The emoji must be URL Encoded or the request will fail with 10014: Unknown Emoji.
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
/// To use custom emoji, the format of the emoji string must be name:id.
|
||||||
/// # Returns
|
///
|
||||||
/// A `Result` containing [`()`] or a [`crate::errors::ChorusLibError`].
|
|
||||||
/// Fires a `Message Reaction Remove` Gateway event.
|
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See [https://discord.com/developers/docs/resources/channel#delete-own-reaction](https://discord.com/developers/docs/resources/channel#delete-own-reaction)
|
/// See <https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji>
|
||||||
pub async fn remove(&self, emoji: &str, user: &mut UserMeta) -> ChorusResult<()> {
|
pub async fn delete_emoji(&self, emoji: &str, user: &mut ChorusUser) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/@me/",
|
"{}/channels/{}/messages/{}/reactions/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id,
|
self.message_id,
|
||||||
emoji
|
emoji
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
|
||||||
request: Client::new().delete(url).bearer_auth(user.token()),
|
let request = ChorusRequest::new(
|
||||||
limit_type: LimitType::Channel(self.channel_id),
|
http::Method::DELETE,
|
||||||
};
|
&url,
|
||||||
chorus_request.handle_request_as_result(user).await
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(self.channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a user's reaction to a message.
|
/// Create a reaction on a message.
|
||||||
/// This endpoint requires the MANAGE_MESSAGES permission to be present on the current user.
|
///
|
||||||
/// # Arguments
|
/// This endpoint requires the [`READ_MESSAGE_HISTORY`](crate::types::PermissionFlags::READ_MESSAGE_HISTORY) permission.
|
||||||
/// * `user_id` - ID of the user whose reaction is to be deleted.
|
///
|
||||||
/// * `emoji` - A string slice containing the emoji to delete. The `emoji` must be URL Encoded or
|
/// Additionally, if nobody else has reacted to the message using this emoji,
|
||||||
/// the request will fail with 10014: Unknown Emoji. To use custom emoji, you must encode it in the
|
/// this endpoint requires the [`ADD_REACTIONS`](crate::types::PermissionFlags::ADD_REACTIONS) permission.
|
||||||
/// format name:id with the emoji name and emoji id.
|
///
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
/// The emoji must be URL Encoded or the request will fail with 10014: Unknown Emoji.
|
||||||
/// # Returns
|
/// To use custom emoji, the format of the emoji string must be `name:id`.
|
||||||
/// A `Result` containing [`()`] or a [`crate::errors::ChorusLibError`].
|
///
|
||||||
/// Fires a Message Reaction Remove Gateway event.
|
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See [https://discord.com/developers/docs/resources/channel#delete-own-reaction](https://discord.com/developers/docs/resources/channel#delete-own-reaction)
|
/// See <https://discord.com/developers/docs/resources/channel#create-reaction>
|
||||||
|
pub async fn create(&self, emoji: &str, user: &mut ChorusUser) -> ChorusResult<()> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/channels/{}/messages/{}/reactions/{}/@me",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
self.channel_id,
|
||||||
|
self.message_id,
|
||||||
|
emoji
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::PUT,
|
||||||
|
&url,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(self.channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes a reaction the current user has made to the message.
|
||||||
|
///
|
||||||
|
/// The reaction emoji must be URL Encoded or the request will fail with 10014: Unknown Emoji.
|
||||||
|
/// To use custom emoji, the format of the emoji string must be name:id.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/resources/channel#delete-own-reaction>
|
||||||
|
pub async fn remove(&self, emoji: &str, user: &mut ChorusUser) -> ChorusResult<()> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/channels/{}/messages/{}/reactions/{}/@me",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
self.channel_id,
|
||||||
|
self.message_id,
|
||||||
|
emoji
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::DELETE,
|
||||||
|
&url,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(self.channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes a user's reaction to a message.
|
||||||
|
///
|
||||||
|
/// This endpoint requires the [`MANAGE_MESSAGES`](crate::types::PermissionFlags::MANAGE_MESSAGES) permission.
|
||||||
|
///
|
||||||
|
/// The reaction emoji must be URL Encoded or the request will fail with 10014: Unknown Emoji.
|
||||||
|
/// To use custom emoji, the format of the emoji string must be name:id.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/resources/channel#delete-user-reaction>
|
||||||
pub async fn delete_user(
|
pub async fn delete_user(
|
||||||
&self,
|
&self,
|
||||||
user_id: Snowflake,
|
user_id: Snowflake,
|
||||||
emoji: &str,
|
emoji: &str,
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
) -> ChorusResult<()> {
|
) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/{}",
|
"{}/channels/{}/messages/{}/reactions/{}/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id,
|
self.message_id,
|
||||||
emoji,
|
emoji,
|
||||||
user_id
|
user_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
|
||||||
request: Client::new().delete(url).bearer_auth(user.token()),
|
let request = ChorusRequest::new(
|
||||||
limit_type: LimitType::Channel(self.channel_id),
|
http::Method::DELETE,
|
||||||
};
|
&url,
|
||||||
chorus_request.handle_request_as_result(user).await
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Channel(self.channel_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,57 +2,41 @@ use reqwest::Client;
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::api::LimitType;
|
|
||||||
use crate::errors::ChorusError;
|
use crate::errors::ChorusError;
|
||||||
use crate::errors::ChorusResult;
|
use crate::errors::ChorusResult;
|
||||||
use crate::instance::UserMeta;
|
use crate::instance::ChorusUser;
|
||||||
use crate::ratelimiter::ChorusRequest;
|
use crate::ratelimiter::ChorusRequest;
|
||||||
use crate::types::Snowflake;
|
use crate::types::{
|
||||||
use crate::types::{Channel, ChannelCreateSchema, Guild, GuildCreateSchema};
|
Channel, ChannelCreateSchema, Guild, GuildBanCreateSchema, GuildBansQuery, GuildCreateSchema,
|
||||||
|
GuildMember, GuildMemberSearchSchema, GuildModifySchema, GuildPreview, LimitType,
|
||||||
|
ModifyGuildMemberProfileSchema, ModifyGuildMemberSchema, UserProfileMetadata,
|
||||||
|
};
|
||||||
|
use crate::types::{GuildBan, Snowflake};
|
||||||
|
|
||||||
impl Guild {
|
impl Guild {
|
||||||
/// Creates a new guild with the given parameters.
|
/// Creates a new guild.
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `user` - A mutable reference to the user creating the guild.
|
|
||||||
/// * `instance` - A mutable reference to the instance where the guild will be created.
|
|
||||||
/// * `guild_create_schema` - A reference to the schema containing the guild creation parameters.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A `Result<Guild>` containing the object of the newly created guild, or an error if the request fails.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an `ChorusLibError` if the request fails.
|
|
||||||
///
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#create-guild>
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
guild_create_schema: GuildCreateSchema,
|
guild_create_schema: GuildCreateSchema,
|
||||||
) -> ChorusResult<Guild> {
|
) -> ChorusResult<Guild> {
|
||||||
let url = format!("{}/guilds/", user.belongs_to.borrow().urls.api);
|
let url = format!("{}/guilds", user.belongs_to.read().unwrap().urls.api);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(url.clone())
|
.post(url.clone())
|
||||||
.bearer_auth(user.token.clone())
|
.header("Authorization", user.token.clone())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
.body(to_string(&guild_create_schema).unwrap()),
|
.body(to_string(&guild_create_schema).unwrap()),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
chorus_request.deserialize_response::<Guild>(user).await
|
chorus_request.deserialize_response::<Guild>(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes a guild.
|
/// Deletes a guild by its id.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// User must be the owner.
|
||||||
///
|
|
||||||
/// * `user` - A mutable reference to a `User` instance.
|
|
||||||
/// * `instance` - A mutable reference to an `Instance` instance.
|
|
||||||
/// * `guild_id` - ID of the guild to delete.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// An `Result` containing an `ChorusLibError` if an error occurred during the request, otherwise `()`.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -61,65 +45,63 @@ impl Guild {
|
||||||
/// let mut instance = Instance::new();
|
/// let mut instance = Instance::new();
|
||||||
/// let guild_id = String::from("1234567890");
|
/// let guild_id = String::from("1234567890");
|
||||||
///
|
///
|
||||||
/// match Guild::delete(&mut user, &mut instance, guild_id) {
|
/// match Guild::delete(&mut user, guild_id) {
|
||||||
/// Some(e) => println!("Error deleting guild: {:?}", e),
|
/// Err(e) => println!("Error deleting guild: {:?}", e),
|
||||||
/// None => println!("Guild deleted successfully"),
|
/// Ok(_) => println!("Guild deleted successfully"),
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn delete(user: &mut UserMeta, guild_id: Snowflake) -> ChorusResult<()> {
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#delete-guild>
|
||||||
|
pub async fn delete(user: &mut ChorusUser, guild_id: Snowflake) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/delete/",
|
"{}/guilds/{}/delete",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id
|
guild_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(url.clone())
|
.post(url.clone())
|
||||||
.bearer_auth(user.token.clone()),
|
.header("Authorization", user.token.clone())
|
||||||
|
.header("Content-Type", "application/json"),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
chorus_request.handle_request_as_result(user).await
|
chorus_request.handle_request_as_result(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a request to create a new channel in the guild.
|
/// Creates a new channel in a guild.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Requires the [MANAGE_CHANNELS](crate::types::PermissionFlags::MANAGE_CHANNELS) permission.
|
||||||
///
|
///
|
||||||
/// * `url_api` - The base URL for the Discord API.
|
/// # Notes
|
||||||
/// * `token` - A Discord bot token.
|
/// This method is a wrapper for [Channel::create].
|
||||||
/// * `schema` - A `ChannelCreateSchema` struct containing the properties of the new channel.
|
|
||||||
/// * `limits_user` - A mutable reference to a `Limits` struct containing the user's rate limits.
|
|
||||||
/// * `limits_instance` - A mutable reference to a `Limits` struct containing the instance's rate limits.
|
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Reference
|
||||||
///
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#create-guild-channel>
|
||||||
/// A `Result` containing a `reqwest::Response` if the request was successful, or an `ChorusLibError` if there was an error.
|
|
||||||
pub async fn create_channel(
|
pub async fn create_channel(
|
||||||
&self,
|
&self,
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
schema: ChannelCreateSchema,
|
schema: ChannelCreateSchema,
|
||||||
) -> ChorusResult<Channel> {
|
) -> ChorusResult<Channel> {
|
||||||
Channel::create(user, self.id, schema).await
|
Channel::create(user, self.id, audit_log_reason, schema).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `Result` containing a vector of `Channel` structs if the request was successful, or an `ChorusLibError` if there was an error.
|
/// Returns a list of the guild's channels.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Doesn't include threads.
|
||||||
///
|
///
|
||||||
/// * `url_api` - A string slice that holds the URL of the API.
|
/// # Reference
|
||||||
/// * `token` - A string slice that holds the authorization token.
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#get-guild-channels>
|
||||||
/// * `limits_user` - A mutable reference to a `Limits` struct containing the user's rate limits.
|
pub async fn channels(&self, user: &mut ChorusUser) -> ChorusResult<Vec<Channel>> {
|
||||||
/// * `limits_instance` - A mutable reference to a `Limits` struct containing the instance's rate limits.
|
|
||||||
///
|
|
||||||
pub async fn channels(&self, user: &mut UserMeta) -> ChorusResult<Vec<Channel>> {
|
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"{}/guilds/{}/channels/",
|
"{}/guilds/{}/channels",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
self.id
|
self.id
|
||||||
))
|
))
|
||||||
.bearer_auth(user.token()),
|
.header("Authorization", user.token()),
|
||||||
limit_type: LimitType::Channel(self.id),
|
limit_type: LimitType::Channel(self.id),
|
||||||
};
|
};
|
||||||
let result = chorus_request.send_request(user).await?;
|
let result = chorus_request.send_request(user).await?;
|
||||||
|
@ -141,61 +123,381 @@ impl Guild {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `Result` containing a `Guild` struct if the request was successful, or an `ChorusLibError` if there was an error.
|
/// Fetches a guild by its id.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild>
|
||||||
/// * `url_api` - A string slice that holds the URL of the API.
|
pub async fn get(guild_id: Snowflake, user: &mut ChorusUser) -> ChorusResult<Guild> {
|
||||||
/// * `guild_id` - ID of the guild.
|
|
||||||
/// * `token` - A string slice that holds the authorization token.
|
|
||||||
/// * `limits_user` - A mutable reference to a `Limits` struct containing the user's rate limits.
|
|
||||||
/// * `limits_instance` - A mutable reference to a `Limits` struct containing the instance's rate limits.
|
|
||||||
///
|
|
||||||
pub async fn get(guild_id: Snowflake, user: &mut UserMeta) -> ChorusResult<Guild> {
|
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"{}/guilds/{}/",
|
"{}/guilds/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id
|
guild_id
|
||||||
))
|
))
|
||||||
.bearer_auth(user.token()),
|
.header("Authorization", user.token()),
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
let response = chorus_request.deserialize_response::<Guild>(user).await?;
|
let response = chorus_request.deserialize_response::<Guild>(user).await?;
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn create_ban(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
user_id: Snowflake,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
|
schema: GuildBanCreateSchema,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
// FIXME: Return GuildBan instead of (). Requires <https://github.com/spacebarchat/server/issues/1096> to be resolved.
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::PUT,
|
||||||
|
format!(
|
||||||
|
"{}/guilds/{}/bans/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
Some(to_string(&schema).unwrap()),
|
||||||
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Reference
|
||||||
|
/// <https://discord-userdoccers.vercel.app/resources/guild#modify-guild>
|
||||||
|
pub async fn modify(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
schema: GuildModifySchema,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Guild> {
|
||||||
|
let chorus_request = ChorusRequest {
|
||||||
|
request: Client::new()
|
||||||
|
.patch(format!(
|
||||||
|
"{}/guilds/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
))
|
||||||
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(to_string(&schema).unwrap()),
|
||||||
|
limit_type: LimitType::Guild(guild_id),
|
||||||
|
};
|
||||||
|
let response = chorus_request.deserialize_response::<Guild>(user).await?;
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a guild preview object for the given guild ID. If the user is not in the guild, the guild must be discoverable.
|
||||||
|
/// # Reference:
|
||||||
|
///
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-preview>
|
||||||
|
pub async fn get_preview(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<GuildPreview> {
|
||||||
|
let chorus_request = ChorusRequest {
|
||||||
|
request: Client::new()
|
||||||
|
.patch(format!(
|
||||||
|
"{}/guilds/{}/preview",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
))
|
||||||
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json"),
|
||||||
|
limit_type: LimitType::Guild(guild_id),
|
||||||
|
};
|
||||||
|
let response = chorus_request
|
||||||
|
.deserialize_response::<GuildPreview>(user)
|
||||||
|
.await?;
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of guild member objects that are members of the guild.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-members>
|
||||||
|
pub async fn get_members(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Vec<GuildMember>> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::GET,
|
||||||
|
format!(
|
||||||
|
"{}/guilds/{}/members",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.deserialize_response::<Vec<GuildMember>>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of guild member objects whose username or nickname starts with a provided string.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#search-guild-members>
|
||||||
|
pub async fn search_members(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
query: GuildMemberSearchSchema,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Vec<GuildMember>> {
|
||||||
|
let mut request = ChorusRequest::new(
|
||||||
|
http::Method::GET,
|
||||||
|
format!(
|
||||||
|
"{}/guilds/{}/members/search",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.request = request
|
||||||
|
.request
|
||||||
|
.query(&[("query", to_string(&query).unwrap())]);
|
||||||
|
request.deserialize_response::<Vec<GuildMember>>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a member from a guild. Requires the KICK_MEMBERS permission. Returns a 204 empty response on success.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#remove-guild-member>
|
||||||
|
pub async fn remove_member(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
member_id: Snowflake,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::DELETE,
|
||||||
|
format!(
|
||||||
|
"{}/guilds/{}/members/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
member_id,
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
None,
|
||||||
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies attributes of a guild member. Returns the updated guild member object on success.
|
||||||
|
/// For required Permissions and an API reference, see:
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// <https://discord-userdoccers.vercel.app/resources/guild#modify-guild-member>
|
||||||
|
pub async fn modify_member(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
member_id: Snowflake,
|
||||||
|
schema: ModifyGuildMemberSchema,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<GuildMember> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::PATCH,
|
||||||
|
format!(
|
||||||
|
"{}/guilds/{}/members/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
member_id,
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
Some(to_string(&schema).unwrap()),
|
||||||
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.deserialize_response::<GuildMember>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies the current user's member in the guild.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#modify-current-guild-member>
|
||||||
|
pub async fn modify_current_member(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
schema: ModifyGuildMemberSchema,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<GuildMember> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::PATCH,
|
||||||
|
format!(
|
||||||
|
"{}/guilds/{}/members/@me",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
Some(to_string(&schema).unwrap()),
|
||||||
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.deserialize_response::<GuildMember>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies the current user's profile in the guild.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#modify-guild-member-profile>
|
||||||
|
pub async fn modify_current_member_profile(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
schema: ModifyGuildMemberProfileSchema,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<UserProfileMetadata> {
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::PATCH,
|
||||||
|
format!(
|
||||||
|
"{}/guilds/{}/profile/@me",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
Some(to_string(&schema).unwrap()),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request
|
||||||
|
.deserialize_response::<UserProfileMetadata>(user)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of ban objects for the guild. Requires the `BAN_MEMBERS` permission.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-bans>
|
||||||
|
pub async fn get_bans(
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
guild_id: Snowflake,
|
||||||
|
query: Option<GuildBansQuery>,
|
||||||
|
) -> ChorusResult<Vec<GuildBan>> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/guilds/{}/bans",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut request = ChorusRequest::new(
|
||||||
|
http::Method::GET,
|
||||||
|
&url,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
if let Some(query) = query {
|
||||||
|
request.request = request.request.query(&to_string(&query).unwrap());
|
||||||
|
}
|
||||||
|
request.deserialize_response::<Vec<GuildBan>>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a ban object for the given user. Requires the `BAN_MEMBERS` permission.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-ban>
|
||||||
|
pub async fn get_ban(
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
guild_id: Snowflake,
|
||||||
|
user_id: Snowflake,
|
||||||
|
) -> ChorusResult<GuildBan> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/guilds/{}/bans/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
user_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::GET,
|
||||||
|
&url,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.deserialize_response::<GuildBan>(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the ban for a user. Requires the BAN_MEMBERS permissions. Returns a 204 empty response on success.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#delete-guild-ban>
|
||||||
|
pub async fn delete_ban(
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
guild_id: Snowflake,
|
||||||
|
user_id: Snowflake,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/guilds/{}/bans/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
user_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::DELETE,
|
||||||
|
&url,
|
||||||
|
None,
|
||||||
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Channel {
|
impl Channel {
|
||||||
/// Sends a request to create a new channel in a guild.
|
/// Creates a new channel in a guild.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Requires the [MANAGE_CHANNELS](crate::types::PermissionFlags::MANAGE_CHANNELS) permission.
|
||||||
///
|
///
|
||||||
/// * `token` - A Discord bot token.
|
/// # Reference
|
||||||
/// * `url_api` - The base URL for the Discord API.
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#create-guild-channel>
|
||||||
/// * `guild_id` - The ID of the guild where the channel will be created.
|
|
||||||
/// * `schema` - A `ChannelCreateSchema` struct containing the properties of the new channel.
|
|
||||||
/// * `limits_user` - A mutable reference to a `Limits` struct containing the user's rate limits.
|
|
||||||
/// * `limits_instance` - A mutable reference to a `Limits` struct containing the instance's rate limits.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A `Result` containing a `reqwest::Response` if the request was successful, or an `ChorusLibError` if there was an error.
|
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
guild_id: Snowflake,
|
guild_id: Snowflake,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
schema: ChannelCreateSchema,
|
schema: ChannelCreateSchema,
|
||||||
) -> ChorusResult<Channel> {
|
) -> ChorusResult<Channel> {
|
||||||
|
let mut request = Client::new()
|
||||||
|
.post(format!(
|
||||||
|
"{}/guilds/{}/channels",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id
|
||||||
|
))
|
||||||
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(to_string(&schema).unwrap());
|
||||||
|
if let Some(reason) = audit_log_reason {
|
||||||
|
request = request.header("X-Audit-Log-Reason", reason);
|
||||||
|
}
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request,
|
||||||
.post(format!(
|
|
||||||
"{}/guilds/{}/channels/",
|
|
||||||
user.belongs_to.borrow().urls.api,
|
|
||||||
guild_id
|
|
||||||
))
|
|
||||||
.bearer_auth(user.token())
|
|
||||||
.body(to_string(&schema).unwrap()),
|
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
chorus_request.deserialize_response::<Channel>(user).await
|
chorus_request.deserialize_response::<Channel>(user).await
|
||||||
|
|
|
@ -1,72 +1,61 @@
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::LimitType,
|
|
||||||
errors::ChorusResult,
|
errors::ChorusResult,
|
||||||
instance::UserMeta,
|
instance::ChorusUser,
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
types::{self, Snowflake},
|
types::{self, GuildMember, LimitType, Snowflake},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl types::GuildMember {
|
impl types::GuildMember {
|
||||||
/// Retrieves a guild member by their ID.
|
/// Retrieves a guild member.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-member>
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
|
||||||
/// * `guild_id` - The ID of the guild.
|
|
||||||
/// * `member_id` - The ID of the member.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A [`Result`] containing a [`GuildMember`] if the request succeeds, or a [`ChorusLibError`] if the request fails.
|
|
||||||
pub async fn get(
|
pub async fn get(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
guild_id: Snowflake,
|
guild_id: Snowflake,
|
||||||
member_id: Snowflake,
|
member_id: Snowflake,
|
||||||
) -> ChorusResult<types::GuildMember> {
|
) -> ChorusResult<GuildMember> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/members/{}/",
|
"{}/guilds/{}/members/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
member_id
|
member_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().get(url).bearer_auth(user.token()),
|
request: Client::new().get(url).header("Authorization", user.token()),
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
chorus_request
|
chorus_request
|
||||||
.deserialize_response::<types::GuildMember>(user)
|
.deserialize_response::<GuildMember>(user)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a role to a guild member.
|
/// Adds a role to a guild member.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Requires the [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) permission.
|
||||||
///
|
///
|
||||||
/// * `user` - A mutable reference to a `UserMeta` instance.
|
/// # Reference
|
||||||
/// * `guild_id` - The ID of the guild.
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#add-guild-member-role>
|
||||||
/// * `member_id` - The ID of the member.
|
|
||||||
/// * `role_id` - The ID of the role to add.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// An `Result` containing a `ChorusLibError` if the request fails, or `()` if the request succeeds.
|
|
||||||
pub async fn add_role(
|
pub async fn add_role(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
guild_id: Snowflake,
|
guild_id: Snowflake,
|
||||||
member_id: Snowflake,
|
member_id: Snowflake,
|
||||||
role_id: Snowflake,
|
role_id: Snowflake,
|
||||||
) -> ChorusResult<()> {
|
) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/members/{}/roles/{}/",
|
"{}/guilds/{}/members/{}/roles/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
member_id,
|
member_id,
|
||||||
role_id
|
role_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().put(url).bearer_auth(user.token()),
|
request: Client::new()
|
||||||
|
.put(url)
|
||||||
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json"),
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
chorus_request.handle_request_as_result(user).await
|
chorus_request.handle_request_as_result(user).await
|
||||||
|
@ -74,31 +63,27 @@ impl types::GuildMember {
|
||||||
|
|
||||||
/// Removes a role from a guild member.
|
/// Removes a role from a guild member.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Requires the [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) permission.
|
||||||
///
|
///
|
||||||
/// * `user` - A mutable reference to a `UserMeta` instance.
|
/// # Reference
|
||||||
/// * `guild_id` - The ID of the guild.
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#remove-guild-member-role>
|
||||||
/// * `member_id` - The ID of the member.
|
|
||||||
/// * `role_id` - The ID of the role to remove.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A `Result` containing a `ChorusLibError` if the request fails, or `()` if the request succeeds.
|
|
||||||
pub async fn remove_role(
|
pub async fn remove_role(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
guild_id: Snowflake,
|
guild_id: Snowflake,
|
||||||
member_id: Snowflake,
|
member_id: Snowflake,
|
||||||
role_id: Snowflake,
|
role_id: Snowflake,
|
||||||
) -> Result<(), crate::errors::ChorusError> {
|
) -> Result<(), crate::errors::ChorusError> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/members/{}/roles/{}/",
|
"{}/guilds/{}/members/{}/roles/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
member_id,
|
member_id,
|
||||||
role_id
|
role_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().delete(url).bearer_auth(user.token()),
|
request: Client::new()
|
||||||
|
.delete(url)
|
||||||
|
.header("Authorization", user.token()),
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
chorus_request.handle_request_as_result(user).await
|
chorus_request.handle_request_as_result(user).await
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
use crate::errors::ChorusResult;
|
||||||
|
use crate::instance::ChorusUser;
|
||||||
|
use crate::types::{Guild, Message, MessageSearchQuery, Snowflake};
|
||||||
|
|
||||||
|
impl Guild {
|
||||||
|
/// Returns messages without the reactions key that match a search query in the guild.
|
||||||
|
/// The messages that are direct results will have an extra hit key set to true.
|
||||||
|
/// If operating on a guild channel, this endpoint requires the `READ_MESSAGE_HISTORY`
|
||||||
|
/// permission to be present on the current user.
|
||||||
|
///
|
||||||
|
/// If the guild/channel you are searching is not yet indexed, the endpoint will return a 202 accepted response.
|
||||||
|
/// In this case, the method will return a [`ChorusError::InvalidResponse`] error.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#search-messages>
|
||||||
|
pub async fn search_messages(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
query: MessageSearchQuery,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Vec<Message>> {
|
||||||
|
Message::search(
|
||||||
|
crate::types::MessageSearchEndpoint::GuildChannel(guild_id),
|
||||||
|
query,
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
pub use guilds::*;
|
pub use guilds::*;
|
||||||
|
pub use messages::*;
|
||||||
pub use roles::*;
|
pub use roles::*;
|
||||||
pub use roles::*;
|
pub use roles::*;
|
||||||
|
|
||||||
pub mod guilds;
|
pub mod guilds;
|
||||||
pub mod member;
|
pub mod member;
|
||||||
|
pub mod messages;
|
||||||
pub mod roles;
|
pub mod roles;
|
||||||
|
|
|
@ -2,79 +2,56 @@ use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::LimitType,
|
|
||||||
errors::{ChorusError, ChorusResult},
|
errors::{ChorusError, ChorusResult},
|
||||||
instance::UserMeta,
|
instance::ChorusUser,
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
types::{self, RoleCreateModifySchema, RoleObject, Snowflake},
|
types::{
|
||||||
|
self, LimitType, RoleCreateModifySchema, RoleObject, RolePositionUpdateSchema, Snowflake,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl types::RoleObject {
|
impl types::RoleObject {
|
||||||
/// Retrieves all roles for a given guild.
|
/// Retrieves a list of roles for a given guild.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-roles>
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
|
||||||
/// * `guild_id` - The ID of the guild to retrieve roles from.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// An `Option` containing a `Vec` of [`RoleObject`]s if roles were found, or `None` if no roles were found.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a [`ChorusLibError`] if the request fails or if the response is invalid.
|
|
||||||
pub async fn get_all(
|
pub async fn get_all(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
guild_id: Snowflake,
|
guild_id: Snowflake,
|
||||||
) -> ChorusResult<Option<Vec<RoleObject>>> {
|
) -> ChorusResult<Vec<RoleObject>> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/roles/",
|
"{}/guilds/{}/roles",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id
|
guild_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().get(url).bearer_auth(user.token()),
|
request: Client::new().get(url).header("Authorization", user.token()),
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
let roles = chorus_request
|
let roles = chorus_request
|
||||||
.deserialize_response::<Vec<RoleObject>>(user)
|
.deserialize_response::<Vec<RoleObject>>(user)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if roles.is_empty() {
|
Ok(roles)
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
Ok(Some(roles))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves a single role for a given guild.
|
/// Retrieves a single role for a given guild.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://docs.spacebar.chat/routes/#get-/guilds/-guild_id-/roles/-role_id-/>
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
|
||||||
/// * `guild_id` - The ID of the guild to retrieve the role from.
|
|
||||||
/// * `role_id` - The ID of the role to retrieve.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A `Result` containing the retrieved [`RoleObject`] if successful, or a [`ChorusLibError`] if the request fails or if the response is invalid.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a [`ChorusLibError`] if the request fails or if the response is invalid.
|
|
||||||
pub async fn get(
|
pub async fn get(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
guild_id: Snowflake,
|
guild_id: Snowflake,
|
||||||
role_id: Snowflake,
|
role_id: Snowflake,
|
||||||
) -> ChorusResult<RoleObject> {
|
) -> ChorusResult<RoleObject> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/roles/{}/",
|
"{}/guilds/{}/roles/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
role_id
|
role_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().get(url).bearer_auth(user.token()),
|
request: Client::new().get(url).header("Authorization", user.token()),
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
chorus_request
|
chorus_request
|
||||||
|
@ -84,27 +61,18 @@ impl types::RoleObject {
|
||||||
|
|
||||||
/// Creates a new role for a given guild.
|
/// Creates a new role for a given guild.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Requires the [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) permission.
|
||||||
///
|
///
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
/// # Reference
|
||||||
/// * `guild_id` - The ID of the guild to create the role in.
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#create-guild-role>
|
||||||
/// * `role_create_schema` - A [`RoleCreateModifySchema`] instance containing the properties of the role to be created.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A `Result` containing the newly created [`RoleObject`] if successful, or a [`ChorusLibError`] if the request fails or if the response is invalid.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a [`ChorusLibError`] if the request fails or if the response is invalid.
|
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
guild_id: Snowflake,
|
guild_id: Snowflake,
|
||||||
role_create_schema: RoleCreateModifySchema,
|
role_create_schema: RoleCreateModifySchema,
|
||||||
) -> ChorusResult<RoleObject> {
|
) -> ChorusResult<RoleObject> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/roles/",
|
"{}/guilds/{}/roles",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id
|
guild_id
|
||||||
);
|
);
|
||||||
let body = to_string::<RoleCreateModifySchema>(&role_create_schema).map_err(|e| {
|
let body = to_string::<RoleCreateModifySchema>(&role_create_schema).map_err(|e| {
|
||||||
|
@ -113,7 +81,11 @@ impl types::RoleObject {
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().post(url).bearer_auth(user.token()).body(body),
|
request: Client::new()
|
||||||
|
.post(url)
|
||||||
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(body),
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
chorus_request
|
chorus_request
|
||||||
|
@ -121,29 +93,20 @@ impl types::RoleObject {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the position of a role in the guild's hierarchy.
|
/// Updates the position of a role in a given guild's hierarchy.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Requires the [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) permission.
|
||||||
///
|
///
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
/// # Reference
|
||||||
/// * `guild_id` - The ID of the guild to update the role position in.
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#modify-guild-role-positions>
|
||||||
/// * `role_position_update_schema` - A [`RolePositionUpdateSchema`] instance containing the new position of the role.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A `Result` containing the updated [`RoleObject`] if successful, or a [`ChorusLibError`] if the request fails or if the response is invalid.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a [`ChorusLibError`] if the request fails or if the response is invalid.
|
|
||||||
pub async fn position_update(
|
pub async fn position_update(
|
||||||
user: &mut UserMeta,
|
user: &mut ChorusUser,
|
||||||
guild_id: Snowflake,
|
guild_id: Snowflake,
|
||||||
role_position_update_schema: types::RolePositionUpdateSchema,
|
role_position_update_schema: RolePositionUpdateSchema,
|
||||||
) -> ChorusResult<RoleObject> {
|
) -> ChorusResult<RoleObject> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/roles/",
|
"{}/guilds/{}/roles",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id
|
guild_id
|
||||||
);
|
);
|
||||||
let body =
|
let body =
|
||||||
|
@ -153,7 +116,8 @@ impl types::RoleObject {
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.patch(url)
|
.patch(url)
|
||||||
.bearer_auth(user.token())
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
.body(body),
|
.body(body),
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
|
@ -162,31 +126,21 @@ impl types::RoleObject {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates a role in a guild.
|
/// Modifies a role in a guild.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Requires the [`MANAGE_ROLES`](crate::types::PermissionFlags::MANAGE_ROLES) permission.
|
||||||
///
|
///
|
||||||
/// * `user` - A mutable reference to a [`UserMeta`] instance.
|
/// # Reference
|
||||||
/// * `guild_id` - The ID of the guild to update the role in.
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#modify-guild-role>
|
||||||
/// * `role_id` - The ID of the role to update.
|
pub async fn modify(
|
||||||
/// * `role_create_schema` - A [`RoleCreateModifySchema`] instance containing the new properties of the role.
|
user: &mut ChorusUser,
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A `Result` containing the updated [`RoleObject`] if successful, or a [`ChorusLibError`] if the request fails or if the response is invalid.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a [`ChorusLibError`] if the request fails or if the response is invalid.
|
|
||||||
pub async fn update(
|
|
||||||
user: &mut UserMeta,
|
|
||||||
guild_id: Snowflake,
|
guild_id: Snowflake,
|
||||||
role_id: Snowflake,
|
role_id: Snowflake,
|
||||||
role_create_schema: RoleCreateModifySchema,
|
role_create_schema: RoleCreateModifySchema,
|
||||||
) -> ChorusResult<RoleObject> {
|
) -> ChorusResult<RoleObject> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/roles/{}",
|
"{}/guilds/{}/roles/{}",
|
||||||
user.belongs_to.borrow().urls.api,
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
role_id
|
role_id
|
||||||
);
|
);
|
||||||
|
@ -198,7 +152,8 @@ impl types::RoleObject {
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.patch(url)
|
.patch(url)
|
||||||
.bearer_auth(user.token())
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
.body(body),
|
.body(body),
|
||||||
limit_type: LimitType::Guild(guild_id),
|
limit_type: LimitType::Guild(guild_id),
|
||||||
};
|
};
|
||||||
|
@ -206,4 +161,33 @@ impl types::RoleObject {
|
||||||
.deserialize_response::<RoleObject>(user)
|
.deserialize_response::<RoleObject>(user)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deletes a guild role. Requires the `MANAGE_ROLES` permission. Returns a 204 empty response on success.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord.com/developers/docs/resources/guild#delete-guild-role>
|
||||||
|
pub async fn delete_role(
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
guild_id: Snowflake,
|
||||||
|
role_id: Snowflake,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/guilds/{}/roles/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
role_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::DELETE,
|
||||||
|
&url,
|
||||||
|
None,
|
||||||
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,17 @@ use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::errors::ChorusResult;
|
use crate::errors::ChorusResult;
|
||||||
use crate::instance::UserMeta;
|
use crate::instance::ChorusUser;
|
||||||
use crate::ratelimiter::ChorusRequest;
|
use crate::ratelimiter::ChorusRequest;
|
||||||
use crate::types::{CreateChannelInviteSchema, GuildInvite, Invite, Snowflake};
|
use crate::types::{CreateChannelInviteSchema, GuildInvite, Invite, LimitType, Snowflake};
|
||||||
|
|
||||||
impl UserMeta {
|
impl ChorusUser {
|
||||||
/// # Arguments
|
/// Accepts an invite to a guild, group DM, or DM.
|
||||||
/// - invite_code: The invite code to accept the invite for.
|
///
|
||||||
/// - session_id: The session ID that is accepting the invite, required for guest invites.
|
/// Note that the session ID is required for guest invites.
|
||||||
///
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// Read <https://discord-userdoccers.vercel.app/resources/invite#accept-invite>
|
/// See <https://discord-userdoccers.vercel.app/resources/invite#accept-invite>
|
||||||
pub async fn accept_invite(
|
pub async fn accept_invite(
|
||||||
&mut self,
|
&mut self,
|
||||||
invite_code: &str,
|
invite_code: &str,
|
||||||
|
@ -21,37 +21,52 @@ impl UserMeta {
|
||||||
let mut request = ChorusRequest {
|
let mut request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(format!(
|
.post(format!(
|
||||||
"{}/invites/{}/",
|
"{}/invites/{}",
|
||||||
self.belongs_to.borrow().urls.api,
|
self.belongs_to.read().unwrap().urls.api,
|
||||||
invite_code
|
invite_code
|
||||||
))
|
))
|
||||||
.bearer_auth(self.token()),
|
.header("Authorization", self.token()),
|
||||||
limit_type: super::LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
if session_id.is_some() {
|
if session_id.is_some() {
|
||||||
request.request = request
|
request.request = request
|
||||||
.request
|
.request
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
.body(to_string(session_id.unwrap()).unwrap());
|
.body(to_string(session_id.unwrap()).unwrap());
|
||||||
}
|
}
|
||||||
request.deserialize_response::<Invite>(self).await
|
request.deserialize_response::<Invite>(self).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new friend invite.
|
||||||
|
///
|
||||||
/// Note: Spacebar does not yet implement this endpoint.
|
/// Note: Spacebar does not yet implement this endpoint.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/invite#create-user-invite>
|
||||||
pub async fn create_user_invite(&mut self, code: Option<&str>) -> ChorusResult<Invite> {
|
pub async fn create_user_invite(&mut self, code: Option<&str>) -> ChorusResult<Invite> {
|
||||||
ChorusRequest {
|
ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(format!(
|
.post(format!(
|
||||||
"{}/users/@me/invites/",
|
"{}/users/@me/invites",
|
||||||
self.belongs_to.borrow().urls.api
|
self.belongs_to.read().unwrap().urls.api
|
||||||
))
|
))
|
||||||
.body(to_string(&code).unwrap())
|
.body(to_string(&code).unwrap())
|
||||||
.bearer_auth(self.token()),
|
.header("Authorization", self.token())
|
||||||
limit_type: super::LimitType::Global,
|
.header("Content-Type", "application/json"),
|
||||||
|
limit_type: LimitType::Global,
|
||||||
}
|
}
|
||||||
.deserialize_response::<Invite>(self)
|
.deserialize_response::<Invite>(self)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_guild_invite(
|
/// Creates a new invite for a guild channel or group DM.
|
||||||
|
///
|
||||||
|
/// # Guild Channels
|
||||||
|
/// For guild channels, the endpoint requires the [`CREATE_INSTANT_INVITE`](crate::types::PermissionFlags::CREATE_INSTANT_INVITE) permission.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/invite#create-channel-invite>
|
||||||
|
pub async fn create_channel_invite(
|
||||||
&mut self,
|
&mut self,
|
||||||
create_channel_invite_schema: CreateChannelInviteSchema,
|
create_channel_invite_schema: CreateChannelInviteSchema,
|
||||||
channel_id: Snowflake,
|
channel_id: Snowflake,
|
||||||
|
@ -59,13 +74,14 @@ impl UserMeta {
|
||||||
ChorusRequest {
|
ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(format!(
|
.post(format!(
|
||||||
"{}/channels/{}/invites/",
|
"{}/channels/{}/invites",
|
||||||
self.belongs_to.borrow().urls.api,
|
self.belongs_to.read().unwrap().urls.api,
|
||||||
channel_id
|
channel_id
|
||||||
))
|
))
|
||||||
.bearer_auth(self.token())
|
.header("Authorization", self.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
.body(to_string(&create_channel_invite_schema).unwrap()),
|
.body(to_string(&create_channel_invite_schema).unwrap()),
|
||||||
limit_type: super::LimitType::Channel(channel_id),
|
limit_type: LimitType::Channel(channel_id),
|
||||||
}
|
}
|
||||||
.deserialize_response::<GuildInvite>(self)
|
.deserialize_response::<GuildInvite>(self)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
//! All of the API's endpoints.
|
||||||
pub use channels::messages::*;
|
pub use channels::messages::*;
|
||||||
pub use guilds::*;
|
pub use guilds::*;
|
||||||
pub use invites::*;
|
pub use invites::*;
|
||||||
pub use policies::instance::instance::*;
|
pub use policies::instance::instance::*;
|
||||||
pub use policies::instance::ratelimits::*;
|
|
||||||
pub use users::*;
|
pub use users::*;
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
|
|
|
@ -6,16 +6,20 @@ use crate::types::GeneralConfiguration;
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
/// Gets the instance policies schema.
|
/// Gets the instance policies schema.
|
||||||
/// # Errors
|
///
|
||||||
/// [`ChorusLibError`] - If the request fails.
|
/// # Notes
|
||||||
|
/// This is a Spacebar only endpoint.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.spacebar.chat/routes/#get-/policies/instance/>
|
||||||
pub async fn general_configuration_schema(&self) -> ChorusResult<GeneralConfiguration> {
|
pub async fn general_configuration_schema(&self) -> ChorusResult<GeneralConfiguration> {
|
||||||
let endpoint_url = self.urls.api.clone() + "/policies/instance/";
|
let endpoint_url = self.urls.api.clone() + "/policies/instance";
|
||||||
let request = match self.client.get(&endpoint_url).send().await {
|
let request = match self.client.get(&endpoint_url).send().await {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ChorusError::RequestFailed {
|
return Err(ChorusError::RequestFailed {
|
||||||
url: endpoint_url,
|
url: endpoint_url,
|
||||||
error: e,
|
error: e.to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
pub use instance::*;
|
pub use instance::*;
|
||||||
pub use ratelimits::*;
|
|
||||||
|
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod ratelimits;
|
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
pub use instance::ratelimits::*;
|
|
||||||
|
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
|
|
|
@ -2,27 +2,33 @@ use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::LimitType,
|
|
||||||
errors::ChorusResult,
|
errors::ChorusResult,
|
||||||
instance::UserMeta,
|
instance::ChorusUser,
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
types::{Channel, PrivateChannelCreateSchema},
|
types::{Channel, LimitType, PrivateChannelCreateSchema},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl UserMeta {
|
impl ChorusUser {
|
||||||
/// Creates a DM channel or group DM channel.
|
/// Creates a DM channel or group DM channel.
|
||||||
///
|
///
|
||||||
|
/// One recipient creates or returns an existing DM channel,
|
||||||
|
/// none or multiple recipients create a group DM channel.
|
||||||
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// Read <https://discord-userdoccers.vercel.app/resources/channel#create-private-channel>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#create-private-channel>
|
||||||
pub async fn create_private_channel(
|
pub async fn create_private_channel(
|
||||||
&mut self,
|
&mut self,
|
||||||
create_private_channel_schema: PrivateChannelCreateSchema,
|
create_private_channel_schema: PrivateChannelCreateSchema,
|
||||||
) -> ChorusResult<Channel> {
|
) -> ChorusResult<Channel> {
|
||||||
let url = format!("{}/users/@me/channels", self.belongs_to.borrow().urls.api);
|
let url = format!(
|
||||||
|
"{}/users/@me/channels",
|
||||||
|
self.belongs_to.read().unwrap().urls.api
|
||||||
|
);
|
||||||
ChorusRequest {
|
ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.post(url)
|
.post(url)
|
||||||
.bearer_auth(self.token())
|
.header("Authorization", self.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
.body(to_string(&create_private_channel_schema).unwrap()),
|
.body(to_string(&create_private_channel_schema).unwrap()),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,29 +2,59 @@ use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::errors::ChorusResult;
|
use crate::errors::ChorusResult;
|
||||||
use crate::instance::UserMeta;
|
use crate::instance::ChorusUser;
|
||||||
use crate::ratelimiter::ChorusRequest;
|
use crate::ratelimiter::ChorusRequest;
|
||||||
use crate::types::Snowflake;
|
use crate::types::{GetUserGuildSchema, Guild, LimitType, Snowflake};
|
||||||
|
|
||||||
impl UserMeta {
|
impl ChorusUser {
|
||||||
/// # Arguments:
|
/// Leaves a given guild.
|
||||||
/// - lurking: Whether the user is lurking in the guild
|
|
||||||
///
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// Read <https://discord-userdoccers.vercel.app/resources/guild#leave-guild>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#leave-guild>
|
||||||
|
// TODO: Docs: What is "lurking" here?
|
||||||
|
// It is documented as "Whether the user is lurking in the guild",
|
||||||
|
// but that says nothing about what this field actually does / means
|
||||||
pub async fn leave_guild(&mut self, guild_id: &Snowflake, lurking: bool) -> ChorusResult<()> {
|
pub async fn leave_guild(&mut self, guild_id: &Snowflake, lurking: bool) -> ChorusResult<()> {
|
||||||
ChorusRequest {
|
ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.delete(format!(
|
.delete(format!(
|
||||||
"{}/users/@me/guilds/{}/",
|
"{}/users/@me/guilds/{}",
|
||||||
self.belongs_to.borrow().urls.api,
|
self.belongs_to.read().unwrap().urls.api,
|
||||||
guild_id
|
guild_id
|
||||||
))
|
))
|
||||||
.bearer_auth(self.token())
|
.header("Authorization", self.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
.body(to_string(&lurking).unwrap()),
|
.body(to_string(&lurking).unwrap()),
|
||||||
limit_type: crate::api::LimitType::Guild(*guild_id),
|
limit_type: LimitType::Guild(*guild_id),
|
||||||
}
|
}
|
||||||
.handle_request_as_result(self)
|
.handle_request_as_result(self)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of user guild objects representing the guilds the current user is a member of.
|
||||||
|
/// This endpoint returns 200 guilds by default
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/guild#get-user-guilds>
|
||||||
|
pub async fn get_guilds(
|
||||||
|
&mut self,
|
||||||
|
query: Option<GetUserGuildSchema>,
|
||||||
|
) -> ChorusResult<Vec<Guild>> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/users/@me/guilds",
|
||||||
|
self.belongs_to.read().unwrap().urls.api,
|
||||||
|
);
|
||||||
|
let chorus_request = ChorusRequest {
|
||||||
|
request: Client::new()
|
||||||
|
.get(url)
|
||||||
|
.header("Authorization", self.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(to_string(&query).unwrap()),
|
||||||
|
|
||||||
|
limit_type: LimitType::Global,
|
||||||
|
};
|
||||||
|
chorus_request
|
||||||
|
.deserialize_response::<Vec<Guild>>(self)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,33 +2,31 @@ use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::LimitType,
|
|
||||||
errors::ChorusResult,
|
errors::ChorusResult,
|
||||||
instance::UserMeta,
|
instance::ChorusUser,
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
types::{self, CreateUserRelationshipSchema, RelationshipType, Snowflake},
|
types::{
|
||||||
|
self, CreateUserRelationshipSchema, FriendRequestSendSchema, LimitType, RelationshipType,
|
||||||
|
Snowflake,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl UserMeta {
|
impl ChorusUser {
|
||||||
/// Retrieves the mutual relationships between the authenticated user and the specified user.
|
/// Retrieves a list of mutual friends between the authenticated user and a given user.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#get-users-peer-id-relationships>
|
||||||
/// * `user_id` - ID of the user to retrieve the mutual relationships with.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// This function returns a [`ChorusResult<Vec<PublicUser>>`].
|
|
||||||
pub async fn get_mutual_relationships(
|
pub async fn get_mutual_relationships(
|
||||||
&mut self,
|
&mut self,
|
||||||
user_id: Snowflake,
|
user_id: Snowflake,
|
||||||
) -> ChorusResult<Vec<types::PublicUser>> {
|
) -> ChorusResult<Vec<types::PublicUser>> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/users/{}/relationships/",
|
"{}/users/{}/relationships",
|
||||||
self.belongs_to.borrow().urls.api,
|
self.belongs_to.read().unwrap().urls.api,
|
||||||
user_id
|
user_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().get(url).bearer_auth(self.token()),
|
request: Client::new().get(url).header("Authorization", self.token()),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
chorus_request
|
chorus_request
|
||||||
|
@ -36,17 +34,17 @@ impl UserMeta {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the authenticated user's relationships.
|
/// Retrieves the user's relationships.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Reference
|
||||||
/// This function returns a [`ChorusResult<Vec<types::Relationship>>`].
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#get-users-me-relationships>
|
||||||
pub async fn get_relationships(&mut self) -> ChorusResult<Vec<types::Relationship>> {
|
pub async fn get_relationships(&mut self) -> ChorusResult<Vec<types::Relationship>> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/users/@me/relationships/",
|
"{}/users/@me/relationships",
|
||||||
self.belongs_to.borrow().urls.api
|
self.belongs_to.read().unwrap().urls.api
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().get(url).bearer_auth(self.token()),
|
request: Client::new().get(url).header("Authorization", self.token()),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
chorus_request
|
chorus_request
|
||||||
|
@ -56,54 +54,43 @@ impl UserMeta {
|
||||||
|
|
||||||
/// Sends a friend request to a user.
|
/// Sends a friend request to a user.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#post-users-me-relationships>
|
||||||
/// * `schema` - A [`FriendRequestSendSchema`] struct that holds the information about the friend request to be sent.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// This function returns a [`Result`] that holds a [`ChorusLibError`] if the request fails.
|
|
||||||
pub async fn send_friend_request(
|
pub async fn send_friend_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
schema: types::FriendRequestSendSchema,
|
schema: FriendRequestSendSchema,
|
||||||
) -> ChorusResult<()> {
|
) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/users/@me/relationships/",
|
"{}/users/@me/relationships",
|
||||||
self.belongs_to.borrow().urls.api
|
self.belongs_to.read().unwrap().urls.api
|
||||||
);
|
);
|
||||||
let body = to_string(&schema).unwrap();
|
let body = to_string(&schema).unwrap();
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().post(url).bearer_auth(self.token()).body(body),
|
request: Client::new()
|
||||||
|
.post(url)
|
||||||
|
.header("Authorization", self.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(body),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
chorus_request.handle_request_as_result(self).await
|
chorus_request.handle_request_as_result(self).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modifies the relationship between the authenticated user and the specified user.
|
/// Modifies the relationship between the authenticated user and a given user.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// Can be used to unfriend users, accept or send friend requests and block or unblock users.
|
||||||
///
|
|
||||||
/// * `user_id` - ID of the user to modify the relationship with.
|
|
||||||
/// * `relationship_type` - A [`RelationshipType`] enum that specifies the type of relationship to modify.
|
|
||||||
/// * [`RelationshipType::None`]: Removes the relationship between the two users.
|
|
||||||
/// * [`RelationshipType::Friends`] | [`RelationshipType::Incoming`] | [`RelationshipType::Outgoing`]:
|
|
||||||
/// Either accepts an incoming friend request, or sends a new friend request, if there is no
|
|
||||||
/// incoming friend request from the specified `user_id`.
|
|
||||||
/// * [`RelationshipType::Blocked`]: Blocks the specified user_id.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// This function returns an [`Result`] that holds a [`ChorusLibError`] if the request fails.
|
|
||||||
pub async fn modify_user_relationship(
|
pub async fn modify_user_relationship(
|
||||||
&mut self,
|
&mut self,
|
||||||
user_id: Snowflake,
|
user_id: Snowflake,
|
||||||
relationship_type: RelationshipType,
|
relationship_type: RelationshipType,
|
||||||
) -> ChorusResult<()> {
|
) -> ChorusResult<()> {
|
||||||
let api_url = self.belongs_to.borrow().urls.api.clone();
|
let api_url = self.belongs_to.read().unwrap().urls.api.clone();
|
||||||
match relationship_type {
|
match relationship_type {
|
||||||
RelationshipType::None => {
|
RelationshipType::None => {
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.delete(format!("{}/users/@me/relationships/{}/", api_url, user_id))
|
.delete(format!("{}/users/@me/relationships/{}", api_url, user_id))
|
||||||
.bearer_auth(self.token()),
|
.header("Authorization", self.token()),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
chorus_request.handle_request_as_result(self).await
|
chorus_request.handle_request_as_result(self).await
|
||||||
|
@ -116,8 +103,8 @@ impl UserMeta {
|
||||||
};
|
};
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.put(format!("{}/users/@me/relationships/{}/", api_url, user_id))
|
.put(format!("{}/users/@me/relationships/{}", api_url, user_id))
|
||||||
.bearer_auth(self.token())
|
.header("Authorization", self.token())
|
||||||
.body(to_string(&body).unwrap()),
|
.body(to_string(&body).unwrap()),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
|
@ -131,8 +118,8 @@ impl UserMeta {
|
||||||
};
|
};
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new()
|
request: Client::new()
|
||||||
.put(format!("{}/users/@me/relationships/{}/", api_url, user_id))
|
.put(format!("{}/users/@me/relationships/{}", api_url, user_id))
|
||||||
.bearer_auth(self.token())
|
.header("Authorization", self.token())
|
||||||
.body(to_string(&body).unwrap()),
|
.body(to_string(&body).unwrap()),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
|
@ -142,22 +129,20 @@ impl UserMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the relationship between the authenticated user and the specified user.
|
/// Removes the relationship between the authenticated user and a given user.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#delete-users-me-relationships-peer-id>
|
||||||
/// * `user_id` - ID of the user to remove the relationship with.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// This function returns a [`Result`] that holds a [`ChorusLibError`] if the request fails.
|
|
||||||
pub async fn remove_relationship(&mut self, user_id: Snowflake) -> ChorusResult<()> {
|
pub async fn remove_relationship(&mut self, user_id: Snowflake) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/users/@me/relationships/{}/",
|
"{}/users/@me/relationships/{}",
|
||||||
self.belongs_to.borrow().urls.api,
|
self.belongs_to.read().unwrap().urls.api,
|
||||||
user_id
|
user_id
|
||||||
);
|
);
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request: Client::new().delete(url).bearer_auth(self.token()),
|
request: Client::new()
|
||||||
|
.delete(url)
|
||||||
|
.header("Authorization", self.token()),
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
};
|
};
|
||||||
chorus_request.handle_request_as_result(self).await
|
chorus_request.handle_request_as_result(self).await
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::LimitType,
|
|
||||||
errors::{ChorusError, ChorusResult},
|
errors::{ChorusError, ChorusResult},
|
||||||
instance::{Instance, UserMeta},
|
instance::{ChorusUser, Instance},
|
||||||
ratelimiter::ChorusRequest,
|
ratelimiter::ChorusRequest,
|
||||||
types::{User, UserModifySchema, UserSettings},
|
types::{LimitType, User, UserModifySchema, UserSettings},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl UserMeta {
|
impl ChorusUser {
|
||||||
/// Get a user object by id, or get the current user.
|
/// Gets a user by id, or if the id is None, gets the current user.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Notes
|
||||||
|
/// This function is a wrapper around [`User::get`].
|
||||||
///
|
///
|
||||||
/// * `token` - A valid access token for the API.
|
/// # Reference
|
||||||
/// * `url_api` - The URL to the API.
|
/// See <https://discord-userdoccers.vercel.app/resources/user#get-user> and
|
||||||
/// * `id` - The id of the user that will be retrieved. If this is None, the current user will be retrieved.
|
/// <https://discord-userdoccers.vercel.app/resources/user#get-current-user>
|
||||||
/// * `instance_limits` - The [`Limits`] of the instance.
|
pub async fn get_user(&mut self, id: Option<&String>) -> ChorusResult<User> {
|
||||||
///
|
User::get(self, id).await
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// * [`ChorusLibError`] - If the request fails.
|
|
||||||
pub async fn get(user: &mut UserMeta, id: Option<&String>) -> ChorusResult<User> {
|
|
||||||
User::get(user, id).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the user's settings.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// This functions is a wrapper around [`User::get_settings`].
|
||||||
pub async fn get_settings(
|
pub async fn get_settings(
|
||||||
token: &String,
|
token: &String,
|
||||||
url_api: &String,
|
url_api: &String,
|
||||||
|
@ -36,15 +35,10 @@ impl UserMeta {
|
||||||
User::get_settings(token, url_api, instance).await
|
User::get_settings(token, url_api, instance).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modify the current user's `UserObject`.
|
/// Modifies the current user's representation. (See [`User`])
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://discord-userdoccers.vercel.app/resources/user#modify-current-user>
|
||||||
/// * `modify_schema` - A `UserModifySchema` object containing the fields to modify.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an `ChorusLibError` if the request fails or if a password is required but not provided.
|
|
||||||
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()
|
if modify_schema.new_password.is_some()
|
||||||
|| modify_schema.email.is_some()
|
|| modify_schema.email.is_some()
|
||||||
|
@ -53,37 +47,32 @@ impl UserMeta {
|
||||||
return Err(ChorusError::PasswordRequired);
|
return Err(ChorusError::PasswordRequired);
|
||||||
}
|
}
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.patch(format!("{}/users/@me/", self.belongs_to.borrow().urls.api))
|
.patch(format!(
|
||||||
|
"{}/users/@me",
|
||||||
|
self.belongs_to.read().unwrap().urls.api
|
||||||
|
))
|
||||||
.body(to_string(&modify_schema).unwrap())
|
.body(to_string(&modify_schema).unwrap())
|
||||||
.bearer_auth(self.token());
|
.header("Authorization", self.token())
|
||||||
|
.header("Content-Type", "application/json");
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request,
|
request,
|
||||||
limit_type: LimitType::default(),
|
limit_type: LimitType::default(),
|
||||||
};
|
};
|
||||||
let user_updated = chorus_request
|
chorus_request.deserialize_response::<User>(self).await
|
||||||
.deserialize_response::<User>(self)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let _ = std::mem::replace(&mut self.object, user_updated.clone());
|
|
||||||
Ok(user_updated)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a request to the server which deletes the user from the Instance.
|
/// Deletes the user from the Instance.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Reference
|
||||||
///
|
/// See <https://discord-userdoccers.vercel.app/resources/user#disable-user>
|
||||||
/// * `self` - The `User` object to delete.
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// Returns `()` if the user was successfully deleted, or a `ChorusLibError` if an error occurred.
|
|
||||||
pub async fn delete(mut self) -> ChorusResult<()> {
|
pub async fn delete(mut self) -> ChorusResult<()> {
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.post(format!(
|
.post(format!(
|
||||||
"{}/users/@me/delete/",
|
"{}/users/@me/delete",
|
||||||
self.belongs_to.borrow().urls.api
|
self.belongs_to.read().unwrap().urls.api
|
||||||
))
|
))
|
||||||
.bearer_auth(self.token());
|
.header("Authorization", self.token())
|
||||||
|
.header("Content-Type", "application/json");
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request,
|
request,
|
||||||
limit_type: LimitType::default(),
|
limit_type: LimitType::default(),
|
||||||
|
@ -93,14 +82,21 @@ impl UserMeta {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub async fn get(user: &mut UserMeta, id: Option<&String>) -> ChorusResult<User> {
|
/// Gets a user by id, or if the id is None, gets the current user.
|
||||||
let url_api = user.belongs_to.borrow().urls.api.clone();
|
///
|
||||||
|
/// # 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 ChorusUser, id: Option<&String>) -> ChorusResult<User> {
|
||||||
|
let url_api = user.belongs_to.read().unwrap().urls.api.clone();
|
||||||
let url = if id.is_none() {
|
let url = if id.is_none() {
|
||||||
format!("{}/users/@me/", url_api)
|
format!("{}/users/@me", url_api)
|
||||||
} else {
|
} else {
|
||||||
format!("{}/users/{}", url_api, id.unwrap())
|
format!("{}/users/{}", url_api, id.unwrap())
|
||||||
};
|
};
|
||||||
let request = reqwest::Client::new().get(url).bearer_auth(user.token());
|
let request = reqwest::Client::new()
|
||||||
|
.get(url)
|
||||||
|
.header("Authorization", user.token());
|
||||||
let chorus_request = ChorusRequest {
|
let chorus_request = ChorusRequest {
|
||||||
request,
|
request,
|
||||||
limit_type: LimitType::Global,
|
limit_type: LimitType::Global,
|
||||||
|
@ -114,16 +110,20 @@ impl User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the user's settings.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/user_settings.html#get-users-me-settings>
|
||||||
pub async fn get_settings(
|
pub async fn get_settings(
|
||||||
token: &String,
|
token: &String,
|
||||||
url_api: &String,
|
url_api: &String,
|
||||||
instance: &mut Instance,
|
instance: &mut Instance,
|
||||||
) -> ChorusResult<UserSettings> {
|
) -> 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))
|
||||||
.bearer_auth(token);
|
.header("Authorization", token);
|
||||||
let mut user =
|
let mut user =
|
||||||
UserMeta::shell(Rc::new(RefCell::new(instance.clone())), token.clone()).await;
|
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,
|
||||||
|
@ -133,30 +133,36 @@ impl User {
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
};
|
};
|
||||||
if instance.limits_information.is_some() {
|
if instance.limits_information.is_some() {
|
||||||
instance.limits_information.as_mut().unwrap().ratelimits =
|
instance.limits_information.as_mut().unwrap().ratelimits = user
|
||||||
user.belongs_to.borrow().clone_limits_if_some().unwrap();
|
.belongs_to
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.clone_limits_if_some()
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
/**
|
/// Gets a user by id, or if the id is None, gets the current user.
|
||||||
Get a user object by id, or get the current user.
|
///
|
||||||
# Arguments
|
/// # Notes
|
||||||
* `token` - A valid access token for the API.
|
/// This function is a wrapper around [`User::get`].
|
||||||
* `id` - The id of the user that will be retrieved. If this is None, the current user will be retrieved.
|
///
|
||||||
# Errors
|
/// # Reference
|
||||||
* [`ChorusLibError`] - If the request fails.
|
/// See <https://discord-userdoccers.vercel.app/resources/user#get-user> and
|
||||||
# Notes
|
/// <https://discord-userdoccers.vercel.app/resources/user#get-current-user>
|
||||||
This function is a wrapper around [`User::get`].
|
|
||||||
*/
|
|
||||||
pub async fn get_user(&mut self, token: String, id: Option<&String>) -> ChorusResult<User> {
|
pub async fn get_user(&mut self, token: String, id: Option<&String>) -> ChorusResult<User> {
|
||||||
let mut user = UserMeta::shell(Rc::new(RefCell::new(self.clone())), token).await;
|
let mut user = ChorusUser::shell(Arc::new(RwLock::new(self.clone())), token).await;
|
||||||
let result = User::get(&mut user, id).await;
|
let result = User::get(&mut user, id).await;
|
||||||
if self.limits_information.is_some() {
|
if self.limits_information.is_some() {
|
||||||
self.limits_information.as_mut().unwrap().ratelimits =
|
self.limits_information.as_mut().unwrap().ratelimits = user
|
||||||
user.belongs_to.borrow().clone_limits_if_some().unwrap();
|
.belongs_to
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.clone_limits_if_some()
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
//! Contains all the errors that can be returned by the library.
|
||||||
use custom_error::custom_error;
|
use custom_error::custom_error;
|
||||||
use reqwest::Error;
|
|
||||||
|
use crate::types::WebSocketEvent;
|
||||||
|
|
||||||
custom_error! {
|
custom_error! {
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||||
pub RegistrationError
|
pub RegistrationError
|
||||||
Consent = "Consent must be 'true' to register.",
|
Consent = "Consent must be 'true' to register.",
|
||||||
}
|
}
|
||||||
|
@ -10,11 +12,12 @@ custom_error! {
|
||||||
pub type ChorusResult<T> = std::result::Result<T, ChorusError>;
|
pub type ChorusResult<T> = std::result::Result<T, ChorusError>;
|
||||||
|
|
||||||
custom_error! {
|
custom_error! {
|
||||||
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
pub ChorusError
|
pub ChorusError
|
||||||
/// Server did not respond.
|
/// Server did not respond.
|
||||||
NoResponse = "Did not receive a response from the Server.",
|
NoResponse = "Did not receive a response from the Server.",
|
||||||
/// Reqwest returned an Error instead of a Response object.
|
/// Reqwest returned an Error instead of a Response object.
|
||||||
RequestFailed{url:String, error: Error} = "An error occured while trying to GET from {url}: {error}",
|
RequestFailed{url:String, error: String} = "An error occured while trying to GET from {url}: {error}",
|
||||||
/// Response received, however, it was not of the successful responses type. Used when no other, special case applies.
|
/// Response received, however, it was not of the successful responses type. Used when no other, special case applies.
|
||||||
ReceivedErrorCode{error_code: u16, error: String} = "Received the following error code while requesting from the route: {error_code}",
|
ReceivedErrorCode{error_code: u16, error: String} = "Received the following error code while requesting from the route: {error_code}",
|
||||||
/// Used when there is likely something wrong with the instance, the request was directed to.
|
/// Used when there is likely something wrong with the instance, the request was directed to.
|
||||||
|
@ -53,9 +56,10 @@ 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(Clone, PartialEq, Eq)]
|
#[derive(PartialEq, Eq, Default, Clone)]
|
||||||
pub GatewayError
|
pub GatewayError
|
||||||
// Errors we have received from the gateway
|
// Errors we have received from the gateway
|
||||||
|
#[default]
|
||||||
Unknown = "We're not sure what went wrong. Try reconnecting?",
|
Unknown = "We're not sure what went wrong. Try reconnecting?",
|
||||||
UnknownOpcode = "You sent an invalid Gateway opcode or an invalid payload for an opcode",
|
UnknownOpcode = "You sent an invalid Gateway opcode or an invalid payload for an opcode",
|
||||||
Decode = "Gateway server couldn't decode payload",
|
Decode = "Gateway server couldn't decode payload",
|
||||||
|
@ -79,25 +83,29 @@ custom_error! {
|
||||||
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! {
|
||||||
// Like GatewayError for webrtc errors
|
/// Voice Gateway errors
|
||||||
// See https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice;
|
///
|
||||||
// Also supposed to be sent by numbers, but discord is asdfghgfjkkjldf when it comes to their errors
|
/// Similar to [GatewayError].
|
||||||
|
///
|
||||||
|
/// See https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice;
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub VoiceGatewayError
|
pub VoiceGatewayError
|
||||||
// Errors we receive
|
// Errors we receive
|
||||||
UnknownOpcodeError = "You sent an invalid opcode",
|
UnknownOpcode = "You sent an invalid opcode",
|
||||||
FailedToDecodePayloadError = "You sent an invalid payload in your identifying to the (Webrtc) Gateway",
|
FailedToDecodePayload = "You sent an invalid payload in your identifying to the (Webrtc) Gateway",
|
||||||
NotAuthenticatedError = "You sent a payload before identifying with the (Webrtc) Gateway",
|
NotAuthenticated = "You sent a payload before identifying with the (Webrtc) Gateway",
|
||||||
AuthenticationFailedError = "The token you sent in your identify payload is incorrect",
|
AuthenticationFailed = "The token you sent in your identify payload is incorrect",
|
||||||
AlreadyAuthenticatedError = "You sent more than one identify payload",
|
AlreadyAuthenticated = "You sent more than one identify payload",
|
||||||
SessionNoLongerValidError = "Your session is no longer valid",
|
SessionNoLongerValid = "Your session is no longer valid",
|
||||||
SessionTimeoutError = "Your session has timed out",
|
SessionTimeout = "Your session has timed out",
|
||||||
ServerNotFoundError = "We can't find the server you're trying to connect to",
|
ServerNotFound = "We can't find the server you're trying to connect to",
|
||||||
UnknownProtocolError = "We didn't recognize the protocol you sent",
|
UnknownProtocol = "We didn't recognize the protocol you sent",
|
||||||
DisconnectedError = "Channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped. Should not reconnect.",
|
Disconnected = "Channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped. Should not reconnect.",
|
||||||
VoiceServerCrashedError = "The server crashed, try resuming",
|
VoiceServerCrashed = "The server crashed, try resuming",
|
||||||
UnknownEncryptionModeError = "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 tungstenite error: {error}",
|
||||||
|
|
213
src/gateway.rs
213
src/gateway.rs
|
@ -1,14 +1,18 @@
|
||||||
|
//! Gateway connection, communication and handling, as well as object caching and updating.
|
||||||
|
|
||||||
use crate::errors::GatewayError;
|
use crate::errors::GatewayError;
|
||||||
use crate::gateway::events::Events;
|
use crate::gateway::events::Events;
|
||||||
use crate::types::{self, Channel, ChannelUpdate, Snowflake};
|
use crate::types::{
|
||||||
use crate::types::{UpdateMessage, WebSocketEvent};
|
self, AutoModerationRule, AutoModerationRuleUpdate, Channel, ChannelCreate, ChannelDelete,
|
||||||
|
ChannelUpdate, Composite, Guild, GuildRoleCreate, GuildRoleUpdate, JsonField, RoleObject,
|
||||||
|
Snowflake, SourceUrlField, ThreadUpdate, UpdateMessage, WebSocketEvent,
|
||||||
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, RwLock};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::sync::watch;
|
|
||||||
use tokio::time::sleep_until;
|
use tokio::time::sleep_until;
|
||||||
|
|
||||||
use futures_util::stream::SplitSink;
|
use futures_util::stream::SplitSink;
|
||||||
|
@ -78,7 +82,7 @@ const GATEWAY_LAZY_REQUEST: u8 = 14;
|
||||||
/// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms
|
/// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms
|
||||||
pub(crate) const HEARTBEAT_ACK_TIMEOUT: u64 = 2000;
|
pub(crate) const HEARTBEAT_ACK_TIMEOUT: u64 = 2000;
|
||||||
|
|
||||||
/// Represents a messsage received from the gateway. This will be either a [GatewayReceivePayload], containing events, or a [GatewayError].
|
/// Represents a messsage 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 struct GatewayMessage {
|
||||||
|
@ -148,11 +152,13 @@ impl GatewayMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type ObservableObject = dyn Send + Sync + Any;
|
||||||
|
|
||||||
/// 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
|
||||||
/// implemented [Types] with the trait [`WebSocketEvent`]
|
/// implemented types with the trait [`WebSocketEvent`]
|
||||||
/// Using this handle you can also send Gateway Events directly.
|
/// Using this handle you can also send Gateway Events directly.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GatewayHandle {
|
pub struct GatewayHandle {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub events: Arc<Mutex<Events>>,
|
pub events: Arc<Mutex<Events>>,
|
||||||
|
@ -164,10 +170,9 @@ pub struct GatewayHandle {
|
||||||
>,
|
>,
|
||||||
>,
|
>,
|
||||||
>,
|
>,
|
||||||
pub handle: JoinHandle<()>,
|
|
||||||
/// Tells gateway tasks to close
|
/// Tells gateway tasks to close
|
||||||
kill_send: tokio::sync::broadcast::Sender<()>,
|
kill_send: tokio::sync::broadcast::Sender<()>,
|
||||||
store: Arc<Mutex<HashMap<Snowflake, Box<dyn Send + Any>>>>,
|
pub(crate) store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
|
@ -196,27 +201,57 @@ impl GatewayHandle {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn observe<T: Updateable>(&self, object: T) -> watch::Receiver<T> {
|
pub async fn observe<T: Updateable + Clone + Debug + Composite<T>>(
|
||||||
|
&self,
|
||||||
|
object: Arc<RwLock<T>>,
|
||||||
|
) -> Arc<RwLock<T>> {
|
||||||
let mut store = self.store.lock().await;
|
let mut store = self.store.lock().await;
|
||||||
if let Some(channel) = store.get(&object.id()) {
|
let id = object.read().unwrap().id();
|
||||||
let (_, rx) = channel
|
if let Some(channel) = store.get(&id) {
|
||||||
.downcast_ref::<(watch::Sender<T>, watch::Receiver<T>)>()
|
let object = channel.clone();
|
||||||
|
drop(store);
|
||||||
|
object
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.downcast_ref::<T>()
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
panic!(
|
panic!(
|
||||||
"Snowflake {} already exists in the store, but it is not of type T.",
|
"Snowflake {} already exists in the store, but it is not of type T.",
|
||||||
object.id()
|
id
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
rx.clone()
|
let ptr = Arc::into_raw(object.clone());
|
||||||
|
// SAFETY:
|
||||||
|
// - We have just checked that the typeid of the `dyn Any ...` matches that of `T`.
|
||||||
|
// - This operation doesn't read or write any shared data, and thus cannot cause a data race
|
||||||
|
// - The reference count is not being modified
|
||||||
|
let downcasted = unsafe { Arc::from_raw(ptr as *const RwLock<T>).clone() };
|
||||||
|
let object = downcasted.read().unwrap().clone();
|
||||||
|
|
||||||
|
let watched_object = object.watch_whole(self).await;
|
||||||
|
*downcasted.write().unwrap() = watched_object;
|
||||||
|
downcasted
|
||||||
} else {
|
} else {
|
||||||
let id = object.id();
|
let id = object.read().unwrap().id();
|
||||||
let channel = watch::channel(object);
|
let object = object.read().unwrap().clone();
|
||||||
let receiver = channel.1.clone();
|
let object = object.clone().watch_whole(self).await;
|
||||||
store.insert(id, Box::new(channel));
|
let wrapped = Arc::new(RwLock::new(object));
|
||||||
receiver
|
store.insert(id, wrapped.clone());
|
||||||
|
wrapped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursively observes and updates all updateable fields on the struct T. Returns an object `T`
|
||||||
|
/// with all of its observable fields being observed.
|
||||||
|
pub async fn observe_and_into_inner<T: Updateable + Clone + Debug + Composite<T>>(
|
||||||
|
&self,
|
||||||
|
object: Arc<RwLock<T>>,
|
||||||
|
) -> T {
|
||||||
|
let channel = self.observe(object.clone()).await;
|
||||||
|
let object = channel.read().unwrap().clone();
|
||||||
|
object
|
||||||
|
}
|
||||||
|
|
||||||
/// Sends an identify event to the gateway
|
/// Sends an identify event to the gateway
|
||||||
pub async fn send_identify(&self, to_send: types::GatewayIdentifyPayload) {
|
pub async fn send_identify(&self, to_send: types::GatewayIdentifyPayload) {
|
||||||
let to_send_value = serde_json::to_value(&to_send).unwrap();
|
let to_send_value = serde_json::to_value(&to_send).unwrap();
|
||||||
|
@ -257,7 +292,7 @@ impl GatewayHandle {
|
||||||
|
|
||||||
/// Sends an update voice state to the server
|
/// Sends an update voice state to the server
|
||||||
pub async fn send_update_voice_state(&self, to_send: types::UpdateVoiceState) {
|
pub async fn send_update_voice_state(&self, to_send: types::UpdateVoiceState) {
|
||||||
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 Update Voice State..");
|
trace!("GW: Sending Update Voice State..");
|
||||||
|
|
||||||
|
@ -293,6 +328,7 @@ impl GatewayHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Gateway {
|
pub struct Gateway {
|
||||||
events: Arc<Mutex<Events>>,
|
events: Arc<Mutex<Events>>,
|
||||||
heartbeat_handler: HeartbeatHandler,
|
heartbeat_handler: HeartbeatHandler,
|
||||||
|
@ -306,7 +342,8 @@ pub struct Gateway {
|
||||||
>,
|
>,
|
||||||
websocket_receive: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
|
websocket_receive: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
|
||||||
kill_send: tokio::sync::broadcast::Sender<()>,
|
kill_send: tokio::sync::broadcast::Sender<()>,
|
||||||
store: Arc<Mutex<HashMap<Snowflake, Box<dyn Send + Any>>>>,
|
store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
|
||||||
|
url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gateway {
|
impl Gateway {
|
||||||
|
@ -370,10 +407,11 @@ impl Gateway {
|
||||||
websocket_receive,
|
websocket_receive,
|
||||||
kill_send: kill_send.clone(),
|
kill_send: kill_send.clone(),
|
||||||
store: store.clone(),
|
store: store.clone(),
|
||||||
|
url: websocket_url.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
||||||
let handle: JoinHandle<()> = task::spawn(async move {
|
task::spawn(async move {
|
||||||
gateway.gateway_listen_task().await;
|
gateway.gateway_listen_task().await;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -381,7 +419,6 @@ impl Gateway {
|
||||||
url: websocket_url.clone(),
|
url: websocket_url.clone(),
|
||||||
events: shared_events,
|
events: shared_events,
|
||||||
websocket_send: shared_websocket_send.clone(),
|
websocket_send: shared_websocket_send.clone(),
|
||||||
handle,
|
|
||||||
kill_send: kill_send.clone(),
|
kill_send: kill_send.clone(),
|
||||||
store,
|
store,
|
||||||
})
|
})
|
||||||
|
@ -444,13 +481,15 @@ impl Gateway {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: handle errors in a good way, maybe observers like events?
|
|
||||||
if msg.is_error() {
|
if msg.is_error() {
|
||||||
warn!("GW: Received error, connection will close..");
|
let error = msg.error().unwrap();
|
||||||
|
|
||||||
let _error = msg.error();
|
warn!("GW: Received error {:?}, connection will close..", error);
|
||||||
|
|
||||||
self.close().await;
|
self.close().await;
|
||||||
|
|
||||||
|
self.events.lock().await.error.notify(error).await;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +501,7 @@ impl Gateway {
|
||||||
GATEWAY_DISPATCH => {
|
GATEWAY_DISPATCH => {
|
||||||
let Some(event_name) = gateway_payload.event_name else {
|
let Some(event_name) = gateway_payload.event_name else {
|
||||||
warn!("Gateway dispatch op without event_name");
|
warn!("Gateway dispatch op without event_name");
|
||||||
return
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("Gateway: Received {event_name}");
|
trace!("Gateway: Received {event_name}");
|
||||||
|
@ -472,16 +511,35 @@ impl Gateway {
|
||||||
match event_name.as_str() {
|
match event_name.as_str() {
|
||||||
$($name => {
|
$($name => {
|
||||||
let event = &mut self.events.lock().await.$($path).+;
|
let event = &mut self.events.lock().await.$($path).+;
|
||||||
match serde_json::from_str(gateway_payload.event_data.unwrap().get()) {
|
let json = gateway_payload.event_data.unwrap().get();
|
||||||
|
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})"),
|
||||||
Ok(message) => {
|
Ok(message) => {
|
||||||
$(
|
$(
|
||||||
let message: $message_type = message;
|
let mut message: $message_type = message;
|
||||||
if let Some(to_update) = self.store.lock().await.get(&message.id()) {
|
let store = self.store.lock().await;
|
||||||
if let Some((tx, _)) = to_update.downcast_ref::<(watch::Sender<$update_type>, watch::Receiver<$update_type>)>() {
|
let id = if message.id().is_some() {
|
||||||
tx.send_modify(|object| message.update(object));
|
message.id().unwrap()
|
||||||
|
} else {
|
||||||
|
event.notify(message).await;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(to_update) = store.get(&id) {
|
||||||
|
let object = to_update.clone();
|
||||||
|
let inner_object = object.read().unwrap();
|
||||||
|
if let Some(_) = inner_object.downcast_ref::<$update_type>() {
|
||||||
|
let ptr = Arc::into_raw(object.clone());
|
||||||
|
// SAFETY:
|
||||||
|
// - We have just checked that the typeid of the `dyn Any ...` matches that of `T`.
|
||||||
|
// - This operation doesn't read or write any shared data, and thus cannot cause a data race
|
||||||
|
// - The reference count is not being modified
|
||||||
|
let downcasted = unsafe { Arc::from_raw(ptr as *const RwLock<$update_type>).clone() };
|
||||||
|
drop(inner_object);
|
||||||
|
message.set_json(json.to_string());
|
||||||
|
message.set_source_url(self.url.clone());
|
||||||
|
message.update(downcasted.clone());
|
||||||
} else {
|
} else {
|
||||||
warn!("Received {} for {}, but it has been observed to be a different type!", $name, message.id())
|
warn!("Received {} for {}, but it has been observed to be a different type!", $name, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)?
|
)?
|
||||||
|
@ -523,69 +581,70 @@ impl Gateway {
|
||||||
"READY_SUPPLEMENTAL" => session.ready_supplemental,
|
"READY_SUPPLEMENTAL" => session.ready_supplemental,
|
||||||
"APPLICATION_COMMAND_PERMISSIONS_UPDATE" => application.command_permissions_update,
|
"APPLICATION_COMMAND_PERMISSIONS_UPDATE" => application.command_permissions_update,
|
||||||
"AUTO_MODERATION_RULE_CREATE" =>auto_moderation.rule_create,
|
"AUTO_MODERATION_RULE_CREATE" =>auto_moderation.rule_create,
|
||||||
"AUTO_MODERATION_RULE_UPDATE" =>auto_moderation.rule_update,
|
"AUTO_MODERATION_RULE_UPDATE" =>auto_moderation.rule_update AutoModerationRuleUpdate: AutoModerationRule,
|
||||||
"AUTO_MODERATION_RULE_DELETE" => auto_moderation.rule_delete,
|
"AUTO_MODERATION_RULE_DELETE" => auto_moderation.rule_delete,
|
||||||
"AUTO_MODERATION_ACTION_EXECUTION" => auto_moderation.action_execution,
|
"AUTO_MODERATION_ACTION_EXECUTION" => auto_moderation.action_execution,
|
||||||
"CHANNEL_CREATE" => channel.create,
|
"CHANNEL_CREATE" => channel.create ChannelCreate: Guild,
|
||||||
"CHANNEL_UPDATE" => channel.update ChannelUpdate: Channel,
|
"CHANNEL_UPDATE" => channel.update ChannelUpdate: Channel,
|
||||||
"CHANNEL_UNREAD_UPDATE" => channel.unread_update,
|
"CHANNEL_UNREAD_UPDATE" => channel.unread_update,
|
||||||
"CHANNEL_DELETE" => channel.delete,
|
"CHANNEL_DELETE" => channel.delete ChannelDelete: Guild,
|
||||||
"CHANNEL_PINS_UPDATE" => channel.pins_update,
|
"CHANNEL_PINS_UPDATE" => channel.pins_update,
|
||||||
"CALL_CREATE" => call.create,
|
"CALL_CREATE" => call.create,
|
||||||
"CALL_UPDATE" => call.update,
|
"CALL_UPDATE" => call.update,
|
||||||
"CALL_DELETE" => call.delete,
|
"CALL_DELETE" => call.delete,
|
||||||
"THREAD_CREATE" => thread.create,
|
"THREAD_CREATE" => thread.create, // TODO
|
||||||
"THREAD_UPDATE" => thread.update,
|
"THREAD_UPDATE" => thread.update ThreadUpdate: Channel,
|
||||||
"THREAD_DELETE" => thread.delete,
|
"THREAD_DELETE" => thread.delete, // TODO
|
||||||
"THREAD_LIST_SYNC" => thread.list_sync,
|
"THREAD_LIST_SYNC" => thread.list_sync, // TODO
|
||||||
"THREAD_MEMBER_UPDATE" => thread.member_update,
|
"THREAD_MEMBER_UPDATE" => thread.member_update, // TODO
|
||||||
"THREAD_MEMBERS_UPDATE" => thread.members_update,
|
"THREAD_MEMBERS_UPDATE" => thread.members_update, // TODO
|
||||||
"GUILD_CREATE" => guild.create,
|
"GUILD_CREATE" => guild.create, // TODO
|
||||||
"GUILD_UPDATE" => guild.update,
|
"GUILD_UPDATE" => guild.update, // TODO
|
||||||
"GUILD_DELETE" => guild.delete,
|
"GUILD_DELETE" => guild.delete, // TODO
|
||||||
"GUILD_AUDIT_LOG_ENTRY_CREATE" => guild.audit_log_entry_create,
|
"GUILD_AUDIT_LOG_ENTRY_CREATE" => guild.audit_log_entry_create,
|
||||||
"GUILD_BAN_ADD" => guild.ban_add,
|
"GUILD_BAN_ADD" => guild.ban_add, // TODO
|
||||||
"GUILD_BAN_REMOVE" => guild.ban_remove,
|
"GUILD_BAN_REMOVE" => guild.ban_remove, // TODO
|
||||||
"GUILD_EMOJIS_UPDATE" => guild.emojis_update,
|
"GUILD_EMOJIS_UPDATE" => guild.emojis_update, // TODO
|
||||||
"GUILD_STICKERS_UPDATE" => guild.stickers_update,
|
"GUILD_STICKERS_UPDATE" => guild.stickers_update, // TODO
|
||||||
"GUILD_INTEGRATIONS_UPDATE" => guild.integrations_update,
|
"GUILD_INTEGRATIONS_UPDATE" => guild.integrations_update,
|
||||||
"GUILD_MEMBER_ADD" => guild.member_add,
|
"GUILD_MEMBER_ADD" => guild.member_add,
|
||||||
"GUILD_MEMBER_REMOVE" => guild.member_remove,
|
"GUILD_MEMBER_REMOVE" => guild.member_remove,
|
||||||
"GUILD_MEMBER_UPDATE" => guild.member_update,
|
"GUILD_MEMBER_UPDATE" => guild.member_update, // TODO
|
||||||
"GUILD_MEMBERS_CHUNK" => guild.members_chunk,
|
"GUILD_MEMBERS_CHUNK" => guild.members_chunk, // TODO
|
||||||
"GUILD_ROLE_CREATE" => guild.role_create,
|
"GUILD_ROLE_CREATE" => guild.role_create GuildRoleCreate: Guild,
|
||||||
"GUILD_ROLE_UPDATE" => guild.role_update,
|
"GUILD_ROLE_UPDATE" => guild.role_update GuildRoleUpdate: RoleObject,
|
||||||
"GUILD_ROLE_DELETE" => guild.role_delete,
|
"GUILD_ROLE_DELETE" => guild.role_delete, // TODO
|
||||||
"GUILD_SCHEDULED_EVENT_CREATE" => guild.role_scheduled_event_create,
|
"GUILD_SCHEDULED_EVENT_CREATE" => guild.role_scheduled_event_create, // TODO
|
||||||
"GUILD_SCHEDULED_EVENT_UPDATE" => guild.role_scheduled_event_update,
|
"GUILD_SCHEDULED_EVENT_UPDATE" => guild.role_scheduled_event_update, // TODO
|
||||||
"GUILD_SCHEDULED_EVENT_DELETE" => guild.role_scheduled_event_delete,
|
"GUILD_SCHEDULED_EVENT_DELETE" => guild.role_scheduled_event_delete, // TODO
|
||||||
"GUILD_SCHEDULED_EVENT_USER_ADD" => guild.role_scheduled_event_user_add,
|
"GUILD_SCHEDULED_EVENT_USER_ADD" => guild.role_scheduled_event_user_add,
|
||||||
"GUILD_SCHEDULED_EVENT_USER_REMOVE" => guild.role_scheduled_event_user_remove,
|
"GUILD_SCHEDULED_EVENT_USER_REMOVE" => guild.role_scheduled_event_user_remove,
|
||||||
"PASSIVE_UPDATE_V1" => guild.passive_update_v1,
|
"PASSIVE_UPDATE_V1" => guild.passive_update_v1, // TODO
|
||||||
"INTEGRATION_CREATE" => integration.create,
|
"INTEGRATION_CREATE" => integration.create, // TODO
|
||||||
"INTEGRATION_UPDATE" => integration.update,
|
"INTEGRATION_UPDATE" => integration.update, // TODO
|
||||||
"INTEGRATION_DELETE" => integration.delete,
|
"INTEGRATION_DELETE" => integration.delete, // TODO
|
||||||
"INTERACTION_CREATE" => interaction.create,
|
"INTERACTION_CREATE" => interaction.create, // TODO
|
||||||
"INVITE_CREATE" => invite.create,
|
"INVITE_CREATE" => invite.create, // TODO
|
||||||
"INVITE_DELETE" => invite.delete,
|
"INVITE_DELETE" => invite.delete, // TODO
|
||||||
"MESSAGE_CREATE" => message.create,
|
"MESSAGE_CREATE" => message.create,
|
||||||
"MESSAGE_UPDATE" => message.update,
|
"MESSAGE_UPDATE" => message.update, // TODO
|
||||||
"MESSAGE_DELETE" => message.delete,
|
"MESSAGE_DELETE" => message.delete,
|
||||||
"MESSAGE_DELETE_BULK" => message.delete_bulk,
|
"MESSAGE_DELETE_BULK" => message.delete_bulk,
|
||||||
"MESSAGE_REACTION_ADD" => message.reaction_add,
|
"MESSAGE_REACTION_ADD" => message.reaction_add, // TODO
|
||||||
"MESSAGE_REACTION_REMOVE" => message.reaction_remove,
|
"MESSAGE_REACTION_REMOVE" => message.reaction_remove, // TODO
|
||||||
"MESSAGE_REACTION_REMOVE_ALL" => message.reaction_remove_all,
|
"MESSAGE_REACTION_REMOVE_ALL" => message.reaction_remove_all, // TODO
|
||||||
"MESSAGE_REACTION_REMOVE_EMOJI" => message.reaction_remove_emoji,
|
"MESSAGE_REACTION_REMOVE_EMOJI" => message.reaction_remove_emoji, // TODO
|
||||||
"MESSAGE_ACK" => message.ack,
|
"MESSAGE_ACK" => message.ack,
|
||||||
"PRESENCE_UPDATE" => user.presence_update,
|
"PRESENCE_UPDATE" => user.presence_update, // TODO
|
||||||
"RELATIONSHIP_ADD" => relationship.add,
|
"RELATIONSHIP_ADD" => relationship.add,
|
||||||
"RELATIONSHIP_REMOVE" => relationship.remove,
|
"RELATIONSHIP_REMOVE" => relationship.remove,
|
||||||
"STAGE_INSTANCE_CREATE" => stage_instance.create,
|
"STAGE_INSTANCE_CREATE" => stage_instance.create,
|
||||||
"STAGE_INSTANCE_UPDATE" => stage_instance.update,
|
"STAGE_INSTANCE_UPDATE" => stage_instance.update, // TODO
|
||||||
"STAGE_INSTANCE_DELETE" => stage_instance.delete,
|
"STAGE_INSTANCE_DELETE" => stage_instance.delete,
|
||||||
"USER_UPDATE" => user.update,
|
"TYPING_START" => user.typing_start,
|
||||||
|
"USER_UPDATE" => user.update, // TODO
|
||||||
"USER_GUILD_SETTINGS_UPDATE" => user.guild_settings_update,
|
"USER_GUILD_SETTINGS_UPDATE" => user.guild_settings_update,
|
||||||
"VOICE_STATE_UPDATE" => voice.state_update,
|
"VOICE_STATE_UPDATE" => voice.state_update, // TODO
|
||||||
"VOICE_SERVER_UPDATE" => voice.server_update,
|
"VOICE_SERVER_UPDATE" => voice.server_update,
|
||||||
"WEBHOOKS_UPDATE" => webhooks.update
|
"WEBHOOKS_UPDATE" => webhooks.update
|
||||||
);
|
);
|
||||||
|
@ -671,6 +730,7 @@ impl Gateway {
|
||||||
|
|
||||||
/// Handles sending heartbeats to the gateway in another thread
|
/// Handles sending heartbeats to the gateway in another thread
|
||||||
#[allow(dead_code)] // FIXME: Remove this, once HeartbeatHandler is used
|
#[allow(dead_code)] // FIXME: Remove this, once HeartbeatHandler is used
|
||||||
|
#[derive(Debug)]
|
||||||
struct HeartbeatHandler {
|
struct HeartbeatHandler {
|
||||||
/// How ofter heartbeats need to be sent at a minimum
|
/// How ofter heartbeats need to be sent at a minimum
|
||||||
pub heartbeat_interval: Duration,
|
pub heartbeat_interval: Duration,
|
||||||
|
@ -857,7 +917,7 @@ impl<T: WebSocketEvent> GatewayEvent<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod events {
|
pub mod events {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -880,6 +940,7 @@ mod events {
|
||||||
pub webhooks: Webhooks,
|
pub webhooks: Webhooks,
|
||||||
pub gateway_identify_payload: GatewayEvent<types::GatewayIdentifyPayload>,
|
pub gateway_identify_payload: GatewayEvent<types::GatewayIdentifyPayload>,
|
||||||
pub gateway_resume: GatewayEvent<types::GatewayResume>,
|
pub gateway_resume: GatewayEvent<types::GatewayResume>,
|
||||||
|
pub error: GatewayEvent<GatewayError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -927,7 +988,7 @@ mod events {
|
||||||
pub update: GatewayEvent<types::UserUpdate>,
|
pub update: GatewayEvent<types::UserUpdate>,
|
||||||
pub guild_settings_update: GatewayEvent<types::UserGuildSettingsUpdate>,
|
pub guild_settings_update: GatewayEvent<types::UserGuildSettingsUpdate>,
|
||||||
pub presence_update: GatewayEvent<types::PresenceUpdate>,
|
pub presence_update: GatewayEvent<types::PresenceUpdate>,
|
||||||
pub typing_start_event: GatewayEvent<types::TypingStartEvent>,
|
pub typing_start: GatewayEvent<types::TypingStartEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
use std::cell::RefCell;
|
//! Instance and ChorusUser objects.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::api::{Limit, LimitType};
|
|
||||||
use crate::errors::ChorusResult;
|
use crate::errors::ChorusResult;
|
||||||
use crate::gateway::{Gateway, GatewayHandle};
|
use crate::gateway::{Gateway, GatewayHandle};
|
||||||
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::{GeneralConfiguration, User, UserSettings};
|
use crate::types::{GeneralConfiguration, Limit, LimitType, User, UserSettings};
|
||||||
use crate::UrlBundle;
|
use crate::UrlBundle;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
/**
|
/// The [`Instance`]; what you will be using to perform all sorts of actions on the Spacebar server.
|
||||||
The [`Instance`] what you will be using to perform all sorts of actions on the Spacebar server.
|
/// If `limits_information` is `None`, then the instance will not be rate limited.
|
||||||
If `limits_information` is `None`, then the instance will not be rate limited.
|
|
||||||
*/
|
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
pub urls: UrlBundle,
|
pub urls: UrlBundle,
|
||||||
pub instance_info: GeneralConfiguration,
|
pub instance_info: GeneralConfiguration,
|
||||||
|
@ -26,19 +25,14 @@ pub struct Instance {
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct LimitsInformation {
|
pub struct LimitsInformation {
|
||||||
pub ratelimits: HashMap<LimitType, Limit>,
|
pub ratelimits: HashMap<LimitType, Limit>,
|
||||||
pub configuration: RateLimits,
|
pub configuration: RateLimits,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
/// Creates a new [`Instance`].
|
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle), where `limited` is whether or not to automatically use rate limits.
|
||||||
/// # Arguments
|
|
||||||
/// * `urls` - The [`URLBundle`] that contains all the URLs that are needed to connect to the Spacebar server.
|
|
||||||
/// * `requester` - The [`LimitedRequester`] that will be used to make requests to the Spacebar server.
|
|
||||||
/// # Errors
|
|
||||||
/// * [`InstanceError`] - If the instance cannot be created.
|
|
||||||
pub async fn new(urls: UrlBundle, limited: bool) -> ChorusResult<Instance> {
|
pub async fn new(urls: UrlBundle, limited: bool) -> ChorusResult<Instance> {
|
||||||
let limits_information;
|
let limits_information;
|
||||||
if limited {
|
if limited {
|
||||||
|
@ -89,17 +83,20 @@ impl fmt::Display for Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UserMeta {
|
/// A ChorusUser is a representation of an authenticated user on an [Instance].
|
||||||
pub belongs_to: Rc<RefCell<Instance>>,
|
/// It is used for most authenticated actions on a Spacebar server.
|
||||||
|
/// It also has its own [Gateway] connection.
|
||||||
|
pub struct ChorusUser {
|
||||||
|
pub belongs_to: Arc<RwLock<Instance>>,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub limits: Option<HashMap<LimitType, Limit>>,
|
pub limits: Option<HashMap<LimitType, Limit>>,
|
||||||
pub settings: UserSettings,
|
pub settings: Arc<RwLock<UserSettings>>,
|
||||||
pub object: User,
|
pub object: Arc<RwLock<User>>,
|
||||||
pub gateway: GatewayHandle,
|
pub gateway: GatewayHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserMeta {
|
impl ChorusUser {
|
||||||
pub fn token(&self) -> String {
|
pub fn token(&self) -> String {
|
||||||
self.token.clone()
|
self.token.clone()
|
||||||
}
|
}
|
||||||
|
@ -108,15 +105,20 @@ impl UserMeta {
|
||||||
self.token = token;
|
self.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new [ChorusUser] from existing data.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// This isn't the prefered way to create a ChorusUser.
|
||||||
|
/// See [Instance::login_account] and [Instance::register_account] instead.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
belongs_to: Rc<RefCell<Instance>>,
|
belongs_to: Arc<RwLock<Instance>>,
|
||||||
token: String,
|
token: String,
|
||||||
limits: Option<HashMap<LimitType, Limit>>,
|
limits: Option<HashMap<LimitType, Limit>>,
|
||||||
settings: UserSettings,
|
settings: Arc<RwLock<UserSettings>>,
|
||||||
object: User,
|
object: Arc<RwLock<User>>,
|
||||||
gateway: GatewayHandle,
|
gateway: GatewayHandle,
|
||||||
) -> UserMeta {
|
) -> ChorusUser {
|
||||||
UserMeta {
|
ChorusUser {
|
||||||
belongs_to,
|
belongs_to,
|
||||||
token,
|
token,
|
||||||
limits,
|
limits,
|
||||||
|
@ -127,21 +129,22 @@ impl UserMeta {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new 'shell' of a user. The user does not exist as an object, and exists so that you have
|
/// Creates a new 'shell' of a user. The user does not exist as an object, and exists so that you have
|
||||||
/// a UserMeta object to make Rate Limited requests with. This is useful in scenarios like
|
/// a ChorusUser object to make Rate Limited requests with. This is useful in scenarios like
|
||||||
/// 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: Rc<RefCell<Instance>>, token: String) -> UserMeta {
|
pub(crate) async fn shell(instance: Arc<RwLock<Instance>>, token: String) -> ChorusUser {
|
||||||
let settings = UserSettings::default();
|
let settings = Arc::new(RwLock::new(UserSettings::default()));
|
||||||
let object = User::default();
|
let object = Arc::new(RwLock::new(User::default()));
|
||||||
let wss_url = instance.borrow().urls.wss.clone();
|
let wss_url = instance.read().unwrap().urls.wss.clone();
|
||||||
// Dummy gateway object
|
// Dummy gateway object
|
||||||
let gateway = Gateway::new(wss_url).await.unwrap();
|
let gateway = Gateway::new(wss_url).await.unwrap();
|
||||||
UserMeta {
|
ChorusUser {
|
||||||
token,
|
token,
|
||||||
belongs_to: instance.clone(),
|
belongs_to: instance.clone(),
|
||||||
limits: instance
|
limits: instance
|
||||||
.borrow()
|
.read()
|
||||||
|
.unwrap()
|
||||||
.limits_information
|
.limits_information
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|info| info.ratelimits.clone()),
|
.map(|info| info.ratelimits.clone()),
|
||||||
|
|
38
src/lib.rs
38
src/lib.rs
|
@ -1,4 +1,19 @@
|
||||||
|
//! A library for interacting with one or multiple Spacebar-compatible APIs and Gateways.
|
||||||
|
//!
|
||||||
|
//! # About
|
||||||
|
//!Chorus is a Rust library that allows developers to interact with multiple Spacebar-compatible APIs and Gateways simultaneously. The library provides a simple and efficient way to communicate with these services, making it easier for developers to build applications that rely on them. Chorus is open-source and welcomes contributions from the community.
|
||||||
|
#![doc(
|
||||||
|
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(
|
||||||
|
missing_debug_implementations,
|
||||||
|
clippy::extra_unused_lifetimes,
|
||||||
|
clippy::from_over_into,
|
||||||
|
clippy::needless_borrow,
|
||||||
|
clippy::new_without_default,
|
||||||
|
clippy::useless_conversion
|
||||||
|
)]
|
||||||
|
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
|
@ -15,16 +30,26 @@ pub mod types;
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
pub mod voice;
|
pub mod voice;
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
|
||||||
/// A URLBundle is a struct which bundles together the API-, Gateway- and CDN-URLs of a Spacebar
|
/// A URLBundle bundles together the API-, Gateway- and CDN-URLs of a Spacebar instance.
|
||||||
/// instance.
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// All the urls can be found on the /api/policies/instance/domains endpoint of a spacebar server
|
||||||
pub struct UrlBundle {
|
pub struct UrlBundle {
|
||||||
|
/// The api's url.
|
||||||
|
/// Ex: `https://old.server.spacebar.chat/api`
|
||||||
pub api: String,
|
pub api: String,
|
||||||
|
/// The gateway websocket url.
|
||||||
|
/// Note that because this is a websocket url, it will always start with `wss://` or `ws://`
|
||||||
|
/// Ex: `wss://gateway.old.server.spacebar.chat`
|
||||||
pub wss: String,
|
pub wss: String,
|
||||||
|
/// The CDN's url.
|
||||||
|
/// Ex: `https://cdn.old.server.spacebar.chat`
|
||||||
pub cdn: String,
|
pub cdn: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UrlBundle {
|
impl UrlBundle {
|
||||||
|
/// Creates a new UrlBundle from the relevant urls.
|
||||||
pub fn new(api: String, wss: String, cdn: String) -> Self {
|
pub fn new(api: String, wss: String, cdn: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
api: UrlBundle::parse_url(api),
|
api: UrlBundle::parse_url(api),
|
||||||
|
@ -33,9 +58,10 @@ impl UrlBundle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// parse(url: String) parses a URL using the Url library and formats it in a standardized
|
/// Parses a URL using the Url library and formats it in a standardized way.
|
||||||
/// way. If no protocol is given, HTTP (not HTTPS) is assumed.
|
/// If no protocol is given, HTTP (not HTTPS) is assumed.
|
||||||
/// # Example:
|
///
|
||||||
|
/// # Examples:
|
||||||
/// ```rs
|
/// ```rs
|
||||||
/// let url = parse_url("localhost:3000");
|
/// let url = parse_url("localhost:3000");
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Ratelimiter and request handling functionality.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use log::{self, debug};
|
use log::{self, debug};
|
||||||
|
@ -6,37 +8,81 @@ use serde::Deserialize;
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{Limit, LimitType},
|
|
||||||
errors::{ChorusError, ChorusResult},
|
errors::{ChorusError, ChorusResult},
|
||||||
instance::UserMeta,
|
instance::ChorusUser,
|
||||||
types::{types::subconfigs::limits::rates::RateLimits, LimitsConfiguration},
|
types::{types::subconfigs::limits::rates::RateLimits, Limit, LimitType, LimitsConfiguration},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Chorus' request struct. This struct is used to send rate-limited requests to the Spacebar server.
|
/// Chorus' request struct. This struct is used to send rate-limited requests to the Spacebar server.
|
||||||
/// 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)]
|
||||||
pub struct ChorusRequest {
|
pub struct ChorusRequest {
|
||||||
pub request: RequestBuilder,
|
pub request: RequestBuilder,
|
||||||
pub limit_type: LimitType,
|
pub limit_type: LimitType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChorusRequest {
|
impl ChorusRequest {
|
||||||
|
/// Makes a new [`ChorusRequest`].
|
||||||
|
/// # Arguments
|
||||||
|
/// * `method` - The HTTP method to use. Must be one of the following:
|
||||||
|
/// * [`http::Method::GET`]
|
||||||
|
/// * [`http::Method::POST`]
|
||||||
|
/// * [`http::Method::PUT`]
|
||||||
|
/// * [`http::Method::DELETE`]
|
||||||
|
/// * [`http::Method::PATCH`]
|
||||||
|
/// * [`http::Method::HEAD`]
|
||||||
|
#[allow(unused_variables)] // TODO: Add mfa_token to request, once we figure out *how* to do so correctly
|
||||||
|
pub fn new(
|
||||||
|
method: http::Method,
|
||||||
|
url: &str,
|
||||||
|
body: Option<String>,
|
||||||
|
audit_log_reason: Option<&str>,
|
||||||
|
mfa_token: Option<&str>,
|
||||||
|
chorus_user: Option<&mut ChorusUser>,
|
||||||
|
limit_type: LimitType,
|
||||||
|
) -> ChorusRequest {
|
||||||
|
let request = Client::new();
|
||||||
|
let mut request = match method {
|
||||||
|
http::Method::GET => request.get(url),
|
||||||
|
http::Method::POST => request.post(url),
|
||||||
|
http::Method::PUT => request.put(url),
|
||||||
|
http::Method::DELETE => request.delete(url),
|
||||||
|
http::Method::PATCH => request.patch(url),
|
||||||
|
http::Method::HEAD => request.head(url),
|
||||||
|
_ => panic!("Illegal state: Method not supported."),
|
||||||
|
};
|
||||||
|
if let Some(user) = chorus_user {
|
||||||
|
request = request.header("Authorization", user.token());
|
||||||
|
}
|
||||||
|
if let Some(body) = body {
|
||||||
|
// ONCE TOLD ME THE WORLD WAS GONNA ROLL ME
|
||||||
|
request = request
|
||||||
|
.body(body)
|
||||||
|
.header("Content-Type", "application/json");
|
||||||
|
}
|
||||||
|
if let Some(reason) = audit_log_reason {
|
||||||
|
request = request.header("X-Audit-Log-Reason", reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChorusRequest {
|
||||||
|
request,
|
||||||
|
limit_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sends a [`ChorusRequest`]. Checks if the user is rate limited, and if not, sends the request.
|
/// Sends a [`ChorusRequest`]. Checks if the user is rate limited, and if not, sends the request.
|
||||||
/// If the user is not rate limited and the instance has rate limits enabled, it will update the
|
/// If the user is not rate limited and the instance has rate limits enabled, it will update the
|
||||||
/// rate limits.
|
/// rate limits.
|
||||||
#[allow(clippy::await_holding_refcell_ref)]
|
#[allow(clippy::await_holding_refcell_ref)]
|
||||||
pub(crate) async fn send_request(self, user: &mut UserMeta) -> ChorusResult<Response> {
|
pub(crate) async fn send_request(self, user: &mut ChorusUser) -> ChorusResult<Response> {
|
||||||
if !ChorusRequest::can_send_request(user, &self.limit_type) {
|
if !ChorusRequest::can_send_request(user, &self.limit_type) {
|
||||||
log::info!("Rate limit hit. Bucket: {:?}", self.limit_type);
|
log::info!("Rate limit hit. Bucket: {:?}", self.limit_type);
|
||||||
return Err(ChorusError::RateLimited {
|
return Err(ChorusError::RateLimited {
|
||||||
bucket: format!("{:?}", self.limit_type),
|
bucket: format!("{:?}", self.limit_type),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let belongs_to = user.belongs_to.borrow();
|
let client = user.belongs_to.read().unwrap().client.clone();
|
||||||
let result = match belongs_to
|
let result = match client.execute(self.request.build().unwrap()).await {
|
||||||
.client
|
|
||||||
.execute(self.request.build().unwrap())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
debug!("Request successful: {:?}", result);
|
debug!("Request successful: {:?}", result);
|
||||||
result
|
result
|
||||||
|
@ -45,16 +91,17 @@ impl ChorusRequest {
|
||||||
log::warn!("Request failed: {:?}", error);
|
log::warn!("Request failed: {:?}", error);
|
||||||
return Err(ChorusError::RequestFailed {
|
return Err(ChorusError::RequestFailed {
|
||||||
url: error.url().unwrap().to_string(),
|
url: error.url().unwrap().to_string(),
|
||||||
error,
|
error: error.to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
drop(belongs_to);
|
drop(client);
|
||||||
if !result.status().is_success() {
|
if !result.status().is_success() {
|
||||||
if result.status().as_u16() == 429 {
|
if result.status().as_u16() == 429 {
|
||||||
log::warn!("Rate limit hit unexpectedly. Bucket: {:?}. Setting the instances' remaining global limit to 0 to have cooldown.", self.limit_type);
|
log::warn!("Rate limit hit unexpectedly. Bucket: {:?}. Setting the instances' remaining global limit to 0 to have cooldown.", self.limit_type);
|
||||||
user.belongs_to
|
user.belongs_to
|
||||||
.borrow_mut()
|
.write()
|
||||||
|
.unwrap()
|
||||||
.limits_information
|
.limits_information
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -73,9 +120,9 @@ impl ChorusRequest {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_send_request(user: &mut UserMeta, limit_type: &LimitType) -> bool {
|
fn can_send_request(user: &mut ChorusUser, limit_type: &LimitType) -> bool {
|
||||||
log::trace!("Checking if user or instance is rate-limited...");
|
log::trace!("Checking if user or instance is rate-limited...");
|
||||||
let mut belongs_to = user.belongs_to.borrow_mut();
|
let mut belongs_to = user.belongs_to.write().unwrap();
|
||||||
if belongs_to.limits_information.is_none() {
|
if belongs_to.limits_information.is_none() {
|
||||||
log::trace!("Instance indicates no rate limits are configured. Continuing.");
|
log::trace!("Instance indicates no rate limits are configured. Continuing.");
|
||||||
return true;
|
return true;
|
||||||
|
@ -236,7 +283,10 @@ impl ChorusRequest {
|
||||||
/// set to the current unix timestamp + the rate limit window. The remaining rate limit is
|
/// set to the current unix timestamp + the rate limit window. The remaining rate limit is
|
||||||
/// reset to the rate limit limit.
|
/// reset to the rate limit limit.
|
||||||
/// 2. The remaining rate limit is decreased by 1.
|
/// 2. The remaining rate limit is decreased by 1.
|
||||||
fn update_rate_limits(user: &mut UserMeta, limit_type: &LimitType, response_was_err: bool) {
|
fn update_rate_limits(user: &mut ChorusUser, limit_type: &LimitType, response_was_err: bool) {
|
||||||
|
if user.belongs_to.read().unwrap().limits_information.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let instance_dictated_limits = [
|
let instance_dictated_limits = [
|
||||||
&LimitType::AuthLogin,
|
&LimitType::AuthLogin,
|
||||||
&LimitType::AuthRegister,
|
&LimitType::AuthRegister,
|
||||||
|
@ -257,7 +307,7 @@ impl ChorusRequest {
|
||||||
}
|
}
|
||||||
let time: u64 = chrono::Utc::now().timestamp() as u64;
|
let time: u64 = chrono::Utc::now().timestamp() as u64;
|
||||||
for relevant_limit in relevant_limits.iter() {
|
for relevant_limit in relevant_limits.iter() {
|
||||||
let mut belongs_to = user.belongs_to.borrow_mut();
|
let mut belongs_to = user.belongs_to.write().unwrap();
|
||||||
let limit = match relevant_limit.0 {
|
let limit = match relevant_limit.0 {
|
||||||
LimitOrigin::Instance => {
|
LimitOrigin::Instance => {
|
||||||
log::trace!(
|
log::trace!(
|
||||||
|
@ -292,6 +342,13 @@ impl ChorusRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the ratelimit configuration.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// This is a spacebar only endpoint.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.spacebar.chat/routes/#get-/policies/instance/limits/>
|
||||||
pub(crate) async fn get_limits_config(url_api: &str) -> ChorusResult<LimitsConfiguration> {
|
pub(crate) async fn get_limits_config(url_api: &str) -> ChorusResult<LimitsConfiguration> {
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.get(format!("{}/policies/instance/limits/", url_api))
|
.get(format!("{}/policies/instance/limits/", url_api))
|
||||||
|
@ -302,7 +359,7 @@ impl ChorusRequest {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ChorusError::RequestFailed {
|
return Err(ChorusError::RequestFailed {
|
||||||
url: url_api.to_string(),
|
url: url_api.to_string(),
|
||||||
error: e,
|
error: e.to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -419,7 +476,7 @@ impl ChorusRequest {
|
||||||
|
|
||||||
/// Sends a [`ChorusRequest`] and returns a [`ChorusResult`] that contains nothing if the request
|
/// Sends a [`ChorusRequest`] and returns a [`ChorusResult`] that contains nothing if the request
|
||||||
/// was successful, or a [`ChorusError`] if the request failed.
|
/// was successful, or a [`ChorusError`] if the request failed.
|
||||||
pub(crate) async fn handle_request_as_result(self, user: &mut UserMeta) -> ChorusResult<()> {
|
pub(crate) async fn handle_request_as_result(self, user: &mut ChorusUser) -> ChorusResult<()> {
|
||||||
match self.send_request(user).await {
|
match self.send_request(user).await {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
|
@ -430,7 +487,7 @@ impl ChorusRequest {
|
||||||
/// was successful, or a [`ChorusError`] if the request failed.
|
/// was successful, or a [`ChorusError`] if the request failed.
|
||||||
pub(crate) async fn deserialize_response<T: for<'a> Deserialize<'a>>(
|
pub(crate) async fn deserialize_response<T: for<'a> Deserialize<'a>>(
|
||||||
self,
|
self,
|
||||||
user: &mut UserMeta,
|
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);
|
debug!("Got response: {:?}", response);
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::types::config::types::subconfigs::guild::{
|
||||||
};
|
};
|
||||||
use crate::types::{Error, GuildError};
|
use crate::types::{Error, GuildError};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
pub enum GuildFeatures {
|
pub enum GuildFeatures {
|
||||||
ActivitiesAlpha,
|
ActivitiesAlpha,
|
||||||
|
@ -139,7 +139,7 @@ pub enum GuildFeatures {
|
||||||
InvitesClosed,
|
InvitesClosed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq)]
|
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq, Hash)]
|
||||||
pub struct GuildFeaturesList(Vec<GuildFeatures>);
|
pub struct GuildFeaturesList(Vec<GuildFeatures>);
|
||||||
|
|
||||||
impl Deref for GuildFeaturesList {
|
impl Deref for GuildFeaturesList {
|
||||||
|
|
|
@ -2,11 +2,9 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::types::{
|
||||||
api::LimitType,
|
config::types::subconfigs::limits::ratelimits::{route::RouteRateLimit, RateLimitOptions},
|
||||||
types::config::types::subconfigs::limits::ratelimits::{
|
LimitType,
|
||||||
route::RouteRateLimit, RateLimitOptions,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
@ -6,8 +8,10 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
use crate::types::{Team, User};
|
use crate::types::{Team, User};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/resources/application#application-resource>
|
||||||
pub struct Application {
|
pub struct Application {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -23,7 +27,7 @@ pub struct Application {
|
||||||
pub bot_require_code_grant: bool,
|
pub bot_require_code_grant: bool,
|
||||||
pub verify_key: String,
|
pub verify_key: String,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub owner: User,
|
pub owner: Arc<RwLock<User>>,
|
||||||
pub flags: u64,
|
pub flags: u64,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>,
|
pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>,
|
||||||
|
@ -45,7 +49,7 @@ pub struct Application {
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub install_params: Option<sqlx::types::Json<InstallParams>>,
|
pub install_params: Option<sqlx::types::Json<InstallParams>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
pub install_params: Option<InstallParams>,
|
pub install_params: Option<Arc<RwLock<InstallParams>>>,
|
||||||
pub terms_of_service_url: Option<String>,
|
pub terms_of_service_url: Option<String>,
|
||||||
pub privacy_policy_url: Option<String>,
|
pub privacy_policy_url: Option<String>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
|
@ -93,44 +97,64 @@ impl Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/resources/application#install-params-object>
|
||||||
pub struct InstallParams {
|
pub struct InstallParams {
|
||||||
pub scopes: Vec<String>,
|
pub scopes: Vec<String>,
|
||||||
pub permissions: String,
|
pub permissions: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/resources/application#application-object-application-flags>
|
||||||
pub struct ApplicationFlags: u64 {
|
pub struct ApplicationFlags: u64 {
|
||||||
|
/// Indicates if an app uses the Auto Moderation API
|
||||||
const APPLICATION_AUTO_MODERATION_RULE_CREATE_BADGE = 1 << 6;
|
const APPLICATION_AUTO_MODERATION_RULE_CREATE_BADGE = 1 << 6;
|
||||||
|
/// Intent required for bots in 100 or more servers to receive presence_update events
|
||||||
const GATEWAY_PRESENCE = 1 << 12;
|
const GATEWAY_PRESENCE = 1 << 12;
|
||||||
|
/// Intent required for bots in under 100 servers to receive presence_update events, found on the Bot page in your app's settings on discord.com
|
||||||
const GATEWAY_PRESENCE_LIMITED = 1 << 13;
|
const GATEWAY_PRESENCE_LIMITED = 1 << 13;
|
||||||
|
/// Intent required for bots in 100 or more servers to receive member-related events like guild_member_add.
|
||||||
|
/// See the list of member-related events under GUILD_MEMBERS
|
||||||
const GATEWAY_GUILD_MEMBERS = 1 << 14;
|
const GATEWAY_GUILD_MEMBERS = 1 << 14;
|
||||||
|
/// Intent required for bots in under 100 servers to receive member-related events like guild_member_add, found on the Bot page in your app's settings on discord.com.
|
||||||
|
/// See the list of member-related events under GUILD_MEMBERS
|
||||||
const GATEWAY_GUILD_MEMBERS_LIMITED = 1 << 15;
|
const GATEWAY_GUILD_MEMBERS_LIMITED = 1 << 15;
|
||||||
|
/// Indicates unusual growth of an app that prevents verification
|
||||||
const VERIFICATION_PENDING_GUILD_LIMIT = 1 << 16;
|
const VERIFICATION_PENDING_GUILD_LIMIT = 1 << 16;
|
||||||
|
/// Indicates if an app is embedded within the Discord client (currently unavailable publicly)
|
||||||
const EMBEDDED = 1 << 17;
|
const EMBEDDED = 1 << 17;
|
||||||
|
/// Intent required for bots in 100 or more servers to receive message content
|
||||||
const GATEWAY_MESSAGE_CONTENT = 1 << 18;
|
const GATEWAY_MESSAGE_CONTENT = 1 << 18;
|
||||||
|
/// Intent required for bots in under 100 servers to receive message content, found on the Bot page in your app's settings on discord.com
|
||||||
const GATEWAY_MESSAGE_CONTENT_LIMITED = 1 << 19;
|
const GATEWAY_MESSAGE_CONTENT_LIMITED = 1 << 19;
|
||||||
|
/// Indicates if an app has registered slash commands
|
||||||
const APPLICATION_COMMAND_BADGE = 1 << 23;
|
const APPLICATION_COMMAND_BADGE = 1 << 23;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-object>
|
||||||
pub struct ApplicationCommand {
|
pub struct ApplicationCommand {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub application_id: Snowflake,
|
pub application_id: Snowflake,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub options: Vec<ApplicationCommandOption>,
|
pub options: Vec<Arc<RwLock<ApplicationCommandOption>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
/// Reference
|
||||||
|
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure>
|
||||||
pub struct ApplicationCommandOption {
|
pub struct ApplicationCommandOption {
|
||||||
pub r#type: ApplicationCommandOptionType,
|
pub r#type: ApplicationCommandOptionType,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub required: bool,
|
pub required: bool,
|
||||||
pub choices: Vec<ApplicationCommandOptionChoice>,
|
pub choices: Vec<ApplicationCommandOptionChoice>,
|
||||||
pub options: Vec<ApplicationCommandOption>,
|
pub options: Arc<RwLock<Vec<ApplicationCommandOption>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -139,45 +163,54 @@ pub struct ApplicationCommandOptionChoice {
|
||||||
pub value: Value,
|
pub value: Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize_repr, Deserialize_repr)]
|
#[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)]
|
#[repr(i32)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-types>
|
||||||
pub enum ApplicationCommandOptionType {
|
pub enum ApplicationCommandOptionType {
|
||||||
SubCommand = 1,
|
SubCommand = 1,
|
||||||
SubCommandGroup = 2,
|
SubCommandGroup = 2,
|
||||||
String = 3,
|
String = 3,
|
||||||
|
/// Any integer between -2^53 and 2^53
|
||||||
Integer = 4,
|
Integer = 4,
|
||||||
Boolean = 5,
|
Boolean = 5,
|
||||||
User = 6,
|
User = 6,
|
||||||
|
/// Includes all channel types + categories
|
||||||
Channel = 7,
|
Channel = 7,
|
||||||
Role = 8,
|
Role = 8,
|
||||||
|
/// Includes users and roles
|
||||||
|
Mentionable = 9,
|
||||||
|
/// Any double between -2^53 and 2^53
|
||||||
|
Number = 10,
|
||||||
|
Attachment = 11,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ApplicationCommandInteractionData {
|
pub struct ApplicationCommandInteractionData {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub options: Vec<ApplicationCommandInteractionDataOption>,
|
pub options: Vec<Arc<RwLock<ApplicationCommandInteractionDataOption>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ApplicationCommandInteractionDataOption {
|
pub struct ApplicationCommandInteractionDataOption {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub value: Value,
|
pub value: Value,
|
||||||
pub options: Vec<ApplicationCommandInteractionDataOption>,
|
pub options: Vec<Arc<RwLock<ApplicationCommandInteractionDataOption>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||||
/// See https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure
|
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure>
|
||||||
pub struct GuildApplicationCommandPermissions {
|
pub struct GuildApplicationCommandPermissions {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub application_id: Snowflake,
|
pub application_id: Snowflake,
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
pub permissions: Vec<ApplicationCommandPermission>,
|
pub permissions: Vec<Arc<RwLock<ApplicationCommandPermission>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
/// 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,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -186,10 +219,10 @@ pub struct ApplicationCommandPermission {
|
||||||
pub permission: bool,
|
pub permission: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq, Eq, Hash)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
/// 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]
|
||||||
Role = 1,
|
Role = 1,
|
||||||
|
|
|
@ -2,11 +2,14 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, PartialOrd)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/resources/channel#attachment-object>
|
||||||
pub struct Attachment {
|
pub struct Attachment {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
|
/// 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: u64,
|
||||||
|
@ -15,17 +18,24 @@ pub struct Attachment {
|
||||||
pub height: Option<u64>,
|
pub height: Option<u64>,
|
||||||
pub width: Option<u64>,
|
pub width: Option<u64>,
|
||||||
pub ephemeral: Option<bool>,
|
pub ephemeral: Option<bool>,
|
||||||
|
/// The duration of the audio file (only for voice messages)
|
||||||
pub duration_secs: Option<f32>,
|
pub duration_secs: Option<f32>,
|
||||||
|
/// A Base64 encoded bytearray representing a sampled waveform (only for voice messages)
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// Note that this is computed on the client side.
|
||||||
|
/// This means it can be spoofed and isn't necessarily accurate.
|
||||||
pub waveform: Option<String>,
|
pub waveform: Option<String>,
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(default))]
|
#[cfg_attr(feature = "sqlx", sqlx(default))]
|
||||||
pub content: Option<Vec<u8>>,
|
pub content: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct PartialDiscordFileAttachment {
|
pub struct PartialDiscordFileAttachment {
|
||||||
pub id: Option<i16>,
|
pub id: Option<i16>,
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
|
/// 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<i64>,
|
||||||
|
@ -34,18 +44,20 @@ pub struct PartialDiscordFileAttachment {
|
||||||
pub height: Option<i32>,
|
pub height: Option<i32>,
|
||||||
pub width: Option<i32>,
|
pub width: Option<i32>,
|
||||||
pub ephemeral: Option<bool>,
|
pub ephemeral: Option<bool>,
|
||||||
|
/// The duration of the audio file (only for voice messages)
|
||||||
pub duration_secs: Option<f32>,
|
pub duration_secs: Option<f32>,
|
||||||
|
/// A Base64 encoded bytearray representing a sampled waveform (only for voice messages)
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// Note that this is computed on the client side.
|
||||||
|
/// This means it can be spoofed and isn't necessarily accurate.
|
||||||
pub waveform: Option<String>,
|
pub waveform: Option<String>,
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
pub content: Vec<u8>,
|
pub content: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialDiscordFileAttachment {
|
impl PartialDiscordFileAttachment {
|
||||||
/**
|
/// Moves `self.content` out of `self` and returns it.
|
||||||
Moves `self.content` out of `self` and returns it.
|
|
||||||
# Returns
|
|
||||||
Vec<u8>
|
|
||||||
*/
|
|
||||||
pub fn move_content(self) -> (Vec<u8>, PartialDiscordFileAttachment) {
|
pub fn move_content(self) -> (Vec<u8>, PartialDiscordFileAttachment) {
|
||||||
let content = self.content;
|
let content = self.content;
|
||||||
let updated_struct = PartialDiscordFileAttachment {
|
let updated_struct = PartialDiscordFileAttachment {
|
||||||
|
@ -66,6 +78,7 @@ impl PartialDiscordFileAttachment {
|
||||||
(content, updated_struct)
|
(content, updated_struct)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Moves `self.filename` out of `self` and returns it.
|
||||||
pub fn move_filename(self) -> (String, PartialDiscordFileAttachment) {
|
pub fn move_filename(self) -> (String, PartialDiscordFileAttachment) {
|
||||||
let filename = self.filename;
|
let filename = self.filename;
|
||||||
let updated_struct = PartialDiscordFileAttachment {
|
let updated_struct = PartialDiscordFileAttachment {
|
||||||
|
@ -87,6 +100,7 @@ impl PartialDiscordFileAttachment {
|
||||||
(filename, updated_struct)
|
(filename, updated_struct)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Moves `self.content_type` out of `self` and returns it.
|
||||||
pub fn move_content_type(self) -> (Option<String>, PartialDiscordFileAttachment) {
|
pub fn move_content_type(self) -> (Option<String>, PartialDiscordFileAttachment) {
|
||||||
let content_type = self.content_type;
|
let content_type = self.content_type;
|
||||||
let updated_struct = PartialDiscordFileAttachment {
|
let updated_struct = PartialDiscordFileAttachment {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
/// 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>,
|
||||||
pub changes: Option<Vec<AuditLogChange>>,
|
pub changes: Option<Vec<Arc<RwLock<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
|
// to:do implement an enum for these types
|
||||||
|
@ -17,7 +19,7 @@ pub struct AuditLogEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 {
|
||||||
pub new_value: Option<serde_json::Value>,
|
pub new_value: Option<serde_json::Value>,
|
||||||
pub old_value: Option<serde_json::Value>,
|
pub old_value: Option<serde_json::Value>,
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::Updateable;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use chorus_macros::Updateable;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "client", derive(Updateable))]
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object>
|
||||||
pub struct AutoModerationRule {
|
pub struct AutoModerationRule {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
|
@ -12,8 +21,8 @@ pub struct AutoModerationRule {
|
||||||
pub creator_id: Snowflake,
|
pub creator_id: Snowflake,
|
||||||
pub event_type: AutoModerationRuleEventType,
|
pub event_type: AutoModerationRuleEventType,
|
||||||
pub trigger_type: AutoModerationRuleTriggerType,
|
pub trigger_type: AutoModerationRuleTriggerType,
|
||||||
pub trigger_metadata: AutoModerationRuleTriggerMetadata,
|
pub trigger_metadata: Arc<RwLock<AutoModerationRuleTriggerMetadata>>,
|
||||||
pub actions: Vec<AutoModerationAction>,
|
pub actions: Vec<Arc<RwLock<AutoModerationAction>>>,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub exempt_roles: Vec<Snowflake>,
|
pub exempt_roles: Vec<Snowflake>,
|
||||||
pub exempt_channels: Vec<Snowflake>,
|
pub exempt_channels: Vec<Snowflake>,
|
||||||
|
@ -22,7 +31,7 @@ pub struct AutoModerationRule {
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[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 {
|
||||||
#[default]
|
#[default]
|
||||||
MessageSend = 1,
|
MessageSend = 1,
|
||||||
|
@ -31,7 +40,7 @@ pub enum AutoModerationRuleEventType {
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[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 {
|
||||||
#[default]
|
#[default]
|
||||||
Keyword = 1,
|
Keyword = 1,
|
||||||
|
@ -42,7 +51,7 @@ pub enum AutoModerationRuleTriggerType {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
#[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 {
|
||||||
ForKeyword(AutoModerationRuleTriggerMetadataForKeyword),
|
ForKeyword(AutoModerationRuleTriggerMetadataForKeyword),
|
||||||
ForKeywordPreset(AutoModerationRuleTriggerMetadataForKeywordPreset),
|
ForKeywordPreset(AutoModerationRuleTriggerMetadataForKeywordPreset),
|
||||||
|
@ -52,7 +61,7 @@ pub enum AutoModerationRuleTriggerMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
/// 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>,
|
||||||
pub regex_patterns: Vec<String>,
|
pub regex_patterns: Vec<String>,
|
||||||
|
@ -60,14 +69,14 @@ pub struct AutoModerationRuleTriggerMetadataForKeyword {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
/// 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)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
/// 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: u8,
|
||||||
|
@ -77,7 +86,7 @@ pub struct AutoModerationRuleTriggerMetadataForMentionSpam {
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[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 {
|
||||||
#[default]
|
#[default]
|
||||||
Profanity = 1,
|
Profanity = 1,
|
||||||
|
@ -86,17 +95,17 @@ pub enum AutoModerationRuleKeywordPresetType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
/// See https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object>
|
||||||
pub struct AutoModerationAction {
|
pub struct AutoModerationAction {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub action_type: AutoModerationActionType,
|
pub action_type: AutoModerationActionType,
|
||||||
pub metadata: Option<AutoModerationActionMetadata>,
|
pub metadata: Option<Arc<RwLock<AutoModerationActionMetadata>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[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 {
|
||||||
#[default]
|
#[default]
|
||||||
BlockMessage = 1,
|
BlockMessage = 1,
|
||||||
|
@ -106,7 +115,7 @@ pub enum AutoModerationActionType {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
#[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 {
|
||||||
ForBlockMessage(AutoModerationActionMetadataForBlockMessage),
|
ForBlockMessage(AutoModerationActionMetadataForBlockMessage),
|
||||||
ForSendAlertMessage(AutoModerationActionMetadataForSendAlertMessage),
|
ForSendAlertMessage(AutoModerationActionMetadataForSendAlertMessage),
|
||||||
|
@ -116,19 +125,19 @@ pub enum AutoModerationActionMetadata {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
/// 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)]
|
||||||
/// 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)]
|
||||||
/// 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
|
||||||
pub duration_seconds: u32,
|
pub duration_seconds: u32,
|
||||||
|
|
|
@ -1,17 +1,32 @@
|
||||||
use chorus_macros::Updateable;
|
use std::sync::{Arc, RwLock};
|
||||||
use chrono::Utc;
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_aux::prelude::deserialize_string_from_number;
|
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 crate::gateway::Updateable;
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
entities::{GuildMember, User},
|
entities::{GuildMember, User},
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Updateable)]
|
#[cfg(feature = "client")]
|
||||||
|
use crate::types::Composite;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::{GatewayHandle, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use chorus_macros::{observe_option_vec, Composite, Updateable};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
|
/// Represents a guild or private channel
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// 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")]
|
#[cfg(feature = "sqlx")]
|
||||||
|
@ -39,7 +54,7 @@ pub struct Channel {
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub last_message_id: Option<Snowflake>,
|
pub last_message_id: Option<Snowflake>,
|
||||||
pub last_pin_timestamp: Option<String>,
|
pub last_pin_timestamp: Option<DateTime<Utc>>,
|
||||||
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>,
|
||||||
|
@ -52,12 +67,14 @@ pub struct Channel {
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub permission_overwrites: Option<sqlx::types::Json<Vec<PermissionOverwrite>>>,
|
pub permission_overwrites: Option<sqlx::types::Json<Vec<PermissionOverwrite>>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
|
#[cfg_attr(feature = "client", observe_option_vec)]
|
||||||
|
pub permission_overwrites: Option<Vec<Arc<RwLock<PermissionOverwrite>>>>,
|
||||||
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>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub recipients: Option<Vec<User>>,
|
#[cfg_attr(feature = "client", observe_option_vec)]
|
||||||
|
pub recipients: Option<Vec<Arc<RwLock<User>>>>,
|
||||||
pub rtc_region: Option<String>,
|
pub rtc_region: Option<String>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub thread_metadata: Option<ThreadMetadata>,
|
pub thread_metadata: Option<ThreadMetadata>,
|
||||||
|
@ -67,16 +84,57 @@ pub struct Channel {
|
||||||
pub video_quality_mode: Option<i32>,
|
pub video_quality_mode: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
impl PartialEq for Channel {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.application_id == other.application_id
|
||||||
|
&& self.bitrate == other.bitrate
|
||||||
|
&& self.channel_type == other.channel_type
|
||||||
|
&& self.created_at == other.created_at
|
||||||
|
&& self.default_auto_archive_duration == other.default_auto_archive_duration
|
||||||
|
&& self.default_forum_layout == other.default_forum_layout
|
||||||
|
&& self.default_sort_order == other.default_sort_order
|
||||||
|
&& self.default_thread_rate_limit_per_user == other.default_thread_rate_limit_per_user
|
||||||
|
&& self.flags == other.flags
|
||||||
|
&& self.guild_id == other.guild_id
|
||||||
|
&& self.icon == other.icon
|
||||||
|
&& self.id == other.id
|
||||||
|
&& self.last_message_id == other.last_message_id
|
||||||
|
&& self.last_pin_timestamp == other.last_pin_timestamp
|
||||||
|
&& self.managed == other.managed
|
||||||
|
&& self.member_count == other.member_count
|
||||||
|
&& self.message_count == other.message_count
|
||||||
|
&& self.name == other.name
|
||||||
|
&& self.nsfw == other.nsfw
|
||||||
|
&& self.owner_id == other.owner_id
|
||||||
|
&& self.parent_id == other.parent_id
|
||||||
|
&& self.permissions == other.permissions
|
||||||
|
&& self.position == other.position
|
||||||
|
&& self.rate_limit_per_user == other.rate_limit_per_user
|
||||||
|
&& self.rtc_region == other.rtc_region
|
||||||
|
&& self.topic == other.topic
|
||||||
|
&& self.total_message_sent == other.total_message_sent
|
||||||
|
&& self.user_limit == other.user_limit
|
||||||
|
&& self.video_quality_mode == other.video_quality_mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
/// A tag that can be applied to a thread in a [ChannelType::GuildForum] or [ChannelType::GuildMedia] channel.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#forum-tag-object>
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
|
/// The name of the tag (max 20 characters)
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// Whether this tag can only be added to or removed from threads by members with the [MANAGE_THREADS](crate::types::PermissionFlags::MANAGE_THREADS) permission
|
||||||
pub moderated: bool,
|
pub moderated: bool,
|
||||||
pub emoji_id: Option<Snowflake>,
|
pub emoji_id: Option<Snowflake>,
|
||||||
pub emoji_name: Option<String>,
|
pub emoji_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd)]
|
||||||
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
pub struct PermissionOverwrite {
|
pub struct PermissionOverwrite {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -91,6 +149,8 @@ pub struct PermissionOverwrite {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
|
/// # Reference
|
||||||
|
/// 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,
|
||||||
|
@ -100,47 +160,93 @@ pub struct ThreadMetadata {
|
||||||
pub create_timestamp: Option<String>,
|
pub create_timestamp: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#thread-member-object>
|
||||||
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<String>,
|
||||||
pub flags: Option<u64>,
|
pub flags: Option<u64>,
|
||||||
pub member: Option<GuildMember>,
|
pub member: Option<Arc<RwLock<GuildMember>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
|
/// Specifies the emoji to use as the default way to react to a [ChannelType::GuildForum] or [ChannelType::GuildMedia] channel post.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#default-reaction-object>
|
||||||
pub struct DefaultReaction {
|
pub struct DefaultReaction {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub emoji_id: Option<Snowflake>,
|
pub emoji_id: Option<Snowflake>,
|
||||||
pub emoji_name: Option<String>,
|
pub emoji_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, Debug, Serialize_repr, Deserialize_repr, PartialEq, Eq)]
|
#[derive(
|
||||||
|
Default,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
)]
|
||||||
#[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(i32)]
|
#[repr(u32)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#channel-type>
|
||||||
pub enum ChannelType {
|
pub enum ChannelType {
|
||||||
#[default]
|
#[default]
|
||||||
|
/// A text channel within a guild
|
||||||
GuildText = 0,
|
GuildText = 0,
|
||||||
|
/// A private channel between two users
|
||||||
Dm = 1,
|
Dm = 1,
|
||||||
|
/// A voice channel within a guild
|
||||||
GuildVoice = 2,
|
GuildVoice = 2,
|
||||||
|
/// A private channel between multiple users
|
||||||
GroupDm = 3,
|
GroupDm = 3,
|
||||||
|
/// An organizational category that contains up to 50 channels
|
||||||
GuildCategory = 4,
|
GuildCategory = 4,
|
||||||
|
/// Similar to [GuildText](ChannelType::GuildText), a channel that users can follow and crosspost into their own guild
|
||||||
GuildNews = 5,
|
GuildNews = 5,
|
||||||
|
/// A channel in which game developers can sell their game on Discord
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// Deprecated.
|
||||||
GuildStore = 6,
|
GuildStore = 6,
|
||||||
|
// FIXME userdoccers says 7 is GuildLfg, is this a spacebar specific thing?
|
||||||
Encrypted = 7,
|
Encrypted = 7,
|
||||||
|
// FIXME userdoccers says 8 is LfgGuildDm, is this a spacebar specific thing?
|
||||||
EncryptedThreads = 8,
|
EncryptedThreads = 8,
|
||||||
|
// FIXME userdoccers says 9 is ThreadAlpha, was this changed?
|
||||||
Transactional = 9,
|
Transactional = 9,
|
||||||
|
/// A thread within a [GuildNews](ChannelType::GuildNews) channel
|
||||||
GuildNewsThread = 10,
|
GuildNewsThread = 10,
|
||||||
|
/// A thread within a [GuildText](ChannelType::GuildText), [GuildForum](ChannelType::GuildForum), or [GuildMedia](ChannelType::GuildMedia) channel
|
||||||
GuildPublicThread = 11,
|
GuildPublicThread = 11,
|
||||||
|
/// A thread within a [GuildText](ChannelType::GuildText) channel, that is only viewable by those invited and those with the [MANAGE_THREADS](crate::types::entities::PermissionFlags::MANAGE_THREADS) permission
|
||||||
GuildPrivateThread = 12,
|
GuildPrivateThread = 12,
|
||||||
|
/// A voice channel for hosting events with an audience in a guild
|
||||||
GuildStageVoice = 13,
|
GuildStageVoice = 13,
|
||||||
|
/// The main channel in a hub containing the listed guilds
|
||||||
Directory = 14,
|
Directory = 14,
|
||||||
|
/// A channel that can only contain threads
|
||||||
GuildForum = 15,
|
GuildForum = 15,
|
||||||
|
/// A channel that can only contain threads in a gallery view
|
||||||
|
GuildMedia = 16,
|
||||||
|
// TODO: Couldn't find reference
|
||||||
TicketTracker = 33,
|
TicketTracker = 33,
|
||||||
|
// TODO: Couldn't find reference
|
||||||
Kanban = 34,
|
Kanban = 34,
|
||||||
|
// TODO: Couldn't find reference
|
||||||
VoicelessWhiteboard = 35,
|
VoicelessWhiteboard = 35,
|
||||||
|
// TODO: Couldn't find reference
|
||||||
CustomStart = 64,
|
CustomStart = 64,
|
||||||
|
// TODO: Couldn't find reference
|
||||||
Unhandled = 255,
|
Unhandled = 255,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,95 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::entities::User;
|
use crate::types::entities::User;
|
||||||
use crate::types::Snowflake;
|
use crate::types::Snowflake;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
|
#[cfg(feature = "client")]
|
||||||
|
use crate::types::Composite;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::{GatewayHandle, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use chorus_macros::{Composite, Updateable};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
||||||
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/emoji#emoji-object>
|
||||||
pub struct Emoji {
|
pub struct Emoji {
|
||||||
pub id: Option<Snowflake>,
|
pub id: Snowflake,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub roles: Option<sqlx::types::Json<Vec<Snowflake>>>,
|
pub roles: Option<sqlx::types::Json<Vec<Snowflake>>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[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<User>,
|
pub user: Option<Arc<RwLock<User>>>,
|
||||||
pub require_colons: Option<bool>,
|
pub require_colons: Option<bool>,
|
||||||
pub managed: Option<bool>,
|
pub managed: Option<bool>,
|
||||||
pub animated: Option<bool>,
|
pub animated: Option<bool>,
|
||||||
pub available: Option<bool>,
|
pub available: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::hash::Hash for Emoji {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.id.hash(state);
|
||||||
|
self.name.hash(state);
|
||||||
|
self.roles.hash(state);
|
||||||
|
self.roles.hash(state);
|
||||||
|
self.require_colons.hash(state);
|
||||||
|
self.managed.hash(state);
|
||||||
|
self.animated.hash(state);
|
||||||
|
self.available.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Emoji {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
!(self.id != other.id
|
||||||
|
|| self.name != other.name
|
||||||
|
|| self.roles != other.roles
|
||||||
|
|| self.require_colons != other.require_colons
|
||||||
|
|| self.managed != other.managed
|
||||||
|
|| self.animated != other.animated
|
||||||
|
|| self.available != other.available)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Emoji {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
match self.id.partial_cmp(&other.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.roles.partial_cmp(&other.roles) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => {}
|
||||||
|
ord => return ord,
|
||||||
|
}
|
||||||
|
match self.roles.partial_cmp(&other.roles) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => {}
|
||||||
|
ord => return ord,
|
||||||
|
}
|
||||||
|
match self.require_colons.partial_cmp(&other.require_colons) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => {}
|
||||||
|
ord => return ord,
|
||||||
|
}
|
||||||
|
match self.managed.partial_cmp(&other.managed) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => {}
|
||||||
|
ord => return ord,
|
||||||
|
}
|
||||||
|
match self.animated.partial_cmp(&other.animated) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => {}
|
||||||
|
ord => return ord,
|
||||||
|
}
|
||||||
|
self.available.partial_cmp(&other.available)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
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};
|
||||||
|
@ -8,9 +11,22 @@ use crate::types::{
|
||||||
interfaces::WelcomeScreenObject,
|
interfaces::WelcomeScreenObject,
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
/// See https://discord.com/developers/docs/resources/guild
|
use super::PublicUser;
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::{GatewayHandle, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use chorus_macros::{observe_option_vec, observe_vec, Composite, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::types::Composite;
|
||||||
|
|
||||||
|
/// See <https://discord.com/developers/docs/resources/guild>
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
pub struct Guild {
|
pub struct Guild {
|
||||||
pub afk_channel_id: Option<Snowflake>,
|
pub afk_channel_id: Option<Snowflake>,
|
||||||
|
@ -25,13 +41,15 @@ pub struct Guild {
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub bans: Option<Vec<GuildBan>>,
|
pub bans: Option<Vec<GuildBan>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub channels: Option<Vec<Channel>>,
|
#[cfg_attr(feature = "client", observe_option_vec)]
|
||||||
pub default_message_notifications: Option<i32>,
|
pub channels: Option<Vec<Arc<RwLock<Channel>>>>,
|
||||||
|
pub default_message_notifications: Option<MessageNotificationLevel>,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub discovery_splash: Option<String>,
|
pub discovery_splash: Option<String>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
|
#[cfg_attr(feature = "client", observe_vec)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub emojis: Vec<Emoji>,
|
pub emojis: Vec<Arc<RwLock<Emoji>>>,
|
||||||
pub explicit_content_filter: Option<i32>,
|
pub explicit_content_filter: Option<i32>,
|
||||||
//#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))]
|
//#[cfg_attr(feature = "sqlx", sqlx(try_from = "String"))]
|
||||||
pub features: Option<GuildFeaturesList>,
|
pub features: Option<GuildFeaturesList>,
|
||||||
|
@ -49,9 +67,9 @@ pub struct Guild {
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub max_stage_video_channel_users: Option<i32>,
|
pub max_stage_video_channel_users: Option<i32>,
|
||||||
pub max_video_channel_users: Option<i32>,
|
pub max_video_channel_users: Option<i32>,
|
||||||
pub mfa_level: Option<i32>,
|
pub mfa_level: Option<MFALevel>,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub nsfw_level: Option<i32>,
|
pub nsfw_level: Option<NSFWLevel>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub owner: Option<bool>,
|
pub owner: Option<bool>,
|
||||||
// True if requesting user is owner
|
// True if requesting user is owner
|
||||||
|
@ -61,27 +79,30 @@ pub struct Guild {
|
||||||
pub preferred_locale: Option<String>,
|
pub preferred_locale: Option<String>,
|
||||||
pub premium_progress_bar_enabled: Option<bool>,
|
pub premium_progress_bar_enabled: Option<bool>,
|
||||||
pub premium_subscription_count: Option<i32>,
|
pub premium_subscription_count: Option<i32>,
|
||||||
pub premium_tier: Option<i32>,
|
pub premium_tier: Option<PremiumTier>,
|
||||||
pub primary_category_id: Option<Snowflake>,
|
pub primary_category_id: Option<Snowflake>,
|
||||||
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))]
|
||||||
pub roles: Option<Vec<RoleObject>>,
|
#[cfg_attr(feature = "client", observe_option_vec)]
|
||||||
|
pub roles: Option<Vec<Arc<RwLock<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>>,
|
pub stickers: Option<Vec<Sticker>>,
|
||||||
pub system_channel_flags: Option<i32>,
|
pub system_channel_flags: Option<u64>,
|
||||||
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<i32>,
|
pub verification_level: Option<VerificationLevel>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub voice_states: Option<Vec<VoiceState>>,
|
#[cfg_attr(feature = "client", observe_option_vec)]
|
||||||
|
pub voice_states: Option<Vec<Arc<RwLock<VoiceState>>>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub webhooks: Option<Vec<Webhook>>,
|
#[cfg_attr(feature = "client", observe_option_vec)]
|
||||||
|
pub webhooks: Option<Vec<Arc<RwLock<Webhook>>>>,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub welcome_screen: Option<sqlx::types::Json<WelcomeScreenObject>>,
|
pub welcome_screen: Option<sqlx::types::Json<WelcomeScreenObject>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
@ -90,17 +111,121 @@ pub struct Guild {
|
||||||
pub widget_enabled: Option<bool>,
|
pub widget_enabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See https://docs.spacebar.chat/routes/#get-/guilds/-guild_id-/bans/-user-
|
impl std::hash::Hash for Guild {
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
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 {
|
||||||
|
self.afk_channel_id == other.afk_channel_id
|
||||||
|
&& self.afk_timeout == other.afk_timeout
|
||||||
|
&& self.application_id == other.application_id
|
||||||
|
&& self.approximate_member_count == other.approximate_member_count
|
||||||
|
&& self.approximate_presence_count == other.approximate_presence_count
|
||||||
|
&& self.banner == other.banner
|
||||||
|
&& self.bans == other.bans
|
||||||
|
&& self.default_message_notifications == other.default_message_notifications
|
||||||
|
&& self.description == other.description
|
||||||
|
&& self.discovery_splash == other.discovery_splash
|
||||||
|
&& self.explicit_content_filter == other.explicit_content_filter
|
||||||
|
&& self.features == other.features
|
||||||
|
&& self.icon == other.icon
|
||||||
|
&& self.icon_hash == other.icon_hash
|
||||||
|
&& self.id == other.id
|
||||||
|
&& self.joined_at == other.joined_at
|
||||||
|
&& self.large == other.large
|
||||||
|
&& self.max_members == other.max_members
|
||||||
|
&& self.max_presences == other.max_presences
|
||||||
|
&& self.max_stage_video_channel_users == other.max_stage_video_channel_users
|
||||||
|
&& self.max_video_channel_users == other.max_video_channel_users
|
||||||
|
&& self.mfa_level == other.mfa_level
|
||||||
|
&& self.name == other.name
|
||||||
|
&& self.nsfw_level == other.nsfw_level
|
||||||
|
&& self.owner == other.owner
|
||||||
|
&& self.owner_id == other.owner_id
|
||||||
|
&& self.permissions == other.permissions
|
||||||
|
&& self.preferred_locale == other.preferred_locale
|
||||||
|
&& self.premium_progress_bar_enabled == other.premium_progress_bar_enabled
|
||||||
|
&& self.premium_subscription_count == other.premium_subscription_count
|
||||||
|
&& self.premium_tier == other.premium_tier
|
||||||
|
&& self.primary_category_id == other.primary_category_id
|
||||||
|
&& self.public_updates_channel_id == other.public_updates_channel_id
|
||||||
|
&& self.region == other.region
|
||||||
|
&& self.rules_channel == other.rules_channel
|
||||||
|
&& self.rules_channel_id == other.rules_channel_id
|
||||||
|
&& self.splash == other.splash
|
||||||
|
&& self.stickers == other.stickers
|
||||||
|
&& self.system_channel_flags == other.system_channel_flags
|
||||||
|
&& self.system_channel_id == other.system_channel_id
|
||||||
|
&& self.vanity_url_code == other.vanity_url_code
|
||||||
|
&& self.verification_level == other.verification_level
|
||||||
|
&& self.welcome_screen == other.welcome_screen
|
||||||
|
&& self.welcome_screen == other.welcome_screen
|
||||||
|
&& self.widget_channel_id == other.widget_channel_id
|
||||||
|
&& self.widget_enabled == other.widget_enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::cmp::Eq for Guild {}
|
||||||
|
|
||||||
|
/// See <https://docs.spacebar.chat/routes/#get-/guilds/-guild_id-/bans/-user->
|
||||||
|
#[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 {
|
||||||
pub user_id: Snowflake,
|
pub user: PublicUser,
|
||||||
pub guild_id: Snowflake,
|
|
||||||
pub reason: Option<String>,
|
pub reason: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See https://docs.spacebar.chat/routes/#cmp--schemas-invite
|
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-invite>
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
pub struct GuildInvite {
|
pub struct GuildInvite {
|
||||||
pub code: String,
|
pub code: String,
|
||||||
|
@ -111,21 +236,40 @@ pub struct GuildInvite {
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub expires_at: Option<DateTime<Utc>>,
|
pub expires_at: Option<DateTime<Utc>>,
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
pub guild: Option<Guild>,
|
pub guild: Option<Arc<RwLock<Guild>>>,
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
pub channel: Option<Channel>,
|
pub channel: Option<Arc<RwLock<Channel>>>,
|
||||||
pub inviter_id: Option<Snowflake>,
|
pub inviter_id: Option<Snowflake>,
|
||||||
pub inviter: Option<User>,
|
pub inviter: Option<Arc<RwLock<User>>>,
|
||||||
pub target_user_id: Option<Snowflake>,
|
pub target_user_id: Option<Snowflake>,
|
||||||
pub target_user: Option<String>,
|
pub target_user: Option<String>,
|
||||||
pub target_user_type: Option<i32>,
|
pub target_user_type: Option<i32>,
|
||||||
pub vanity_url: Option<bool>,
|
pub vanity_url: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
impl std::hash::Hash for GuildInvite {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.code.hash(state);
|
||||||
|
self.temporary.hash(state);
|
||||||
|
self.uses.hash(state);
|
||||||
|
self.max_uses.hash(state);
|
||||||
|
self.max_age.hash(state);
|
||||||
|
self.created_at.hash(state);
|
||||||
|
self.expires_at.hash(state);
|
||||||
|
self.guild_id.hash(state);
|
||||||
|
self.channel_id.hash(state);
|
||||||
|
self.inviter_id.hash(state);
|
||||||
|
self.target_user_id.hash(state);
|
||||||
|
self.target_user.hash(state);
|
||||||
|
self.target_user_type.hash(state);
|
||||||
|
self.vanity_url.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Hash)]
|
||||||
pub struct UnavailableGuild {
|
pub struct UnavailableGuild {
|
||||||
id: Snowflake,
|
pub id: Snowflake,
|
||||||
unavailable: bool,
|
pub unavailable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
||||||
|
@ -134,7 +278,7 @@ pub struct GuildCreateResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object
|
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object>
|
||||||
pub struct GuildScheduledEvent {
|
pub struct GuildScheduledEvent {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
|
@ -149,14 +293,14 @@ pub struct GuildScheduledEvent {
|
||||||
pub entity_type: GuildScheduledEventEntityType,
|
pub entity_type: GuildScheduledEventEntityType,
|
||||||
pub entity_id: Option<Snowflake>,
|
pub entity_id: Option<Snowflake>,
|
||||||
pub entity_metadata: Option<GuildScheduledEventEntityMetadata>,
|
pub entity_metadata: Option<GuildScheduledEventEntityMetadata>,
|
||||||
pub creator: Option<User>,
|
pub creator: Option<Arc<RwLock<User>>>,
|
||||||
pub user_count: Option<u64>,
|
pub user_count: Option<u64>,
|
||||||
pub image: Option<String>,
|
pub image: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
/// 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,
|
||||||
|
@ -164,7 +308,7 @@ pub enum GuildScheduledEventPrivacyLevel {
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
/// 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]
|
||||||
Scheduled = 1,
|
Scheduled = 1,
|
||||||
|
@ -175,7 +319,7 @@ pub enum GuildScheduledEventStatus {
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
/// 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]
|
||||||
StageInstance = 1,
|
StageInstance = 1,
|
||||||
|
@ -184,7 +328,99 @@ pub enum GuildScheduledEventEntityType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
/// 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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct VoiceRegion {
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
optimal: bool,
|
||||||
|
deprecated: bool,
|
||||||
|
custom: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#message-notification-level>
|
||||||
|
pub enum MessageNotificationLevel {
|
||||||
|
#[default]
|
||||||
|
AllMessages = 0,
|
||||||
|
OnlyMentions = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#explicit-content-filter-level>
|
||||||
|
pub enum ExplicitContentFilterLevel {
|
||||||
|
#[default]
|
||||||
|
Disabled = 0,
|
||||||
|
MembersWithoutRoles = 1,
|
||||||
|
AllMembers = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
||||||
|
pub enum VerificationLevel {
|
||||||
|
#[default]
|
||||||
|
None = 0,
|
||||||
|
Low = 1,
|
||||||
|
Medium = 2,
|
||||||
|
High = 3,
|
||||||
|
VeryHigh = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
||||||
|
pub enum MFALevel {
|
||||||
|
#[default]
|
||||||
|
None = 0,
|
||||||
|
Elevated = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
||||||
|
pub enum NSFWLevel {
|
||||||
|
#[default]
|
||||||
|
Default = 0,
|
||||||
|
Explicit = 1,
|
||||||
|
Safe = 2,
|
||||||
|
AgeRestricted = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, Eq, PartialEq, Hash, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
||||||
|
pub enum PremiumTier {
|
||||||
|
#[default]
|
||||||
|
None = 0,
|
||||||
|
Tier1 = 1,
|
||||||
|
Tier2 = 2,
|
||||||
|
Tier3 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#system-channel-flags>
|
||||||
|
pub struct SystemChannelFlags: u64 {
|
||||||
|
/// Indicates if an app uses the Auto Moderation API
|
||||||
|
const SUPPRESS_JOIN_NOTIFICATIONS = 1 << 0;
|
||||||
|
const SUPPRESS_PREMIUM_SUBSCRIPTIONS = 1 << 1;
|
||||||
|
const SUPPRESS_GUILD_REMINDER_NOTIFICATIONS = 1 << 2;
|
||||||
|
const SUPPRESS_JOIN_NOTIFICATION_REPLIES = 1 << 3;
|
||||||
|
const SUPPRESS_ROLE_SUBSCRIPTION_PURCHASE_NOTIFICATIONS = 1 << 4;
|
||||||
|
const SUPPRESS_ROLE_SUBSCRIPTION_PURCHASE_NOTIFICATIONS_REPLIES = 1 << 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{entities::PublicUser, Snowflake};
|
use crate::types::{entities::PublicUser, Snowflake};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default, Serialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Deserialize, Default, Serialize, Clone)]
|
||||||
|
/// Represents a participating user in a guild.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#guild-member-object>
|
||||||
pub struct GuildMember {
|
pub struct GuildMember {
|
||||||
pub user: Option<PublicUser>,
|
pub user: Option<Arc<RwLock<PublicUser>>>,
|
||||||
pub nick: Option<String>,
|
pub nick: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub roles: Vec<Snowflake>,
|
pub roles: Vec<Snowflake>,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -8,7 +10,7 @@ use crate::types::{
|
||||||
|
|
||||||
#[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))]
|
||||||
/// See https://discord.com/developers/docs/resources/guild#integration-object-integration-structure
|
/// See <https://discord.com/developers/docs/resources/guild#integration-object-integration-structure>
|
||||||
pub struct Integration {
|
pub struct Integration {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -21,19 +23,19 @@ pub struct Integration {
|
||||||
pub expire_behaviour: Option<u8>,
|
pub expire_behaviour: Option<u8>,
|
||||||
pub expire_grace_period: Option<u16>,
|
pub expire_grace_period: Option<u16>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub user: Option<User>,
|
pub user: Option<Arc<RwLock<User>>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub account: IntegrationAccount,
|
pub account: IntegrationAccount,
|
||||||
pub synced_at: Option<DateTime<Utc>>,
|
pub synced_at: Option<DateTime<Utc>>,
|
||||||
pub subscriber_count: Option<f64>,
|
pub subscriber_count: Option<f64>,
|
||||||
pub revoked: Option<bool>,
|
pub revoked: Option<bool>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub application: Option<Application>,
|
pub application: Option<Arc<RwLock<Application>>>,
|
||||||
pub scopes: Option<Vec<String>>,
|
pub scopes: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
|
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
|
||||||
/// See https://discord.com/developers/docs/resources/guild#integration-account-object-integration-account-structure
|
/// See <https://discord.com/developers/docs/resources/guild#integration-account-object-integration-account-structure>
|
||||||
pub struct IntegrationAccount {
|
pub struct IntegrationAccount {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -19,7 +21,7 @@ pub struct Invite {
|
||||||
pub flags: Option<i32>,
|
pub flags: Option<i32>,
|
||||||
pub guild: Option<InviteGuild>,
|
pub guild: Option<InviteGuild>,
|
||||||
pub guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
pub guild_scheduled_event: Option<GuildScheduledEvent>,
|
pub guild_scheduled_event: Option<Arc<RwLock<GuildScheduledEvent>>>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub invite_type: Option<i32>,
|
pub invite_type: Option<i32>,
|
||||||
pub inviter: Option<User>,
|
pub inviter: Option<User>,
|
||||||
|
@ -68,7 +70,7 @@ pub enum NSFWLevel {
|
||||||
/// 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 {
|
||||||
pub members: Vec<GuildMember>,
|
pub members: Vec<Arc<RwLock<GuildMember>>>,
|
||||||
pub participant_count: i32,
|
pub participant_count: i32,
|
||||||
pub speaker_count: i32,
|
pub speaker_count: i32,
|
||||||
pub topic: String,
|
pub topic: String,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
|
@ -8,13 +10,17 @@ use crate::types::{
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
/// Represents a message sent in a channel.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#message-object>
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub author: PublicUser,
|
pub author: Option<PublicUser>,
|
||||||
pub content: Option<String>,
|
pub content: Option<String>,
|
||||||
pub timestamp: String,
|
pub timestamp: String,
|
||||||
pub edited_timestamp: Option<String>,
|
pub edited_timestamp: Option<String>,
|
||||||
|
@ -23,15 +29,15 @@ pub struct Message {
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub mentions: Option<Vec<User>>,
|
pub mentions: Option<Vec<User>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub mention_roles: Vec<Snowflake>,
|
pub mention_roles: Option<Vec<Snowflake>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub mention_channels: Option<Vec<ChannelMention>>,
|
pub mention_channels: Option<Vec<ChannelMention>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub attachments: Vec<Attachment>,
|
pub attachments: Option<Vec<Attachment>>,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub embeds: Vec<sqlx::types::Json<Embed>>,
|
pub embeds: Vec<sqlx::types::Json<Embed>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
pub embeds: Vec<Embed>,
|
pub embeds: Option<Vec<Embed>>,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub reactions: Option<sqlx::types::Json<Vec<Reaction>>>,
|
pub reactions: Option<sqlx::types::Json<Vec<Reaction>>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
@ -63,7 +69,44 @@ pub struct Message {
|
||||||
pub role_subscription_data: Option<RoleSubscriptionData>,
|
pub role_subscription_data: Option<RoleSubscriptionData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
impl PartialEq for Message {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
&& self.channel_id == other.channel_id
|
||||||
|
&& self.author == other.author
|
||||||
|
&& self.content == other.content
|
||||||
|
&& self.timestamp == other.timestamp
|
||||||
|
&& self.edited_timestamp == other.edited_timestamp
|
||||||
|
&& self.tts == other.tts
|
||||||
|
&& self.mention_everyone == other.mention_everyone
|
||||||
|
&& self.mentions == other.mentions
|
||||||
|
&& self.mention_roles == other.mention_roles
|
||||||
|
&& self.mention_channels == other.mention_channels
|
||||||
|
&& self.attachments == other.attachments
|
||||||
|
&& self.embeds == other.embeds
|
||||||
|
&& self.embeds == other.embeds
|
||||||
|
&& self.nonce == other.nonce
|
||||||
|
&& self.pinned == other.pinned
|
||||||
|
&& self.webhook_id == other.webhook_id
|
||||||
|
&& self.message_type == other.message_type
|
||||||
|
&& self.activity == other.activity
|
||||||
|
&& self.activity == other.activity
|
||||||
|
&& self.application_id == other.application_id
|
||||||
|
&& self.message_reference == other.message_reference
|
||||||
|
&& self.message_reference == other.message_reference
|
||||||
|
&& self.flags == other.flags
|
||||||
|
&& self.referenced_message == other.referenced_message
|
||||||
|
&& self.thread == other.thread
|
||||||
|
&& self.components == other.components
|
||||||
|
&& self.sticker_items == other.sticker_items
|
||||||
|
&& self.position == other.position
|
||||||
|
&& self.role_subscription_data == other.role_subscription_data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Ord, PartialOrd)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#message-reference-object>
|
||||||
pub struct MessageReference {
|
pub struct MessageReference {
|
||||||
pub message_id: Snowflake,
|
pub message_id: Snowflake,
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
|
@ -71,17 +114,17 @@ pub struct MessageReference {
|
||||||
pub fail_if_not_exists: Option<bool>,
|
pub fail_if_not_exists: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, 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: u8,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub user: User,
|
pub user: User,
|
||||||
pub member: Option<GuildMember>,
|
pub member: Option<Arc<RwLock<GuildMember>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize, Eq, PartialOrd, Ord)]
|
||||||
pub struct AllowedMention {
|
pub struct AllowedMention {
|
||||||
parse: Vec<AllowedMentionType>,
|
parse: Vec<AllowedMentionType>,
|
||||||
roles: Vec<Snowflake>,
|
roles: Vec<Snowflake>,
|
||||||
|
@ -89,7 +132,7 @@ pub struct AllowedMention {
|
||||||
replied_user: bool,
|
replied_user: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum AllowedMentionType {
|
pub enum AllowedMentionType {
|
||||||
Roles,
|
Roles,
|
||||||
|
@ -106,7 +149,7 @@ pub struct ChannelMention {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
|
||||||
pub struct Embed {
|
pub struct Embed {
|
||||||
title: Option<String>,
|
title: Option<String>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -124,14 +167,14 @@ pub struct Embed {
|
||||||
fields: Option<Vec<EmbedField>>,
|
fields: Option<Vec<EmbedField>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
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, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
pub struct EmbedImage {
|
pub struct EmbedImage {
|
||||||
url: String,
|
url: String,
|
||||||
proxy_url: String,
|
proxy_url: String,
|
||||||
|
@ -139,7 +182,7 @@ pub struct EmbedImage {
|
||||||
width: Option<i32>,
|
width: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
pub struct EmbedThumbnail {
|
pub struct EmbedThumbnail {
|
||||||
url: String,
|
url: String,
|
||||||
proxy_url: Option<String>,
|
proxy_url: Option<String>,
|
||||||
|
@ -147,7 +190,7 @@ pub struct EmbedThumbnail {
|
||||||
width: Option<i32>,
|
width: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
struct EmbedVideo {
|
struct EmbedVideo {
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
proxy_url: Option<String>,
|
proxy_url: Option<String>,
|
||||||
|
@ -155,13 +198,13 @@ struct EmbedVideo {
|
||||||
width: Option<i32>,
|
width: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
pub struct EmbedProvider {
|
pub struct EmbedProvider {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
pub struct EmbedAuthor {
|
pub struct EmbedAuthor {
|
||||||
name: String,
|
name: String,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
|
@ -169,21 +212,24 @@ pub struct EmbedAuthor {
|
||||||
proxy_icon_url: Option<String>,
|
proxy_icon_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
pub struct EmbedField {
|
pub struct EmbedField {
|
||||||
name: String,
|
name: String,
|
||||||
value: String,
|
value: String,
|
||||||
inline: Option<bool>,
|
inline: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialOrd, PartialEq)]
|
||||||
pub struct Reaction {
|
pub struct Reaction {
|
||||||
pub count: i32,
|
pub count: u32,
|
||||||
|
pub burst_count: u32,
|
||||||
pub me: bool,
|
pub me: bool,
|
||||||
|
pub burst_me: bool,
|
||||||
|
pub burst_colors: Vec<String>,
|
||||||
pub emoji: Emoji,
|
pub emoji: Emoji,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)]
|
||||||
pub enum Component {
|
pub enum Component {
|
||||||
ActionRow = 1,
|
ActionRow = 1,
|
||||||
Button = 2,
|
Button = 2,
|
||||||
|
@ -196,6 +242,8 @@ pub enum Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#message-activity-object>
|
||||||
pub struct MessageActivity {
|
pub struct MessageActivity {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub activity_type: i64,
|
pub activity_type: i64,
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub use guild_member::*;
|
||||||
pub use integration::*;
|
pub use integration::*;
|
||||||
pub use invite::*;
|
pub use invite::*;
|
||||||
pub use message::*;
|
pub use message::*;
|
||||||
|
pub use ratelimits::*;
|
||||||
pub use relationship::*;
|
pub use relationship::*;
|
||||||
pub use role::*;
|
pub use role::*;
|
||||||
pub use security_key::*;
|
pub use security_key::*;
|
||||||
|
@ -22,6 +23,18 @@ pub use user_settings::*;
|
||||||
pub use voice_state::*;
|
pub use voice_state::*;
|
||||||
pub use webhook::*;
|
pub use webhook::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::{GatewayHandle, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
mod application;
|
mod application;
|
||||||
mod attachment;
|
mod attachment;
|
||||||
mod audit_log;
|
mod audit_log;
|
||||||
|
@ -34,6 +47,7 @@ mod guild_member;
|
||||||
mod integration;
|
mod integration;
|
||||||
mod invite;
|
mod invite;
|
||||||
mod message;
|
mod message;
|
||||||
|
mod ratelimits;
|
||||||
mod relationship;
|
mod relationship;
|
||||||
mod role;
|
mod role;
|
||||||
mod security_key;
|
mod security_key;
|
||||||
|
@ -45,3 +59,63 @@ mod user;
|
||||||
mod user_settings;
|
mod user_settings;
|
||||||
mod voice_state;
|
mod voice_state;
|
||||||
mod webhook;
|
mod webhook;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
pub trait Composite<T: Updateable + Clone + Debug> {
|
||||||
|
async fn watch_whole(self, gateway: &GatewayHandle) -> Self;
|
||||||
|
|
||||||
|
async fn option_observe_fn(
|
||||||
|
value: Option<Arc<RwLock<T>>>,
|
||||||
|
gateway: &GatewayHandle,
|
||||||
|
) -> Option<Arc<RwLock<T>>>
|
||||||
|
where
|
||||||
|
T: Composite<T> + Debug,
|
||||||
|
{
|
||||||
|
if let Some(value) = value {
|
||||||
|
let value = value.clone();
|
||||||
|
Some(gateway.observe(value).await)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn option_vec_observe_fn(
|
||||||
|
value: Option<Vec<Arc<RwLock<T>>>>,
|
||||||
|
gateway: &GatewayHandle,
|
||||||
|
) -> Option<Vec<Arc<RwLock<T>>>>
|
||||||
|
where
|
||||||
|
T: Composite<T>,
|
||||||
|
{
|
||||||
|
if let Some(value) = value {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for component in value.into_iter() {
|
||||||
|
vec.push(gateway.observe(component).await);
|
||||||
|
}
|
||||||
|
Some(vec)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn value_observe_fn(value: Arc<RwLock<T>>, gateway: &GatewayHandle) -> Arc<RwLock<T>>
|
||||||
|
where
|
||||||
|
T: Composite<T>,
|
||||||
|
{
|
||||||
|
gateway.observe(value).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn vec_observe_fn(
|
||||||
|
value: Vec<Arc<RwLock<T>>>,
|
||||||
|
gateway: &GatewayHandle,
|
||||||
|
) -> Vec<Arc<RwLock<T>>>
|
||||||
|
where
|
||||||
|
T: Composite<T>,
|
||||||
|
{
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for component in value.into_iter() {
|
||||||
|
vec.push(gateway.observe(component).await);
|
||||||
|
}
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,8 +24,6 @@ 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.
|
||||||
/// Unlike [`RateLimits`], this struct shows the current ratelimits, not the rate limit
|
|
||||||
/// configuration for the instance.
|
|
||||||
/// 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)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Limit {
|
pub struct Limit {
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
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};
|
||||||
|
@ -6,20 +8,29 @@ use crate::types::Snowflake;
|
||||||
|
|
||||||
use super::PublicUser;
|
use super::PublicUser;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialEq, Eq)]
|
#[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>
|
||||||
pub struct Relationship {
|
pub struct Relationship {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub relationship_type: RelationshipType,
|
pub relationship_type: RelationshipType,
|
||||||
pub nickname: Option<String>,
|
pub nickname: Option<String>,
|
||||||
pub user: PublicUser,
|
pub user: Arc<RwLock<PublicUser>>,
|
||||||
pub since: Option<DateTime<Utc>>,
|
pub since: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Relationship {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
&& self.relationship_type == other.relationship_type
|
||||||
|
&& self.since == other.since
|
||||||
|
&& self.nickname == other.nickname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default, Eq, PartialEq)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default, Eq, PartialEq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
/// 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,
|
||||||
Implicit = 5,
|
Implicit = 5,
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
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, deserialize_string_from_number};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use chorus_macros::{Composite, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::{GatewayHandle, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::types::Composite;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
/// See https://discord.com/developers/docs/topics/permissions#role-object
|
/// See <https://discord.com/developers/docs/topics/permissions#role-object>
|
||||||
pub struct RoleObject {
|
pub struct RoleObject {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -34,8 +45,8 @@ pub struct RoleSubscriptionData {
|
||||||
pub is_renewal: bool,
|
pub is_renewal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)]
|
||||||
/// 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)]
|
||||||
#[serde(deserialize_with = "deserialize_option_number_from_string")]
|
#[serde(deserialize_with = "deserialize_option_number_from_string")]
|
||||||
|
@ -53,57 +64,110 @@ pub struct RoleTags {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
/// Permissions limit what users of certain roles can do on a Guild to Guild basis.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord.com/developers/docs/topics/permissions#permissions>
|
||||||
pub struct PermissionFlags: u64 {
|
pub struct PermissionFlags: u64 {
|
||||||
|
/// Allows creation of instant invites
|
||||||
const CREATE_INSTANT_INVITE = 1 << 0;
|
const CREATE_INSTANT_INVITE = 1 << 0;
|
||||||
|
/// Allows kicking members
|
||||||
const KICK_MEMBERS = 1 << 1;
|
const KICK_MEMBERS = 1 << 1;
|
||||||
|
/// Allows banning members
|
||||||
const BAN_MEMBERS = 1 << 2;
|
const BAN_MEMBERS = 1 << 2;
|
||||||
|
/// Allows all permissions and bypasses channel permission overwrites
|
||||||
const ADMINISTRATOR = 1 << 3;
|
const ADMINISTRATOR = 1 << 3;
|
||||||
|
/// Allows management and editing of channels
|
||||||
const MANAGE_CHANNELS = 1 << 4;
|
const MANAGE_CHANNELS = 1 << 4;
|
||||||
|
/// Allows management and editing of the guild and guild settings
|
||||||
const MANAGE_GUILD = 1 << 5;
|
const MANAGE_GUILD = 1 << 5;
|
||||||
|
/// Allows for the addition of reactions to messages
|
||||||
const ADD_REACTIONS = 1 << 6;
|
const ADD_REACTIONS = 1 << 6;
|
||||||
|
/// Allows viewing of the audit log
|
||||||
const VIEW_AUDIT_LOG = 1 << 7;
|
const VIEW_AUDIT_LOG = 1 << 7;
|
||||||
|
/// Allows using priority speaker in a voice channel
|
||||||
const PRIORITY_SPEAKER = 1 << 8;
|
const PRIORITY_SPEAKER = 1 << 8;
|
||||||
|
/// Allows the user to go live and share their screen
|
||||||
const STREAM = 1 << 9;
|
const STREAM = 1 << 9;
|
||||||
|
/// Allows guild members to view a channel, which includes reading messages in text channels and joining voice channels
|
||||||
const VIEW_CHANNEL = 1 << 10;
|
const VIEW_CHANNEL = 1 << 10;
|
||||||
|
/// Allows sending messages in a channel and creating threads in a forum (does not allow sending messages in threads)
|
||||||
const SEND_MESSAGES = 1 << 11;
|
const SEND_MESSAGES = 1 << 11;
|
||||||
|
/// Allows sending /tts messages
|
||||||
const SEND_TTS_MESSAGES = 1 << 12;
|
const SEND_TTS_MESSAGES = 1 << 12;
|
||||||
|
/// Allows deletion of other users' messages
|
||||||
const MANAGE_MESSAGES = 1 << 13;
|
const MANAGE_MESSAGES = 1 << 13;
|
||||||
|
/// Links sent by users with this permission will be auto-embedded
|
||||||
const EMBED_LINKS = 1 << 14;
|
const EMBED_LINKS = 1 << 14;
|
||||||
|
/// Allows uploading images and files
|
||||||
const ATTACH_FILES = 1 << 15;
|
const ATTACH_FILES = 1 << 15;
|
||||||
|
/// Allows reading of message history
|
||||||
const READ_MESSAGE_HISTORY = 1 << 16;
|
const READ_MESSAGE_HISTORY = 1 << 16;
|
||||||
|
/// Allows using the @everyone tag to notify all users in a channel, and the @here tag to notify all online users in a channel
|
||||||
const MENTION_EVERYONE = 1 << 17;
|
const MENTION_EVERYONE = 1 << 17;
|
||||||
|
/// Allows the usage of custom emojis from other servers
|
||||||
const USE_EXTERNAL_EMOJIS = 1 << 18;
|
const USE_EXTERNAL_EMOJIS = 1 << 18;
|
||||||
|
/// Allows viewing guild insights
|
||||||
const VIEW_GUILD_INSIGHTS = 1 << 19;
|
const VIEW_GUILD_INSIGHTS = 1 << 19;
|
||||||
|
/// Allows joining of a voice channel
|
||||||
const CONNECT = 1 << 20;
|
const CONNECT = 1 << 20;
|
||||||
|
/// Allows speaking in a voice channel
|
||||||
const SPEAK = 1 << 21;
|
const SPEAK = 1 << 21;
|
||||||
|
/// Allows muting members in a voice channel
|
||||||
const MUTE_MEMBERS = 1 << 22;
|
const MUTE_MEMBERS = 1 << 22;
|
||||||
|
/// Allows deafening of members in a voice channel
|
||||||
const DEAFEN_MEMBERS = 1 << 23;
|
const DEAFEN_MEMBERS = 1 << 23;
|
||||||
|
/// Allows moving of members between voice channels
|
||||||
const MOVE_MEMBERS = 1 << 24;
|
const MOVE_MEMBERS = 1 << 24;
|
||||||
|
/// Allows using voice activity (VAD = voice-activity-detection) in a voice channel
|
||||||
const USE_VAD = 1 << 25;
|
const USE_VAD = 1 << 25;
|
||||||
|
/// Allows modification of own nickname
|
||||||
const CHANGE_NICKNAME = 1 << 26;
|
const CHANGE_NICKNAME = 1 << 26;
|
||||||
|
/// Allows modification of other users' nicknames
|
||||||
const MANAGE_NICKNAMES = 1 << 27;
|
const MANAGE_NICKNAMES = 1 << 27;
|
||||||
|
/// Allows management and editing of roles
|
||||||
const MANAGE_ROLES = 1 << 28;
|
const MANAGE_ROLES = 1 << 28;
|
||||||
|
/// Allows management and editing of webhooks
|
||||||
const MANAGE_WEBHOOKS = 1 << 29;
|
const MANAGE_WEBHOOKS = 1 << 29;
|
||||||
|
/// Allows management and editing of emojis, stickers, and soundboard sounds
|
||||||
const MANAGE_GUILD_EXPRESSIONS = 1 << 30;
|
const MANAGE_GUILD_EXPRESSIONS = 1 << 30;
|
||||||
|
/// Allows members to use application commands, including slash commands and context menu commands.
|
||||||
const USE_APPLICATION_COMMANDS = 1 << 31;
|
const USE_APPLICATION_COMMANDS = 1 << 31;
|
||||||
|
/// Allows requesting to speak in stage channels. (*This permission is under active development and may be changed or removed.*)
|
||||||
const REQUEST_TO_SPEAK = 1 << 32;
|
const REQUEST_TO_SPEAK = 1 << 32;
|
||||||
|
/// Allows creating, editing, and deleting scheduled events
|
||||||
const MANAGE_EVENTS = 1 << 33;
|
const MANAGE_EVENTS = 1 << 33;
|
||||||
|
/// Allows deleting and archiving threads, and viewing all private threads
|
||||||
const MANAGE_THREADS = 1 << 34;
|
const MANAGE_THREADS = 1 << 34;
|
||||||
|
/// Allows creating public and announcement threads
|
||||||
const CREATE_PUBLIC_THREADS = 1 << 35;
|
const CREATE_PUBLIC_THREADS = 1 << 35;
|
||||||
|
/// Allows creating private threads
|
||||||
const CREATE_PRIVATE_THREADS = 1 << 36;
|
const CREATE_PRIVATE_THREADS = 1 << 36;
|
||||||
|
/// Allows the usage of custom stickers from other servers
|
||||||
const USE_EXTERNAL_STICKERS = 1 << 37;
|
const USE_EXTERNAL_STICKERS = 1 << 37;
|
||||||
|
/// Allows sending messages in threads
|
||||||
const SEND_MESSAGES_IN_THREADS = 1 << 38;
|
const SEND_MESSAGES_IN_THREADS = 1 << 38;
|
||||||
|
/// Allows using Activities in a voice channel
|
||||||
const USE_EMBEDDED_ACTIVITIES = 1 << 39;
|
const USE_EMBEDDED_ACTIVITIES = 1 << 39;
|
||||||
|
/// Allows timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels
|
||||||
const MODERATE_MEMBERS = 1 << 40;
|
const MODERATE_MEMBERS = 1 << 40;
|
||||||
|
/// Allows viewing role subscription insights
|
||||||
const VIEW_CREATOR_MONETIZATION_ANALYTICS = 1 << 41;
|
const VIEW_CREATOR_MONETIZATION_ANALYTICS = 1 << 41;
|
||||||
|
/// Allows using the soundboard in a voice channel
|
||||||
const USE_SOUNDBOARD = 1 << 42;
|
const USE_SOUNDBOARD = 1 << 42;
|
||||||
|
/// Allows using custom soundboard sounds from other servers
|
||||||
const USE_EXTERNAL_SOUNDS = 1 << 45;
|
const USE_EXTERNAL_SOUNDS = 1 << 45;
|
||||||
|
/// Allows sending voice messages
|
||||||
const SEND_VOICE_MESSAGES = 1 << 46;
|
const SEND_VOICE_MESSAGES = 1 << 46;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PermissionFlags {
|
impl PermissionFlags {
|
||||||
|
/// Returns if the PermissionFlags object has specific permissions
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// Note that if the object has the [PermissionFlags::ADMINISTRATOR] permission, this always returns true
|
||||||
pub fn has_permission(&self, permission: PermissionFlags) -> bool {
|
pub fn has_permission(&self, permission: PermissionFlags) -> bool {
|
||||||
self.contains(permission) || self.contains(PermissionFlags::ADMINISTRATOR)
|
self.contains(permission) || self.contains(PermissionFlags::ADMINISTRATOR)
|
||||||
}
|
}
|
||||||
|
@ -114,6 +178,7 @@ impl PermissionFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a String of Permissions from a given [`Vec`] of [`PermissionFlags`].
|
/// Creates a String of Permissions from a given [`Vec`] of [`PermissionFlags`].
|
||||||
|
///
|
||||||
/// # Example:
|
/// # Example:
|
||||||
/// ```
|
/// ```
|
||||||
/// use chorus::types::{PermissionFlags};
|
/// use chorus::types::{PermissionFlags};
|
||||||
|
|
|
@ -4,12 +4,12 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
use crate::types::Snowflake;
|
use crate::types::Snowflake;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
/// See https://discord.com/developers/docs/resources/stage-instance
|
/// See <https://discord.com/developers/docs/resources/stage-instance>
|
||||||
pub struct StageInstance {
|
pub struct StageInstance {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
/// 1 - 120 chars
|
/// 1 - 120 characters
|
||||||
pub topic: String,
|
pub topic: String,
|
||||||
pub privacy_level: StageInstancePrivacyLevel,
|
pub privacy_level: StageInstancePrivacyLevel,
|
||||||
/// deprecated, apparently
|
/// deprecated, apparently
|
||||||
|
@ -20,7 +20,7 @@ pub struct StageInstance {
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[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 {
|
||||||
/// deprecated, apparently
|
/// deprecated, apparently
|
||||||
Public = 1,
|
Public = 1,
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{entities::User, utils::Snowflake};
|
use crate::types::{entities::User, utils::Snowflake};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
/// Represents a sticker that can be sent in messages.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/sticker#sticker-object>
|
||||||
pub struct Sticker {
|
pub struct Sticker {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
|
@ -18,11 +24,95 @@ pub struct Sticker {
|
||||||
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<User>,
|
pub user: Option<Arc<RwLock<User>>>,
|
||||||
pub sort_value: Option<u8>,
|
pub sort_value: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::hash::Hash for Sticker {
|
||||||
|
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 {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
&& self.pack_id == other.pack_id
|
||||||
|
&& self.name == other.name
|
||||||
|
&& self.description == other.description
|
||||||
|
&& self.tags == other.tags
|
||||||
|
&& self.asset == other.asset
|
||||||
|
&& self.sticker_type == other.sticker_type
|
||||||
|
&& self.format_type == other.format_type
|
||||||
|
&& self.available == other.available
|
||||||
|
&& self.guild_id == other.guild_id
|
||||||
|
&& self.sort_value == other.sort_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Sticker {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
match self.id.partial_cmp(&other.id) {
|
||||||
|
Some(core::cmp::Ordering::Equal) => {}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
/// A partial sticker object.
|
||||||
|
///
|
||||||
|
/// Represents the smallest amount of data required to render a sticker.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/sticker#sticker-item-object>
|
||||||
pub struct StickerItem {
|
pub struct StickerItem {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::entities::User;
|
use crate::types::entities::User;
|
||||||
use crate::types::Snowflake;
|
use crate::types::Snowflake;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
pub struct Team {
|
pub struct Team {
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
|
@ -14,10 +16,10 @@ pub struct Team {
|
||||||
pub owner_user_id: Snowflake,
|
pub owner_user_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct TeamMember {
|
pub struct TeamMember {
|
||||||
pub membership_state: u8,
|
pub membership_state: u8,
|
||||||
pub permissions: Vec<String>,
|
pub permissions: Vec<String>,
|
||||||
pub team_id: Snowflake,
|
pub team_id: Snowflake,
|
||||||
pub user: User,
|
pub user: Arc<RwLock<User>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -6,8 +8,8 @@ use crate::types::{
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// See https://docs.spacebar.chat/routes/#cmp--schemas-template
|
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-template>
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
pub struct GuildTemplate {
|
pub struct GuildTemplate {
|
||||||
pub code: String,
|
pub code: String,
|
||||||
|
@ -16,13 +18,13 @@ pub struct GuildTemplate {
|
||||||
pub usage_count: Option<u64>,
|
pub usage_count: Option<u64>,
|
||||||
pub creator_id: Snowflake,
|
pub creator_id: Snowflake,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub creator: User,
|
pub creator: Arc<RwLock<User>>,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub updated_at: DateTime<Utc>,
|
pub updated_at: DateTime<Utc>,
|
||||||
pub source_guild_id: Snowflake,
|
pub source_guild_id: Snowflake,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub source_guild: Vec<Guild>,
|
pub source_guild: Vec<Arc<RwLock<Guild>>>,
|
||||||
// Unsure how a {recursive: Guild} looks like, might be a Vec?
|
// Unsure how a {recursive: Guild} looks like, might be a Vec?
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub serialized_source_guild: Vec<Guild>,
|
pub serialized_source_guild: Vec<Arc<RwLock<Guild>>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,18 @@ use crate::types::utils::Snowflake;
|
||||||
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::fmt::Debug;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::{GatewayHandle, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::types::Composite;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use chorus_macros::{Composite, Updateable};
|
||||||
|
|
||||||
|
use super::Emoji;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
|
@ -15,7 +27,8 @@ impl User {
|
||||||
PublicUser::from(self)
|
PublicUser::from(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
|
@ -50,7 +63,7 @@ pub struct User {
|
||||||
pub disabled: Option<bool>,
|
pub disabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[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>,
|
||||||
|
@ -91,7 +104,7 @@ 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, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
pub struct UserFlags: u64 {
|
pub struct UserFlags: u64 {
|
||||||
const DISCORD_EMPLOYEE = 1 << 0;
|
const DISCORD_EMPLOYEE = 1 << 0;
|
||||||
|
@ -116,3 +129,15 @@ bitflags::bitflags! {
|
||||||
const BOT_HTTP_INTERACTIONS = 1 << 19;
|
const BOT_HTTP_INTERACTIONS = 1 << 19;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct UserProfileMetadata {
|
||||||
|
pub guild_id: Option<Snowflake>,
|
||||||
|
pub pronouns: String,
|
||||||
|
pub bio: Option<String>,
|
||||||
|
pub banner: Option<String>,
|
||||||
|
pub accent_color: Option<i32>,
|
||||||
|
pub theme_colors: Option<Vec<i32>>,
|
||||||
|
pub popout_animation_particle_type: Option<Snowflake>,
|
||||||
|
pub emoji: Option<Emoji>,
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
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};
|
||||||
|
|
||||||
|
@ -28,7 +30,7 @@ pub enum UserTheme {
|
||||||
Light,
|
Light,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, 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: u16,
|
||||||
|
@ -51,15 +53,17 @@ pub struct UserSettings {
|
||||||
pub friend_source_flags: sqlx::types::Json<FriendSourceFlags>,
|
pub friend_source_flags: sqlx::types::Json<FriendSourceFlags>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
pub friend_source_flags: FriendSourceFlags,
|
pub friend_source_flags: FriendSourceFlags,
|
||||||
pub gateway_connected: bool,
|
pub gateway_connected: Option<bool>,
|
||||||
pub gif_auto_play: bool,
|
pub gif_auto_play: bool,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
pub guild_folders: sqlx::types::Json<Vec<GuildFolder>>,
|
pub guild_folders: sqlx::types::Json<Vec<GuildFolder>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
pub guild_folders: Vec<GuildFolder>,
|
pub guild_folders: Vec<GuildFolder>,
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
|
#[serde(default)]
|
||||||
pub guild_positions: sqlx::types::Json<Vec<String>>,
|
pub guild_positions: sqlx::types::Json<Vec<String>>,
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
#[serde(default)]
|
||||||
pub guild_positions: Vec<String>,
|
pub guild_positions: Vec<String>,
|
||||||
pub inline_attachment_media: bool,
|
pub inline_attachment_media: bool,
|
||||||
pub inline_embed_media: bool,
|
pub inline_embed_media: bool,
|
||||||
|
@ -73,7 +77,7 @@ pub struct UserSettings {
|
||||||
#[cfg(not(feature = "sqlx"))]
|
#[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: UserStatus,
|
pub status: Arc<RwLock<UserStatus>>,
|
||||||
pub stream_notifications_enabled: bool,
|
pub stream_notifications_enabled: bool,
|
||||||
pub theme: UserTheme,
|
pub theme: UserTheme,
|
||||||
pub timezone_offset: i16,
|
pub timezone_offset: i16,
|
||||||
|
@ -96,7 +100,7 @@ impl Default for UserSettings {
|
||||||
enable_tts_command: false,
|
enable_tts_command: false,
|
||||||
explicit_content_filter: 0,
|
explicit_content_filter: 0,
|
||||||
friend_source_flags: Default::default(),
|
friend_source_flags: Default::default(),
|
||||||
gateway_connected: false,
|
gateway_connected: Some(false),
|
||||||
gif_auto_play: false,
|
gif_auto_play: false,
|
||||||
guild_folders: Default::default(),
|
guild_folders: Default::default(),
|
||||||
guild_positions: Default::default(),
|
guild_positions: Default::default(),
|
||||||
|
@ -109,7 +113,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: UserStatus::Online,
|
status: Arc::new(RwLock::new(UserStatus::Online)),
|
||||||
stream_notifications_enabled: false,
|
stream_notifications_enabled: false,
|
||||||
theme: UserTheme::Dark,
|
theme: UserTheme::Dark,
|
||||||
timezone_offset: 0,
|
timezone_offset: 0,
|
||||||
|
@ -138,7 +142,7 @@ impl Default for FriendSourceFlags {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct GuildFolder {
|
pub struct GuildFolder {
|
||||||
pub color: u32,
|
pub color: u32,
|
||||||
pub guild_ids: Vec<String>,
|
pub guild_ids: Vec<String>,
|
||||||
|
@ -149,5 +153,5 @@ pub struct GuildFolder {
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct LoginResult {
|
pub struct LoginResult {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub settings: UserSettings,
|
pub settings: Arc<RwLock<UserSettings>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,33 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use chorus_macros::{Composite, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::types::Composite;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::{GatewayHandle, Updateable};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
entities::{Guild, GuildMember},
|
entities::{Guild, GuildMember},
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// See https://docs.spacebar.chat/routes/#cmp--schemas-voicestate
|
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-voicestate>
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
pub struct VoiceState {
|
pub struct VoiceState {
|
||||||
pub guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
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,
|
||||||
pub member: Option<GuildMember>,
|
pub member: Option<Arc<RwLock<GuildMember>>>,
|
||||||
pub session_id: Snowflake,
|
pub session_id: Snowflake,
|
||||||
pub token: Option<String>,
|
pub token: Option<String>,
|
||||||
pub deaf: bool,
|
pub deaf: bool,
|
||||||
|
@ -25,5 +38,5 @@ pub struct VoiceState {
|
||||||
pub self_video: bool,
|
pub self_video: bool,
|
||||||
pub suppress: bool,
|
pub suppress: bool,
|
||||||
pub request_to_speak_timestamp: Option<DateTime<Utc>>,
|
pub request_to_speak_timestamp: Option<DateTime<Utc>>,
|
||||||
pub id: Option<Snowflake>,
|
pub id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,25 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::{GatewayHandle, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use chorus_macros::{Composite, Updateable};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::types::Composite;
|
||||||
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
entities::{Guild, User},
|
entities::{Guild, User},
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// See https://docs.spacebar.chat/routes/#cmp--schemas-webhook
|
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-webhook>
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
|
#[cfg_attr(feature = "client", derive(Updateable, Composite))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
pub struct Webhook {
|
pub struct Webhook {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
|
@ -20,10 +33,10 @@ pub struct Webhook {
|
||||||
pub application_id: Snowflake,
|
pub application_id: 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<User>,
|
pub user: Option<Arc<RwLock<User>>>,
|
||||||
#[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 source_guild: Option<Guild>,
|
pub source_guild: Option<Arc<RwLock<Guild>>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::types::{GuildApplicationCommandPermissions, WebSocketEvent};
|
use crate::types::{GuildApplicationCommandPermissions, WebSocketEvent};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// 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,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::types::{JsonField, SourceUrlField};
|
||||||
|
use chorus_macros::{JsonField, SourceUrlField};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
|
@ -5,8 +7,11 @@ use crate::types::{
|
||||||
WebSocketEvent,
|
WebSocketEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use super::UpdateMessage;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// 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,
|
||||||
|
@ -14,17 +19,28 @@ pub struct AutoModerationRuleCreate {
|
||||||
|
|
||||||
impl WebSocketEvent for AutoModerationRuleCreate {}
|
impl WebSocketEvent for AutoModerationRuleCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[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)]
|
||||||
pub rule: AutoModerationRule,
|
pub rule: AutoModerationRule,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
impl UpdateMessage<AutoModerationRule> for AutoModerationRuleUpdate {
|
||||||
|
fn id(&self) -> Option<Snowflake> {
|
||||||
|
Some(self.rule.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for AutoModerationRuleUpdate {}
|
impl WebSocketEvent for AutoModerationRuleUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[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,
|
||||||
|
@ -33,7 +49,7 @@ pub struct AutoModerationRuleDelete {
|
||||||
impl WebSocketEvent for AutoModerationRuleDelete {}
|
impl WebSocketEvent for AutoModerationRuleDelete {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[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,
|
||||||
pub action: AutoModerationAction,
|
pub action: AutoModerationAction,
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub struct CallCreate {
|
||||||
|
|
||||||
impl WebSocketEvent for CallCreate {}
|
impl WebSocketEvent for CallCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[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?;
|
||||||
///
|
///
|
||||||
|
@ -38,7 +38,7 @@ pub struct CallUpdate {
|
||||||
|
|
||||||
impl WebSocketEvent for CallUpdate {}
|
impl WebSocketEvent for CallUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
|
||||||
/// Officially Undocumented;
|
/// Officially Undocumented;
|
||||||
/// Deletes a ringing call;
|
/// Deletes a ringing call;
|
||||||
/// Ex: {"t":"CALL_DELETE","s":8,"op":0,"d":{"channel_id":"837609115475771392"}}
|
/// Ex: {"t":"CALL_DELETE","s":8,"op":0,"d":{"channel_id":"837609115475771392"}}
|
||||||
|
@ -48,9 +48,9 @@ pub struct CallDelete {
|
||||||
|
|
||||||
impl WebSocketEvent for CallDelete {}
|
impl WebSocketEvent for CallDelete {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
|
||||||
/// Officially Undocumented;
|
/// Officially Undocumented;
|
||||||
/// See https://unofficial-discord-docs.vercel.app/gateway/op13;
|
/// See <https://unofficial-discord-docs.vercel.app/gateway/op13>;
|
||||||
///
|
///
|
||||||
/// Ex: {"op":13,"d":{"channel_id":"837609115475771392"}}
|
/// Ex: {"op":13,"d":{"channel_id":"837609115475771392"}}
|
||||||
pub struct CallSync {
|
pub struct CallSync {
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
use crate::types::events::WebSocketEvent;
|
use crate::types::events::WebSocketEvent;
|
||||||
use crate::types::{entities::Channel, Snowflake};
|
use crate::types::{entities::Channel, JsonField, Snowflake, SourceUrlField};
|
||||||
|
use chorus_macros::{JsonField, SourceUrlField};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
use super::UpdateMessage;
|
use super::UpdateMessage;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::types::Guild;
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||||
/// 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>,
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
|
@ -15,30 +23,57 @@ pub struct ChannelPinsUpdate {
|
||||||
|
|
||||||
impl WebSocketEvent for ChannelPinsUpdate {}
|
impl WebSocketEvent for ChannelPinsUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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)]
|
||||||
pub channel: Channel,
|
pub channel: Channel,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for ChannelCreate {}
|
impl WebSocketEvent for ChannelCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[cfg(feature = "client")]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#channel-update
|
impl UpdateMessage<Guild> for ChannelCreate {
|
||||||
|
fn id(&self) -> Option<Snowflake> {
|
||||||
|
self.channel.guild_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, object_to_update: Arc<RwLock<Guild>>) {
|
||||||
|
let mut write = object_to_update.write().unwrap();
|
||||||
|
let update = Arc::new(RwLock::new(self.channel.clone()));
|
||||||
|
if write.channels.is_some() {
|
||||||
|
write.channels.as_mut().unwrap().push(update);
|
||||||
|
} else {
|
||||||
|
write.channels = Some(Vec::from([update]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)]
|
||||||
|
/// See <https://discord.com/developers/docs/topics/gateway-events#channel-update>
|
||||||
pub struct ChannelUpdate {
|
pub struct ChannelUpdate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub channel: Channel,
|
pub channel: Channel,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for ChannelUpdate {}
|
impl WebSocketEvent for ChannelUpdate {}
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
impl UpdateMessage<Channel> for ChannelUpdate {
|
impl UpdateMessage<Channel> for ChannelUpdate {
|
||||||
fn update(&self, object_to_update: &mut Channel) {
|
fn update(&mut self, object_to_update: Arc<RwLock<Channel>>) {
|
||||||
*object_to_update = self.channel.clone();
|
let mut write = object_to_update.write().unwrap();
|
||||||
|
*write = self.channel.clone();
|
||||||
}
|
}
|
||||||
fn id(&self) -> Snowflake {
|
fn id(&self) -> Option<Snowflake> {
|
||||||
self.channel.id
|
Some(self.channel.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +88,7 @@ pub struct ChannelUnreadUpdate {
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// Contains very few fields from [Channel]
|
/// Contains very few fields from [Channel]
|
||||||
/// See also [ChannelUnreadUpdates]
|
/// 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,
|
||||||
|
@ -62,11 +97,38 @@ pub struct ChannelUnreadUpdateObject {
|
||||||
|
|
||||||
impl WebSocketEvent for ChannelUnreadUpdate {}
|
impl WebSocketEvent for ChannelUnreadUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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)]
|
||||||
pub channel: Channel,
|
pub channel: Channel,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
impl UpdateMessage<Guild> for ChannelDelete {
|
||||||
|
fn id(&self) -> Option<Snowflake> {
|
||||||
|
self.channel.guild_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, object_to_update: Arc<RwLock<Guild>>) {
|
||||||
|
if self.id().is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut write = object_to_update.write().unwrap();
|
||||||
|
if write.channels.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (iteration, item) in (0_u32..).zip(write.channels.as_mut().unwrap().iter()) {
|
||||||
|
if item.read().unwrap().id == self.id().unwrap() {
|
||||||
|
write.channels.as_mut().unwrap().remove(iteration as usize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for ChannelDelete {}
|
impl WebSocketEvent for ChannelDelete {}
|
||||||
|
|
|
@ -1,21 +1,43 @@
|
||||||
|
use chorus_macros::{JsonField, SourceUrlField};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
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, RoleObject, Snowflake, Sticker,
|
AuditLogEntry, Emoji, GuildMember, GuildScheduledEvent, JsonField, RoleObject, Snowflake,
|
||||||
|
SourceUrlField, Sticker,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::PresenceUpdate;
|
use super::PresenceUpdate;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[cfg(feature = "client")]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#guild-create;
|
use super::UpdateMessage;
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, SourceUrlField, JsonField)]
|
||||||
|
/// 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
|
||||||
pub struct GuildCreate {
|
pub struct GuildCreate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub d: GuildCreateDataOption,
|
pub d: GuildCreateDataOption,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateMessage<Guild> for GuildCreate {
|
||||||
|
fn id(&self) -> Option<Snowflake> {
|
||||||
|
match &self.d {
|
||||||
|
GuildCreateDataOption::UnavailableGuild(unavailable) => Some(unavailable.id),
|
||||||
|
GuildCreateDataOption::Guild(guild) => Some(guild.id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _: Arc<RwLock<Guild>>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
@ -34,7 +56,7 @@ impl Default for GuildCreateDataOption {
|
||||||
impl WebSocketEvent for GuildCreate {}
|
impl WebSocketEvent for GuildCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#guild-ban-add-guild-ban-add-event-fields;
|
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-ban-add-guild-ban-add-event-fields>;
|
||||||
/// Received to give info about a user being banned from a guild;
|
/// Received to give info about a user being banned from a guild;
|
||||||
pub struct GuildBanAdd {
|
pub struct GuildBanAdd {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
|
@ -44,7 +66,7 @@ pub struct GuildBanAdd {
|
||||||
impl WebSocketEvent for GuildBanAdd {}
|
impl WebSocketEvent for GuildBanAdd {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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 {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
|
@ -53,28 +75,49 @@ pub struct GuildBanRemove {
|
||||||
|
|
||||||
impl WebSocketEvent for GuildBanRemove {}
|
impl WebSocketEvent for GuildBanRemove {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#guild-update;
|
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-update>;
|
||||||
/// Received to give info about a guild being updated;
|
/// Received to give info about a guild being updated;
|
||||||
pub struct GuildUpdate {
|
pub struct GuildUpdate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub guild: Guild,
|
pub guild: Guild,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildUpdate {}
|
impl WebSocketEvent for GuildUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
impl UpdateMessage<Guild> for GuildUpdate {
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#guild-delete;
|
fn id(&self) -> Option<Snowflake> {
|
||||||
|
Some(self.guild.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, SourceUrlField, JsonField)]
|
||||||
|
/// 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 {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub guild: UnavailableGuild,
|
pub guild: UnavailableGuild,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateMessage<Guild> for GuildDelete {
|
||||||
|
fn id(&self) -> Option<Snowflake> {
|
||||||
|
Some(self.guild.id)
|
||||||
|
}
|
||||||
|
fn update(&mut self, _: Arc<RwLock<Guild>>) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildDelete {}
|
impl WebSocketEvent for GuildDelete {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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 {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -84,7 +127,7 @@ pub struct GuildAuditLogEntryCreate {
|
||||||
impl WebSocketEvent for GuildAuditLogEntryCreate {}
|
impl WebSocketEvent for GuildAuditLogEntryCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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 {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
|
@ -94,7 +137,7 @@ pub struct GuildEmojisUpdate {
|
||||||
impl WebSocketEvent for GuildEmojisUpdate {}
|
impl WebSocketEvent for GuildEmojisUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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 {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
|
@ -104,7 +147,7 @@ pub struct GuildStickersUpdate {
|
||||||
impl WebSocketEvent for GuildStickersUpdate {}
|
impl WebSocketEvent for GuildStickersUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
}
|
}
|
||||||
|
@ -112,7 +155,7 @@ pub struct GuildIntegrationsUpdate {
|
||||||
impl WebSocketEvent for GuildIntegrationsUpdate {}
|
impl WebSocketEvent for GuildIntegrationsUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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 {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -123,7 +166,7 @@ pub struct GuildMemberAdd {
|
||||||
impl WebSocketEvent for GuildMemberAdd {}
|
impl WebSocketEvent for GuildMemberAdd {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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 {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
|
@ -133,7 +176,7 @@ pub struct GuildMemberRemove {
|
||||||
impl WebSocketEvent for GuildMemberRemove {}
|
impl WebSocketEvent for GuildMemberRemove {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
pub roles: Vec<Snowflake>,
|
pub roles: Vec<Snowflake>,
|
||||||
|
@ -151,7 +194,7 @@ pub struct GuildMemberUpdate {
|
||||||
impl WebSocketEvent for GuildMemberUpdate {}
|
impl WebSocketEvent for GuildMemberUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
pub members: Vec<GuildMember>,
|
pub members: Vec<GuildMember>,
|
||||||
|
@ -164,26 +207,66 @@ pub struct GuildMembersChunk {
|
||||||
|
|
||||||
impl WebSocketEvent for GuildMembersChunk {}
|
impl WebSocketEvent for GuildMembersChunk {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#guild-role-create
|
/// See <https://discord.com/developers/docs/topics/gateway-events#guild-role-create>
|
||||||
pub struct GuildRoleCreate {
|
pub struct GuildRoleCreate {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
pub role: RoleObject,
|
pub role: RoleObject,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildRoleCreate {}
|
impl WebSocketEvent for GuildRoleCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[cfg(feature = "client")]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#guild-role-update
|
impl UpdateMessage<Guild> for GuildRoleCreate {
|
||||||
|
fn id(&self) -> Option<Snowflake> {
|
||||||
|
Some(self.guild_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, object_to_update: Arc<RwLock<Guild>>) {
|
||||||
|
let mut object_to_update = object_to_update.write().unwrap();
|
||||||
|
if object_to_update.roles.is_some() {
|
||||||
|
object_to_update
|
||||||
|
.roles
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.push(Arc::new(RwLock::new(self.role.clone())));
|
||||||
|
} else {
|
||||||
|
object_to_update.roles = Some(Vec::from([Arc::new(RwLock::new(self.role.clone()))]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)]
|
||||||
|
/// 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,
|
||||||
pub role: RoleObject,
|
pub role: RoleObject,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GuildRoleUpdate {}
|
impl WebSocketEvent for GuildRoleUpdate {}
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
impl UpdateMessage<RoleObject> for GuildRoleUpdate {
|
||||||
|
fn id(&self) -> Option<Snowflake> {
|
||||||
|
Some(self.role.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, object_to_update: Arc<RwLock<RoleObject>>) {
|
||||||
|
let mut write = object_to_update.write().unwrap();
|
||||||
|
*write = self.role.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// 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,
|
||||||
|
@ -192,7 +275,7 @@ pub struct GuildRoleDelete {
|
||||||
impl WebSocketEvent for GuildRoleDelete {}
|
impl WebSocketEvent for GuildRoleDelete {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
|
@ -201,7 +284,7 @@ pub struct GuildScheduledEventCreate {
|
||||||
impl WebSocketEvent for GuildScheduledEventCreate {}
|
impl WebSocketEvent for GuildScheduledEventCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
|
@ -210,7 +293,7 @@ pub struct GuildScheduledEventUpdate {
|
||||||
impl WebSocketEvent for GuildScheduledEventUpdate {}
|
impl WebSocketEvent for GuildScheduledEventUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
|
@ -219,7 +302,7 @@ pub struct GuildScheduledEventDelete {
|
||||||
impl WebSocketEvent for GuildScheduledEventDelete {}
|
impl WebSocketEvent for GuildScheduledEventDelete {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
pub user_id: Snowflake,
|
pub user_id: Snowflake,
|
||||||
|
@ -229,7 +312,7 @@ pub struct GuildScheduledEventUserAdd {
|
||||||
impl WebSocketEvent for GuildScheduledEventUserAdd {}
|
impl WebSocketEvent for GuildScheduledEventUserAdd {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::types::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)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
pub struct GatewayHello {
|
pub struct GatewayHello {
|
||||||
pub op: i32,
|
pub op: i32,
|
||||||
pub d: HelloData,
|
pub d: HelloData,
|
||||||
|
@ -10,7 +10,7 @@ pub struct GatewayHello {
|
||||||
|
|
||||||
impl WebSocketEvent for GatewayHello {}
|
impl WebSocketEvent for GatewayHello {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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
|
||||||
|
|
|
@ -2,7 +2,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)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
pub struct GatewayIdentifyPayload {
|
pub struct GatewayIdentifyPayload {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub properties: GatewayIdentifyConnectionProps,
|
pub properties: GatewayIdentifyConnectionProps,
|
||||||
|
@ -68,7 +68,7 @@ impl GatewayIdentifyPayload {
|
||||||
|
|
||||||
impl WebSocketEvent for GatewayIdentifyPayload {}
|
impl WebSocketEvent for GatewayIdentifyPayload {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
pub struct GatewayIdentifyConnectionProps {
|
pub struct GatewayIdentifyConnectionProps {
|
||||||
/// Almost always sent
|
/// Almost always sent
|
||||||
|
@ -144,7 +144,7 @@ impl GatewayIdentifyConnectionProps {
|
||||||
referring_domain: None,
|
referring_domain: None,
|
||||||
referrer_current: None,
|
referrer_current: None,
|
||||||
release_channel: String::from("stable"),
|
release_channel: String::from("stable"),
|
||||||
client_build_number: 199933,
|
client_build_number: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ impl GatewayIdentifyConnectionProps {
|
||||||
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")),
|
||||||
client_build_number: 199933,
|
client_build_number: 222963,
|
||||||
release_channel: String::from("stable"),
|
release_channel: String::from("stable"),
|
||||||
..Self::minimal()
|
..Self::minimal()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::types::{Integration, Snowflake, WebSocketEvent};
|
use crate::types::{Integration, Snowflake, WebSocketEvent};
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// 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)]
|
||||||
pub integration: Integration,
|
pub integration: Integration,
|
||||||
|
@ -13,7 +13,7 @@ pub struct IntegrationCreate {
|
||||||
impl WebSocketEvent for IntegrationCreate {}
|
impl WebSocketEvent for IntegrationCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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)]
|
||||||
pub integration: Integration,
|
pub integration: Integration,
|
||||||
|
@ -23,7 +23,7 @@ pub struct IntegrationUpdate {
|
||||||
impl WebSocketEvent for IntegrationUpdate {}
|
impl WebSocketEvent for IntegrationUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::types::{Interaction, WebSocketEvent};
|
use crate::types::{Interaction, WebSocketEvent};
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// 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,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::types::{GuildInvite, Snowflake, WebSocketEvent};
|
use crate::types::{GuildInvite, Snowflake, WebSocketEvent};
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// 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,
|
||||||
|
@ -12,7 +12,7 @@ pub struct InviteCreate {
|
||||||
impl WebSocketEvent for InviteCreate {}
|
impl WebSocketEvent for InviteCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[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,
|
||||||
pub guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
|
|
|
@ -13,7 +13,7 @@ use super::WebSocketEvent;
|
||||||
/// Sent by the official client when switching to a guild or channel;
|
/// Sent by the official client when switching to a guild or channel;
|
||||||
/// After this, you should recieve message updates
|
/// After this, you should recieve message updates
|
||||||
///
|
///
|
||||||
/// See https://luna.gitlab.io/discord-unofficial-docs/lazy_guilds.html#op-14-lazy-request
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/lazy_guilds.html#op-14-lazy-request>
|
||||||
///
|
///
|
||||||
/// {"op":14,"d":{"guild_id":"848582562217590824","typing":true,"activities":true,"threads":true}}
|
/// {"op":14,"d":{"guild_id":"848582562217590824","typing":true,"activities":true,"threads":true}}
|
||||||
pub struct LazyRequest {
|
pub struct LazyRequest {
|
||||||
|
|
|
@ -8,6 +8,8 @@ use crate::types::{
|
||||||
use super::WebSocketEvent;
|
use super::WebSocketEvent;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/topics/gateway-events#typing-start>
|
||||||
pub struct TypingStartEvent {
|
pub struct TypingStartEvent {
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
pub guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
|
@ -19,92 +21,106 @@ pub struct TypingStartEvent {
|
||||||
impl WebSocketEvent for TypingStartEvent {}
|
impl WebSocketEvent for TypingStartEvent {}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[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)]
|
||||||
message: Message,
|
pub message: Message,
|
||||||
guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
member: Option<GuildMember>,
|
pub member: Option<GuildMember>,
|
||||||
mentions: Option<Vec<MessageCreateUser>>,
|
pub mentions: Option<Vec<MessageCreateUser>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
/// 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)]
|
||||||
user: PublicUser,
|
pub user: PublicUser,
|
||||||
member: Option<GuildMember>,
|
pub member: Option<GuildMember>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageCreate {}
|
impl WebSocketEvent for MessageCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/topics/gateway-events#message-update>
|
||||||
pub struct MessageUpdate {
|
pub struct MessageUpdate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
message: Message,
|
pub message: Message,
|
||||||
guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
member: Option<GuildMember>,
|
pub member: Option<GuildMember>,
|
||||||
mentions: Option<Vec<MessageCreateUser>>,
|
pub mentions: Option<Vec<MessageCreateUser>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageUpdate {}
|
impl WebSocketEvent for MessageUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/topics/gateway-events#message-delete>
|
||||||
pub struct MessageDelete {
|
pub struct MessageDelete {
|
||||||
id: Snowflake,
|
pub id: Snowflake,
|
||||||
channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageDelete {}
|
impl WebSocketEvent for MessageDelete {}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/topics/gateway-events#message-delete-bulk>
|
||||||
pub struct MessageDeleteBulk {
|
pub struct MessageDeleteBulk {
|
||||||
ids: Vec<Snowflake>,
|
pub ids: Vec<Snowflake>,
|
||||||
channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageDeleteBulk {}
|
impl WebSocketEvent for MessageDeleteBulk {}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-add>
|
||||||
pub struct MessageReactionAdd {
|
pub struct MessageReactionAdd {
|
||||||
user_id: Snowflake,
|
pub user_id: Snowflake,
|
||||||
channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
message_id: Snowflake,
|
pub message_id: Snowflake,
|
||||||
guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
member: Option<GuildMember>,
|
pub member: Option<GuildMember>,
|
||||||
emoji: Emoji,
|
pub emoji: Emoji,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageReactionAdd {}
|
impl WebSocketEvent for MessageReactionAdd {}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove>
|
||||||
pub struct MessageReactionRemove {
|
pub struct MessageReactionRemove {
|
||||||
user_id: Snowflake,
|
pub user_id: Snowflake,
|
||||||
channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
message_id: Snowflake,
|
pub message_id: Snowflake,
|
||||||
guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
emoji: Emoji,
|
pub emoji: Emoji,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageReactionRemove {}
|
impl WebSocketEvent for MessageReactionRemove {}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-all>
|
||||||
pub struct MessageReactionRemoveAll {
|
pub struct MessageReactionRemoveAll {
|
||||||
channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
message_id: Snowflake,
|
pub message_id: Snowflake,
|
||||||
guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageReactionRemoveAll {}
|
impl WebSocketEvent for MessageReactionRemoveAll {}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/topics/gateway-events#message-reaction-remove-emoji>
|
||||||
pub struct MessageReactionRemoveEmoji {
|
pub struct MessageReactionRemoveEmoji {
|
||||||
channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
message_id: Snowflake,
|
pub message_id: Snowflake,
|
||||||
guild_id: Option<Snowflake>,
|
pub guild_id: Option<Snowflake>,
|
||||||
emoji: Emoji,
|
pub emoji: Emoji,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for MessageReactionRemoveEmoji {}
|
impl WebSocketEvent for MessageReactionRemoveEmoji {}
|
||||||
|
@ -114,7 +130,7 @@ impl WebSocketEvent for MessageReactionRemoveEmoji {}
|
||||||
///
|
///
|
||||||
/// Not documented anywhere unofficially
|
/// Not documented anywhere unofficially
|
||||||
///
|
///
|
||||||
/// Apparently "Message ACK refers to marking a message as read for Discord's API." (https://github.com/Rapptz/discord.py/issues/1851)
|
/// Apparently "Message ACK refers to marking a message as read for Discord's API." (<https://github.com/Rapptz/discord.py/issues/1851>)
|
||||||
/// I suspect this is sent and recieved from the gateway to let clients on other devices know the user has read a message
|
/// I suspect this is sent and recieved from the gateway to let clients on other devices know the user has read a message
|
||||||
///
|
///
|
||||||
/// {"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"}}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::gateway::Updateable;
|
|
||||||
pub use application::*;
|
pub use application::*;
|
||||||
pub use auto_moderation::*;
|
pub use auto_moderation::*;
|
||||||
pub use call::*;
|
pub use call::*;
|
||||||
|
@ -28,8 +27,25 @@ pub use voice::*;
|
||||||
pub use webhooks::*;
|
pub use webhooks::*;
|
||||||
pub use webrtc::*;
|
pub use webrtc::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
use super::Snowflake;
|
use super::Snowflake;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use crate::gateway::Updateable;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use serde_json::{from_str, from_value, to_value, Value};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
mod application;
|
mod application;
|
||||||
mod auto_moderation;
|
mod auto_moderation;
|
||||||
mod call;
|
mod call;
|
||||||
|
@ -58,12 +74,12 @@ mod webhooks;
|
||||||
|
|
||||||
mod webrtc;
|
mod webrtc;
|
||||||
|
|
||||||
pub trait WebSocketEvent {}
|
pub trait WebSocketEvent: Send + Sync + Debug {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Clone)]
|
#[derive(Debug, Default, Serialize, Clone)]
|
||||||
/// 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 [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]
|
||||||
/// Also, we never need to send the event name
|
/// Also, we never need to send the event name
|
||||||
pub struct GatewaySendPayload {
|
pub struct GatewaySendPayload {
|
||||||
#[serde(rename = "op")]
|
#[serde(rename = "op")]
|
||||||
|
@ -82,9 +98,6 @@ 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
|
||||||
///
|
|
||||||
/// Similar to [GatewaySendPayload], except we send a [Value] for d whilst we receive a [serde_json::value::RawValue]
|
|
||||||
/// Also, we never need to sent the event name
|
|
||||||
pub struct GatewayReceivePayload<'a> {
|
pub struct GatewayReceivePayload<'a> {
|
||||||
#[serde(rename = "op")]
|
#[serde(rename = "op")]
|
||||||
pub op_code: u8,
|
pub op_code: u8,
|
||||||
|
@ -102,6 +115,7 @@ pub struct GatewayReceivePayload<'a> {
|
||||||
|
|
||||||
impl<'a> WebSocketEvent for GatewayReceivePayload<'a> {}
|
impl<'a> WebSocketEvent for GatewayReceivePayload<'a> {}
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
/// An [`UpdateMessage<T>`] represents a received Gateway Message which contains updated
|
/// An [`UpdateMessage<T>`] represents a received Gateway Message which contains updated
|
||||||
/// information for an [`Updateable`] of Type T.
|
/// information for an [`Updateable`] of Type T.
|
||||||
/// # Example:
|
/// # Example:
|
||||||
|
@ -114,10 +128,37 @@ impl<'a> WebSocketEvent for GatewayReceivePayload<'a> {}
|
||||||
/// This would imply, that the [`WebSocketEvent`] "[`ChannelUpdate`]" contains new/updated information
|
/// This would imply, that the [`WebSocketEvent`] "[`ChannelUpdate`]" contains new/updated information
|
||||||
/// about a [`Channel`]. The update method describes how this new information will be turned into
|
/// about a [`Channel`]. The update method describes how this new information will be turned into
|
||||||
/// a [`Channel`] object.
|
/// a [`Channel`] object.
|
||||||
pub(crate) trait UpdateMessage<T>: Clone
|
pub(crate) trait UpdateMessage<T>: Clone + JsonField + SourceUrlField
|
||||||
where
|
where
|
||||||
T: Updateable,
|
T: Updateable + Serialize + DeserializeOwned + Clone,
|
||||||
{
|
{
|
||||||
fn update(&self, object_to_update: &mut T);
|
fn update(&mut self, object_to_update: Arc<RwLock<T>>) {
|
||||||
fn id(&self) -> Snowflake;
|
update_object(self.get_json(), object_to_update)
|
||||||
|
}
|
||||||
|
fn id(&self) -> Option<Snowflake>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait JsonField: Clone {
|
||||||
|
fn set_json(&mut self, json: String);
|
||||||
|
fn get_json(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SourceUrlField: Clone {
|
||||||
|
fn set_source_url(&mut self, url: String);
|
||||||
|
fn get_source_url(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
/// Only applicable for events where the Update struct is the same as the Entity struct
|
||||||
|
pub(crate) fn update_object(
|
||||||
|
value: String,
|
||||||
|
object: Arc<RwLock<(impl Updateable + Serialize + DeserializeOwned + Clone)>>,
|
||||||
|
) {
|
||||||
|
let data_from_event: HashMap<String, Value> = from_str(&value).unwrap();
|
||||||
|
let mut original_data: HashMap<String, Value> =
|
||||||
|
from_value(to_value(object.clone()).unwrap()).unwrap();
|
||||||
|
for (updated_entry_key, updated_entry_value) in data_from_event.into_iter() {
|
||||||
|
original_data.insert(updated_entry_key.clone(), updated_entry_value);
|
||||||
|
}
|
||||||
|
*object.write().unwrap() = from_value(to_value(original_data).unwrap()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// 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 {
|
||||||
/// unix time of when the client went idle, or none if client is not idle
|
/// Unix time of when the client went idle, or none if client is not idle.
|
||||||
pub since: Option<u128>,
|
pub since: Option<u128>,
|
||||||
/// the client's status (online, invisible, offline, dnd, idle..)
|
/// the client's status (online, invisible, offline, dnd, idle..)
|
||||||
pub status: UserStatus,
|
pub status: UserStatus,
|
||||||
|
@ -14,9 +14,9 @@ pub struct UpdatePresence {
|
||||||
pub afk: bool,
|
pub afk: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq)]
|
||||||
/// 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>
|
||||||
pub struct PresenceUpdate {
|
pub struct PresenceUpdate {
|
||||||
pub user: PublicUser,
|
pub user: PublicUser,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::types::{Activity, GuildMember, PresenceUpdate, VoiceState};
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// 1/2 half documented;
|
/// 1/2 half documented;
|
||||||
/// Received after identifying, provides initial user info;
|
/// Received after identifying, provides initial user info;
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#ready;
|
/// See <https://discord.com/developers/docs/topics/gateway-events#ready;>
|
||||||
pub struct GatewayReady {
|
pub struct GatewayReady {
|
||||||
pub analytics_token: Option<String>,
|
pub analytics_token: Option<String>,
|
||||||
pub auth_session_id_hash: Option<String>,
|
pub auth_session_id_hash: Option<String>,
|
||||||
|
@ -16,7 +16,7 @@ pub struct GatewayReady {
|
||||||
|
|
||||||
pub v: u8,
|
pub v: u8,
|
||||||
pub user: User,
|
pub user: User,
|
||||||
/// For bots these are [UnavailableGuild]s, for users they are [Guild]
|
/// For bots these are [crate::types::UnavailableGuild]s, for users they are [Guild]
|
||||||
pub guilds: Vec<Guild>,
|
pub guilds: Vec<Guild>,
|
||||||
pub presences: Option<Vec<PresenceUpdate>>,
|
pub presences: Option<Vec<PresenceUpdate>>,
|
||||||
pub sessions: Option<Vec<Session>>,
|
pub sessions: Option<Vec<Session>>,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::types::{events::WebSocketEvent, Relationship, RelationshipType, Snowf
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default)]
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
/// See https://github.com/spacebarchat/server/issues/204
|
/// See <https://github.com/spacebarchat/server/issues/204>
|
||||||
pub struct RelationshipAdd {
|
pub struct RelationshipAdd {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub relationship: Relationship,
|
pub relationship: Relationship,
|
||||||
|
@ -12,7 +12,7 @@ pub struct RelationshipAdd {
|
||||||
impl WebSocketEvent for RelationshipAdd {}
|
impl WebSocketEvent for RelationshipAdd {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// See https://github.com/spacebarchat/server/issues/203
|
/// See <https://github.com/spacebarchat/server/issues/203>
|
||||||
pub struct RelationshipRemove {
|
pub struct RelationshipRemove {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::types::{events::WebSocketEvent, Snowflake};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default)]
|
#[derive(Debug, Deserialize, Serialize, Default)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#request-guild-members-request-guild-members-structure
|
/// See <https://discord.com/developers/docs/topics/gateway-events#request-guild-members-request-guild-members-structure>
|
||||||
pub struct GatewayRequestGuildMembers {
|
pub struct GatewayRequestGuildMembers {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
pub query: Option<String>,
|
pub query: Option<String>,
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub struct SessionsReplace {
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// Session info for the current user
|
/// Session info for the current user
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
pub activities: Vec<Activity>,
|
pub activities: Option<Vec<Activity>>,
|
||||||
pub client_info: ClientInfo,
|
pub client_info: ClientInfo,
|
||||||
pub session_id: String,
|
pub session_id: String,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
|
@ -24,7 +24,7 @@ pub struct Session {
|
||||||
/// {"client":"web","os":"other","version":0}
|
/// {"client":"web","os":"other","version":0}
|
||||||
// Note: I don't think this one exists yet? Though I might've made a mistake and this might be a duplicate
|
// Note: I don't think this one exists yet? Though I might've made a mistake and this might be a duplicate
|
||||||
pub struct ClientInfo {
|
pub struct ClientInfo {
|
||||||
pub client: String,
|
pub client: Option<String>,
|
||||||
pub os: String,
|
pub os: String,
|
||||||
pub version: u8,
|
pub version: u8,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::types::{StageInstance, WebSocketEvent};
|
use crate::types::{StageInstance, WebSocketEvent};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#stage-instance-create
|
/// See <https://discord.com/developers/docs/topics/gateway-events#stage-instance-create>
|
||||||
pub struct StageInstanceCreate {
|
pub struct StageInstanceCreate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub stage_instance: StageInstance,
|
pub stage_instance: StageInstance,
|
||||||
|
@ -12,7 +12,7 @@ pub struct StageInstanceCreate {
|
||||||
impl WebSocketEvent for StageInstanceCreate {}
|
impl WebSocketEvent for StageInstanceCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#stage-instance-update
|
/// See <https://discord.com/developers/docs/topics/gateway-events#stage-instance-update>
|
||||||
pub struct StageInstanceUpdate {
|
pub struct StageInstanceUpdate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub stage_instance: StageInstance,
|
pub stage_instance: StageInstance,
|
||||||
|
@ -21,7 +21,7 @@ pub struct StageInstanceUpdate {
|
||||||
impl WebSocketEvent for StageInstanceUpdate {}
|
impl WebSocketEvent for StageInstanceUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#stage-instance-delete
|
/// See <https://discord.com/developers/docs/topics/gateway-events#stage-instance-delete>
|
||||||
pub struct StageInstanceDelete {
|
pub struct StageInstanceDelete {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub stage_instance: StageInstance,
|
pub stage_instance: StageInstance,
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
|
use chorus_macros::{JsonField, SourceUrlField};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::entities::{Channel, ThreadMember};
|
use crate::types::entities::{Channel, ThreadMember};
|
||||||
use crate::types::events::WebSocketEvent;
|
use crate::types::events::WebSocketEvent;
|
||||||
use crate::types::Snowflake;
|
use crate::types::{JsonField, Snowflake, SourceUrlField};
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use super::UpdateMessage;
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#thread-create
|
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-create>
|
||||||
pub struct ThreadCreate {
|
pub struct ThreadCreate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub thread: Channel,
|
pub thread: Channel,
|
||||||
|
@ -13,17 +17,28 @@ pub struct ThreadCreate {
|
||||||
|
|
||||||
impl WebSocketEvent for ThreadCreate {}
|
impl WebSocketEvent for ThreadCreate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, JsonField, SourceUrlField)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#thread-update
|
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-update>
|
||||||
pub struct ThreadUpdate {
|
pub struct ThreadUpdate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub thread: Channel,
|
pub thread: Channel,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub json: String,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub source_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for ThreadUpdate {}
|
impl WebSocketEvent for ThreadUpdate {}
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
impl UpdateMessage<Channel> for ThreadUpdate {
|
||||||
|
fn id(&self) -> Option<Snowflake> {
|
||||||
|
Some(self.thread.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#thread-delete
|
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-delete>
|
||||||
pub struct ThreadDelete {
|
pub struct ThreadDelete {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub thread: Channel,
|
pub thread: Channel,
|
||||||
|
@ -32,7 +47,7 @@ pub struct ThreadDelete {
|
||||||
impl WebSocketEvent for ThreadDelete {}
|
impl WebSocketEvent for ThreadDelete {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#thread-list-sync
|
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-list-sync>
|
||||||
pub struct ThreadListSync {
|
pub struct ThreadListSync {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
pub channel_ids: Option<Vec<Snowflake>>,
|
pub channel_ids: Option<Vec<Snowflake>>,
|
||||||
|
@ -43,7 +58,7 @@ pub struct ThreadListSync {
|
||||||
impl WebSocketEvent for ThreadListSync {}
|
impl WebSocketEvent for ThreadListSync {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#thread-member-update
|
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-member-update>
|
||||||
/// The inner payload is a thread member object with an extra field.
|
/// The inner payload is a thread member object with an extra field.
|
||||||
pub struct ThreadMemberUpdate {
|
pub struct ThreadMemberUpdate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -54,7 +69,7 @@ pub struct ThreadMemberUpdate {
|
||||||
impl WebSocketEvent for ThreadMemberUpdate {}
|
impl WebSocketEvent for ThreadMemberUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#thread-members-update
|
/// See <https://discord.com/developers/docs/topics/gateway-events#thread-members-update>
|
||||||
pub struct ThreadMembersUpdate {
|
pub struct ThreadMembersUpdate {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
|
|
|
@ -4,8 +4,8 @@ use crate::types::entities::PublicUser;
|
||||||
use crate::types::events::WebSocketEvent;
|
use crate::types::events::WebSocketEvent;
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#user-update;
|
/// See <https://discord.com/developers/docs/topics/gateway-events#user-update>;
|
||||||
/// Sent to indicate updates to a user object; (name changes, discriminator changes, etc);
|
/// Sent to indicate updates to a user object; (name changes, discriminator changes, etc);
|
||||||
pub struct UserUpdate {
|
pub struct UserUpdate {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -14,7 +14,7 @@ pub struct UserUpdate {
|
||||||
|
|
||||||
impl WebSocketEvent for UserUpdate {}
|
impl WebSocketEvent for UserUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
/// Undocumented;
|
/// Undocumented;
|
||||||
///
|
///
|
||||||
/// Possibly an update for muted guild / channel settings for the current user;
|
/// Possibly an update for muted guild / channel settings for the current user;
|
||||||
|
@ -39,7 +39,7 @@ pub struct UserGuildSettingsUpdate {
|
||||||
|
|
||||||
impl WebSocketEvent for UserGuildSettingsUpdate {}
|
impl WebSocketEvent for UserGuildSettingsUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
/// Undocumented;
|
/// Undocumented;
|
||||||
///
|
///
|
||||||
/// Received in [UserGuildSettingsUpdate];
|
/// Received in [UserGuildSettingsUpdate];
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::types::{events::WebSocketEvent, Snowflake, VoiceState};
|
use crate::types::{events::WebSocketEvent, Snowflake, VoiceState};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
///
|
///
|
||||||
/// Sent to the server to indicate an update of the voice state (leave voice channel, join voice channel, mute, deafen);
|
/// Sent to the server to indicate an update of the voice state (leave voice channel, join voice channel, mute, deafen);
|
||||||
///
|
///
|
||||||
|
@ -16,7 +16,7 @@ pub struct UpdateVoiceState {
|
||||||
impl WebSocketEvent for UpdateVoiceState {}
|
impl WebSocketEvent for UpdateVoiceState {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#voice-state-update;
|
/// See <https://discord.com/developers/docs/topics/gateway-events#voice-state-update>;
|
||||||
///
|
///
|
||||||
/// Received from the server to indicate an update in a user's voice state (leave voice channel, join voice channel, mute, deafen, etc);
|
/// Received from the server to indicate an update in a user's voice state (leave voice channel, join voice channel, mute, deafen, etc);
|
||||||
///
|
///
|
||||||
|
@ -28,8 +28,8 @@ pub struct VoiceStateUpdate {
|
||||||
|
|
||||||
impl WebSocketEvent for VoiceStateUpdate {}
|
impl WebSocketEvent for VoiceStateUpdate {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#voice-server-update;
|
/// See <https://discord.com/developers/docs/topics/gateway-events#voice-server-update>;
|
||||||
///
|
///
|
||||||
/// Received to indicate which voice endpoint, token and guild_id to use;
|
/// Received to indicate which voice endpoint, token and guild_id to use;
|
||||||
pub struct VoiceServerUpdate {
|
pub struct VoiceServerUpdate {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::types::Snowflake;
|
||||||
use super::WebSocketEvent;
|
use super::WebSocketEvent;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#webhooks-update
|
/// See <https://discord.com/developers/docs/topics/gateway-events#webhooks-update>
|
||||||
pub struct WebhooksUpdate {
|
pub struct WebhooksUpdate {
|
||||||
pub guild_id: Snowflake,
|
pub guild_id: Snowflake,
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::types::{Snowflake, WebSocketEvent};
|
use crate::types::{Snowflake, WebSocketEvent};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
|
||||||
/// The identify payload for the webrtc stream;
|
/// The identify payload for the webrtc stream;
|
||||||
/// Contains info to begin a webrtc connection;
|
/// Contains info to begin a webrtc connection;
|
||||||
/// See https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-websocket-connection-example-voice-identify-payload;
|
/// See https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-websocket-connection-example-voice-identify-payload;
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl<'a> WebSocketEvent for VoiceGatewayReceivePayload<'a> {}
|
||||||
|
|
||||||
/// The modes of encryption available in webrtc connections;
|
/// The modes of encryption available in webrtc connections;
|
||||||
/// See https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-encryption-modes;
|
/// See https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-encryption-modes;
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy)]
|
#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum WebrtcEncryptionMode {
|
pub enum WebrtcEncryptionMode {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::WebrtcEncryptionMode;
|
use super::WebrtcEncryptionMode;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
/// The ready event for the webrtc stream;
|
/// The ready event for the webrtc stream;
|
||||||
/// Used to give info after the identify event;
|
/// Used to give info after the identify event;
|
||||||
/// See https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-websocket-connection-example-voice-ready-payload;
|
/// See https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-websocket-connection-example-voice-ready-payload;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{entities::Emoji, Snowflake};
|
use crate::types::{entities::Emoji, Snowflake};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
pub struct Activity {
|
pub struct Activity {
|
||||||
name: String,
|
name: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -22,19 +22,19 @@ pub struct Activity {
|
||||||
buttons: Option<Vec<ActivityButton>>,
|
buttons: Option<Vec<ActivityButton>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq)]
|
||||||
struct ActivityTimestamps {
|
struct ActivityTimestamps {
|
||||||
start: Option<i64>,
|
start: Option<i64>,
|
||||||
end: Option<i64>,
|
end: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
struct ActivityParty {
|
struct ActivityParty {
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
size: Option<Vec<(i32, i32)>>,
|
size: Option<Vec<(i32, i32)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
struct ActivityAssets {
|
struct ActivityAssets {
|
||||||
large_image: Option<String>,
|
large_image: Option<String>,
|
||||||
large_text: Option<String>,
|
large_text: Option<String>,
|
||||||
|
@ -42,7 +42,7 @@ struct ActivityAssets {
|
||||||
small_text: Option<String>,
|
small_text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
struct ActivitySecrets {
|
struct ActivitySecrets {
|
||||||
join: Option<String>,
|
join: Option<String>,
|
||||||
spectate: Option<String>,
|
spectate: Option<String>,
|
||||||
|
@ -50,7 +50,7 @@ struct ActivitySecrets {
|
||||||
match_string: Option<String>,
|
match_string: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
struct ActivityButton {
|
struct ActivityButton {
|
||||||
label: String,
|
label: String,
|
||||||
url: String,
|
url: String,
|
||||||
|
|
|
@ -2,14 +2,14 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Hash)]
|
||||||
pub struct WelcomeScreenObject {
|
pub struct WelcomeScreenObject {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub welcome_channels: Vec<WelcomeScreenChannel>,
|
pub welcome_channels: Vec<WelcomeScreenChannel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Hash)]
|
||||||
pub struct WelcomeScreenChannel {
|
pub struct WelcomeScreenChannel {
|
||||||
pub channel_id: Snowflake,
|
pub channel_id: Snowflake,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub enum InteractionType {
|
||||||
ApplicationCommand = 2,
|
ApplicationCommand = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum InteractionResponseType {
|
pub enum InteractionResponseType {
|
||||||
SelfCommandResponse = 0,
|
SelfCommandResponse = 0,
|
||||||
Pong = 1,
|
Pong = 1,
|
||||||
|
@ -33,6 +34,7 @@ pub enum InteractionResponseType {
|
||||||
AcknowledgeWithSource = 5,
|
AcknowledgeWithSource = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct InteractionApplicationCommandCallbackData {
|
pub struct InteractionApplicationCommandCallbackData {
|
||||||
pub tts: bool,
|
pub tts: bool,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, PartialEq, Eq)]
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#client-status-object
|
/// See <https://discord.com/developers/docs/topics/gateway-events#client-status-object>
|
||||||
pub struct ClientStatusObject {
|
pub struct ClientStatusObject {
|
||||||
pub desktop: Option<String>,
|
pub desktop: Option<String>,
|
||||||
pub mobile: Option<String>,
|
pub mobile: Option<String>,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! All the types, entities, events and interfaces of the Spacebar API.
|
||||||
|
|
||||||
pub use config::*;
|
pub use config::*;
|
||||||
pub use entities::*;
|
pub use entities::*;
|
||||||
pub use errors::*;
|
pub use errors::*;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct RegisterSchema {
|
pub struct RegisterSchema {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
@ -15,7 +15,7 @@ pub struct RegisterSchema {
|
||||||
pub promotional_email_opt_in: Option<bool>,
|
pub promotional_email_opt_in: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct LoginSchema {
|
pub struct LoginSchema {
|
||||||
/// For Discord, usernames must be between 2 and 32 characters,
|
/// For Discord, usernames must be between 2 and 32 characters,
|
||||||
|
@ -30,7 +30,7 @@ pub struct LoginSchema {
|
||||||
pub gift_code_sku_id: Option<String>,
|
pub gift_code_sku_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct TotpSchema {
|
pub struct TotpSchema {
|
||||||
code: String,
|
code: String,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::types::ChannelType;
|
||||||
use crate::types::{entities::PermissionOverwrite, Snowflake};
|
use crate::types::{entities::PermissionOverwrite, Snowflake};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, PartialOrd)]
|
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, PartialOrd)]
|
||||||
|
@ -8,7 +9,7 @@ use crate::types::{entities::PermissionOverwrite, Snowflake};
|
||||||
pub struct ChannelCreateSchema {
|
pub struct ChannelCreateSchema {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub channel_type: Option<u8>,
|
pub channel_type: Option<ChannelType>,
|
||||||
pub topic: Option<String>,
|
pub topic: Option<String>,
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
pub bitrate: Option<i32>,
|
pub bitrate: Option<i32>,
|
||||||
|
@ -148,3 +149,12 @@ pub struct AddChannelRecipientSchema {
|
||||||
pub access_token: Option<String>,
|
pub access_token: Option<String>,
|
||||||
pub nick: Option<String>,
|
pub nick: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#add-channel-recipient>
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
|
pub struct ModifyChannelPositionsSchema {
|
||||||
|
pub id: Snowflake,
|
||||||
|
pub position: Option<u32>,
|
||||||
|
pub lock_permissions: Option<bool>,
|
||||||
|
pub parent_id: Option<Snowflake>,
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::entities::Channel;
|
use crate::types::entities::Channel;
|
||||||
|
use crate::types::types::guild_configuration::GuildFeatures;
|
||||||
|
use crate::types::{
|
||||||
|
Emoji, ExplicitContentFilterLevel, MessageNotificationLevel, Snowflake, Sticker,
|
||||||
|
SystemChannelFlags, VerificationLevel,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
/// Represents the schema which needs to be sent to create a Guild.
|
/// Represents the schema which needs to be sent to create a Guild.
|
||||||
/// See: [https://docs.spacebar.chat/routes/#cmp--schemas-guildcreateschema](https://docs.spacebar.chat/routes/#cmp--schemas-guildcreateschema)
|
/// See: <https://docs.spacebar.chat/routes/#cmp--schemas-guildcreateschema>
|
||||||
pub struct GuildCreateSchema {
|
pub struct GuildCreateSchema {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub region: Option<String>,
|
pub region: Option<String>,
|
||||||
|
@ -15,3 +22,144 @@ pub struct GuildCreateSchema {
|
||||||
pub system_channel_id: Option<String>,
|
pub system_channel_id: Option<String>,
|
||||||
pub rules_channel_id: Option<String>,
|
pub rules_channel_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, Copy, Eq, PartialEq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
/// Represents the schema which needs to be sent to create a Guild Ban.
|
||||||
|
/// See: <https://discord-userdoccers.vercel.app/resources/guild#create-guild-ban>
|
||||||
|
pub struct GuildBanCreateSchema {
|
||||||
|
pub delete_message_days: Option<u8>,
|
||||||
|
pub delete_message_seconds: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Default, Clone, Eq, PartialEq)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub struct GuildModifySchema {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub icon: Option<Vec<u8>>,
|
||||||
|
pub banner: Option<Vec<u8>>,
|
||||||
|
pub home_header: Option<Vec<u8>>,
|
||||||
|
pub splash: Option<Vec<u8>>,
|
||||||
|
pub discovery_splash: Option<Vec<u8>>,
|
||||||
|
pub owner_id: Option<Snowflake>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub region: Option<String>,
|
||||||
|
pub afk_channel_id: Option<Snowflake>,
|
||||||
|
pub afk_timeout: Option<u16>,
|
||||||
|
pub verification_level: Option<VerificationLevel>,
|
||||||
|
pub default_message_notifications: Option<MessageNotificationLevel>,
|
||||||
|
pub explicit_content_filter: Option<ExplicitContentFilterLevel>,
|
||||||
|
pub features: Option<Vec<GuildFeatures>>,
|
||||||
|
pub system_channel_id: Option<Snowflake>,
|
||||||
|
pub system_channel_flags: Option<SystemChannelFlags>,
|
||||||
|
pub rules_channel_id: Option<Snowflake>,
|
||||||
|
pub public_updates_channel_id: Option<Snowflake>,
|
||||||
|
pub safety_alerts_channel_id: Option<Snowflake>,
|
||||||
|
pub preferred_locale: Option<String>,
|
||||||
|
pub premium_progress_bar_enabled: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
pub struct GetUserGuildSchema {
|
||||||
|
pub before: Option<Snowflake>,
|
||||||
|
pub after: Option<Snowflake>,
|
||||||
|
pub limit: Option<u8>,
|
||||||
|
pub with_counts: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for GetUserGuildSchema {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
before: Default::default(),
|
||||||
|
after: Default::default(),
|
||||||
|
limit: Some(200),
|
||||||
|
with_counts: Some(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct GuildPreview {
|
||||||
|
pub id: Snowflake,
|
||||||
|
pub name: String,
|
||||||
|
pub icon: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub splash: Option<String>,
|
||||||
|
pub discovery_splash: Option<String>,
|
||||||
|
pub home_header: Option<String>,
|
||||||
|
pub features: Vec<String>,
|
||||||
|
pub emojis: Vec<Emoji>,
|
||||||
|
pub stickers: Vec<Sticker>,
|
||||||
|
pub approximate_member_count: u32,
|
||||||
|
pub approximate_presence_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
pub struct GuildMemberSearchSchema {
|
||||||
|
pub query: String,
|
||||||
|
pub limit: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GuildMemberSearchSchema {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
query: Default::default(),
|
||||||
|
limit: Some(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
pub struct ModifyGuildMemberSchema {
|
||||||
|
pub nick: Option<String>,
|
||||||
|
pub roles: Option<Vec<Snowflake>>,
|
||||||
|
pub mute: Option<bool>,
|
||||||
|
pub deaf: Option<bool>,
|
||||||
|
pub channel_id: Option<Snowflake>,
|
||||||
|
pub communication_disabled_until: Option<DateTime<Utc>>,
|
||||||
|
pub flags: Option<GuildMemberFlags>,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
|
||||||
|
/// Represents the flags of a Guild Member.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#guild-member-flags>
|
||||||
|
pub struct GuildMemberFlags: u64 {
|
||||||
|
const DID_REJOIN = 1 << 0;
|
||||||
|
const COMPLETED_ONBOARDING = 1 << 1;
|
||||||
|
const BYPASSES_VERIFICATION = 1 << 2;
|
||||||
|
const STARTED_ONBOARDING = 1 << 3;
|
||||||
|
const GUEST = 1 << 3;
|
||||||
|
const AUTOMOD_QUARANTINED_NAME = 1 << 7;
|
||||||
|
const AUTOMOD_QUARANTINED_BIO = 1 << 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
pub struct ModifyCurrentGuildMemberSchema {
|
||||||
|
pub nick: Option<String>,
|
||||||
|
pub avatar: Option<String>,
|
||||||
|
pub bio: Option<String>,
|
||||||
|
pub banner: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
pub struct ModifyGuildMemberProfileSchema {
|
||||||
|
pub pronouns: Option<String>,
|
||||||
|
pub bio: Option<String>,
|
||||||
|
pub banner: Option<String>,
|
||||||
|
pub accent_color: Option<String>,
|
||||||
|
pub theme_colors: Option<Vec<i32>>,
|
||||||
|
pub popout_animation_particle_type: Option<Snowflake>,
|
||||||
|
pub emoji_id: Option<Snowflake>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
/// The limit argument is a number between 1 and 1000.
|
||||||
|
pub struct GuildBansQuery {
|
||||||
|
pub before: Option<Snowflake>,
|
||||||
|
pub after: Option<Snowflake>,
|
||||||
|
pub limit: Option<u16>,
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,9 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::types::entities::{
|
use crate::types::entities::{
|
||||||
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
|
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
|
||||||
};
|
};
|
||||||
|
use crate::types::{Attachment, Snowflake};
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct MessageSendSchema {
|
pub struct MessageSendSchema {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -19,3 +20,107 @@ pub struct MessageSendSchema {
|
||||||
pub sticker_ids: Option<Vec<String>>,
|
pub sticker_ids: Option<Vec<String>>,
|
||||||
pub attachments: Option<Vec<PartialDiscordFileAttachment>>,
|
pub attachments: Option<Vec<PartialDiscordFileAttachment>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MessageSearchEndpoint {
|
||||||
|
GuildChannel(Snowflake),
|
||||||
|
Channel(Snowflake),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MessageSearchEndpoint {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
MessageSearchEndpoint::Channel(id) => {
|
||||||
|
write!(f, "channels/{}", &id.to_string())
|
||||||
|
}
|
||||||
|
MessageSearchEndpoint::GuildChannel(id) => {
|
||||||
|
write!(f, "guilds/{}", &id.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
/// Represents a Message Search Query JSON Body.
|
||||||
|
/// The `channel_id` field is not applicable when using the `GET /channels/{channel.id}/messages/search` endpoint.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/message#search-messages>
|
||||||
|
pub struct MessageSearchQuery {
|
||||||
|
pub attachment_extension: Option<Vec<String>>,
|
||||||
|
pub attachment_filename: Option<Vec<String>>,
|
||||||
|
pub author_id: Option<Vec<Snowflake>>,
|
||||||
|
pub author_type: Option<Vec<String>>,
|
||||||
|
pub channel_id: Option<Vec<Snowflake>>,
|
||||||
|
pub command_id: Option<Vec<Snowflake>>,
|
||||||
|
pub content: Option<String>,
|
||||||
|
pub embed_provider: Option<Vec<String>>,
|
||||||
|
pub embed_type: Option<Vec<String>>,
|
||||||
|
pub has: Option<Vec<String>>,
|
||||||
|
pub include_nsfw: Option<bool>,
|
||||||
|
pub limit: Option<i32>,
|
||||||
|
pub link_hostname: Option<Vec<String>>,
|
||||||
|
pub max_id: Option<String>,
|
||||||
|
pub mention_everyone: Option<bool>,
|
||||||
|
pub mentions: Option<Vec<Snowflake>>,
|
||||||
|
pub min_id: Option<String>,
|
||||||
|
pub offset: Option<i32>,
|
||||||
|
pub pinned: Option<bool>,
|
||||||
|
pub sort_by: Option<String>,
|
||||||
|
pub sort_order: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for MessageSearchQuery {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
attachment_extension: Default::default(),
|
||||||
|
attachment_filename: Default::default(),
|
||||||
|
author_id: Default::default(),
|
||||||
|
author_type: Default::default(),
|
||||||
|
channel_id: Default::default(),
|
||||||
|
command_id: Default::default(),
|
||||||
|
content: Default::default(),
|
||||||
|
embed_provider: Default::default(),
|
||||||
|
embed_type: Default::default(),
|
||||||
|
has: Default::default(),
|
||||||
|
include_nsfw: Some(false),
|
||||||
|
limit: Some(25),
|
||||||
|
link_hostname: Default::default(),
|
||||||
|
max_id: Default::default(),
|
||||||
|
mention_everyone: Default::default(),
|
||||||
|
mentions: Default::default(),
|
||||||
|
min_id: Default::default(),
|
||||||
|
offset: Some(0),
|
||||||
|
pinned: Default::default(),
|
||||||
|
sort_by: Default::default(),
|
||||||
|
sort_order: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct CreateGreetMessage {
|
||||||
|
pub sticker_ids: Vec<Snowflake>,
|
||||||
|
pub allowed_mentions: Option<AllowedMention>,
|
||||||
|
pub message_reference: Option<MessageReference>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct MessageAck {
|
||||||
|
pub token: Option<String>,
|
||||||
|
pub manual: Option<bool>,
|
||||||
|
pub mention_count: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct MessageModifySchema {
|
||||||
|
content: Option<String>,
|
||||||
|
embeds: Option<Vec<Embed>>,
|
||||||
|
embed: Option<Embed>,
|
||||||
|
allowed_mentions: Option<AllowedMention>,
|
||||||
|
components: Option<Vec<Component>>,
|
||||||
|
flags: Option<i32>,
|
||||||
|
files: Option<Vec<u8>>,
|
||||||
|
payload_json: Option<String>,
|
||||||
|
attachments: Option<Vec<Attachment>>,
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
/// Represents the schema which needs to be sent to create or modify a Role.
|
/// Represents the schema which needs to be sent to create or modify a Role.
|
||||||
/// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema)
|
/// See: [https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema](https://docs.spacebar.chat/routes/#cmp--schemas-rolemodifyschema)
|
||||||
|
|
|
@ -4,8 +4,9 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::Snowflake;
|
use crate::types::Snowflake;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
/// A schema used to modify a user.
|
||||||
pub struct UserModifySchema {
|
pub struct UserModifySchema {
|
||||||
pub username: Option<String>,
|
pub username: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
|
@ -19,6 +20,8 @@ pub struct UserModifySchema {
|
||||||
pub discriminator: Option<i16>,
|
pub discriminator: Option<i16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A schema used to create a private channel.
|
||||||
|
///
|
||||||
/// # Attributes:
|
/// # Attributes:
|
||||||
/// - recipients: The users to include in the private channel
|
/// - recipients: The users to include in the private channel
|
||||||
/// - access_tokens: The access tokens of users that have granted your app the `gdm.join` scope. Only usable for OAuth2 requests (which can only create group DMs).
|
/// - access_tokens: The access tokens of users that have granted your app the `gdm.join` scope. Only usable for OAuth2 requests (which can only create group DMs).
|
||||||
|
@ -26,7 +29,7 @@ pub struct UserModifySchema {
|
||||||
///
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// Read: <https://discord-userdoccers.vercel.app/resources/channel#json-params>
|
/// Read: <https://discord-userdoccers.vercel.app/resources/channel#json-params>
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
|
||||||
pub struct PrivateChannelCreateSchema {
|
pub struct PrivateChannelCreateSchema {
|
||||||
pub recipients: Option<Vec<Snowflake>>,
|
pub recipients: Option<Vec<Snowflake>>,
|
||||||
pub access_tokens: Option<Vec<String>>,
|
pub access_tokens: Option<Vec<String>>,
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
|
use crate::types::utils::Snowflake;
|
||||||
use jsonwebtoken::{encode, EncodingKey, Header};
|
use jsonwebtoken::{encode, EncodingKey, Header};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
|
||||||
|
|
||||||
pub fn generate_token(id: &Snowflake, email: String, jwt_key: &str) -> String {
|
pub fn generate_token(id: &Snowflake, email: String, jwt_key: &str) -> String {
|
||||||
let claims = Claims::new(&email, id);
|
let claims = Claims::new(&email, id);
|
||||||
|
|
||||||
|
@ -11,7 +10,9 @@ pub fn generate_token(id: &Snowflake, email: String, jwt_key: &str) -> String {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Claims {
|
pub struct Claims {
|
||||||
|
/// When the token expires, unix epoch
|
||||||
pub exp: i64,
|
pub exp: i64,
|
||||||
|
/// When the token was issued
|
||||||
pub iat: i64,
|
pub iat: i64,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
|
@ -1,56 +1,124 @@
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
/// Rights are instance-wide, per-user permissions for everything you may perform on the instance,
|
||||||
|
/// such as sending messages, editing messages, or shutting down the server.
|
||||||
|
/// They are separate from guild member permissions, which only apply to a given guild.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// The default rights on Discord.com are 648540060672 ([source](https://github.com/spacebarchat/server/issues/878#issuecomment-1234669715))
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://docs.spacebar.chat/setup/server/security/rights/>
|
||||||
pub struct Rights: u64 {
|
pub struct Rights: u64 {
|
||||||
|
/// All rights
|
||||||
const OPERATOR = 1 << 0;
|
const OPERATOR = 1 << 0;
|
||||||
|
/// Ability to alter or remove others' applications
|
||||||
const MANAGE_APPLICATIONS = 1 << 1;
|
const MANAGE_APPLICATIONS = 1 << 1;
|
||||||
|
/// Same as the per-guild [MANAGE_GUILD] permission, but applies to all guilds and DM channels, can join any guild without invite
|
||||||
const MANAGE_GUILDS = 1 << 2;
|
const MANAGE_GUILDS = 1 << 2;
|
||||||
|
/// Can delete or edit any message they can read
|
||||||
const MANAGE_MESSAGES = 1 << 3;
|
const MANAGE_MESSAGES = 1 << 3;
|
||||||
|
/// Can add, change, define rate limits of other users,
|
||||||
|
/// can also grant others [BYPASS_RATE_LIMITS] when combined
|
||||||
|
/// with [BYPASS_RATE_LIMITS] and [MANAGE_USERS].
|
||||||
const MANAGE_RATE_LIMITS = 1 << 4;
|
const MANAGE_RATE_LIMITS = 1 << 4;
|
||||||
|
/// Can create, alter, enable and disable custom message routing rules in any channel/guild
|
||||||
const MANAGE_ROUTING = 1 << 5;
|
const MANAGE_ROUTING = 1 << 5;
|
||||||
|
/// Respond to or resolve other users' support tickets
|
||||||
const MANAGE_TICKETS = 1 << 6;
|
const MANAGE_TICKETS = 1 << 6;
|
||||||
|
/// Can create, alter, remove and ban users; can also create, modify and remove user groups
|
||||||
const MANAGE_USERS = 1 << 7;
|
const MANAGE_USERS = 1 << 7;
|
||||||
|
/// Can manually add members into their guilds and group DMs
|
||||||
const ADD_MEMBERS = 1 << 8;
|
const ADD_MEMBERS = 1 << 8;
|
||||||
|
/// Makes the user exempt from all rate limits
|
||||||
const BYPASS_RATE_LIMITS = 1 << 9;
|
const BYPASS_RATE_LIMITS = 1 << 9;
|
||||||
|
/// Can create, edit and remove own applications
|
||||||
const CREATE_APPLICATIONS = 1 << 10;
|
const CREATE_APPLICATIONS = 1 << 10;
|
||||||
|
/// Can create guild channels and custom channels
|
||||||
const CREATE_CHANNELS = 1 << 11;
|
const CREATE_CHANNELS = 1 << 11;
|
||||||
|
/// Can create 1:1 DMs
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// A user without [SEND_MESSAGES] cannot be added to a DM
|
||||||
const CREATE_DMS = 1 << 12;
|
const CREATE_DMS = 1 << 12;
|
||||||
|
/// Can create group DMs
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// A user without [SEND_MESSAGES] cannot be added to a DM
|
||||||
const CREATE_DM_GROUPS = 1 << 13;
|
const CREATE_DM_GROUPS = 1 << 13;
|
||||||
|
/// Can create guilds
|
||||||
const CREATE_GUILDS = 1 << 14;
|
const CREATE_GUILDS = 1 << 14;
|
||||||
|
/// Can create mass invites in guilds where they have [CREATE_INSTANT_INVITE]
|
||||||
const CREATE_INVITES = 1 << 15;
|
const CREATE_INVITES = 1 << 15;
|
||||||
|
/// Can create roles and per-guild or per-channel permission
|
||||||
|
/// overrides in the guilds that they have permissions
|
||||||
const CREATE_ROLES = 1 << 16;
|
const CREATE_ROLES = 1 << 16;
|
||||||
|
/// Can create templates for guilds, custom channels and channels with custom routing
|
||||||
const CREATE_TEMPLATES = 1 << 17;
|
const CREATE_TEMPLATES = 1 << 17;
|
||||||
|
/// Can create webhooks in the guilds that they have permissions
|
||||||
const CREATE_WEBHOOKS = 1 << 18;
|
const CREATE_WEBHOOKS = 1 << 18;
|
||||||
|
/// Can join guilds by using invites or vanity names
|
||||||
const JOIN_GUILDS = 1 << 19;
|
const JOIN_GUILDS = 1 << 19;
|
||||||
|
/// Can modify the pinned messages in the guilds that they have permission
|
||||||
const PIN_MESSAGES = 1 << 20;
|
const PIN_MESSAGES = 1 << 20;
|
||||||
|
/// Can react to messages, subject to permissions
|
||||||
const SELF_ADD_REACTIONS = 1 << 21;
|
const SELF_ADD_REACTIONS = 1 << 21;
|
||||||
|
/// Can delete own messages
|
||||||
const SELF_DELETE_MESSAGES = 1 << 22;
|
const SELF_DELETE_MESSAGES = 1 << 22;
|
||||||
|
/// Can edit own messages
|
||||||
const SELF_EDIT_MESSAGES = 1 << 23;
|
const SELF_EDIT_MESSAGES = 1 << 23;
|
||||||
|
/// Can edit own username, nickname and avatar
|
||||||
const SELF_EDIT_NAME = 1 << 24;
|
const SELF_EDIT_NAME = 1 << 24;
|
||||||
|
/// Can send messages in the channels that they have permissions
|
||||||
const SEND_MESSAGES = 1 << 25;
|
const SEND_MESSAGES = 1 << 25;
|
||||||
|
/// Can use voice activities, such as watch together or whiteboard
|
||||||
const USE_ACTIVITIES = 1 << 26;
|
const USE_ACTIVITIES = 1 << 26;
|
||||||
|
/// Can use video and screenshare in guilds/channels that they have permissions
|
||||||
const USE_VIDEO = 1 << 27;
|
const USE_VIDEO = 1 << 27;
|
||||||
|
/// Can use voice in guilds/channels that they have permissions
|
||||||
const USE_VOICE = 1 << 28;
|
const USE_VOICE = 1 << 28;
|
||||||
|
/// Can create user-specific invites in guilds that they have the [`INVITE_USERS`] right in.
|
||||||
const INVITE_USERS = 1 << 29;
|
const INVITE_USERS = 1 << 29;
|
||||||
|
/// Can delete/disable own account
|
||||||
const SELF_DELETE_DISABLE = 1 << 30;
|
const SELF_DELETE_DISABLE = 1 << 30;
|
||||||
|
/// Can use pay-to-use features once paid
|
||||||
const DEBTABLE = 1 << 31;
|
const DEBTABLE = 1 << 31;
|
||||||
|
/// Can earn money using monetization features in guilds that have [`MonetizationEnabled`](crate::types::types::guild_configuration::GuildFeatures::MonetizationEnabled)
|
||||||
const CREDITABLE = 1 << 32;
|
const CREDITABLE = 1 << 32;
|
||||||
|
/// Can kick or ban guild or group DM members in the guilds/groups where they have [`KICK_MEMBERS`](crate::types::PermissionFlags::KICK_MEMBERS) or [`BAN_MEMBERS`](crate::types::PermissionFlags::BAN_MEMBERS)
|
||||||
const KICK_BAN_MEMBERS = 1 << 33;
|
const KICK_BAN_MEMBERS = 1 << 33;
|
||||||
|
/// Can leave the guilds or group DMs that they joined on their own (one can always leave a guild or group DMs where they have been force-added)
|
||||||
const SELF_LEAVE_GROUPS = 1 << 34;
|
const SELF_LEAVE_GROUPS = 1 << 34;
|
||||||
|
/// Inverts the presence confidentiality default ([`OPERATOR`]'s presence is not routed by default, others' are) for a given user
|
||||||
const PRESENCE = 1 << 35;
|
const PRESENCE = 1 << 35;
|
||||||
|
/// Can mark discoverable guilds where they have permissions to mark as discoverable
|
||||||
const SELF_ADD_DISCOVERABLE = 1 << 36;
|
const SELF_ADD_DISCOVERABLE = 1 << 36;
|
||||||
|
/// Can change anything in the primary guild directory
|
||||||
const MANAGE_GUILD_DIRECTORY = 1 << 37;
|
const MANAGE_GUILD_DIRECTORY = 1 << 37;
|
||||||
|
/// Can send confetti, screenshake and use the random user mention (@someone)
|
||||||
const POGGERS = 1 << 38;
|
const POGGERS = 1 << 38;
|
||||||
|
/// Can use achievements and cheers
|
||||||
const USE_ACHIEVEMENTS = 1 << 39;
|
const USE_ACHIEVEMENTS = 1 << 39;
|
||||||
|
/// Can initiate interactions
|
||||||
const INITIATE_INTERACTIONS = 1 << 40;
|
const INITIATE_INTERACTIONS = 1 << 40;
|
||||||
|
/// Can respond to interactions
|
||||||
const RESPOND_TO_INTERACTIONS = 1 << 41;
|
const RESPOND_TO_INTERACTIONS = 1 << 41;
|
||||||
|
/// Can send backdated events
|
||||||
const SEND_BACKDATED_EVENTS = 1 << 42;
|
const SEND_BACKDATED_EVENTS = 1 << 42;
|
||||||
|
/// Can accept mass (guild) invites
|
||||||
const USE_MASS_INVITES = 1 << 43;
|
const USE_MASS_INVITES = 1 << 43;
|
||||||
|
/// Can accept user-specific invites and DM requests
|
||||||
const ACCEPT_INVITES = 1 << 44;
|
const ACCEPT_INVITES = 1 << 44;
|
||||||
|
/// Can modify own flags
|
||||||
const SELF_EDIT_FLAGS = 1 << 45;
|
const SELF_EDIT_FLAGS = 1 << 45;
|
||||||
|
/// Can modify other's flags
|
||||||
const EDIT_FLAGS = 1 << 46;
|
const EDIT_FLAGS = 1 << 46;
|
||||||
|
/// Can manage other's groups
|
||||||
const MANAGE_GROUPS = 1 << 47;
|
const MANAGE_GROUPS = 1 << 47;
|
||||||
|
/// Can view server stats at /api/policies/stats
|
||||||
const VIEW_SERVER_STATS = 1 << 48;
|
const VIEW_SERVER_STATS = 1 << 48;
|
||||||
|
/// Can resend verification emails using /auth/verify/resend
|
||||||
const RESEND_VERIFICATION_EMAIL = 1 << 49;
|
const RESEND_VERIFICATION_EMAIL = 1 << 49;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,10 +128,16 @@ impl Rights {
|
||||||
(check_operator && self.contains(Rights::OPERATOR)) || self.contains(permission)
|
(check_operator && self.contains(Rights::OPERATOR)) || self.contains(permission)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether or not the Rights object has specific rights
|
||||||
pub fn has(&self, permission: Rights, check_operator: bool) -> bool {
|
pub fn has(&self, permission: Rights, check_operator: bool) -> bool {
|
||||||
(check_operator && self.contains(Rights::OPERATOR)) || self.contains(permission)
|
(check_operator && self.contains(Rights::OPERATOR)) || self.contains(permission)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether or not the Rights object has specific rights.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// Unlike has, this returns an Error if we are missing rights
|
||||||
|
/// and Ok(true) otherwise
|
||||||
pub fn has_throw(&self, permission: Rights) -> Result<bool, &'static str> {
|
pub fn has_throw(&self, permission: Rights) -> Result<bool, &'static str> {
|
||||||
if self.has(permission, true) {
|
if self.has(permission, true) {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|
|
@ -11,13 +11,16 @@ use sqlx::Type;
|
||||||
const EPOCH: i64 = 1420070400000;
|
const EPOCH: i64 = 1420070400000;
|
||||||
|
|
||||||
/// Unique identifier including a timestamp.
|
/// Unique identifier including a timestamp.
|
||||||
/// See https://discord.com/developers/docs/reference#snowflakes
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord.com/developers/docs/reference#snowflakes>
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(Type))]
|
#[cfg_attr(feature = "sqlx", derive(Type))]
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(transparent))]
|
#[cfg_attr(feature = "sqlx", sqlx(transparent))]
|
||||||
pub struct Snowflake(u64);
|
pub struct Snowflake(pub u64);
|
||||||
|
|
||||||
impl Snowflake {
|
impl Snowflake {
|
||||||
|
/// Generates a snowflake for the current timestamp, with worker id 0 and process id 1.
|
||||||
pub fn generate() -> Self {
|
pub fn generate() -> Self {
|
||||||
const WORKER_ID: u64 = 0;
|
const WORKER_ID: u64 = 0;
|
||||||
const PROCESS_ID: u64 = 1;
|
const PROCESS_ID: u64 = 1;
|
||||||
|
@ -31,6 +34,7 @@ impl Snowflake {
|
||||||
Self(time as u64 | worker | process | increment)
|
Self(time as u64 | worker | process | increment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the snowflake's timestamp
|
||||||
pub fn timestamp(self) -> DateTime<Utc> {
|
pub fn timestamp(self) -> DateTime<Utc> {
|
||||||
Utc.timestamp_millis_opt((self.0 >> 22) as i64 + EPOCH)
|
Utc.timestamp_millis_opt((self.0 >> 22) as i64 + EPOCH)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -49,6 +53,15 @@ impl Display for Snowflake {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for Snowflake
|
||||||
|
where
|
||||||
|
T: Into<u64>,
|
||||||
|
{
|
||||||
|
fn from(item: T) -> Self {
|
||||||
|
Self(item.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl serde::Serialize for Snowflake {
|
impl serde::Serialize for Snowflake {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue