Compare commits
17 Commits
5fc1457f9c
...
218ea9aa12
Author | SHA1 | Date |
---|---|---|
kozabrada123 | 218ea9aa12 | |
kozabrada123 | 81b1525ac0 | |
bitfl0wer | 261fe452c1 | |
kozabrada123 | 1c90c8e32b | |
bitfl0wer | 411db01786 | |
Flori | 76186a08f0 | |
bitfl0wer | d846ce9948 | |
bitfl0wer | e316372631 | |
bitfl0wer | 72c5d13eaf | |
bitfl0wer | 05b9f1c801 | |
Flori | 9926f8ab94 | |
Flori | 5bee907733 | |
Flori | f0dbd3410f | |
Flori | ebea18c991 | |
bitfl0wer | e9b3de2342 | |
bitfl0wer | cfccae8060 | |
Flori | 86b022912f |
|
@ -101,7 +101,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
rustup target add wasm32-unknown-unknown
|
rustup target add wasm32-unknown-unknown
|
||||||
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
||||||
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.92" --force
|
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.93" --force
|
||||||
GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
|
GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
|
||||||
wasm-chrome:
|
wasm-chrome:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -130,5 +130,5 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
rustup target add wasm32-unknown-unknown
|
rustup target add wasm32-unknown-unknown
|
||||||
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
||||||
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.92" --force
|
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.93" --force
|
||||||
CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
|
CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
29
Cargo.toml
29
Cargo.toml
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chorus"
|
name = "chorus"
|
||||||
description = "A library for interacting with multiple Spacebar-compatible Instances at once."
|
description = "A library for interacting with multiple Spacebar-compatible Instances at once."
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/polyphony-chat/chorus"
|
repository = "https://github.com/polyphony-chat/chorus"
|
||||||
|
@ -13,18 +13,19 @@ rust-version = "1.70.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["client", "rt-multi-thread"]
|
default = ["client", "rt-multi-thread"]
|
||||||
backend = ["poem", "sqlx"]
|
backend = ["poem", "sqlx", "sqlx-pg-uint"]
|
||||||
rt-multi-thread = ["tokio/rt-multi-thread"]
|
rt-multi-thread = ["tokio/rt-multi-thread"]
|
||||||
rt = ["tokio/rt"]
|
rt = ["tokio/rt"]
|
||||||
client = ["flate2"]
|
client = ["flate2"]
|
||||||
voice = ["voice_udp", "voice_gateway"]
|
voice = ["voice_udp", "voice_gateway"]
|
||||||
voice_udp = ["dep:discortp", "dep:crypto_secretbox"]
|
voice_udp = ["dep:discortp", "dep:crypto_secretbox"]
|
||||||
voice_gateway = []
|
voice_gateway = []
|
||||||
|
sqlx-pg-uint = ["dep:sqlx-pg-uint", "sqlx-pg-uint/serde"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.38.1", features = ["macros", "sync"] }
|
tokio = { version = "1.39.3", features = ["macros", "sync"] }
|
||||||
serde = { version = "1.0.204", features = ["derive", "rc"] }
|
serde = { version = "1.0.209", features = ["derive", "rc"] }
|
||||||
serde_json = { version = "1.0.120", features = ["raw_value"] }
|
serde_json = { version = "1.0.127", features = ["raw_value"] }
|
||||||
serde-aux = "4.5.0"
|
serde-aux = "4.5.0"
|
||||||
serde_with = "3.9.0"
|
serde_with = "3.9.0"
|
||||||
serde_repr = "0.1.19"
|
serde_repr = "0.1.19"
|
||||||
|
@ -35,7 +36,7 @@ reqwest = { features = [
|
||||||
], version = "=0.11.26", default-features = false }
|
], version = "=0.11.26", default-features = false }
|
||||||
url = "2.5.2"
|
url = "2.5.2"
|
||||||
chrono = { version = "0.4.38", features = ["serde"] }
|
chrono = { version = "0.4.38", features = ["serde"] }
|
||||||
regex = "1.10.5"
|
regex = "1.10.6"
|
||||||
custom_error = "1.9.2"
|
custom_error = "1.9.2"
|
||||||
futures-util = "0.3.30"
|
futures-util = "0.3.30"
|
||||||
http = "0.2.12"
|
http = "0.2.12"
|
||||||
|
@ -48,14 +49,13 @@ jsonwebtoken = "8.3.0"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
async-trait = "0.1.81"
|
async-trait = "0.1.81"
|
||||||
chorus-macros = { path = "./chorus-macros", version = "0" } # Note: version here is used when releasing. This will use the latest release. Make sure to republish the crate when code in macros is changed!
|
chorus-macros = { path = "./chorus-macros", version = "0" } # Note: version here is used when releasing. This will use the latest release. Make sure to republish the crate when code in macros is changed!
|
||||||
sqlx = { version = "0.8.0", features = [
|
sqlx = { version = "0.8.1", features = [
|
||||||
"mysql",
|
|
||||||
"sqlite",
|
|
||||||
"json",
|
"json",
|
||||||
"chrono",
|
"chrono",
|
||||||
"ipnetwork",
|
"ipnetwork",
|
||||||
"runtime-tokio-rustls",
|
"runtime-tokio-rustls",
|
||||||
"any",
|
"postgres",
|
||||||
|
"bigdecimal",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
discortp = { version = "0.5.0", optional = true, features = [
|
discortp = { version = "0.5.0", optional = true, features = [
|
||||||
"rtp",
|
"rtp",
|
||||||
|
@ -64,9 +64,10 @@ discortp = { version = "0.5.0", optional = true, features = [
|
||||||
] }
|
] }
|
||||||
crypto_secretbox = { version = "0.1.1", optional = true }
|
crypto_secretbox = { version = "0.1.1", optional = true }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
flate2 = { version = "1.0.30", optional = true }
|
flate2 = { version = "1.0.33", optional = true }
|
||||||
webpki-roots = "0.26.3"
|
webpki-roots = "0.26.3"
|
||||||
pubserve = { version = "1.1.0", features = ["async", "send"] }
|
pubserve = { version = "1.1.0", features = ["async", "send"] }
|
||||||
|
sqlx-pg-uint = { version = "0.5.0", features = ["serde"], optional = true }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
rustls = "0.21.12"
|
rustls = "0.21.12"
|
||||||
|
@ -79,13 +80,13 @@ getrandom = { version = "0.2.15" }
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
getrandom = { version = "0.2.15", features = ["js"] }
|
getrandom = { version = "0.2.15", features = ["js"] }
|
||||||
ws_stream_wasm = "0.7.4"
|
ws_stream_wasm = "0.7.4"
|
||||||
wasm-bindgen-futures = "0.4.42"
|
wasm-bindgen-futures = "0.4.43"
|
||||||
wasmtimer = "0.2.0"
|
wasmtimer = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
wasm-bindgen-test = "0.3.42"
|
wasm-bindgen-test = "0.3.43"
|
||||||
wasm-bindgen = "0.2.92"
|
wasm-bindgen = "0.2.93"
|
||||||
simple_logger = { version = "5.0.0", default-features = false }
|
simple_logger = { version = "5.0.0", default-features = false }
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
|
|
130
README.md
130
README.md
|
@ -28,14 +28,15 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Chorus is a Rust library which poses as an API wrapper for [Spacebar Chat](https://github.com/spacebarchat/)
|
Chorus is a Rust library which poses as an API wrapper for [Spacebar Chat](https://github.com/spacebarchat/),
|
||||||
and Discord. It is designed to be easy to use, and to be compatible with both Discord and Spacebar Chat.
|
Discord and our own Polyphony. Its high-level API is designed to be easy to use, while still providing the
|
||||||
|
flexibility one would expect from a library like this.
|
||||||
|
|
||||||
You can establish as many connections to as many servers as you want, and you can use them all at the same time.
|
You can establish as many connections to as many servers as you want, and you can use them all at the same time.
|
||||||
|
|
||||||
## A Tour of Chorus
|
## A Tour of Chorus
|
||||||
|
|
||||||
Chorus combines all the required functionalities of a user-centric Spacebar library into one package.
|
Chorus combines all the required functionalities of an API wrapper for chat services into one modular library.
|
||||||
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
|
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
|
||||||
a WebSocket connection to the Gateway. This means that you can focus on building your application,
|
a WebSocket connection to the Gateway. This means that you can focus on building your application,
|
||||||
instead of worrying about the underlying implementation details.
|
instead of worrying about the underlying implementation details.
|
||||||
|
@ -44,19 +45,19 @@ To get started with Chorus, import it into your project by adding the following
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chorus = "0.15.0"
|
chorus = "0.16.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Establishing a Connection
|
### Establishing a Connection
|
||||||
|
|
||||||
To connect to a Spacebar compatible server, you need to create an [`Instance`](https://docs.rs/chorus/latest/chorus/instance/struct.Instance.html) like this:
|
To connect to a Polyphony/Spacebar compatible server, you'll need to create an [`Instance`](https://docs.rs/chorus/latest/chorus/instance/struct.Instance.html) like this:
|
||||||
|
|
||||||
```rs
|
```rs
|
||||||
use chorus::instance::Instance;
|
use chorus::instance::Instance;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let instance = Instance::new("https://example.com")
|
let instance = Instance::new("https://example.com", None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to the Spacebar server");
|
.expect("Failed to connect to the Spacebar server");
|
||||||
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
|
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
|
||||||
|
@ -81,7 +82,7 @@ let login_schema = LoginSchema {
|
||||||
password: "Correct-Horse-Battery-Staple".to_string(),
|
password: "Correct-Horse-Battery-Staple".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
// Each user connects to the Gateway. The Gateway connection lives on a separate thread. Depending on
|
// Each user connects to the Gateway. Each users' Gateway connection lives on a separate thread. Depending on
|
||||||
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
|
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
|
||||||
let user = instance
|
let user = instance
|
||||||
.login_account(login_schema)
|
.login_account(login_schema)
|
||||||
|
@ -148,98 +149,23 @@ This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read
|
||||||
|
|
||||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||||
|
|
||||||
<details>
|
[Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust
|
||||||
<summary>Progress Tracker/Roadmap</summary>
|
[Rust-url]: https://www.rust-lang.org/
|
||||||
|
[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/build_and_test.yml?style=flat
|
||||||
### Core Functionality
|
[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/build_and_test.yml
|
||||||
- [x] Rate Limiter (hint: couldn't be fully tested due to [an Issue with the Spacebar Server](https://github.com/spacebarchat/server/issues/1022))
|
[clippy-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/clippy.yml?style=flat
|
||||||
- [x] [Login (the conventional way)](https://github.com/polyphony-chat/chorus/issues/1)
|
[clippy-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/clippy.yml
|
||||||
- [ ] [2FA](https://github.com/polyphony-chat/chorus/issues/40)
|
[contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat
|
||||||
- [x] [Registration](https://github.com/polyphony-chat/chorus/issues/1)
|
[contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors
|
||||||
|
[coverage-shield]: https://coveralls.io/repos/github/polyphony-chat/chorus/badge.svg?branch=main
|
||||||
### Messaging
|
[coverage-url]: https://coveralls.io/github/polyphony-chat/chorus?branch=main
|
||||||
- [x] [Sending messages](https://github.com/polyphony-chat/chorus/issues/23)
|
[forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat
|
||||||
- [x] [Events (Message, User, Channel, etc.)](https://github.com/polyphony-chat/chorus/issues/51)
|
[forks-url]: https://github.com/polyphony-chat/chorus/network/members
|
||||||
- [x] Channel creation
|
[stars-shield]: https://img.shields.io/github/stars/polyphony-chat/chorus.svg?style=flat
|
||||||
- [x] Channel deletion
|
[stars-url]: https://github.com/polyphony-chat/chorus/stargazers
|
||||||
- [x] [Channel management (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/48)
|
[issues-shield]: https://img.shields.io/github/issues/polyphony-chat/chorus.svg?style=flat
|
||||||
- [x] [Join and Leave Guilds](https://github.com/polyphony-chat/chorus/issues/45)
|
[issues-url]: https://github.com/polyphony-chat/chorus/issues
|
||||||
- [x] [Start DMs](https://github.com/polyphony-chat/chorus/issues/45)
|
[license-shield]: https://img.shields.io/github/license/polyphony-chat/chorus.svg?style=f;at
|
||||||
- [x] [Group DM creation, deletion and member management](https://github.com/polyphony-chat/chorus/issues/89)
|
[license-url]: https://github.com/polyphony-chat/chorus/blob/master/LICENSE
|
||||||
- [ ] [Deleting messages](https://github.com/polyphony-chat/chorus/issues/91)
|
[Discord]: https://dcbadge.vercel.app/api/server/m3FpcapGDD?style=flat
|
||||||
- [ ] [Message threads](https://github.com/polyphony-chat/chorus/issues/90)
|
[Discord-invite]: https://discord.com/invite/m3FpcapGDD
|
||||||
- [x] [Reactions](https://github.com/polyphony-chat/chorus/issues/85)
|
|
||||||
- [ ] Message Search
|
|
||||||
- [ ] Message history
|
|
||||||
- [ ] Emoji
|
|
||||||
- [ ] Stickers
|
|
||||||
- [ ] [Forum channels](https://github.com/polyphony-chat/chorus/issues/90)
|
|
||||||
|
|
||||||
### User Management
|
|
||||||
- [ ] [User profile customization](https://github.com/polyphony-chat/chorus/issues/41)
|
|
||||||
- [x] Gettings users and user profiles
|
|
||||||
- [x] [Friend requests](https://github.com/polyphony-chat/chorus/issues/92)
|
|
||||||
- [x] [Blocking users](https://github.com/polyphony-chat/chorus/issues/92)
|
|
||||||
- [ ] User presence (online, offline, idle, etc.)
|
|
||||||
- [ ] User status (custom status, etc.)
|
|
||||||
- [x] Account deletion
|
|
||||||
|
|
||||||
### Additional Features
|
|
||||||
- [ ] Server discovery
|
|
||||||
- [ ] Server templates
|
|
||||||
|
|
||||||
### Voice and Video
|
|
||||||
- [ ] [Voice chat support](https://github.com/polyphony-chat/chorus/issues/49)
|
|
||||||
- [ ] [Video chat support](https://github.com/polyphony-chat/chorus/issues/49)
|
|
||||||
|
|
||||||
### Permissions and Roles
|
|
||||||
- [x] [Role management](https://github.com/polyphony-chat/chorus/issues/46) (creation, deletion, modification)
|
|
||||||
- [x] [Permission management](https://github.com/polyphony-chat/chorus/issues/46) (assigning and revoking permissions)
|
|
||||||
- [x] [Channel-specific permissions](https://github.com/polyphony-chat/chorus/issues/88)
|
|
||||||
- [x] Role-based access control
|
|
||||||
|
|
||||||
### Guild Management
|
|
||||||
- [x] Guild creation
|
|
||||||
- [x] Guild deletion
|
|
||||||
- [ ] [Guild settings (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/43)
|
|
||||||
- [ ] Guild invites
|
|
||||||
|
|
||||||
### Moderation
|
|
||||||
- [ ] Channel moderation (slow mode, etc.)
|
|
||||||
- [ ] User sanctions (mute, kick, ban)
|
|
||||||
- [ ] Audit logs
|
|
||||||
|
|
||||||
### Embeds and Rich Content
|
|
||||||
- [x] Sending rich content in messages (links, images, videos)
|
|
||||||
- [ ] Customizing embed appearance (title, description, color, fields)
|
|
||||||
|
|
||||||
### Webhooks
|
|
||||||
- [ ] Webhook creation and management
|
|
||||||
- [ ] Handling incoming webhook events
|
|
||||||
|
|
||||||
### Documentation and Examples
|
|
||||||
- [ ] Comprehensive documentation
|
|
||||||
- [ ] Example usage and code snippets
|
|
||||||
- [ ] Tutorials and guides
|
|
||||||
|
|
||||||
[Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust
|
|
||||||
[Rust-url]: https://www.rust-lang.org/
|
|
||||||
[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/build_and_test.yml?style=flat
|
|
||||||
[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/build_and_test.yml
|
|
||||||
[clippy-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/clippy.yml?style=flat
|
|
||||||
[clippy-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/clippy.yml
|
|
||||||
[contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat
|
|
||||||
[contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors
|
|
||||||
[coverage-shield]: https://coveralls.io/repos/github/polyphony-chat/chorus/badge.svg?branch=main
|
|
||||||
[coverage-url]: https://coveralls.io/github/polyphony-chat/chorus?branch=main
|
|
||||||
[forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat
|
|
||||||
[forks-url]: https://github.com/polyphony-chat/chorus/network/members
|
|
||||||
[stars-shield]: https://img.shields.io/github/stars/polyphony-chat/chorus.svg?style=flat
|
|
||||||
[stars-url]: https://github.com/polyphony-chat/chorus/stargazers
|
|
||||||
[issues-shield]: https://img.shields.io/github/issues/polyphony-chat/chorus.svg?style=flat
|
|
||||||
[issues-url]: https://github.com/polyphony-chat/chorus/issues
|
|
||||||
[license-shield]: https://img.shields.io/github/license/polyphony-chat/chorus.svg?style=f;at
|
|
||||||
[license-url]: https://github.com/polyphony-chat/chorus/blob/master/LICENSE
|
|
||||||
[Discord]: https://dcbadge.vercel.app/api/server/m3FpcapGDD?style=flat
|
|
||||||
[Discord-invite]: https://discord.com/invite/m3FpcapGDD
|
|
||||||
</details>
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chorus-macros"
|
name = "chorus-macros"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
description = "Macros for the chorus crate."
|
description = "Macros for the chorus crate."
|
||||||
|
|
|
@ -164,24 +164,23 @@ pub fn sqlx_bitflag_derive(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
quote!{
|
quote!{
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl sqlx::Type<sqlx::Any> for #name {
|
impl sqlx::Type<sqlx::Postgres> for #name {
|
||||||
fn type_info() -> sqlx::any::AnyTypeInfo {
|
fn type_info() -> sqlx::postgres::PgTypeInfo {
|
||||||
<Vec<u8> as sqlx::Type<sqlx::Any>>::type_info()
|
<sqlx_pg_uint::PgU64 as sqlx::Type<sqlx::Postgres>>::type_info()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl<'q> sqlx::Encode<'q, sqlx::Any> for #name {
|
impl<'q> sqlx::Encode<'q, sqlx::Postgres> for #name {
|
||||||
fn encode_by_ref(&self, buf: &mut <sqlx::Any as sqlx::Database>::ArgumentBuffer<'q>) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
|
fn encode_by_ref(&self, buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
|
||||||
<Vec<u8> as sqlx::Encode<sqlx::Any>>::encode_by_ref(&self.bits().to_be_bytes().into(), buf)
|
<sqlx_pg_uint::PgU64 as sqlx::Encode<sqlx::Postgres>>::encode_by_ref(&self.bits().into(), buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl<'q> sqlx::Decode<'q, sqlx::Any> for #name {
|
impl<'q> sqlx::Decode<'q, sqlx::Postgres> for #name {
|
||||||
fn decode(value: <sqlx::Any as sqlx::Database>::ValueRef<'q>) -> Result<Self, sqlx::error::BoxDynError> {
|
fn decode(value: <sqlx::Postgres as sqlx::Database>::ValueRef<'q>) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
let vec = <Vec<u8> as sqlx::Decode<sqlx::Any>>::decode(value)?;
|
<sqlx_pg_uint::PgU64 as sqlx::Decode<sqlx::Postgres>>::decode(value).map(|v| Self::from_bits_truncate(v.to_uint()))
|
||||||
Ok(Self::from_bits(vec_u8_to_u64(vec)).unwrap())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ impl Subscriber<GatewayReady> for ExampleObserver {
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let gateway_websocket_url = GATEWAY_URL.to_string();
|
let gateway_websocket_url = GATEWAY_URL;
|
||||||
|
|
||||||
// These options specify the encoding format, compression, etc
|
// These options specify the encoding format, compression, etc
|
||||||
//
|
//
|
||||||
|
|
|
@ -25,7 +25,7 @@ use wasmtimer::tokio::sleep;
|
||||||
/// This example creates a simple gateway connection and a session with an Identify event
|
/// This example creates a simple gateway connection and a session with an Identify event
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let gateway_websocket_url = GATEWAY_URL.to_string();
|
let gateway_websocket_url = GATEWAY_URL;
|
||||||
|
|
||||||
// These options specify the encoding format, compression, etc
|
// These options specify the encoding format, compression, etc
|
||||||
//
|
//
|
||||||
|
@ -34,7 +34,9 @@ async fn main() {
|
||||||
let options = GatewayOptions::default();
|
let options = GatewayOptions::default();
|
||||||
|
|
||||||
// Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another
|
// Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another
|
||||||
let gateway = Gateway::spawn(gateway_websocket_url, options).await.unwrap();
|
let gateway = Gateway::spawn(gateway_websocket_url, options)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated
|
// At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use chorus::instance::Instance;
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let instance = Instance::new("https://example.com/")
|
let instance = Instance::new("https://example.com/", None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to the Spacebar server");
|
.expect("Failed to connect to the Spacebar server");
|
||||||
dbg!(instance.instance_info);
|
dbg!(instance.instance_info);
|
||||||
|
|
|
@ -7,7 +7,7 @@ use chorus::types::LoginSchema;
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let mut instance = Instance::new("https://example.com/")
|
let mut instance = Instance::new("https://example.com/", None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to the Spacebar server");
|
.expect("Failed to connect to the Spacebar server");
|
||||||
// Assume, you already have an account created on this instance. Registering an account works
|
// Assume, you already have an account created on this instance. Registering an account works
|
||||||
|
|
|
@ -30,13 +30,12 @@ impl Instance {
|
||||||
// We do not have a user yet, and the UserRateLimits will not be affected by a login
|
// We do not have a user yet, and the UserRateLimits will not be affected by a login
|
||||||
// request (since login is an instance wide limit), which is why we are just cloning the
|
// request (since login is an instance wide limit), which is why we are just cloning the
|
||||||
// instances' limits to pass them on as user_rate_limits later.
|
// instances' limits to pass them on as user_rate_limits later.
|
||||||
let mut user =
|
let mut user = ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None").await;
|
||||||
ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None".to_string()).await;
|
|
||||||
|
|
||||||
let login_result = chorus_request
|
let login_result = chorus_request
|
||||||
.deserialize_response::<LoginResult>(&mut user)
|
.deserialize_response::<LoginResult>(&mut user)
|
||||||
.await?;
|
.await?;
|
||||||
user.set_token(login_result.token);
|
user.set_token(&login_result.token);
|
||||||
user.settings = login_result.settings;
|
user.settings = login_result.settings;
|
||||||
|
|
||||||
let object = User::get_current(&mut user).await?;
|
let object = User::get_current(&mut user).await?;
|
||||||
|
|
|
@ -22,9 +22,8 @@ pub mod register;
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
/// Logs into an existing account on the spacebar server, using only a token.
|
/// Logs into an existing account on the spacebar server, using only a token.
|
||||||
pub async fn login_with_token(&mut self, token: String) -> ChorusResult<ChorusUser> {
|
pub async fn login_with_token(&mut self, token: &str) -> ChorusResult<ChorusUser> {
|
||||||
let mut user =
|
let mut user = ChorusUser::shell(Arc::new(RwLock::new(self.clone())), token).await;
|
||||||
ChorusUser::shell(Arc::new(RwLock::new(self.clone())), token).await;
|
|
||||||
|
|
||||||
let object = User::get_current(&mut user).await?;
|
let object = User::get_current(&mut user).await?;
|
||||||
let settings = User::get_settings(&mut user).await?;
|
let settings = User::get_settings(&mut user).await?;
|
||||||
|
|
|
@ -37,14 +37,14 @@ impl Instance {
|
||||||
// We do not have a user yet, and the UserRateLimits will not be affected by a login
|
// We do not have a user yet, and the UserRateLimits will not be affected by a login
|
||||||
// request (since register is an instance wide limit), which is why we are just cloning
|
// request (since register is an instance wide limit), which is why we are just cloning
|
||||||
// the instances' limits to pass them on as user_rate_limits later.
|
// the instances' limits to pass them on as user_rate_limits later.
|
||||||
let mut user =
|
let mut user = ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None").await;
|
||||||
ChorusUser::shell(Arc::new(RwLock::new(self.clone())), "None".to_string()).await;
|
|
||||||
|
|
||||||
let token = chorus_request
|
let token = chorus_request
|
||||||
.deserialize_response::<Token>(&mut user)
|
.deserialize_response::<Token>(&mut user)
|
||||||
.await?
|
.await?
|
||||||
.token;
|
.token;
|
||||||
user.set_token(token);
|
|
||||||
|
user.set_token(&token);
|
||||||
|
|
||||||
let object = User::get_current(&mut user).await?;
|
let object = User::get_current(&mut user).await?;
|
||||||
let settings = User::get_settings(&mut user).await?;
|
let settings = User::get_settings(&mut user).await?;
|
||||||
|
|
|
@ -16,6 +16,7 @@ use crate::types::{
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
|
#[allow(clippy::useless_conversion)]
|
||||||
/// Sends a message in the channel with the provided channel_id.
|
/// Sends a message in the channel with the provided channel_id.
|
||||||
/// Returns the sent message.
|
/// Returns the sent message.
|
||||||
///
|
///
|
||||||
|
@ -40,7 +41,7 @@ impl Message {
|
||||||
chorus_request.deserialize_response::<Message>(user).await
|
chorus_request.deserialize_response::<Message>(user).await
|
||||||
} else {
|
} else {
|
||||||
for (index, attachment) in message.attachments.iter_mut().enumerate() {
|
for (index, attachment) in message.attachments.iter_mut().enumerate() {
|
||||||
attachment.get_mut(index).unwrap().id = Some(index as i16);
|
attachment.get_mut(index).unwrap().id = Some((index as u64).into());
|
||||||
}
|
}
|
||||||
let mut form = reqwest::multipart::Form::new();
|
let mut form = reqwest::multipart::Form::new();
|
||||||
let payload_json = to_string(&message).unwrap();
|
let payload_json = to_string(&message).unwrap();
|
||||||
|
@ -111,7 +112,7 @@ impl Message {
|
||||||
let result = request.send_request(user).await?;
|
let result = request.send_request(user).await?;
|
||||||
let result_json = result.json::<Value>().await.unwrap();
|
let result_json = result.json::<Value>().await.unwrap();
|
||||||
if !result_json.is_object() {
|
if !result_json.is_object() {
|
||||||
return Err(search_error(result_json.to_string()));
|
return Err(search_error(result_json.to_string().as_str()));
|
||||||
}
|
}
|
||||||
let value_map = result_json.as_object().unwrap();
|
let value_map = result_json.as_object().unwrap();
|
||||||
if let Some(messages) = value_map.get("messages") {
|
if let Some(messages) = value_map.get("messages") {
|
||||||
|
@ -122,7 +123,7 @@ impl Message {
|
||||||
}
|
}
|
||||||
// The code below might be incorrect. We'll cross that bridge when we come to it
|
// The code below might be incorrect. We'll cross that bridge when we come to it
|
||||||
if !value_map.contains_key("code") || !value_map.contains_key("retry_after") {
|
if !value_map.contains_key("code") || !value_map.contains_key("retry_after") {
|
||||||
return Err(search_error(result_json.to_string()));
|
return Err(search_error(result_json.to_string().as_str()));
|
||||||
}
|
}
|
||||||
let code = value_map.get("code").unwrap().as_u64().unwrap();
|
let code = value_map.get("code").unwrap().as_u64().unwrap();
|
||||||
let retry_after = value_map.get("retry_after").unwrap().as_u64().unwrap();
|
let retry_after = value_map.get("retry_after").unwrap().as_u64().unwrap();
|
||||||
|
@ -481,7 +482,7 @@ impl Message {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_error(result_text: String) -> ChorusError {
|
fn search_error(result_text: &str) -> ChorusError {
|
||||||
ChorusError::InvalidResponse {
|
ChorusError::InvalidResponse {
|
||||||
error: format!(
|
error: format!(
|
||||||
"Got unexpected Response, or Response which is not valid JSON. Response: \n{}",
|
"Got unexpected Response, or Response which is not valid JSON. Response: \n{}",
|
||||||
|
|
|
@ -9,8 +9,10 @@ use futures_util::{
|
||||||
};
|
};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_tungstenite::{
|
use tokio_tungstenite::{
|
||||||
connect_async_tls_with_config, tungstenite, Connector, MaybeTlsStream, WebSocketStream,
|
connect_async_tls_with_config, connect_async_with_config, tungstenite, Connector,
|
||||||
|
MaybeTlsStream, WebSocketStream,
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use crate::gateway::{GatewayMessage, RawGatewayMessage};
|
use crate::gateway::{GatewayMessage, RawGatewayMessage};
|
||||||
|
|
||||||
|
@ -32,6 +34,21 @@ impl TungsteniteBackend {
|
||||||
pub async fn connect(
|
pub async fn connect(
|
||||||
websocket_url: &str,
|
websocket_url: &str,
|
||||||
) -> Result<(TungsteniteSink, TungsteniteStream), TungsteniteBackendError> {
|
) -> Result<(TungsteniteSink, TungsteniteStream), TungsteniteBackendError> {
|
||||||
|
let websocket_url_parsed =
|
||||||
|
Url::parse(websocket_url).map_err(|_| TungsteniteBackendError::TungsteniteError {
|
||||||
|
error: tungstenite::error::Error::Url(
|
||||||
|
tungstenite::error::UrlError::UnsupportedUrlScheme,
|
||||||
|
),
|
||||||
|
})?;
|
||||||
|
if websocket_url_parsed.scheme() == "ws" {
|
||||||
|
let (websocket_stream, _) =
|
||||||
|
match connect_async_with_config(websocket_url, None, false).await {
|
||||||
|
Ok(websocket_stream) => websocket_stream,
|
||||||
|
Err(e) => return Err(TungsteniteBackendError::TungsteniteError { error: e }),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(websocket_stream.split())
|
||||||
|
} else if websocket_url_parsed.scheme() == "wss" {
|
||||||
let certs = webpki_roots::TLS_SERVER_ROOTS;
|
let certs = webpki_roots::TLS_SERVER_ROOTS;
|
||||||
let roots = rustls::RootCertStore {
|
let roots = rustls::RootCertStore {
|
||||||
roots: certs
|
roots: certs
|
||||||
|
@ -64,6 +81,13 @@ impl TungsteniteBackend {
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(websocket_stream.split())
|
Ok(websocket_stream.split())
|
||||||
|
} else {
|
||||||
|
Err(TungsteniteBackendError::TungsteniteError {
|
||||||
|
error: tungstenite::error::Error::Url(
|
||||||
|
tungstenite::error::UrlError::UnsupportedUrlScheme,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl Gateway {
|
||||||
/// # Note
|
/// # Note
|
||||||
/// The websocket url should begin with the prefix wss:// or ws:// (for unsecure connections)
|
/// The websocket url should begin with the prefix wss:// or ws:// (for unsecure connections)
|
||||||
pub async fn spawn(
|
pub async fn spawn(
|
||||||
websocket_url: String,
|
websocket_url: &str,
|
||||||
options: GatewayOptions,
|
options: GatewayOptions,
|
||||||
) -> Result<GatewayHandle, GatewayError> {
|
) -> Result<GatewayHandle, GatewayError> {
|
||||||
let url = options.add_to_url(websocket_url);
|
let url = options.add_to_url(websocket_url);
|
||||||
|
|
|
@ -10,9 +10,10 @@ use std::fmt::Debug;
|
||||||
use super::{events::Events, *};
|
use super::{events::Events, *};
|
||||||
use crate::types::{self, Composite, Shared};
|
use crate::types::{self, Composite, Shared};
|
||||||
|
|
||||||
/// Represents a handle to a Gateway connection. A Gateway connection will create observable
|
/// Represents a handle to a Gateway connection.
|
||||||
/// [`GatewayEvents`](GatewayEvent), which you can subscribe to. Gateway events include all currently
|
///
|
||||||
/// implemented types with the trait [`WebSocketEvent`]
|
/// A Gateway connection will create observable [`Events`], which you can subscribe to.
|
||||||
|
///
|
||||||
/// Using this handle you can also send Gateway Events directly.
|
/// Using this handle you can also send Gateway Events directly.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GatewayHandle {
|
pub struct GatewayHandle {
|
||||||
|
|
|
@ -25,8 +25,8 @@ impl GatewayOptions {
|
||||||
/// Adds the options to an existing gateway url
|
/// Adds the options to an existing gateway url
|
||||||
///
|
///
|
||||||
/// Returns the new url
|
/// Returns the new url
|
||||||
pub(crate) fn add_to_url(&self, url: String) -> String {
|
pub(crate) fn add_to_url(&self, url: &str) -> String {
|
||||||
let mut url = url;
|
let mut url = url.to_string();
|
||||||
|
|
||||||
let mut parameters = Vec::with_capacity(2);
|
let mut parameters = Vec::with_capacity(2);
|
||||||
|
|
||||||
|
|
|
@ -69,8 +69,13 @@ impl Instance {
|
||||||
|
|
||||||
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle).
|
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle).
|
||||||
///
|
///
|
||||||
|
/// If `options` is `None`, the default [`GatewayOptions`] will be used.
|
||||||
|
///
|
||||||
/// To create an Instance from one singular url, use [`Instance::new()`].
|
/// To create an Instance from one singular url, use [`Instance::new()`].
|
||||||
pub async fn from_url_bundle(urls: UrlBundle) -> ChorusResult<Instance> {
|
pub async fn from_url_bundle(
|
||||||
|
urls: UrlBundle,
|
||||||
|
options: Option<GatewayOptions>,
|
||||||
|
) -> ChorusResult<Instance> {
|
||||||
let is_limited: Option<LimitsConfiguration> = Instance::is_limited(&urls.api).await?;
|
let is_limited: Option<LimitsConfiguration> = Instance::is_limited(&urls.api).await?;
|
||||||
let limit_information;
|
let limit_information;
|
||||||
|
|
||||||
|
@ -89,7 +94,7 @@ impl Instance {
|
||||||
instance_info: GeneralConfiguration::default(),
|
instance_info: GeneralConfiguration::default(),
|
||||||
limits_information: limit_information,
|
limits_information: limit_information,
|
||||||
client: Client::new(),
|
client: Client::new(),
|
||||||
gateway_options: GatewayOptions::default(),
|
gateway_options: options.unwrap_or_default(),
|
||||||
};
|
};
|
||||||
instance.instance_info = match instance.general_configuration_schema().await {
|
instance.instance_info = match instance.general_configuration_schema().await {
|
||||||
Ok(schema) => schema,
|
Ok(schema) => schema,
|
||||||
|
@ -103,14 +108,16 @@ impl Instance {
|
||||||
|
|
||||||
/// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url.
|
/// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url.
|
||||||
///
|
///
|
||||||
|
/// If `options` is `None`, the default [`GatewayOptions`] will be used.
|
||||||
|
///
|
||||||
/// Shorthand for `Instance::from_url_bundle(UrlBundle::from_root_domain(root_domain).await?)`.
|
/// Shorthand for `Instance::from_url_bundle(UrlBundle::from_root_domain(root_domain).await?)`.
|
||||||
pub async fn new(root_url: &str) -> ChorusResult<Instance> {
|
pub async fn new(root_url: &str, options: Option<GatewayOptions>) -> ChorusResult<Instance> {
|
||||||
let urls = UrlBundle::from_root_url(root_url).await?;
|
let urls = UrlBundle::from_root_url(root_url).await?;
|
||||||
Instance::from_url_bundle(urls).await
|
Instance::from_url_bundle(urls, options).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_limited(api_url: &str) -> ChorusResult<Option<LimitsConfiguration>> {
|
pub async fn is_limited(api_url: &str) -> ChorusResult<Option<LimitsConfiguration>> {
|
||||||
let api_url = UrlBundle::parse_url(api_url.to_string());
|
let api_url = UrlBundle::parse_url(api_url);
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let request = client
|
let request = client
|
||||||
.get(format!("{}/policies/instance/limits", &api_url))
|
.get(format!("{}/policies/instance/limits", &api_url))
|
||||||
|
@ -163,8 +170,8 @@ impl ChorusUser {
|
||||||
self.token.clone()
|
self.token.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_token(&mut self, token: String) {
|
pub fn set_token(&mut self, token: &str) {
|
||||||
self.token = token;
|
self.token = token.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [ChorusUser] from existing data.
|
/// Creates a new [ChorusUser] from existing data.
|
||||||
|
@ -195,16 +202,15 @@ impl ChorusUser {
|
||||||
/// registering or logging in to the Instance, where you do not yet have a User object, but still
|
/// registering or logging in to the Instance, where you do not yet have a User object, but still
|
||||||
/// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify
|
/// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify
|
||||||
/// first.
|
/// first.
|
||||||
pub(crate) async fn shell(instance: Shared<Instance>, token: String) -> ChorusUser {
|
pub(crate) async fn shell(instance: Shared<Instance>, token: &str) -> ChorusUser {
|
||||||
let settings = Arc::new(RwLock::new(UserSettings::default()));
|
let settings = Arc::new(RwLock::new(UserSettings::default()));
|
||||||
let object = Arc::new(RwLock::new(User::default()));
|
let object = Arc::new(RwLock::new(User::default()));
|
||||||
let wss_url = instance.read().unwrap().urls.wss.clone();
|
let wss_url = &instance.read().unwrap().urls.wss.clone();
|
||||||
|
let gateway_options = instance.read().unwrap().gateway_options;
|
||||||
// Dummy gateway object
|
// Dummy gateway object
|
||||||
let gateway = Gateway::spawn(wss_url, GatewayOptions::default())
|
let gateway = Gateway::spawn(wss_url, gateway_options).await.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
ChorusUser {
|
ChorusUser {
|
||||||
token,
|
token: token.to_string(),
|
||||||
belongs_to: instance.clone(),
|
belongs_to: instance.clone(),
|
||||||
limits: instance
|
limits: instance
|
||||||
.read()
|
.read()
|
||||||
|
|
115
src/lib.rs
115
src/lib.rs
|
@ -3,27 +3,29 @@
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Chorus combines all the required functionalities of a user-centric Spacebar library into one package.
|
Chorus is a Rust library which poses as an API wrapper for [Spacebar Chat](https://github.com/spacebarchat/),
|
||||||
|
Discord and our own Polyphony. Its high-level API is designed to be easy to use, while still providing the
|
||||||
|
flexibility one would expect from a library like this.
|
||||||
|
|
||||||
|
You can establish as many connections to as many servers as you want, and you can use them all at the same time.
|
||||||
|
|
||||||
|
## A Tour of Chorus
|
||||||
|
|
||||||
|
Chorus combines all the required functionalities of an API wrapper for chat services into one modular library.
|
||||||
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
|
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
|
||||||
a WebSocket connection to the Gateway. This means that you can focus on building your application,
|
a WebSocket connection to the Gateway. This means that you can focus on building your application,
|
||||||
instead of worrying about the underlying implementation details.
|
instead of worrying about the underlying implementation details.
|
||||||
|
|
||||||
### Establishing a Connection
|
### Establishing a Connection
|
||||||
|
|
||||||
To connect to a Spacebar compatible server, you need to create an [`Instance`](https://docs.rs/chorus/latest/chorus/instance/struct.Instance.html) like this:
|
To connect to a Polyphony/Spacebar compatible server, you'll need to create an [`Instance`](https://docs.rs/chorus/latest/chorus/instance/struct.Instance.html) like this:
|
||||||
|
|
||||||
```rs
|
```rs
|
||||||
use chorus::instance::Instance;
|
use chorus::instance::Instance;
|
||||||
use chorus::UrlBundle;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let bundle = UrlBundle::new(
|
let instance = Instance::new("https://example.com")
|
||||||
"https://example.com/api".to_string(),
|
|
||||||
"wss://example.com/".to_string(),
|
|
||||||
"https://example.com/cdn".to_string(),
|
|
||||||
);
|
|
||||||
let instance = Instance::new(bundle)
|
|
||||||
.await
|
.await
|
||||||
.expect("Failed to connect to the Spacebar server");
|
.expect("Failed to connect to the Spacebar server");
|
||||||
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
|
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
|
||||||
|
@ -36,7 +38,7 @@ This Instance can now be used to log in, register and from there on, interact wi
|
||||||
|
|
||||||
### Logging In
|
### Logging In
|
||||||
|
|
||||||
Logging in correctly provides you with an instance of [`ChorusUser`](https://docs.rs/chorus/latest/chorus/instance/struct.ChorusUser.html), with which you can interact with the server and
|
Logging in correctly provides you with an instance of `ChorusUser`, with which you can interact with the server and
|
||||||
manipulate the account. Assuming you already have an account on the server, you can log in like this:
|
manipulate the account. Assuming you already have an account on the server, you can log in like this:
|
||||||
|
|
||||||
```rs
|
```rs
|
||||||
|
@ -48,7 +50,7 @@ let login_schema = LoginSchema {
|
||||||
password: "Correct-Horse-Battery-Staple".to_string(),
|
password: "Correct-Horse-Battery-Staple".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
// Each user connects to the Gateway. The Gateway connection lives on a separate thread. Depending on
|
// Each user connects to the Gateway. Each users' Gateway connection lives on a separate thread. Depending on
|
||||||
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
|
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
|
||||||
let user = instance
|
let user = instance
|
||||||
.login_account(login_schema)
|
.login_account(login_schema)
|
||||||
|
@ -64,15 +66,33 @@ All major desktop operating systems (Windows, macOS (aarch64/x86_64), Linux (aar
|
||||||
`wasm32-unknown-unknown` is a supported compilation target on versions `0.12.0` and up. This allows you to use
|
`wasm32-unknown-unknown` is a supported compilation target on versions `0.12.0` and up. This allows you to use
|
||||||
Chorus in your browser, or in any other environment that supports WebAssembly.
|
Chorus in your browser, or in any other environment that supports WebAssembly.
|
||||||
|
|
||||||
We recommend checking out the examples directory, as well as the documentation for more information.
|
To compile for `wasm32-unknown-unknown`, execute the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --target=wasm32-unknown-unknown --no-default-features
|
||||||
|
```
|
||||||
|
|
||||||
|
The following features are supported on `wasm32-unknown-unknown`:
|
||||||
|
|
||||||
|
| Feature | WASM Support |
|
||||||
|
| ----------------- | ------------ |
|
||||||
|
| `client` | ✅ |
|
||||||
|
| `rt` | ✅ |
|
||||||
|
| `rt-multi-thread` | ❌ |
|
||||||
|
| `backend` | ❌ |
|
||||||
|
| `voice` | ❌ |
|
||||||
|
| `voice_udp` | ❌ |
|
||||||
|
| `voice_gateway` | ✅ |
|
||||||
|
|
||||||
|
We recommend checking out the "examples" directory, as well as the documentation for more information.
|
||||||
|
|
||||||
## MSRV (Minimum Supported Rust Version)
|
## MSRV (Minimum Supported Rust Version)
|
||||||
|
|
||||||
Rust **1.67.1**. This number might change at any point while Chorus is not yet at version 1.0.0.
|
Rust **1.70.0**. This number might change at any point while Chorus is not yet at version 1.0.0.
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
Make sure that you have at least Rust 1.67.1 installed. You can check your Rust version by running `cargo --version`
|
Make sure that you have at least Rust 1.70.0 installed. You can check your Rust version by running `cargo --version`
|
||||||
in your terminal. To compile for `wasm32-unknown-unknown`, you need to install the `wasm32-unknown-unknown` target.
|
in your terminal. To compile for `wasm32-unknown-unknown`, you need to install the `wasm32-unknown-unknown` target.
|
||||||
You can do this by running `rustup target add wasm32-unknown-unknown`.
|
You can do this by running `rustup target add wasm32-unknown-unknown`.
|
||||||
|
|
||||||
|
@ -86,12 +106,16 @@ like "proxy connection checking" are already disabled on this version, which oth
|
||||||
### wasm
|
### wasm
|
||||||
|
|
||||||
To test for wasm, you will need to `cargo install wasm-pack`. You can then run
|
To test for wasm, you will need to `cargo install wasm-pack`. You can then run
|
||||||
`wasm-pack test --<chrome/firefox/safari> --headless -- --target wasm32-unknown-unknown --features="rt, client" --no-default-features`
|
`wasm-pack test --<chrome/firefox/safari> --headless -- --target wasm32-unknown-unknown --features="rt, client, voice_gateway" --no-default-features`
|
||||||
to run the tests for wasm.
|
to run the tests for wasm.
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read the specification [here](https://semver.org/spec/v2.0.0.html).
|
This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read the specification [here](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||||
!*/
|
!*/
|
||||||
#![doc(
|
#![doc(
|
||||||
html_logo_url = "https://raw.githubusercontent.com/polyphony-chat/design/main/branding/polyphony-chorus-round-8bit.png"
|
html_logo_url = "https://raw.githubusercontent.com/polyphony-chat/design/main/branding/polyphony-chorus-round-8bit.png"
|
||||||
|
@ -101,8 +125,7 @@ This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read
|
||||||
clippy::extra_unused_lifetimes,
|
clippy::extra_unused_lifetimes,
|
||||||
clippy::from_over_into,
|
clippy::from_over_into,
|
||||||
clippy::needless_borrow,
|
clippy::needless_borrow,
|
||||||
clippy::new_without_default,
|
clippy::new_without_default
|
||||||
clippy::useless_conversion
|
|
||||||
)]
|
)]
|
||||||
#![warn(
|
#![warn(
|
||||||
clippy::todo,
|
clippy::todo,
|
||||||
|
@ -111,7 +134,8 @@ This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read
|
||||||
clippy::print_stdout,
|
clippy::print_stdout,
|
||||||
clippy::print_stderr,
|
clippy::print_stderr,
|
||||||
missing_debug_implementations,
|
missing_debug_implementations,
|
||||||
missing_copy_implementations
|
missing_copy_implementations,
|
||||||
|
clippy::useless_conversion
|
||||||
)]
|
)]
|
||||||
#[cfg(all(feature = "rt", feature = "rt_multi_thread"))]
|
#[cfg(all(feature = "rt", feature = "rt_multi_thread"))]
|
||||||
compile_error!("feature \"rt\" and feature \"rt_multi_thread\" cannot be enabled at the same time");
|
compile_error!("feature \"rt\" and feature \"rt_multi_thread\" cannot be enabled at the same time");
|
||||||
|
@ -139,6 +163,27 @@ pub mod types;
|
||||||
))]
|
))]
|
||||||
pub mod voice;
|
pub mod voice;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt128 = u128;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt128 = sqlx_pg_uint::PgU128;
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt64 = u64;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt64 = sqlx_pg_uint::PgU64;
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt32 = u32;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt32 = sqlx_pg_uint::PgU32;
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt16 = u16;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt16 = sqlx_pg_uint::PgU16;
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
|
pub type UInt8 = u8;
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
pub type UInt8 = sqlx_pg_uint::PgU8;
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
/// A URLBundle bundles together the API-, Gateway- and CDN-URLs of a Spacebar instance.
|
/// A URLBundle bundles together the API-, Gateway- and CDN-URLs of a Spacebar instance.
|
||||||
///
|
///
|
||||||
|
@ -165,7 +210,7 @@ pub struct UrlBundle {
|
||||||
|
|
||||||
impl UrlBundle {
|
impl UrlBundle {
|
||||||
/// Creates a new UrlBundle from the relevant urls.
|
/// Creates a new UrlBundle from the relevant urls.
|
||||||
pub fn new(root: String, api: String, wss: String, cdn: String) -> Self {
|
pub fn new(root: &str, api: &str, wss: &str, cdn: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
root: UrlBundle::parse_url(root),
|
root: UrlBundle::parse_url(root),
|
||||||
api: UrlBundle::parse_url(api),
|
api: UrlBundle::parse_url(api),
|
||||||
|
@ -182,17 +227,17 @@ impl UrlBundle {
|
||||||
/// let url = parse_url("localhost:3000");
|
/// let url = parse_url("localhost:3000");
|
||||||
/// ```
|
/// ```
|
||||||
/// `-> Outputs "http://localhost:3000".`
|
/// `-> Outputs "http://localhost:3000".`
|
||||||
pub fn parse_url(url: String) -> String {
|
pub fn parse_url(url: &str) -> String {
|
||||||
let url = match Url::parse(&url) {
|
let url = match Url::parse(url) {
|
||||||
Ok(url) => {
|
Ok(url) => {
|
||||||
if url.scheme() == "localhost" {
|
if url.scheme() == "localhost" {
|
||||||
return UrlBundle::parse_url(format!("http://{}", url));
|
return UrlBundle::parse_url(&format!("http://{}", url));
|
||||||
}
|
}
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
Err(ParseError::RelativeUrlWithoutBase) => {
|
Err(ParseError::RelativeUrlWithoutBase) => {
|
||||||
let url_fmt = format!("http://{}", url);
|
let url_fmt = format!("http://{}", url);
|
||||||
return UrlBundle::parse_url(url_fmt);
|
return UrlBundle::parse_url(&url_fmt);
|
||||||
}
|
}
|
||||||
Err(_) => panic!("Invalid URL"), // TODO: should not panic here
|
Err(_) => panic!("Invalid URL"), // TODO: should not panic here
|
||||||
};
|
};
|
||||||
|
@ -215,7 +260,7 @@ impl UrlBundle {
|
||||||
/// of the above approaches fail, it is very likely that the instance is misconfigured, unreachable, or that
|
/// of the above approaches fail, it is very likely that the instance is misconfigured, unreachable, or that
|
||||||
/// a wrong URL was provided.
|
/// a wrong URL was provided.
|
||||||
pub async fn from_root_url(url: &str) -> ChorusResult<UrlBundle> {
|
pub async fn from_root_url(url: &str) -> ChorusResult<UrlBundle> {
|
||||||
let parsed = UrlBundle::parse_url(url.to_string());
|
let parsed = UrlBundle::parse_url(url);
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let request_wellknown = client
|
let request_wellknown = client
|
||||||
.get(format!("{}/.well-known/spacebar", &parsed))
|
.get(format!("{}/.well-known/spacebar", &parsed))
|
||||||
|
@ -253,10 +298,10 @@ impl UrlBundle {
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(UrlBundle::new(
|
Ok(UrlBundle::new(
|
||||||
url.to_string(),
|
url,
|
||||||
body.api_endpoint,
|
&body.api_endpoint,
|
||||||
body.gateway,
|
&body.gateway,
|
||||||
body.cdn,
|
&body.cdn,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(ChorusError::RequestFailed {
|
Err(ChorusError::RequestFailed {
|
||||||
|
@ -273,13 +318,13 @@ mod lib {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_url() {
|
fn test_parse_url() {
|
||||||
let mut result = UrlBundle::parse_url(String::from("localhost:3000/"));
|
let mut result = UrlBundle::parse_url("localhost:3000/");
|
||||||
assert_eq!(result, String::from("http://localhost:3000"));
|
assert_eq!(result, "http://localhost:3000");
|
||||||
result = UrlBundle::parse_url(String::from("https://some.url.com/"));
|
result = UrlBundle::parse_url("https://some.url.com/");
|
||||||
assert_eq!(result, String::from("https://some.url.com"));
|
|
||||||
result = UrlBundle::parse_url(String::from("https://some.url.com/"));
|
|
||||||
assert_eq!(result, String::from("https://some.url.com"));
|
|
||||||
result = UrlBundle::parse_url(String::from("https://some.url.com"));
|
|
||||||
assert_eq!(result, String::from("https://some.url.com"));
|
assert_eq!(result, String::from("https://some.url.com"));
|
||||||
|
result = UrlBundle::parse_url("https://some.url.com/");
|
||||||
|
assert_eq!(result, "https://some.url.com");
|
||||||
|
result = UrlBundle::parse_url("https://some.url.com");
|
||||||
|
assert_eq!(result, "https://some.url.com");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,11 +162,11 @@ impl Display for GuildFeaturesList {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl<'r> sqlx::Decode<'r, sqlx::Any> for GuildFeaturesList {
|
impl<'r> sqlx::Decode<'r, sqlx::Postgres> for GuildFeaturesList {
|
||||||
fn decode(
|
fn decode(
|
||||||
value: <sqlx::Any as sqlx::Database>::ValueRef<'r>,
|
value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>,
|
||||||
) -> Result<Self, sqlx::error::BoxDynError> {
|
) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
let v = <String as sqlx::Decode<sqlx::Any>>::decode(value)?;
|
let v = <String as sqlx::Decode<sqlx::Postgres>>::decode(value)?;
|
||||||
Ok(Self(
|
Ok(Self(
|
||||||
v.split(',')
|
v.split(',')
|
||||||
.filter(|f| !f.is_empty())
|
.filter(|f| !f.is_empty())
|
||||||
|
@ -177,10 +177,10 @@ impl<'r> sqlx::Decode<'r, sqlx::Any> for GuildFeaturesList {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl<'q> sqlx::Encode<'q, sqlx::Any> for GuildFeaturesList {
|
impl<'q> sqlx::Encode<'q, sqlx::Postgres> for GuildFeaturesList {
|
||||||
fn encode_by_ref(
|
fn encode_by_ref(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut <sqlx::Any as sqlx::Database>::ArgumentBuffer<'q>,
|
buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
|
||||||
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return Ok(sqlx::encode::IsNull::Yes);
|
return Ok(sqlx::encode::IsNull::Yes);
|
||||||
|
@ -191,18 +191,18 @@ impl<'q> sqlx::Encode<'q, sqlx::Any> for GuildFeaturesList {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(",");
|
.join(",");
|
||||||
|
|
||||||
<String as sqlx::Encode<sqlx::Any>>::encode_by_ref(&features, buf)
|
<String as sqlx::Encode<sqlx::Postgres>>::encode_by_ref(&features, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl sqlx::Type<sqlx::Any> for GuildFeaturesList {
|
impl sqlx::Type<sqlx::Postgres> for GuildFeaturesList {
|
||||||
fn type_info() -> sqlx::any::AnyTypeInfo {
|
fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
|
||||||
<String as sqlx::Type<sqlx::Any>>::type_info()
|
<String as sqlx::Type<sqlx::Postgres>>::type_info()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compatible(ty: &sqlx::any::AnyTypeInfo) -> bool {
|
fn compatible(ty: &<sqlx::Postgres as sqlx::Database>::TypeInfo) -> bool {
|
||||||
<String as sqlx::Type<sqlx::Any>>::compatible(ty)
|
<String as sqlx::Type<sqlx::Postgres>>::compatible(ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,8 @@ pub struct ApplicationCommandOptionChoice {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(i32)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-types>
|
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-types>
|
||||||
pub enum ApplicationCommandOptionType {
|
pub enum ApplicationCommandOptionType {
|
||||||
|
@ -294,7 +295,8 @@ pub struct ApplicationCommandPermission {
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permission-type>
|
/// See <https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permission-type>
|
||||||
pub enum ApplicationCommandPermissionType {
|
pub enum ApplicationCommandPermissionType {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, PartialOrd)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -16,11 +17,11 @@ pub struct Attachment {
|
||||||
/// Max 1024 characters
|
/// Max 1024 characters
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub content_type: Option<String>,
|
pub content_type: Option<String>,
|
||||||
pub size: u64,
|
pub size: UInt64,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub proxy_url: String,
|
pub proxy_url: String,
|
||||||
pub height: Option<u64>,
|
pub height: Option<UInt64>,
|
||||||
pub width: Option<u64>,
|
pub width: Option<UInt64>,
|
||||||
pub ephemeral: Option<bool>,
|
pub ephemeral: Option<bool>,
|
||||||
/// The duration of the audio file (only for voice messages)
|
/// The duration of the audio file (only for voice messages)
|
||||||
pub duration_secs: Option<f32>,
|
pub duration_secs: Option<f32>,
|
||||||
|
@ -37,12 +38,12 @@ pub struct Attachment {
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
pub struct PartialDiscordFileAttachment {
|
pub struct PartialDiscordFileAttachment {
|
||||||
pub id: Option<i16>,
|
pub id: Option<UInt64>,
|
||||||
pub filename: String,
|
pub filename: String,
|
||||||
/// Max 1024 characters
|
/// Max 1024 characters
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub content_type: Option<String>,
|
pub content_type: Option<String>,
|
||||||
pub size: Option<i64>,
|
pub size: Option<UInt64>,
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub proxy_url: Option<String>,
|
pub proxy_url: Option<String>,
|
||||||
pub height: Option<i32>,
|
pub height: Option<i32>,
|
||||||
|
|
|
@ -12,6 +12,7 @@ use crate::types::utils::Snowflake;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
AutoModerationRuleTriggerType, IntegrationType, PermissionOverwriteType, Shared,
|
AutoModerationRuleTriggerType, IntegrationType, PermissionOverwriteType, Shared,
|
||||||
};
|
};
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -108,7 +109,8 @@ pub struct AuditLogChange {
|
||||||
PartialOrd,
|
PartialOrd,
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// See <https://docs.discord.sex/resources/audit-log#audit-log-events>
|
/// See <https://docs.discord.sex/resources/audit-log#audit-log-events>
|
||||||
|
@ -251,16 +253,16 @@ pub struct AuditEntryInfo {
|
||||||
pub auto_moderation_rule_trigger_type: Option<AutoModerationRuleTriggerType>,
|
pub auto_moderation_rule_trigger_type: Option<AutoModerationRuleTriggerType>,
|
||||||
pub channel_id: Option<Snowflake>,
|
pub channel_id: Option<Snowflake>,
|
||||||
// #[serde(option_string)]
|
// #[serde(option_string)]
|
||||||
pub count: Option<u64>,
|
pub count: Option<UInt64>,
|
||||||
// #[serde(option_string)]
|
// #[serde(option_string)]
|
||||||
pub delete_member_days: Option<u64>,
|
pub delete_member_days: Option<UInt64>,
|
||||||
/// The ID of the overwritten entity
|
/// The ID of the overwritten entity
|
||||||
pub id: Option<Snowflake>,
|
pub id: Option<Snowflake>,
|
||||||
pub integration_type: Option<IntegrationType>,
|
pub integration_type: Option<IntegrationType>,
|
||||||
// #[serde(option_string)]
|
// #[serde(option_string)]
|
||||||
pub members_removed: Option<u64>,
|
pub members_removed: Option<UInt64>,
|
||||||
// #[serde(option_string)]
|
// #[serde(option_string)]
|
||||||
pub message_id: Option<u64>,
|
pub message_id: Option<UInt64>,
|
||||||
pub role_name: Option<String>,
|
pub role_name: Option<String>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub overwrite_type: Option<PermissionOverwriteType>,
|
pub overwrite_type: Option<PermissionOverwriteType>,
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use crate::gateway::Updateable;
|
use crate::gateway::Updateable;
|
||||||
use crate::types::Shared;
|
use crate::types::Shared;
|
||||||
|
use crate::UInt8;
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use chorus_macros::Updateable;
|
use chorus_macros::Updateable;
|
||||||
|
@ -32,7 +33,8 @@ pub struct AutoModerationRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default, Copy)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default, Copy)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-event-types>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-event-types>
|
||||||
pub enum AutoModerationRuleEventType {
|
pub enum AutoModerationRuleEventType {
|
||||||
|
@ -43,7 +45,8 @@ pub enum AutoModerationRuleEventType {
|
||||||
#[derive(
|
#[derive(
|
||||||
Serialize_repr, Deserialize_repr, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy,
|
Serialize_repr, Deserialize_repr, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types>
|
||||||
pub enum AutoModerationRuleTriggerType {
|
pub enum AutoModerationRuleTriggerType {
|
||||||
|
@ -80,18 +83,20 @@ pub struct AutoModerationRuleTriggerMetadataForKeywordPreset {
|
||||||
pub allow_list: Vec<String>,
|
pub allow_list: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy)]
|
#[allow(missing_copy_implementations)]
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-metadata>
|
||||||
pub struct AutoModerationRuleTriggerMetadataForMentionSpam {
|
pub struct AutoModerationRuleTriggerMetadataForMentionSpam {
|
||||||
/// Max 50
|
/// Max 50
|
||||||
pub mention_total_limit: u8,
|
pub mention_total_limit: UInt8,
|
||||||
pub mention_raid_protection_enabled: bool,
|
pub mention_raid_protection_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Serialize_repr, Deserialize_repr, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy,
|
Serialize_repr, Deserialize_repr, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-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 {
|
||||||
|
@ -110,9 +115,20 @@ pub struct AutoModerationAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Serialize_repr, Deserialize_repr, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Copy, Hash
|
Serialize_repr,
|
||||||
|
Deserialize_repr,
|
||||||
|
Debug,
|
||||||
|
Clone,
|
||||||
|
Default,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
Copy,
|
||||||
|
Hash,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-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 {
|
||||||
|
|
|
@ -22,6 +22,7 @@ use crate::gateway::GatewayHandle;
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use crate::gateway::Updateable;
|
use crate::gateway::Updateable;
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use chorus_macros::{observe_option_vec, Composite, Updateable};
|
use chorus_macros::{observe_option_vec, Composite, Updateable};
|
||||||
|
@ -41,13 +42,7 @@ use super::{option_arc_rwlock_ptr_eq, option_vec_arc_rwlock_ptr_eq};
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#channels-resource>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#channels-resource>
|
||||||
pub struct Channel {
|
pub struct Channel {
|
||||||
pub application_id: Option<Snowflake>,
|
pub application_id: Option<Snowflake>,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub applied_tags: Option<sqlx::types::Json<Vec<String>>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub applied_tags: Option<Vec<String>>,
|
pub applied_tags: Option<Vec<String>>,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub available_tags: Option<sqlx::types::Json<Vec<Tag>>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub available_tags: Option<Vec<Tag>>,
|
pub available_tags: Option<Vec<Tag>>,
|
||||||
pub bitrate: Option<i32>,
|
pub bitrate: Option<i32>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -55,9 +50,7 @@ pub struct Channel {
|
||||||
pub created_at: Option<chrono::DateTime<Utc>>,
|
pub created_at: Option<chrono::DateTime<Utc>>,
|
||||||
pub default_auto_archive_duration: Option<i32>,
|
pub default_auto_archive_duration: Option<i32>,
|
||||||
pub default_forum_layout: Option<i32>,
|
pub default_forum_layout: Option<i32>,
|
||||||
#[cfg(feature = "sqlx")]
|
// DefaultReaction could be stored in a separate table. However, there are a lot of default emojis. How would we handle that?
|
||||||
pub default_reaction_emoji: Option<sqlx::types::Json<DefaultReaction>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub default_reaction_emoji: Option<DefaultReaction>,
|
pub default_reaction_emoji: Option<DefaultReaction>,
|
||||||
pub default_sort_order: Option<i32>,
|
pub default_sort_order: Option<i32>,
|
||||||
pub default_thread_rate_limit_per_user: Option<i32>,
|
pub default_thread_rate_limit_per_user: Option<i32>,
|
||||||
|
@ -179,6 +172,8 @@ fn compare_permission_overwrites(
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#forum-tag-object>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#forum-tag-object>
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
|
#[cfg_attr(feature = "sqlx", sqlx(type_name = "interface_type"))]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
/// The name of the tag (max 20 characters)
|
/// The name of the tag (max 20 characters)
|
||||||
|
@ -202,7 +197,8 @@ pub struct PermissionOverwrite {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize_repr, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
|
#[derive(Debug, Serialize_repr, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
///
|
///
|
||||||
/// See <https://docs.discord.sex/resources/channel#permission-overwrite-type>
|
/// See <https://docs.discord.sex/resources/channel#permission-overwrite-type>
|
||||||
|
@ -301,7 +297,7 @@ 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<DateTime<Utc>>,
|
pub join_timestamp: Option<DateTime<Utc>>,
|
||||||
pub flags: Option<u64>,
|
pub flags: Option<UInt64>,
|
||||||
pub member: Option<Shared<GuildMember>>,
|
pub member: Option<Shared<GuildMember>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,6 +317,8 @@ impl PartialEq for ThreadMember {
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#default-reaction-object>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#default-reaction-object>
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
|
#[cfg_attr(feature = "sqlx", sqlx(type_name = "interface_type"))]
|
||||||
pub struct DefaultReaction {
|
pub struct DefaultReaction {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub emoji_id: Option<Snowflake>,
|
pub emoji_id: Option<Snowflake>,
|
||||||
|
@ -342,7 +340,7 @@ pub struct DefaultReaction {
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[repr(u32)]
|
#[repr(i32)]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#channel-type>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#channel-type>
|
||||||
pub enum ChannelType {
|
pub enum ChannelType {
|
||||||
|
|
|
@ -32,9 +32,6 @@ use super::option_arc_rwlock_ptr_eq;
|
||||||
pub struct Emoji {
|
pub struct Emoji {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub roles: Option<sqlx::types::Json<Vec<Snowflake>>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub roles: Option<Vec<Snowflake>>,
|
pub roles: Option<Vec<Snowflake>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub user: Option<Shared<User>>,
|
pub user: Option<Shared<User>>,
|
||||||
|
|
|
@ -17,6 +17,7 @@ use crate::types::{
|
||||||
interfaces::WelcomeScreenObject,
|
interfaces::WelcomeScreenObject,
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
};
|
};
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
use super::{option_arc_rwlock_ptr_eq, vec_arc_rwlock_ptr_eq, PublicUser};
|
use super::{option_arc_rwlock_ptr_eq, vec_arc_rwlock_ptr_eq, PublicUser};
|
||||||
|
|
||||||
|
@ -273,7 +274,7 @@ pub struct GuildScheduledEvent {
|
||||||
pub entity_id: Option<Snowflake>,
|
pub entity_id: Option<Snowflake>,
|
||||||
pub entity_metadata: Option<GuildScheduledEventEntityMetadata>,
|
pub entity_metadata: Option<GuildScheduledEventEntityMetadata>,
|
||||||
pub creator: Option<Shared<User>>,
|
pub creator: Option<Shared<User>>,
|
||||||
pub user_count: Option<u64>,
|
pub user_count: Option<UInt64>,
|
||||||
pub image: Option<String>,
|
pub image: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +301,8 @@ impl PartialEq for GuildScheduledEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq, Copy)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq, Copy)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-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]
|
||||||
|
@ -308,7 +310,8 @@ pub enum GuildScheduledEventPrivacyLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq, Copy)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone, PartialEq, Copy)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status>
|
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status>
|
||||||
pub enum GuildScheduledEventStatus {
|
pub enum GuildScheduledEventStatus {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -331,7 +334,8 @@ pub enum GuildScheduledEventStatus {
|
||||||
Copy,
|
Copy,
|
||||||
Hash,
|
Hash,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-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]
|
||||||
|
@ -369,7 +373,8 @@ pub struct VoiceRegion {
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#message-notification-level>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#message-notification-level>
|
||||||
pub enum MessageNotificationLevel {
|
pub enum MessageNotificationLevel {
|
||||||
|
@ -392,7 +397,8 @@ pub enum MessageNotificationLevel {
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#explicit-content-filter-level>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#explicit-content-filter-level>
|
||||||
pub enum ExplicitContentFilterLevel {
|
pub enum ExplicitContentFilterLevel {
|
||||||
|
@ -416,7 +422,8 @@ pub enum ExplicitContentFilterLevel {
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#verification-level>
|
||||||
pub enum VerificationLevel {
|
pub enum VerificationLevel {
|
||||||
|
@ -442,7 +449,8 @@ pub enum VerificationLevel {
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://docs.discord.sex/resources/guild#mfa-level>
|
/// See <https://docs.discord.sex/resources/guild#mfa-level>
|
||||||
pub enum MFALevel {
|
pub enum MFALevel {
|
||||||
|
@ -465,7 +473,8 @@ pub enum MFALevel {
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://docs.discord.sex/resources/guild#nsfw-level>
|
/// See <https://docs.discord.sex/resources/guild#nsfw-level>
|
||||||
pub enum NSFWLevel {
|
pub enum NSFWLevel {
|
||||||
|
@ -490,7 +499,8 @@ pub enum NSFWLevel {
|
||||||
Ord,
|
Ord,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
// Note: Maybe rename this to GuildPremiumTier?
|
// Note: Maybe rename this to GuildPremiumTier?
|
||||||
/// **Guild** premium (Boosting) tier
|
/// **Guild** premium (Boosting) tier
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::types::{
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
Shared,
|
Shared,
|
||||||
};
|
};
|
||||||
|
use crate::{UInt16, UInt8};
|
||||||
|
|
||||||
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
|
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -25,7 +26,7 @@ pub struct Integration {
|
||||||
pub role_id: Option<String>,
|
pub role_id: Option<String>,
|
||||||
pub enabled_emoticons: Option<bool>,
|
pub enabled_emoticons: Option<bool>,
|
||||||
pub expire_behaviour: Option<IntegrationExpireBehaviour>,
|
pub expire_behaviour: Option<IntegrationExpireBehaviour>,
|
||||||
pub expire_grace_period: Option<u16>,
|
pub expire_grace_period: Option<UInt16>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub user: Option<Shared<User>>,
|
pub user: Option<Shared<User>>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
|
|
|
@ -5,8 +5,12 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{Snowflake, WelcomeScreenObject, Shared, InviteFlags, InviteType, InviteTargetType, Guild, VerificationLevel};
|
|
||||||
use crate::types::types::guild_configuration::GuildFeaturesList;
|
use crate::types::types::guild_configuration::GuildFeaturesList;
|
||||||
|
use crate::types::{
|
||||||
|
Guild, InviteFlags, InviteTargetType, InviteType, Shared, Snowflake, VerificationLevel,
|
||||||
|
WelcomeScreenObject,
|
||||||
|
};
|
||||||
|
use crate::{UInt32, UInt8};
|
||||||
|
|
||||||
use super::guild::GuildScheduledEvent;
|
use super::guild::GuildScheduledEvent;
|
||||||
use super::{Application, Channel, GuildMember, NSFWLevel, User};
|
use super::{Application, Channel, GuildMember, NSFWLevel, User};
|
||||||
|
@ -36,8 +40,8 @@ pub struct Invite {
|
||||||
pub invite_type: Option<InviteType>,
|
pub invite_type: Option<InviteType>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub inviter: Option<User>,
|
pub inviter: Option<User>,
|
||||||
pub max_age: Option<u32>,
|
pub max_age: Option<UInt32>,
|
||||||
pub max_uses: Option<u8>,
|
pub max_uses: Option<UInt8>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub stage_instance: Option<InviteStageInstance>,
|
pub stage_instance: Option<InviteStageInstance>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
|
@ -47,7 +51,7 @@ pub struct Invite {
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub target_user: Option<User>,
|
pub target_user: Option<User>,
|
||||||
pub temporary: Option<bool>,
|
pub temporary: Option<bool>,
|
||||||
pub uses: Option<u32>,
|
pub uses: Option<UInt32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The guild an invite is for.
|
/// The guild an invite is for.
|
||||||
|
|
|
@ -15,6 +15,7 @@ use crate::types::{
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
Shared,
|
Shared,
|
||||||
};
|
};
|
||||||
|
use crate::{UInt32, UInt8};
|
||||||
|
|
||||||
use super::option_arc_rwlock_ptr_eq;
|
use super::option_arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
|
@ -150,7 +151,7 @@ pub enum MessageReferenceType {
|
||||||
pub struct MessageInteraction {
|
pub struct MessageInteraction {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub interaction_type: u8,
|
pub interaction_type: UInt8,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub user: User,
|
pub user: User,
|
||||||
pub member: Option<Shared<GuildMember>>,
|
pub member: Option<Shared<GuildMember>>,
|
||||||
|
@ -282,8 +283,8 @@ pub struct EmbedField {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct Reaction {
|
pub struct Reaction {
|
||||||
pub count: u32,
|
pub count: UInt32,
|
||||||
pub burst_count: u32,
|
pub burst_count: UInt32,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub me: bool,
|
pub me: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -296,6 +297,8 @@ pub struct Reaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
pub enum Component {
|
pub enum Component {
|
||||||
ActionRow = 1,
|
ActionRow = 1,
|
||||||
Button = 2,
|
Button = 2,
|
||||||
|
@ -320,7 +323,8 @@ pub struct MessageActivity {
|
||||||
Debug, Default, PartialEq, Clone, Copy, Serialize_repr, Deserialize_repr, Eq, PartialOrd, Ord,
|
Debug, Default, PartialEq, Clone, Copy, Serialize_repr, Deserialize_repr, Eq, PartialOrd, Ord,
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://docs.discord.sex/resources/message#message-type>
|
/// See <https://docs.discord.sex/resources/message#message-type>
|
||||||
|
@ -464,7 +468,8 @@ pub struct PartialEmoji {
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, PartialOrd, Ord, Eq, Hash)]
|
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, PartialOrd, Ord, Eq, Hash)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
pub enum ReactionType {
|
pub enum ReactionType {
|
||||||
Normal = 0,
|
Normal = 0,
|
||||||
Burst = 1, // The dreaded super reactions
|
Burst = 1, // The dreaded super reactions
|
||||||
|
|
|
@ -132,10 +132,10 @@ pub trait Composite<T: Updateable + Clone + Debug> {
|
||||||
pub trait IntoShared {
|
pub trait IntoShared {
|
||||||
/// Uses [`Shared`] to provide an ergonomic alternative to `Arc::new(RwLock::new(obj))`.
|
/// Uses [`Shared`] to provide an ergonomic alternative to `Arc::new(RwLock::new(obj))`.
|
||||||
///
|
///
|
||||||
/// [`Shared<Self>`] can then be observed using the [`Gateway`], turning the underlying
|
/// [`Shared<Self>`] can then be observed using the gateway, turning the underlying
|
||||||
/// `dyn Composite<Self>` into a self-updating struct, which is a tracked variant of a chorus
|
/// `dyn Composite<Self>` into a self-updating struct, which is a tracked variant of a chorus
|
||||||
/// entity struct, updating its' held information when new information concerning itself arrives
|
/// entity struct, updating its' held information when new information concerning itself arrives
|
||||||
/// over the [`Gateway`] connection, reducing the need for expensive network-API calls.
|
/// over the gateway connection, reducing the need for expensive network-API calls.
|
||||||
fn into_shared(self) -> Shared<Self>;
|
fn into_shared(self) -> Shared<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,8 @@ impl PartialEq for Relationship {
|
||||||
Copy,
|
Copy,
|
||||||
Hash,
|
Hash,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/user#relationship-type>
|
/// See <https://discord-userdoccers.vercel.app/resources/user#relationship-type>
|
||||||
pub enum RelationshipType {
|
pub enum RelationshipType {
|
||||||
Suggestion = 6,
|
Suggestion = 6,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use serde_aux::prelude::deserialize_option_number_from_string;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
use crate::{UInt16, UInt32};
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
use chorus_macros::{Composite, Updateable};
|
use chorus_macros::{Composite, Updateable};
|
||||||
|
@ -32,7 +33,7 @@ pub struct RoleObject {
|
||||||
pub hoist: bool,
|
pub hoist: bool,
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
pub unicode_emoji: Option<String>,
|
pub unicode_emoji: Option<String>,
|
||||||
pub position: u16,
|
pub position: UInt16,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub permissions: PermissionFlags,
|
pub permissions: PermissionFlags,
|
||||||
pub managed: bool,
|
pub managed: bool,
|
||||||
|
@ -47,11 +48,13 @@ pub struct RoleObject {
|
||||||
pub struct RoleSubscriptionData {
|
pub struct RoleSubscriptionData {
|
||||||
pub role_subscription_listing_id: Snowflake,
|
pub role_subscription_listing_id: Snowflake,
|
||||||
pub tier_name: String,
|
pub tier_name: String,
|
||||||
pub total_months_subscribed: u32,
|
pub total_months_subscribed: UInt32,
|
||||||
pub is_renewal: bool,
|
pub is_renewal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)]
|
#[derive(
|
||||||
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
/// See <https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure>
|
/// See <https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure>
|
||||||
pub struct RoleTags {
|
pub struct RoleTags {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
|
@ -13,7 +14,7 @@ pub struct SecurityKey {
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub key_id: String,
|
pub key_id: String,
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
pub counter: u64,
|
pub counter: UInt64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +25,8 @@ impl Default for SecurityKey {
|
||||||
user_id: String::new(),
|
user_id: String::new(),
|
||||||
key_id: String::new(),
|
key_id: String::new(),
|
||||||
public_key: String::new(),
|
public_key: String::new(),
|
||||||
counter: 0,
|
#[allow(clippy::useless_conversion)]
|
||||||
|
counter: 0u64.into(),
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,11 @@ pub struct StageInstance {
|
||||||
pub guild_scheduled_event_id: Option<Snowflake>,
|
pub guild_scheduled_event_id: Option<Snowflake>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(
|
||||||
#[repr(u8)]
|
Serialize_repr, Deserialize_repr, Debug, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
/// See <https://discord.com/developers/docs/resources/stage-instance#stage-instance-object-privacy-level>
|
/// See <https://discord.com/developers/docs/resources/stage-instance#stage-instance-object-privacy-level>
|
||||||
pub enum StageInstancePrivacyLevel {
|
pub enum StageInstancePrivacyLevel {
|
||||||
|
|
|
@ -77,7 +77,8 @@ pub struct StickerItem {
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Serialize_repr, Deserialize_repr,
|
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Serialize_repr, Deserialize_repr,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename = "SCREAMING_SNAKE_CASE")]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
|
@ -93,7 +94,8 @@ pub enum StickerType {
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Serialize_repr, Deserialize_repr,
|
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Serialize_repr, Deserialize_repr,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://docs.discord.sex/resources/sticker#sticker-format-types>
|
/// See <https://docs.discord.sex/resources/sticker#sticker-format-types>
|
||||||
|
|
|
@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::types::entities::User;
|
use crate::types::entities::User;
|
||||||
use crate::types::Shared;
|
use crate::types::Shared;
|
||||||
use crate::types::Snowflake;
|
use crate::types::Snowflake;
|
||||||
|
use crate::UInt8;
|
||||||
|
|
||||||
use super::arc_rwlock_ptr_eq;
|
use super::arc_rwlock_ptr_eq;
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ impl PartialEq for Team {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct TeamMember {
|
pub struct TeamMember {
|
||||||
pub membership_state: u8,
|
pub membership_state: UInt8,
|
||||||
pub permissions: Vec<String>,
|
pub permissions: Vec<String>,
|
||||||
pub team_id: Snowflake,
|
pub team_id: Snowflake,
|
||||||
pub user: Shared<User>,
|
pub user: Shared<User>,
|
||||||
|
|
|
@ -6,10 +6,11 @@ use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
Shared,
|
|
||||||
entities::{Guild, User},
|
entities::{Guild, User},
|
||||||
utils::Snowflake,
|
utils::Snowflake,
|
||||||
|
Shared,
|
||||||
};
|
};
|
||||||
|
use crate::UInt64;
|
||||||
|
|
||||||
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-template>
|
/// See <https://docs.spacebar.chat/routes/#cmp--schemas-template>
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
|
||||||
|
@ -18,7 +19,7 @@ pub struct GuildTemplate {
|
||||||
pub code: String,
|
pub code: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub usage_count: Option<u64>,
|
pub usage_count: Option<UInt64>,
|
||||||
pub creator_id: Snowflake,
|
pub creator_id: Snowflake,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
#[cfg_attr(feature = "sqlx", sqlx(skip))]
|
||||||
pub creator: Shared<User>,
|
pub creator: Shared<User>,
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use crate::errors::ChorusError;
|
use crate::errors::ChorusError;
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
use crate::{UInt32, UInt8};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_aux::prelude::{deserialize_option_number_from_string, deserialize_default_from_null};
|
use serde_aux::prelude::{deserialize_option_number_from_string, deserialize_default_from_null};
|
||||||
|
@ -50,7 +51,7 @@ pub struct User {
|
||||||
pub bot: Option<bool>,
|
pub bot: Option<bool>,
|
||||||
pub system: Option<bool>,
|
pub system: Option<bool>,
|
||||||
pub mfa_enabled: Option<bool>,
|
pub mfa_enabled: Option<bool>,
|
||||||
pub accent_color: Option<u32>,
|
pub accent_color: Option<UInt32>,
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(default))]
|
#[cfg_attr(feature = "sqlx", sqlx(default))]
|
||||||
pub locale: Option<String>,
|
pub locale: Option<String>,
|
||||||
pub verified: Option<bool>,
|
pub verified: Option<bool>,
|
||||||
|
@ -116,32 +117,32 @@ impl TryFrom<Vec<u8>> for ThemeColors {
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
// TODO: Add tests for Encode and Decode.
|
// TODO: Add tests for Encode and Decode.
|
||||||
impl<'q> sqlx::Encode<'q, sqlx::Any> for ThemeColors {
|
impl<'q> sqlx::Encode<'q, sqlx::Postgres> for ThemeColors {
|
||||||
fn encode_by_ref(
|
fn encode_by_ref(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut <sqlx::Any as sqlx::Database>::ArgumentBuffer<'q>,
|
buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
|
||||||
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let mut vec_u8 = Vec::new();
|
let mut vec_u8 = Vec::new();
|
||||||
vec_u8.extend_from_slice(&self.inner.0.to_be_bytes());
|
vec_u8.extend_from_slice(&self.inner.0.to_be_bytes());
|
||||||
vec_u8.extend_from_slice(&self.inner.1.to_be_bytes());
|
vec_u8.extend_from_slice(&self.inner.1.to_be_bytes());
|
||||||
<Vec<u8> as sqlx::Encode<sqlx::Any>>::encode_by_ref(&vec_u8, buf)
|
<Vec<u8> as sqlx::Encode<sqlx::Postgres>>::encode_by_ref(&vec_u8, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl<'d> sqlx::Decode<'d, sqlx::Any> for ThemeColors {
|
impl<'d> sqlx::Decode<'d, sqlx::Postgres> for ThemeColors {
|
||||||
fn decode(
|
fn decode(
|
||||||
value: <sqlx::Any as sqlx::Database>::ValueRef<'d>,
|
value: <sqlx::Postgres as sqlx::Database>::ValueRef<'d>,
|
||||||
) -> Result<Self, sqlx::error::BoxDynError> {
|
) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
let value_vec = <Vec<u8> as sqlx::Decode<'d, sqlx::Any>>::decode(value)?;
|
let value_vec = <Vec<u8> as sqlx::Decode<'d, sqlx::Postgres>>::decode(value)?;
|
||||||
value_vec.try_into().map_err(|e: ChorusError| e.into())
|
value_vec.try_into().map_err(|e: ChorusError| e.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl sqlx::Type<sqlx::Any> for ThemeColors {
|
impl sqlx::Type<sqlx::Postgres> for ThemeColors {
|
||||||
fn type_info() -> <sqlx::Any as sqlx::Database>::TypeInfo {
|
fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
|
||||||
<String as sqlx::Type<sqlx::Any>>::type_info()
|
<String as sqlx::Type<sqlx::Postgres>>::type_info()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +154,7 @@ pub struct PublicUser {
|
||||||
pub username: Option<String>,
|
pub username: Option<String>,
|
||||||
pub discriminator: Option<String>,
|
pub discriminator: Option<String>,
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
pub accent_color: Option<u32>,
|
pub accent_color: Option<UInt32>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
pub theme_colors: Option<ThemeColors>,
|
pub theme_colors: Option<ThemeColors>,
|
||||||
pub pronouns: Option<String>,
|
pub pronouns: Option<String>,
|
||||||
|
|
|
@ -6,9 +6,12 @@ use chrono::{serde::ts_milliseconds_option, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::Shared;
|
use crate::types::Shared;
|
||||||
|
use crate::{UInt16, UInt32, UInt8};
|
||||||
use serde_aux::field_attributes::deserialize_option_number_from_string;
|
use serde_aux::field_attributes::deserialize_option_number_from_string;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Copy, PartialOrd, Ord, Hash)]
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Copy, PartialOrd, Ord, Hash,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum UserStatus {
|
pub enum UserStatus {
|
||||||
|
@ -26,7 +29,9 @@ impl std::fmt::Display for UserStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Copy, PartialOrd, Ord, Hash)]
|
#[derive(
|
||||||
|
Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Copy, PartialOrd, Ord, Hash,
|
||||||
|
)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum UserTheme {
|
pub enum UserTheme {
|
||||||
|
@ -38,36 +43,23 @@ pub enum UserTheme {
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
||||||
pub struct UserSettings {
|
pub struct UserSettings {
|
||||||
pub afk_timeout: Option<u16>,
|
pub afk_timeout: Option<UInt16>,
|
||||||
pub allow_accessibility_detection: bool,
|
pub allow_accessibility_detection: bool,
|
||||||
pub animate_emoji: bool,
|
pub animate_emoji: bool,
|
||||||
pub animate_stickers: u8,
|
pub animate_stickers: UInt8,
|
||||||
pub contact_sync_enabled: bool,
|
pub contact_sync_enabled: bool,
|
||||||
pub convert_emoticons: bool,
|
pub convert_emoticons: bool,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub custom_status: Option<sqlx::types::Json<CustomStatus>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub custom_status: Option<CustomStatus>,
|
pub custom_status: Option<CustomStatus>,
|
||||||
pub default_guilds_restricted: bool,
|
pub default_guilds_restricted: bool,
|
||||||
pub detect_platform_accounts: bool,
|
pub detect_platform_accounts: bool,
|
||||||
pub developer_mode: bool,
|
pub developer_mode: bool,
|
||||||
pub disable_games_tab: bool,
|
pub disable_games_tab: bool,
|
||||||
pub enable_tts_command: bool,
|
pub enable_tts_command: bool,
|
||||||
pub explicit_content_filter: u8,
|
pub explicit_content_filter: UInt8,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub friend_source_flags: sqlx::types::Json<FriendSourceFlags>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub friend_source_flags: FriendSourceFlags,
|
pub friend_source_flags: FriendSourceFlags,
|
||||||
pub gateway_connected: Option<bool>,
|
pub gateway_connected: Option<bool>,
|
||||||
pub gif_auto_play: bool,
|
pub gif_auto_play: bool,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub guild_folders: sqlx::types::Json<Vec<GuildFolder>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub guild_folders: Vec<GuildFolder>,
|
pub guild_folders: Vec<GuildFolder>,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub guild_positions: sqlx::types::Json<Vec<String>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub guild_positions: Vec<String>,
|
pub guild_positions: Vec<String>,
|
||||||
pub inline_attachment_media: bool,
|
pub inline_attachment_media: bool,
|
||||||
|
@ -77,9 +69,6 @@ pub struct UserSettings {
|
||||||
pub native_phone_integration_enabled: bool,
|
pub native_phone_integration_enabled: bool,
|
||||||
pub render_embeds: bool,
|
pub render_embeds: bool,
|
||||||
pub render_reactions: bool,
|
pub render_reactions: bool,
|
||||||
#[cfg(feature = "sqlx")]
|
|
||||||
pub restricted_guilds: sqlx::types::Json<Vec<String>>,
|
|
||||||
#[cfg(not(feature = "sqlx"))]
|
|
||||||
pub restricted_guilds: Vec<String>,
|
pub restricted_guilds: Vec<String>,
|
||||||
pub show_current_game: bool,
|
pub show_current_game: bool,
|
||||||
pub status: Shared<UserStatus>,
|
pub status: Shared<UserStatus>,
|
||||||
|
@ -91,10 +80,14 @@ pub struct UserSettings {
|
||||||
impl Default for UserSettings {
|
impl Default for UserSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
afk_timeout: Some(3600),
|
#[allow(clippy::useless_conversion)]
|
||||||
|
afk_timeout: Some(3600u16.into()),
|
||||||
allow_accessibility_detection: true,
|
allow_accessibility_detection: true,
|
||||||
animate_emoji: true,
|
animate_emoji: true,
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
animate_stickers: 0,
|
animate_stickers: 0,
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
animate_stickers: 0.into(),
|
||||||
contact_sync_enabled: false,
|
contact_sync_enabled: false,
|
||||||
convert_emoticons: false,
|
convert_emoticons: false,
|
||||||
custom_status: None,
|
custom_status: None,
|
||||||
|
@ -103,7 +96,10 @@ impl Default for UserSettings {
|
||||||
developer_mode: true,
|
developer_mode: true,
|
||||||
disable_games_tab: true,
|
disable_games_tab: true,
|
||||||
enable_tts_command: false,
|
enable_tts_command: false,
|
||||||
|
#[cfg(not(feature = "sqlx"))]
|
||||||
explicit_content_filter: 0,
|
explicit_content_filter: 0,
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
explicit_content_filter: 0.into(),
|
||||||
friend_source_flags: Default::default(),
|
friend_source_flags: Default::default(),
|
||||||
gateway_connected: Some(false),
|
gateway_connected: Some(false),
|
||||||
gif_auto_play: false,
|
gif_auto_play: false,
|
||||||
|
@ -127,7 +123,8 @@ impl Default for UserSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
|
#[cfg_attr(feature = "sqlx", sqlx(type_name = "interface_type"))]
|
||||||
pub struct CustomStatus {
|
pub struct CustomStatus {
|
||||||
pub emoji_id: Option<String>,
|
pub emoji_id: Option<String>,
|
||||||
pub emoji_name: Option<String>,
|
pub emoji_name: Option<String>,
|
||||||
|
@ -137,6 +134,7 @@ pub struct CustomStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
pub struct FriendSourceFlags {
|
pub struct FriendSourceFlags {
|
||||||
pub all: bool,
|
pub all: bool,
|
||||||
}
|
}
|
||||||
|
@ -148,8 +146,10 @@ impl Default for FriendSourceFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow, sqlx::Type))]
|
||||||
|
#[cfg_attr(feature = "sqlx", sqlx(type_name = "interface_type"))]
|
||||||
pub struct GuildFolder {
|
pub struct GuildFolder {
|
||||||
pub color: Option<u32>,
|
pub color: Option<UInt32>,
|
||||||
pub guild_ids: Vec<String>,
|
pub guild_ids: Vec<String>,
|
||||||
// FIXME: What is this thing?
|
// FIXME: What is this thing?
|
||||||
// It's not a snowflake, and it's sometimes a string and sometimes an integer.
|
// It's not a snowflake, and it's sometimes a string and sometimes an integer.
|
||||||
|
|
|
@ -71,7 +71,8 @@ impl PartialEq for Webhook {
|
||||||
#[derive(
|
#[derive(
|
||||||
Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
|
Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
pub enum WebhookType {
|
pub enum WebhookType {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{ChannelType, DefaultReaction, entities::PermissionOverwrite, Snowflake};
|
use crate::types::{entities::PermissionOverwrite, ChannelType, DefaultReaction, Snowflake};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, PartialOrd)]
|
#[derive(Debug, Deserialize, Serialize, Default, PartialEq, PartialOrd)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
@ -141,7 +141,8 @@ bitflags! {
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
pub enum InviteType {
|
pub enum InviteType {
|
||||||
#[default]
|
#[default]
|
||||||
Guild = 0,
|
Guild = 0,
|
||||||
|
@ -152,7 +153,8 @@ pub enum InviteType {
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
#[repr(u8)]
|
#[cfg_attr(not(feature = "sqlx"), repr(u8))]
|
||||||
|
#[cfg_attr(feature = "sqlx", repr(i16))]
|
||||||
pub enum InviteTargetType {
|
pub enum InviteTargetType {
|
||||||
#[default]
|
#[default]
|
||||||
Stream = 1,
|
Stream = 1,
|
||||||
|
@ -169,7 +171,9 @@ pub struct AddChannelRecipientSchema {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/channel#add-channel-recipient>
|
/// See <https://discord-userdoccers.vercel.app/resources/channel#add-channel-recipient>
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Copy, Hash)]
|
#[derive(
|
||||||
|
Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Copy, Hash,
|
||||||
|
)]
|
||||||
pub struct ModifyChannelPositionsSchema {
|
pub struct ModifyChannelPositionsSchema {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
pub position: Option<u32>,
|
pub position: Option<u32>,
|
||||||
|
@ -178,7 +182,9 @@ pub struct ModifyChannelPositionsSchema {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See <https://docs.discord.sex/resources/channel#follow-channel>
|
/// See <https://docs.discord.sex/resources/channel#follow-channel>
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Copy, Hash)]
|
#[derive(
|
||||||
|
Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Copy, Hash,
|
||||||
|
)]
|
||||||
pub struct AddFollowingChannelSchema {
|
pub struct AddFollowingChannelSchema {
|
||||||
pub webhook_channel_id: Snowflake,
|
pub webhook_channel_id: Snowflake,
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,11 @@ pub struct UserModifySchema {
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// This is not yet implemented on Spacebar
|
/// This is not yet implemented on Spacebar
|
||||||
|
///
|
||||||
|
/// [UserFlags]: crate::types::UserFlags
|
||||||
|
/// [UserFlags::PREMIUM_PROMO_DISMISSED]: crate::types::UserFlags::PREMIUM_PROMO_DISMISSED
|
||||||
|
/// [UserFlags::HAS_UNREAD_URGENT_MESSAGES]:
|
||||||
|
/// crate::types::UserFlags::HAS_UNREAD_URGENT_MESSAGES
|
||||||
pub flags: Option<u64>,
|
pub flags: Option<u64>,
|
||||||
/// The user's date of birth, can only be set once
|
/// The user's date of birth, can only be set once
|
||||||
///
|
///
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
use jsonwebtoken::{encode, EncodingKey, Header};
|
use jsonwebtoken::errors::Error;
|
||||||
|
use jsonwebtoken::{
|
||||||
|
decode, encode, Algorithm, DecodingKey, EncodingKey, Header, TokenData, Validation,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub fn generate_token(id: &Snowflake, email: String, jwt_key: &str) -> String {
|
pub fn generate_token(id: &Snowflake, email: &str, jwt_key: &str) -> String {
|
||||||
let claims = Claims::new(&email, id);
|
let claims = Claims::new(email, id);
|
||||||
|
|
||||||
build_token(&claims, jwt_key).unwrap()
|
build_token(&claims, jwt_key).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -42,8 +45,13 @@ pub fn build_token(claims: &Claims, jwt_key: &str) -> Result<String, jsonwebtoke
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*pub fn decode_token(token: &str) -> Result<TokenData<Claims>, Error> {
|
pub fn decode_token(token: &str, jwt_secret: &str) -> Result<TokenData<Claims>, Error> {
|
||||||
let mut validation = Validation::new(Algorithm::HS256);
|
let mut validation = Validation::new(Algorithm::HS256);
|
||||||
validation.sub = Some("quartzauth".to_string());
|
//TODO: What is this?
|
||||||
decode(token, &DecodingKey::from_secret(JWT_SECRET), &validation)
|
//validation.sub = Some("quartzauth".to_string());
|
||||||
}*/
|
decode(
|
||||||
|
token,
|
||||||
|
&DecodingKey::from_secret(jwt_secret.as_bytes()),
|
||||||
|
&validation,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl Snowflake {
|
||||||
const PROCESS_ID: u64 = 1;
|
const PROCESS_ID: u64 = 1;
|
||||||
static INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
static INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
let time = (Utc::now().naive_utc().timestamp_millis() - EPOCH) << 22;
|
let time = (Utc::now().naive_utc().and_utc().timestamp_millis() - EPOCH) << 22;
|
||||||
let worker = WORKER_ID << 17;
|
let worker = WORKER_ID << 17;
|
||||||
let process = PROCESS_ID << 12;
|
let process = PROCESS_ID << 12;
|
||||||
let increment = INCREMENT.fetch_add(1, Ordering::Relaxed) as u64 % 32;
|
let increment = INCREMENT.fetch_add(1, Ordering::Relaxed) as u64 % 32;
|
||||||
|
@ -53,12 +53,15 @@ impl Display for Snowflake {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<T> for Snowflake
|
impl From<u64> for Snowflake {
|
||||||
where
|
fn from(item: u64) -> Self {
|
||||||
T: Into<u64>,
|
Self(item)
|
||||||
{
|
}
|
||||||
fn from(item: T) -> Self {
|
}
|
||||||
Self(item.into())
|
|
||||||
|
impl From<Snowflake> for u64 {
|
||||||
|
fn from(item: Snowflake) -> Self {
|
||||||
|
item.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,29 +102,39 @@ impl<'de> serde::Deserialize<'de> for Snowflake {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl sqlx::Type<sqlx::Any> for Snowflake {
|
impl sqlx::Type<sqlx::Postgres> for Snowflake {
|
||||||
fn type_info() -> <sqlx::Any as sqlx::Database>::TypeInfo {
|
fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
|
||||||
<String as sqlx::Type<sqlx::Any>>::type_info()
|
<sqlx_pg_uint::PgU64 as sqlx::Type<sqlx::Postgres>>::type_info()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl<'q> sqlx::Encode<'q, sqlx::Any> for Snowflake {
|
impl sqlx::postgres::PgHasArrayType for Snowflake {
|
||||||
|
fn array_type_info() -> sqlx::postgres::PgTypeInfo {
|
||||||
|
<Vec<sqlx_pg_uint::PgU64> as sqlx::Type<sqlx::Postgres>>::type_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
|
impl<'q> sqlx::Encode<'q, sqlx::Postgres> for Snowflake {
|
||||||
fn encode_by_ref(
|
fn encode_by_ref(
|
||||||
&self,
|
&self,
|
||||||
buf: &mut <sqlx::Any as sqlx::Database>::ArgumentBuffer<'q>,
|
buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer<'q>,
|
||||||
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
|
) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
|
||||||
<String as sqlx::Encode<'q, sqlx::Any>>::encode_by_ref(&self.0.to_string(), buf)
|
<sqlx_pg_uint::PgU64 as sqlx::Encode<'q, sqlx::Postgres>>::encode_by_ref(
|
||||||
|
&sqlx_pg_uint::PgU64::from(self.0),
|
||||||
|
buf,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
impl<'d> sqlx::Decode<'d, sqlx::Any> for Snowflake {
|
impl<'d> sqlx::Decode<'d, sqlx::Postgres> for Snowflake {
|
||||||
fn decode(
|
fn decode(
|
||||||
value: <sqlx::Any as sqlx::Database>::ValueRef<'d>,
|
value: <sqlx::Postgres as sqlx::Database>::ValueRef<'d>,
|
||||||
) -> Result<Self, sqlx::error::BoxDynError> {
|
) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
<String as sqlx::Decode<'d, sqlx::Any>>::decode(value)
|
<sqlx_pg_uint::PgU64 as sqlx::Decode<'d, sqlx::Postgres>>::decode(value)
|
||||||
.map(|s| s.parse::<u64>().map(Snowflake).unwrap())
|
.map(|s| s.to_uint().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ pub struct VoiceGateway {
|
||||||
|
|
||||||
impl VoiceGateway {
|
impl VoiceGateway {
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub async fn spawn(websocket_url: String) -> Result<VoiceGatewayHandle, VoiceGatewayError> {
|
pub async fn spawn(websocket_url: &str) -> Result<VoiceGatewayHandle, VoiceGatewayError> {
|
||||||
// Append the needed things to the websocket url
|
// Append the needed things to the websocket url
|
||||||
let processed_url = format!("wss://{}/?v=7", websocket_url);
|
let processed_url = format!("wss://{}/?v=7", websocket_url);
|
||||||
trace!("VGW: Connecting to {}", processed_url.clone());
|
trace!("VGW: Connecting to {}", processed_url.clone());
|
||||||
|
@ -110,7 +110,7 @@ impl VoiceGateway {
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(VoiceGatewayHandle {
|
Ok(VoiceGatewayHandle {
|
||||||
url: websocket_url.clone(),
|
url: websocket_url.to_string(),
|
||||||
events: shared_events,
|
events: shared_events,
|
||||||
websocket_send: shared_websocket_send.clone(),
|
websocket_send: shared_websocket_send.clone(),
|
||||||
kill_send: kill_send.clone(),
|
kill_send: kill_send.clone(),
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl VoiceGatewayHandle {
|
||||||
|
|
||||||
/// Sends a speaking event to the gateway
|
/// Sends a speaking event to the gateway
|
||||||
pub async fn send_speaking(&self, to_send: Speaking) {
|
pub async fn send_speaking(&self, to_send: Speaking) {
|
||||||
let to_send_value = serde_json::to_value(&to_send).unwrap();
|
let to_send_value = serde_json::to_value(to_send).unwrap();
|
||||||
|
|
||||||
trace!("VGW: Sending Speaking");
|
trace!("VGW: Sending Speaking");
|
||||||
|
|
||||||
|
|
|
@ -79,11 +79,7 @@ async fn test_login_with_token() {
|
||||||
let mut bundle = common::setup().await;
|
let mut bundle = common::setup().await;
|
||||||
|
|
||||||
let token = &bundle.user.token;
|
let token = &bundle.user.token;
|
||||||
let other_user = bundle
|
let other_user = bundle.instance.login_with_token(token).await.unwrap();
|
||||||
.instance
|
|
||||||
.login_with_token(token.clone())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bundle.user.object.read().unwrap().id,
|
bundle.user.object.read().unwrap().id,
|
||||||
other_user.object.read().unwrap().id
|
other_user.object.read().unwrap().id
|
||||||
|
@ -98,8 +94,8 @@ async fn test_login_with_token() {
|
||||||
async fn test_login_with_invalid_token() {
|
async fn test_login_with_invalid_token() {
|
||||||
let mut bundle = common::setup().await;
|
let mut bundle = common::setup().await;
|
||||||
|
|
||||||
let token = "invalid token lalalalala".to_string();
|
let token = "invalid token lalalalala";
|
||||||
let other_user = bundle.instance.login_with_token(token.clone()).await;
|
let other_user = bundle.instance.login_with_token(token).await;
|
||||||
|
|
||||||
assert!(other_user.is_err());
|
assert!(other_user.is_err());
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl TestBundle {
|
||||||
limits: self.user.limits.clone(),
|
limits: self.user.limits.clone(),
|
||||||
settings: self.user.settings.clone(),
|
settings: self.user.settings.clone(),
|
||||||
object: self.user.object.clone(),
|
object: self.user.object.clone(),
|
||||||
gateway: Gateway::spawn(self.instance.urls.wss.clone(), GatewayOptions::default())
|
gateway: Gateway::spawn(&self.instance.urls.wss, GatewayOptions::default())
|
||||||
.await
|
.await
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,9 @@ pub(crate) async fn setup() -> TestBundle {
|
||||||
)
|
)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let instance = Instance::new("http://localhost:3001/api").await.unwrap();
|
let instance = Instance::new("http://localhost:3001/api", None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
// Requires the existence of the below user.
|
// Requires the existence of the below user.
|
||||||
let reg = RegisterSchema {
|
let reg = RegisterSchema {
|
||||||
username: "integrationtestuser".into(),
|
username: "integrationtestuser".into(),
|
||||||
|
@ -124,10 +126,10 @@ pub(crate) async fn setup() -> TestBundle {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let urls = UrlBundle::new(
|
let urls = UrlBundle::new(
|
||||||
"http://localhost:3001/api".to_string(),
|
"http://localhost:3001/api",
|
||||||
"http://localhost:3001/api".to_string(),
|
"http://localhost:3001/api",
|
||||||
"ws://localhost:3001/".to_string(),
|
"ws://localhost:3001/",
|
||||||
"http://localhost:3001".to_string(),
|
"http://localhost:3001",
|
||||||
);
|
);
|
||||||
TestBundle {
|
TestBundle {
|
||||||
urls,
|
urls,
|
||||||
|
|
|
@ -31,7 +31,7 @@ use wasmtimer::tokio::sleep;
|
||||||
async fn test_gateway_establish() {
|
async fn test_gateway_establish() {
|
||||||
let bundle = common::setup().await;
|
let bundle = common::setup().await;
|
||||||
|
|
||||||
let _: GatewayHandle = Gateway::spawn(bundle.urls.wss.clone(), GatewayOptions::default())
|
let _: GatewayHandle = Gateway::spawn(&bundle.urls.wss, GatewayOptions::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
common::teardown(bundle).await
|
common::teardown(bundle).await
|
||||||
|
@ -55,7 +55,7 @@ impl Subscriber<GatewayReady> for GatewayReadyObserver {
|
||||||
async fn test_gateway_authenticate() {
|
async fn test_gateway_authenticate() {
|
||||||
let bundle = common::setup().await;
|
let bundle = common::setup().await;
|
||||||
|
|
||||||
let gateway: GatewayHandle = Gateway::spawn(bundle.urls.wss.clone(), GatewayOptions::default())
|
let gateway: GatewayHandle = Gateway::spawn(&bundle.urls.wss, GatewayOptions::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue