merge w/ dev
This commit is contained in:
commit
2deb63af10
|
@ -13,13 +13,14 @@ jobs:
|
||||||
linux:
|
linux:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 30
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Clone spacebar server
|
- name: Clone spacebar server
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/bitfl0wer/server.git
|
git clone https://github.com/bitfl0wer/server.git
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
@ -52,7 +53,7 @@ jobs:
|
||||||
# - name: Clone spacebar server
|
# - name: Clone spacebar server
|
||||||
# run: |
|
# run: |
|
||||||
# git clone https://github.com/bitfl0wer/server.git
|
# git clone https://github.com/bitfl0wer/server.git
|
||||||
# - uses: actions/setup-node@v3
|
# - uses: actions/setup-node@v4
|
||||||
# with:
|
# with:
|
||||||
# node-version: 18
|
# node-version: 18
|
||||||
# cache: 'npm'
|
# cache: 'npm'
|
||||||
|
@ -75,12 +76,13 @@ jobs:
|
||||||
# SAFARIDRIVER=$(which safaridriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt" --no-fail-fast
|
# SAFARIDRIVER=$(which safaridriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt" --no-fail-fast
|
||||||
wasm-gecko:
|
wasm-gecko:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Clone spacebar server
|
- name: Clone spacebar server
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/bitfl0wer/server.git
|
git clone https://github.com/bitfl0wer/server.git
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
@ -100,15 +102,16 @@ jobs:
|
||||||
rustup target add wasm32-unknown-unknown
|
rustup target add wasm32-unknown-unknown
|
||||||
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
||||||
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force
|
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force
|
||||||
GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt"
|
GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
|
||||||
wasm-chrome:
|
wasm-chrome:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Clone spacebar server
|
- name: Clone spacebar server
|
||||||
run: |
|
run: |
|
||||||
git clone https://github.com/bitfl0wer/server.git
|
git clone https://github.com/bitfl0wer/server.git
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
@ -128,4 +131,4 @@ jobs:
|
||||||
rustup target add wasm32-unknown-unknown
|
rustup target add wasm32-unknown-unknown
|
||||||
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
||||||
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force
|
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force
|
||||||
CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt"
|
CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
|
||||||
|
|
|
@ -29,12 +29,9 @@ jobs:
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
|
||||||
components: clippy
|
components: clippy
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Install required cargo
|
- name: Install required cargo
|
||||||
run: cargo install clippy-sarif sarif-fmt
|
run: cargo install clippy-sarif sarif-fmt
|
||||||
|
@ -47,7 +44,7 @@ jobs:
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Upload analysis results to GitHub
|
- name: Upload analysis results to GitHub
|
||||||
uses: github/codeql-action/upload-sarif@v2
|
uses: github/codeql-action/upload-sarif@v3
|
||||||
with:
|
with:
|
||||||
sarif_file: rust-clippy-results.sarif
|
sarif_file: rust-clippy-results.sarif
|
||||||
wait-for-processing: true
|
wait-for-processing: true
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
**Please refer to the [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) and [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md) before making a contribution.**
|
||||||
|
|
||||||
|
Chorus is currently missing voice support and a lot of API endpoints, many of which should be trivial to implement,
|
||||||
|
ever since [we streamlined the process of doing so](https://github.com/polyphony-chat/chorus/discussions/401).
|
||||||
|
|
||||||
|
If you'd like to contribute new functionality, check out [The 'Meta'-issues.](https://github.com/polyphony-chat/chorus/issues?q=is%3Aissue+label%3A%22Type%3A+Meta%22+) They contain a comprehensive list of all features which are yet missing for full Discord.com compatibility.
|
||||||
|
Please feel free to open an Issue with the idea you have, or a Pull Request.
|
File diff suppressed because it is too large
Load Diff
64
Cargo.toml
64
Cargo.toml
|
@ -1,13 +1,14 @@
|
||||||
[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.13.0"
|
version = "0.15.0"
|
||||||
license = "AGPL-3.0"
|
license = "MPL-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/polyphony-chat/chorus"
|
repository = "https://github.com/polyphony-chat/chorus"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
keywords = ["spacebar", "discord", "polyphony"]
|
keywords = ["spacebar", "discord", "polyphony"]
|
||||||
website = ["https://discord.com/invite/m3FpcapGDD"]
|
website = ["https://discord.com/invite/m3FpcapGDD"]
|
||||||
|
rust-version = "1.67.1"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -16,34 +17,34 @@ backend = ["dep:poem", "dep:sqlx"]
|
||||||
rt-multi-thread = ["tokio/rt-multi-thread"]
|
rt-multi-thread = ["tokio/rt-multi-thread"]
|
||||||
rt = ["tokio/rt"]
|
rt = ["tokio/rt"]
|
||||||
client = []
|
client = []
|
||||||
|
voice = ["voice_udp", "voice_gateway"]
|
||||||
|
voice_udp = ["dep:discortp", "dep:crypto_secretbox"]
|
||||||
|
voice_gateway = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.34.0", features = ["macros", "sync"] }
|
tokio = { version = "1.35.1", features = ["macros", "sync"] }
|
||||||
serde = { version = "1.0.188", features = ["derive", "rc"] }
|
serde = { version = "1.0.195", features = ["derive", "rc"] }
|
||||||
serde_json = { version = "1.0.105", features = ["raw_value"] }
|
serde_json = { version = "1.0.111", features = ["raw_value"] }
|
||||||
serde-aux = "4.2.0"
|
serde-aux = "4.3.1"
|
||||||
serde_with = "3.3.0"
|
serde_with = "3.4.0"
|
||||||
serde_repr = "0.1.16"
|
serde_repr = "0.1.18"
|
||||||
reqwest = { git = "https://github.com/bitfl0wer/reqwest.git", branch = "wasm-headers", features = [
|
reqwest = { features = ["multipart", "json"], version = "0.11.23" }
|
||||||
"multipart",
|
url = "2.5.0"
|
||||||
"json",
|
chrono = { version = "0.4.31", features = ["serde"] }
|
||||||
], version = "0.11.22" } # reqwest versions > 0.11.22 will have adequate support for WASM. Until there is such a version, we will use a fork of reqwest v.0.11.22
|
regex = "1.10.2"
|
||||||
url = "2.4.0"
|
|
||||||
chrono = { version = "0.4.26", features = ["serde"] }
|
|
||||||
regex = "1.9.4"
|
|
||||||
custom_error = "1.9.2"
|
custom_error = "1.9.2"
|
||||||
futures-util = "0.3.28"
|
futures-util = "0.3.30"
|
||||||
http = "0.2.9"
|
http = "0.2.11"
|
||||||
base64 = "0.21.3"
|
base64 = "0.21.7"
|
||||||
bitflags = { version = "2.4.0", features = ["serde"] }
|
bitflags = { version = "2.4.1", features = ["serde"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
poem = { version = "1.3.57", optional = true }
|
poem = { version = "1.3.59", optional = true }
|
||||||
thiserror = "1.0.47"
|
thiserror = "1.0.56"
|
||||||
jsonwebtoken = "8.3.0"
|
jsonwebtoken = "8.3.0"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
async-trait = "0.1.73"
|
async-trait = "0.1.77"
|
||||||
chorus-macros = "0.2.0"
|
chorus-macros = "0.3.0"
|
||||||
sqlx = { version = "0.7.1", features = [
|
sqlx = { version = "0.7.3", features = [
|
||||||
"mysql",
|
"mysql",
|
||||||
"sqlite",
|
"sqlite",
|
||||||
"json",
|
"json",
|
||||||
|
@ -52,11 +53,12 @@ sqlx = { version = "0.7.1", features = [
|
||||||
"runtime-tokio-native-tls",
|
"runtime-tokio-native-tls",
|
||||||
"any",
|
"any",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
safina-timer = "0.1.11"
|
discortp = { version = "0.5.0", optional = true, features = ["rtp", "discord", "demux"] }
|
||||||
|
crypto_secretbox = { version = "0.1.1", optional = true }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
rustls = "0.21.8"
|
rustls = "0.21.10"
|
||||||
rustls-native-certs = "0.6.3"
|
rustls-native-certs = "0.6.3"
|
||||||
tokio-tungstenite = { version = "0.20.1", features = [
|
tokio-tungstenite = { version = "0.20.1", features = [
|
||||||
"rustls-tls-native-roots",
|
"rustls-tls-native-roots",
|
||||||
|
@ -64,13 +66,15 @@ tokio-tungstenite = { version = "0.20.1", features = [
|
||||||
] }
|
] }
|
||||||
native-tls = "0.2.11"
|
native-tls = "0.2.11"
|
||||||
hostname = "0.3.1"
|
hostname = "0.3.1"
|
||||||
|
getrandom = { version = "0.2.12" }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
getrandom = { version = "0.2.11", features = ["js"] }
|
getrandom = { version = "0.2.12", features = ["js"] }
|
||||||
ws_stream_wasm = "0.7.4"
|
ws_stream_wasm = "0.7.4"
|
||||||
wasm-bindgen-futures = "0.4.38"
|
wasm-bindgen-futures = "0.4.39"
|
||||||
|
wasmtimer = "0.2.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
wasm-bindgen-test = "0.3.38"
|
wasm-bindgen-test = "0.3.39"
|
||||||
wasm-bindgen = "0.2.88"
|
wasm-bindgen = "0.2.89"
|
||||||
|
|
20
README.md
20
README.md
|
@ -44,7 +44,7 @@ To get started with Chorus, import it into your project by adding the following
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chorus = "0.13.0"
|
chorus = "0.15.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Establishing a Connection
|
### Establishing a Connection
|
||||||
|
@ -53,16 +53,10 @@ To connect to a Spacebar compatible server, you need to create an [`Instance`](h
|
||||||
|
|
||||||
```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.
|
||||||
|
@ -87,7 +81,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 seperate thread. Depending on
|
// Each user connects to the Gateway. The 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)
|
||||||
|
@ -125,7 +119,7 @@ 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
|
||||||
|
@ -134,11 +128,7 @@ This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Chorus is currently missing voice support and a lot of API endpoints, many of which should be trivial to implement,
|
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||||
ever since [we streamlined the process of doing so](https://github.com/polyphony-chat/chorus/discussions/401).
|
|
||||||
|
|
||||||
If you'd like to contribute new functionality, check out [The 'Meta'-issues.](https://github.com/polyphony-chat/chorus/issues?q=is%3Aissue+label%3A%22Type%3A+Meta%22+) They contain a comprehensive list of all features which are yet missing for full Discord.com compatibility.
|
|
||||||
Please feel free to open an Issue with the idea you have, or a Pull Request. Please keep our [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) in mind. Your contribution might not be accepted if it violates these guidelines or [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md).
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Progress Tracker/Roadmap</summary>
|
<summary>Progress Tracker/Roadmap</summary>
|
||||||
|
|
|
@ -15,7 +15,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chorus-macros"
|
name = "chorus-macros"
|
||||||
version = "0.1.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chorus-macros"
|
name = "chorus-macros"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0"
|
license = "MPL-2.0"
|
||||||
description = "Macros for the chorus crate."
|
description = "Macros for the chorus crate."
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
|
@ -1,7 +1,23 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed};
|
use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed};
|
||||||
|
|
||||||
|
#[proc_macro_derive(WebSocketEvent)]
|
||||||
|
pub fn websocket_event_macro_derive(input: TokenStream) -> TokenStream {
|
||||||
|
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||||
|
|
||||||
|
let name = &ast.ident;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl WebSocketEvent for #name {}
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(Updateable)]
|
#[proc_macro_derive(Updateable)]
|
||||||
pub fn updateable_macro_derive(input: TokenStream) -> TokenStream {
|
pub fn updateable_macro_derive(input: TokenStream) -> TokenStream {
|
||||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
// This example showcase how to properly use gateway observers.
|
||||||
|
//
|
||||||
|
// To properly run it, you will need to change the token below.
|
||||||
|
|
||||||
|
const TOKEN: &str = "";
|
||||||
|
|
||||||
|
/// Find the gateway websocket url of the server we want to connect to
|
||||||
|
const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/";
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chorus::gateway::Gateway;
|
use chorus::gateway::Gateway;
|
||||||
use chorus::{
|
use chorus::{
|
||||||
|
@ -8,6 +21,11 @@ use chorus::{
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use tokio::{self};
|
use tokio::{self};
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use tokio::time::sleep;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use wasmtimer::tokio::sleep;
|
||||||
|
|
||||||
// This example creates a simple gateway connection and a basic observer struct
|
// This example creates a simple gateway connection and a basic observer struct
|
||||||
|
|
||||||
// Due to certain limitations all observers must impl debug
|
// Due to certain limitations all observers must impl debug
|
||||||
|
@ -27,11 +45,10 @@ impl Observer<GatewayReady> for ExampleObserver {
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// Find the gateway websocket url of the server we want to connect to
|
let gateway_websocket_url = GATEWAY_URL.to_string();
|
||||||
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
|
|
||||||
|
|
||||||
// Initiate the gateway connection
|
// Initiate the gateway connection
|
||||||
let gateway = Gateway::spawn(websocket_url_spacebar).await.unwrap();
|
let gateway = Gateway::spawn(gateway_websocket_url).await.unwrap();
|
||||||
|
|
||||||
// Create an instance of our observer
|
// Create an instance of our observer
|
||||||
let observer = ExampleObserver {};
|
let observer = ExampleObserver {};
|
||||||
|
@ -50,14 +67,13 @@ async fn main() {
|
||||||
.subscribe(shared_observer);
|
.subscribe(shared_observer);
|
||||||
|
|
||||||
// Authenticate so we will receive any events
|
// Authenticate so we will receive any events
|
||||||
let token = "SecretToken".to_string();
|
let token = TOKEN.to_string();
|
||||||
let mut identify = GatewayIdentifyPayload::common();
|
let mut identify = GatewayIdentifyPayload::common();
|
||||||
identify.token = token;
|
identify.token = token;
|
||||||
gateway.send_identify(identify).await;
|
gateway.send_identify(identify).await;
|
||||||
safina_timer::start_timer_thread();
|
|
||||||
|
|
||||||
// Do something on the main thread so we don't quit
|
// Do something on the main thread so we don't quit
|
||||||
loop {
|
loop {
|
||||||
safina_timer::sleep_for(Duration::MAX).await
|
sleep(Duration::from_secs(3600)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,39 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
// This example showcases how to initiate a gateway connection manually
|
||||||
|
// (e. g. not through ChorusUser)
|
||||||
|
//
|
||||||
|
// To properly run it, you will need to modify the token below.
|
||||||
|
|
||||||
|
const TOKEN: &str = "";
|
||||||
|
|
||||||
|
/// Find the gateway websocket url of the server we want to connect to
|
||||||
|
const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/";
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use chorus::gateway::Gateway;
|
use chorus::gateway::Gateway;
|
||||||
use chorus::{self, types::GatewayIdentifyPayload};
|
use chorus::{self, types::GatewayIdentifyPayload};
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use tokio::time::sleep;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
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() {
|
||||||
// Find the gateway websocket url of the server we want to connect to
|
let gateway_websocket_url = GATEWAY_URL.to_string();
|
||||||
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
|
|
||||||
|
|
||||||
// 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::spawn(websocket_url_spacebar).await.unwrap();
|
let gateway = Gateway::spawn(gateway_websocket_url).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
|
||||||
|
|
||||||
// Get a token for an account on the server
|
// Get a token for an account on the server
|
||||||
let token = "SecretToken".to_string();
|
let token = TOKEN.to_string();
|
||||||
|
|
||||||
// Create an identify event
|
// Create an identify event
|
||||||
// An Identify event is how the server authenticates us and gets info about our os and browser, along with our intents / capabilities
|
// An Identify event is how the server authenticates us and gets info about our os and browser, along with our intents / capabilities
|
||||||
|
@ -26,10 +44,10 @@ async fn main() {
|
||||||
identify.token = token;
|
identify.token = token;
|
||||||
|
|
||||||
// Send off the event
|
// Send off the event
|
||||||
safina_timer::start_timer_thread();
|
gateway.send_identify(identify).await;
|
||||||
|
|
||||||
// Do something on the main thread so we don't quit
|
// Do something on the main thread so we don't quit
|
||||||
loop {
|
loop {
|
||||||
safina_timer::sleep_for(Duration::MAX).await
|
sleep(Duration::from_secs(3600)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use chorus::instance::Instance;
|
use chorus::instance::Instance;
|
||||||
use chorus::UrlBundle;
|
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
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");
|
||||||
dbg!(instance.instance_info);
|
dbg!(instance.instance_info);
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use chorus::instance::Instance;
|
use chorus::instance::Instance;
|
||||||
use chorus::types::LoginSchema;
|
use chorus::types::LoginSchema;
|
||||||
use chorus::UrlBundle;
|
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let bundle = UrlBundle::new(
|
let mut instance = Instance::new("https://example.com/")
|
||||||
"https://example.com/api".to_string(),
|
|
||||||
"wss://example.com/".to_string(),
|
|
||||||
"https://example.com/cdn".to_string(),
|
|
||||||
);
|
|
||||||
let mut instance = Instance::new(bundle)
|
|
||||||
.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
|
||||||
|
@ -19,7 +17,7 @@ async fn main() {
|
||||||
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 seperate thread. Depending on
|
// Each user connects to the Gateway. The 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)
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "voice_simple"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-trait = "*"
|
||||||
|
chorus = { path = "../../", features = ["rt", "client", "voice"] }
|
||||||
|
tokio = { version = "*", features = ["full"] }
|
||||||
|
simplelog = "*"
|
||||||
|
log = "*"
|
|
@ -0,0 +1,311 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
// This example showcases how to use the voice udp channel.
|
||||||
|
//
|
||||||
|
// To use this to properly communicate with voice, you will need to bring your own opus bindings
|
||||||
|
// along with potentially sending some other events, like Speaking
|
||||||
|
//
|
||||||
|
// To properly run this example, you will need to change some values below,
|
||||||
|
// like the token, guild and channel ids.
|
||||||
|
|
||||||
|
const TOKEN: &str = "";
|
||||||
|
|
||||||
|
const VOICE_GUILD_ID: Option<Snowflake> = None;
|
||||||
|
const VOICE_CHANNEL_ID: Option<Snowflake> = Some(Snowflake(0_u64));
|
||||||
|
|
||||||
|
const GATEWAY_URL: &str = "wss://gateway.discord.gg";
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use simplelog::{TermLogger, Config, WriteLogger};
|
||||||
|
use std::{net::SocketAddrV4, sync::Arc, fs::File, time::Duration};
|
||||||
|
|
||||||
|
use chorus::{
|
||||||
|
gateway::{Observer, Gateway},
|
||||||
|
types::{
|
||||||
|
GatewayReady, SelectProtocol, SelectProtocolData, SessionDescription, Snowflake, Speaking,
|
||||||
|
SpeakingBitflags, SsrcDefinition, VoiceEncryptionMode, VoiceIdentify, VoiceProtocol,
|
||||||
|
VoiceReady, VoiceServerUpdate, GatewayIdentifyPayload, UpdateVoiceState,
|
||||||
|
},
|
||||||
|
voice::{
|
||||||
|
gateway::{VoiceGateway, VoiceGatewayHandle},
|
||||||
|
udp::{UdpHandle, UdpHandler},
|
||||||
|
voice_data::VoiceData,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use log::{info, LevelFilter};
|
||||||
|
use tokio::sync::{Mutex, RwLock};
|
||||||
|
|
||||||
|
extern crate chorus;
|
||||||
|
extern crate tokio;
|
||||||
|
|
||||||
|
/// Handles in between connections between the gateway and UDP modules
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct VoiceHandler {
|
||||||
|
pub voice_gateway_connection: Arc<Mutex<Option<VoiceGatewayHandle>>>,
|
||||||
|
pub voice_udp_connection: Arc<Mutex<Option<UdpHandle>>>,
|
||||||
|
pub data: Arc<RwLock<VoiceData>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VoiceHandler {
|
||||||
|
/// Creates a new [VoiceHandler], only initializing the data
|
||||||
|
pub fn new() -> VoiceHandler {
|
||||||
|
Self {
|
||||||
|
data: Arc::new(RwLock::new(VoiceData::default())),
|
||||||
|
voice_gateway_connection: Arc::new(Mutex::new(None)),
|
||||||
|
voice_udp_connection: Arc::new(Mutex::new(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VoiceHandler {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
// On [VoiceServerUpdate] we get our starting data and URL for the voice gateway server.
|
||||||
|
impl Observer<VoiceServerUpdate> for VoiceHandler {
|
||||||
|
async fn update(&self, data: &VoiceServerUpdate) {
|
||||||
|
let mut data_lock = self.data.write().await;
|
||||||
|
|
||||||
|
data_lock.server_data = Some(data.clone());
|
||||||
|
let user_id = data_lock.user_id;
|
||||||
|
let session_id = data_lock.session_id.clone();
|
||||||
|
|
||||||
|
drop(data_lock);
|
||||||
|
|
||||||
|
// Create and connect to the voice gateway
|
||||||
|
let voice_gateway_handle = VoiceGateway::spawn(data.endpoint.clone().unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let server_id: Snowflake;
|
||||||
|
|
||||||
|
if data.guild_id.is_some() {
|
||||||
|
server_id = data.guild_id.unwrap();
|
||||||
|
} else {
|
||||||
|
server_id = data.channel_id.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let voice_identify = VoiceIdentify {
|
||||||
|
server_id,
|
||||||
|
user_id,
|
||||||
|
session_id,
|
||||||
|
token: data.token.clone(),
|
||||||
|
video: Some(false),
|
||||||
|
};
|
||||||
|
|
||||||
|
voice_gateway_handle.send_identify(voice_identify).await;
|
||||||
|
|
||||||
|
let cloned_gateway_handle = voice_gateway_handle.clone();
|
||||||
|
|
||||||
|
let mut voice_events = cloned_gateway_handle.events.lock().await;
|
||||||
|
|
||||||
|
let self_reference = Arc::new(self.clone());
|
||||||
|
|
||||||
|
// Subscribe to voice gateway events
|
||||||
|
voice_events.voice_ready.subscribe(self_reference.clone());
|
||||||
|
voice_events
|
||||||
|
.session_description
|
||||||
|
.subscribe(self_reference.clone());
|
||||||
|
voice_events.speaking.subscribe(self_reference.clone());
|
||||||
|
voice_events
|
||||||
|
.ssrc_definition
|
||||||
|
.subscribe(self_reference.clone());
|
||||||
|
|
||||||
|
*self.voice_gateway_connection.lock().await = Some(voice_gateway_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
// On [VoiceReady] we get info for establishing a UDP connection, and we immediately need said UDP
|
||||||
|
// connection for ip discovery.
|
||||||
|
impl Observer<VoiceReady> for VoiceHandler {
|
||||||
|
async fn update(&self, data: &VoiceReady) {
|
||||||
|
let mut data_lock = self.data.write().await;
|
||||||
|
|
||||||
|
data_lock.ready_data = Some(data.clone());
|
||||||
|
|
||||||
|
drop(data_lock);
|
||||||
|
|
||||||
|
// Create a udp connection and perform ip discovery
|
||||||
|
let udp_handle = UdpHandler::spawn(
|
||||||
|
self.data.clone(),
|
||||||
|
std::net::SocketAddr::V4(SocketAddrV4::new(data.ip, data.port)),
|
||||||
|
data.ssrc,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Subscribe ourself to receiving rtp data
|
||||||
|
udp_handle
|
||||||
|
.events
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.rtp
|
||||||
|
.subscribe(Arc::new(self.clone()));
|
||||||
|
|
||||||
|
let ip_discovery = self.data.read().await.ip_discovery.clone().unwrap();
|
||||||
|
|
||||||
|
*self.voice_udp_connection.lock().await = Some(udp_handle.clone());
|
||||||
|
|
||||||
|
let string_ip_address =
|
||||||
|
String::from_utf8(ip_discovery.address).expect("Ip discovery gave non string ip");
|
||||||
|
|
||||||
|
// Send a select protocol, which tells the server where we'll be receiving data and what
|
||||||
|
// mode to encrypt data in
|
||||||
|
self.voice_gateway_connection
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.clone()
|
||||||
|
.unwrap()
|
||||||
|
.send_select_protocol(SelectProtocol {
|
||||||
|
protocol: VoiceProtocol::Udp,
|
||||||
|
data: SelectProtocolData {
|
||||||
|
address: string_ip_address,
|
||||||
|
port: ip_discovery.port,
|
||||||
|
// There are several other voice encryption modes available, though not all are
|
||||||
|
// implemented in chorus
|
||||||
|
mode: VoiceEncryptionMode::Xsalsa20Poly1305,
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
// Session descryption gives us final info regarding codecs and our encryption key
|
||||||
|
impl Observer<SessionDescription> for VoiceHandler {
|
||||||
|
async fn update(&self, data: &SessionDescription) {
|
||||||
|
let mut data_write = self.data.write().await;
|
||||||
|
|
||||||
|
data_write.session_description = Some(data.clone());
|
||||||
|
|
||||||
|
drop(data_write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
// Ready is used just to obtain some info, like the user id and session id
|
||||||
|
impl Observer<GatewayReady> for VoiceHandler {
|
||||||
|
async fn update(&self, data: &GatewayReady) {
|
||||||
|
let mut lock = self.data.write().await;
|
||||||
|
lock.user_id = data.user.id;
|
||||||
|
lock.session_id = data.session_id.clone();
|
||||||
|
drop(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
// This is the received voice data
|
||||||
|
impl Observer<chorus::voice::discortp::rtp::Rtp> for VoiceHandler {
|
||||||
|
async fn update(&self, data: &chorus::voice::discortp::rtp::Rtp) {
|
||||||
|
info!(
|
||||||
|
"Received decrypted voice data! {:?} (SSRC: {})",
|
||||||
|
data.payload.clone(),
|
||||||
|
data.ssrc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
// This event gives extra info about who is speaking
|
||||||
|
impl Observer<Speaking> for VoiceHandler {
|
||||||
|
async fn update(&self, data: &Speaking) {
|
||||||
|
println!(
|
||||||
|
"Received Speaking! (SRRC: {}, flags: {:?})",
|
||||||
|
data.ssrc,
|
||||||
|
SpeakingBitflags::from_bits(data.speaking).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
// This event gives some info about which user has which ssrc
|
||||||
|
impl Observer<SsrcDefinition> for VoiceHandler {
|
||||||
|
async fn update(&self, data: &SsrcDefinition) {
|
||||||
|
println!(
|
||||||
|
"Received SSRC Definition! (User {} has audio ssrc {})",
|
||||||
|
data.user_id.unwrap(),
|
||||||
|
data.audio_ssrc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
simplelog::CombinedLogger::init(vec![
|
||||||
|
TermLogger::new(
|
||||||
|
LevelFilter::Debug,
|
||||||
|
Config::default(),
|
||||||
|
simplelog::TerminalMode::Mixed,
|
||||||
|
simplelog::ColorChoice::Auto,
|
||||||
|
),
|
||||||
|
WriteLogger::new(
|
||||||
|
LevelFilter::Trace,
|
||||||
|
Config::default(),
|
||||||
|
File::create("latest.log").unwrap(),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let gateway = Gateway::spawn(GATEWAY_URL.to_string())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut identify = GatewayIdentifyPayload::common();
|
||||||
|
identify.token = TOKEN.to_string();
|
||||||
|
|
||||||
|
gateway.send_identify(identify).await;
|
||||||
|
|
||||||
|
let voice_handler = Arc::new(VoiceHandler::new());
|
||||||
|
|
||||||
|
// Voice handler needs voice server update
|
||||||
|
gateway
|
||||||
|
.events
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.voice
|
||||||
|
.server_update
|
||||||
|
.subscribe(voice_handler.clone());
|
||||||
|
|
||||||
|
// It also needs a bit of the data in ready
|
||||||
|
gateway
|
||||||
|
.events
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.session
|
||||||
|
.ready
|
||||||
|
.subscribe(voice_handler.clone());
|
||||||
|
|
||||||
|
// Data which channel to update the local user to be joined into.
|
||||||
|
//
|
||||||
|
// guild_id and channel_id can be some to join guild voice channels
|
||||||
|
//
|
||||||
|
// guild_id can be none and channel id some to join dm calls
|
||||||
|
//
|
||||||
|
// both can be none to leave all voice channels
|
||||||
|
let voice_state_update = UpdateVoiceState {
|
||||||
|
guild_id: VOICE_GUILD_ID,
|
||||||
|
channel_id: VOICE_CHANNEL_ID,
|
||||||
|
self_mute: false,
|
||||||
|
self_deaf: false,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
gateway.send_update_voice_state(voice_state_update).await;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||||
|
|
||||||
|
// Potentially send some data here
|
||||||
|
/*let voice_udp_option = voice_handler.voice_udp_connection.lock().await.clone();
|
||||||
|
if voice_udp_option.is_some() {
|
||||||
|
voice_udp_option.unwrap().send_opus_data(0, vec![1, 2, 3, 4, 5]).await.unwrap();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
pub use login::*;
|
pub use login::*;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
pub use register::*;
|
pub use register::*;
|
||||||
|
|
||||||
use crate::gateway::Gateway;
|
use crate::gateway::Gateway;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use http::header::CONTENT_DISPOSITION;
|
use http::header::CONTENT_DISPOSITION;
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
use reqwest::{multipart, Client};
|
use reqwest::{multipart, Client};
|
||||||
|
@ -36,7 +40,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().set_id(index as i16);
|
attachment.get_mut(index).unwrap().id = Some(index as i16);
|
||||||
}
|
}
|
||||||
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();
|
||||||
|
@ -45,8 +49,8 @@ impl Message {
|
||||||
form = form.part("payload_json", payload_field);
|
form = form.part("payload_json", payload_field);
|
||||||
|
|
||||||
for (index, attachment) in message.attachments.unwrap().into_iter().enumerate() {
|
for (index, attachment) in message.attachments.unwrap().into_iter().enumerate() {
|
||||||
let (attachment_content, current_attachment) = attachment.move_content();
|
let attachment_content = attachment.content;
|
||||||
let (attachment_filename, _) = current_attachment.move_filename();
|
let attachment_filename = attachment.filename;
|
||||||
let part_name = format!("files[{}]", index);
|
let part_name = format!("files[{}]", index);
|
||||||
let content_disposition = format!(
|
let content_disposition = format!(
|
||||||
"form-data; name=\"{}\"'; filename=\"{}\"",
|
"form-data; name=\"{}\"'; filename=\"{}\"",
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#![allow(unused_imports)]
|
||||||
pub use channels::*;
|
pub use channels::*;
|
||||||
pub use messages::*;
|
pub use messages::*;
|
||||||
pub use permissions::*;
|
pub use permissions::*;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::ChorusResult,
|
errors::ChorusResult,
|
||||||
instance::ChorusUser,
|
instance::ChorusUser,
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
@ -14,6 +18,25 @@ use crate::types::{
|
||||||
use crate::types::{GuildBan, Snowflake};
|
use crate::types::{GuildBan, Snowflake};
|
||||||
|
|
||||||
impl Guild {
|
impl Guild {
|
||||||
|
/// Fetches a guild by its id.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild>
|
||||||
|
pub async fn get(guild_id: Snowflake, user: &mut ChorusUser) -> ChorusResult<Guild> {
|
||||||
|
let chorus_request = ChorusRequest {
|
||||||
|
request: Client::new()
|
||||||
|
.get(format!(
|
||||||
|
"{}/guilds/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id
|
||||||
|
))
|
||||||
|
.header("Authorization", user.token()),
|
||||||
|
limit_type: LimitType::Guild(guild_id),
|
||||||
|
};
|
||||||
|
let response = chorus_request.deserialize_response::<Guild>(user).await?;
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new guild.
|
/// Creates a new guild.
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
|
@ -34,6 +57,35 @@ impl Guild {
|
||||||
chorus_request.deserialize_response::<Guild>(user).await
|
chorus_request.deserialize_response::<Guild>(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Modify a guild's settings.
|
||||||
|
///
|
||||||
|
/// Requires the [MANAGE_GUILD](crate::types::PermissionFlags::MANAGE_GUILD) permission.
|
||||||
|
///
|
||||||
|
/// Returns the updated guild.
|
||||||
|
///
|
||||||
|
/// # Reference
|
||||||
|
/// <https://discord-userdoccers.vercel.app/resources/guild#modify-guild>
|
||||||
|
pub async fn modify(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
schema: GuildModifySchema,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<Guild> {
|
||||||
|
let chorus_request = ChorusRequest {
|
||||||
|
request: Client::new()
|
||||||
|
.patch(format!(
|
||||||
|
"{}/guilds/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
))
|
||||||
|
.header("Authorization", user.token())
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(to_string(&schema).unwrap()),
|
||||||
|
limit_type: LimitType::Guild(guild_id),
|
||||||
|
};
|
||||||
|
let response = chorus_request.deserialize_response::<Guild>(user).await?;
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
/// Deletes a guild by its id.
|
/// Deletes a guild by its id.
|
||||||
///
|
///
|
||||||
/// User must be the owner.
|
/// User must be the owner.
|
||||||
|
@ -123,77 +175,9 @@ impl Guild {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches a guild by its id.
|
|
||||||
///
|
|
||||||
/// # Reference
|
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild>
|
|
||||||
pub async fn get(guild_id: Snowflake, user: &mut ChorusUser) -> ChorusResult<Guild> {
|
|
||||||
let chorus_request = ChorusRequest {
|
|
||||||
request: Client::new()
|
|
||||||
.get(format!(
|
|
||||||
"{}/guilds/{}",
|
|
||||||
user.belongs_to.read().unwrap().urls.api,
|
|
||||||
guild_id
|
|
||||||
))
|
|
||||||
.header("Authorization", user.token()),
|
|
||||||
limit_type: LimitType::Guild(guild_id),
|
|
||||||
};
|
|
||||||
let response = chorus_request.deserialize_response::<Guild>(user).await?;
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_ban(
|
|
||||||
guild_id: Snowflake,
|
|
||||||
user_id: Snowflake,
|
|
||||||
audit_log_reason: Option<String>,
|
|
||||||
schema: GuildBanCreateSchema,
|
|
||||||
user: &mut ChorusUser,
|
|
||||||
) -> ChorusResult<()> {
|
|
||||||
// FIXME: Return GuildBan instead of (). Requires <https://github.com/spacebarchat/server/issues/1096> to be resolved.
|
|
||||||
let request = ChorusRequest::new(
|
|
||||||
http::Method::PUT,
|
|
||||||
format!(
|
|
||||||
"{}/guilds/{}/bans/{}",
|
|
||||||
user.belongs_to.read().unwrap().urls.api,
|
|
||||||
guild_id,
|
|
||||||
user_id
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
Some(to_string(&schema).unwrap()),
|
|
||||||
audit_log_reason.as_deref(),
|
|
||||||
None,
|
|
||||||
Some(user),
|
|
||||||
LimitType::Guild(guild_id),
|
|
||||||
);
|
|
||||||
request.handle_request_as_result(user).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Reference
|
|
||||||
/// <https://discord-userdoccers.vercel.app/resources/guild#modify-guild>
|
|
||||||
pub async fn modify(
|
|
||||||
guild_id: Snowflake,
|
|
||||||
schema: GuildModifySchema,
|
|
||||||
user: &mut ChorusUser,
|
|
||||||
) -> ChorusResult<Guild> {
|
|
||||||
let chorus_request = ChorusRequest {
|
|
||||||
request: Client::new()
|
|
||||||
.patch(format!(
|
|
||||||
"{}/guilds/{}",
|
|
||||||
user.belongs_to.read().unwrap().urls.api,
|
|
||||||
guild_id,
|
|
||||||
))
|
|
||||||
.header("Authorization", user.token())
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.body(to_string(&schema).unwrap()),
|
|
||||||
limit_type: LimitType::Guild(guild_id),
|
|
||||||
};
|
|
||||||
let response = chorus_request.deserialize_response::<Guild>(user).await?;
|
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetches a guild preview object for the given guild ID. If the user is not in the guild, the guild must be discoverable.
|
/// Fetches a guild preview object for the given guild ID. If the user is not in the guild, the guild must be discoverable.
|
||||||
/// # Reference:
|
|
||||||
///
|
///
|
||||||
|
/// # Reference:
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-preview>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-preview>
|
||||||
// RAGC: These aren't just getters, they fetch something on the server.
|
// RAGC: These aren't just getters, they fetch something on the server.
|
||||||
// I am going to excuse these namings, since .preview() indicates we are just returning
|
// I am going to excuse these namings, since .preview() indicates we are just returning
|
||||||
|
@ -273,7 +257,9 @@ impl Guild {
|
||||||
request.deserialize_response::<Vec<GuildMember>>(user).await
|
request.deserialize_response::<Vec<GuildMember>>(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a member from a guild. Requires the KICK_MEMBERS permission. Returns a 204 empty response on success.
|
/// Removes a member from a guild.
|
||||||
|
///
|
||||||
|
/// Requires the [KICK_MEMBERS](crate::types::PermissionFlags::KICK_MEMBERS) permission.
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#remove-guild-member>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#remove-guild-member>
|
||||||
|
@ -386,7 +372,9 @@ impl Guild {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches a list of ban objects for the guild. Requires the `BAN_MEMBERS` permission.
|
/// Returns a list of ban objects for the guild.
|
||||||
|
///
|
||||||
|
/// Requires the [BAN_MEMBERS](crate::types::PermissionFlags::BAN_MEMBERS) permission.
|
||||||
///
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-bans>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-bans>
|
||||||
|
@ -416,7 +404,9 @@ impl Guild {
|
||||||
request.deserialize_response::<Vec<GuildBan>>(user).await
|
request.deserialize_response::<Vec<GuildBan>>(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches a ban object for the given user. Requires the `BAN_MEMBERS` permission.
|
/// Returns a ban object for the given user.
|
||||||
|
///
|
||||||
|
/// Requires the [BAN_MEMBERS](crate::types::PermissionFlags::BAN_MEMBERS) permission.
|
||||||
///
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-ban>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-ban>
|
||||||
|
@ -444,7 +434,39 @@ impl Guild {
|
||||||
request.deserialize_response::<GuildBan>(user).await
|
request.deserialize_response::<GuildBan>(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the ban for a user. Requires the BAN_MEMBERS permissions. Returns a 204 empty response on success.
|
/// Creates a ban from the guild.
|
||||||
|
///
|
||||||
|
/// Requires the [BAN_MEMBERS](crate::types::PermissionFlags::BAN_MEMBERS) permission.
|
||||||
|
///
|
||||||
|
pub async fn create_ban(
|
||||||
|
guild_id: Snowflake,
|
||||||
|
user_id: Snowflake,
|
||||||
|
audit_log_reason: Option<String>,
|
||||||
|
schema: GuildBanCreateSchema,
|
||||||
|
user: &mut ChorusUser,
|
||||||
|
) -> ChorusResult<()> {
|
||||||
|
// FIXME: Return GuildBan instead of (). Requires <https://github.com/spacebarchat/server/issues/1096> to be resolved.
|
||||||
|
let request = ChorusRequest::new(
|
||||||
|
http::Method::PUT,
|
||||||
|
format!(
|
||||||
|
"{}/guilds/{}/bans/{}",
|
||||||
|
user.belongs_to.read().unwrap().urls.api,
|
||||||
|
guild_id,
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
Some(to_string(&schema).unwrap()),
|
||||||
|
audit_log_reason.as_deref(),
|
||||||
|
None,
|
||||||
|
Some(user),
|
||||||
|
LimitType::Guild(guild_id),
|
||||||
|
);
|
||||||
|
request.handle_request_as_result(user).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the ban for a user.
|
||||||
|
///
|
||||||
|
/// Requires the [BAN_MEMBERS](crate::types::PermissionFlags::BAN_MEMBERS) permission.
|
||||||
///
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#delete-guild-ban>
|
/// See <https://discord-userdoccers.vercel.app/resources/guild#delete-guild-ban>
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use crate::errors::ChorusResult;
|
use crate::errors::ChorusResult;
|
||||||
use crate::instance::ChorusUser;
|
use crate::instance::ChorusUser;
|
||||||
use crate::types::{Guild, Message, MessageSearchQuery, Snowflake};
|
use crate::types::{Guild, Message, MessageSearchQuery, Snowflake};
|
||||||
|
@ -9,7 +13,7 @@ impl Guild {
|
||||||
/// permission to be present on the current user.
|
/// permission to be present on the current user.
|
||||||
///
|
///
|
||||||
/// If the guild/channel you are searching is not yet indexed, the endpoint will return a 202 accepted response.
|
/// If the guild/channel you are searching is not yet indexed, the endpoint will return a 202 accepted response.
|
||||||
/// In this case, the method will return a [`ChorusError::InvalidResponse`] error.
|
/// In this case, the method will return a [`ChorusError::InvalidResponse`](crate::errors::ChorusError::InvalidResponse) error.
|
||||||
///
|
///
|
||||||
/// # Reference:
|
/// # Reference:
|
||||||
/// See <https://discord-userdoccers.vercel.app/resources/message#search-messages>
|
/// See <https://discord-userdoccers.vercel.app/resources/message#search-messages>
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#![allow(unused_imports)]
|
||||||
pub use guilds::*;
|
pub use guilds::*;
|
||||||
pub use messages::*;
|
pub use messages::*;
|
||||||
pub use roles::*;
|
pub use roles::*;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//! All of the API's endpoints.
|
//! All of the API's endpoints.
|
||||||
|
|
||||||
|
#![allow(unused_imports)]
|
||||||
pub use channels::messages::*;
|
pub use channels::messages::*;
|
||||||
pub use guilds::*;
|
pub use guilds::*;
|
||||||
pub use invites::*;
|
pub use invites::*;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
|
|
||||||
use crate::errors::{ChorusError, ChorusResult};
|
use crate::errors::{ChorusError, ChorusResult};
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#![allow(unused_imports)]
|
||||||
pub use instance::*;
|
pub use instance::*;
|
||||||
|
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
|
|
|
@ -1 +1,5 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
|
@ -9,6 +13,26 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
impl ChorusUser {
|
impl ChorusUser {
|
||||||
|
/// Fetches a list of private channels the user is in.
|
||||||
|
///
|
||||||
|
/// # Reference:
|
||||||
|
/// See <https://docs.discord.sex/resources/channel#get-private-channels>
|
||||||
|
pub async fn get_private_channels(&mut self) -> ChorusResult<Vec<Channel>> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/users/@me/channels",
|
||||||
|
self.belongs_to.read().unwrap().urls.api
|
||||||
|
);
|
||||||
|
ChorusRequest {
|
||||||
|
request: Client::new()
|
||||||
|
.get(url)
|
||||||
|
.header("Authorization", self.token())
|
||||||
|
.header("Content-Type", "application/json"),
|
||||||
|
limit_type: LimitType::Global,
|
||||||
|
}
|
||||||
|
.deserialize_response::<Vec<Channel>>(self)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a DM channel or group DM channel.
|
/// Creates a DM channel or group DM channel.
|
||||||
///
|
///
|
||||||
/// One recipient creates or returns an existing DM channel,
|
/// One recipient creates or returns an existing DM channel,
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#![allow(unused_imports)]
|
||||||
pub use channels::*;
|
pub use channels::*;
|
||||||
pub use guilds::*;
|
pub use guilds::*;
|
||||||
pub use relationships::*;
|
pub use relationships::*;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
|
@ -15,7 +19,7 @@ impl ChorusUser {
|
||||||
/// Retrieves a list of mutual friends between the authenticated user and a given user.
|
/// Retrieves a list of mutual friends between the authenticated user and a given user.
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#get-users-peer-id-relationships>
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#get-userspeer_idrelationships>
|
||||||
pub async fn get_mutual_relationships(
|
pub async fn get_mutual_relationships(
|
||||||
&mut self,
|
&mut self,
|
||||||
user_id: Snowflake,
|
user_id: Snowflake,
|
||||||
|
@ -37,7 +41,7 @@ impl ChorusUser {
|
||||||
/// Retrieves the user's relationships.
|
/// Retrieves the user's relationships.
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#get-users-me-relationships>
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#get-usersmerelationships>
|
||||||
pub async fn get_relationships(&mut self) -> ChorusResult<Vec<types::Relationship>> {
|
pub async fn get_relationships(&mut self) -> ChorusResult<Vec<types::Relationship>> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/users/@me/relationships",
|
"{}/users/@me/relationships",
|
||||||
|
@ -55,7 +59,7 @@ impl ChorusUser {
|
||||||
/// Sends a friend request to a user.
|
/// Sends a friend request to a user.
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#post-users-me-relationships>
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#post-usersmerelationships>
|
||||||
pub async fn send_friend_request(
|
pub async fn send_friend_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
schema: FriendRequestSendSchema,
|
schema: FriendRequestSendSchema,
|
||||||
|
@ -132,7 +136,7 @@ impl ChorusUser {
|
||||||
/// Removes the relationship between the authenticated user and a given user.
|
/// Removes the relationship between the authenticated user and a given user.
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#delete-users-me-relationships-peer-id>
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#delete-usersmerelationshipspeer_id>
|
||||||
pub async fn remove_relationship(&mut self, user_id: Snowflake) -> ChorusResult<()> {
|
pub async fn remove_relationship(&mut self, user_id: Snowflake) -> ChorusResult<()> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/users/@me/relationships/{}",
|
"{}/users/@me/relationships/{}",
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
@ -113,7 +117,7 @@ impl User {
|
||||||
/// Fetches the user's settings.
|
/// Fetches the user's settings.
|
||||||
///
|
///
|
||||||
/// # Reference
|
/// # Reference
|
||||||
/// See <https://luna.gitlab.io/discord-unofficial-docs/user_settings.html#get-users-me-settings>
|
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/user_settings.html#get-usersmesettings>
|
||||||
pub async fn get_settings(
|
pub async fn get_settings(
|
||||||
token: &String,
|
token: &String,
|
||||||
url_api: &String,
|
url_api: &String,
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//! Contains all the errors that can be returned by the library.
|
//! Contains all the errors that can be returned by the library.
|
||||||
use custom_error::custom_error;
|
use custom_error::custom_error;
|
||||||
|
|
||||||
use crate::types::WebSocketEvent;
|
use crate::types::WebSocketEvent;
|
||||||
|
use chorus_macros::WebSocketEvent;
|
||||||
|
|
||||||
custom_error! {
|
custom_error! {
|
||||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||||
|
@ -17,7 +22,7 @@ custom_error! {
|
||||||
/// Server did not respond.
|
/// Server did not respond.
|
||||||
NoResponse = "Did not receive a response from the Server.",
|
NoResponse = "Did not receive a response from the Server.",
|
||||||
/// Reqwest returned an Error instead of a Response object.
|
/// Reqwest returned an Error instead of a Response object.
|
||||||
RequestFailed{url:String, error: String} = "An error occured while trying to GET from {url}: {error}",
|
RequestFailed{url:String, error: String} = "An error occurred while trying to GET from {url}: {error}",
|
||||||
/// Response received, however, it was not of the successful responses type. Used when no other, special case applies.
|
/// Response received, however, it was not of the successful responses type. Used when no other, special case applies.
|
||||||
ReceivedErrorCode{error_code: u16, error: String} = "Received the following error code while requesting from the route: {error_code}",
|
ReceivedErrorCode{error_code: u16, error: String} = "Received the following error code while requesting from the route: {error_code}",
|
||||||
/// Used when there is likely something wrong with the instance, the request was directed to.
|
/// Used when there is likely something wrong with the instance, the request was directed to.
|
||||||
|
@ -63,12 +68,12 @@ custom_error! {
|
||||||
}
|
}
|
||||||
|
|
||||||
custom_error! {
|
custom_error! {
|
||||||
/// For errors we receive from the gateway, see https://discord-userdoccers.vercel.app/topics/opcodes-and-status-codes#gateway-close-event-codes;
|
/// For errors we receive from the gateway, see <https://discord-userdoccers.vercel.app/topics/opcodes-and-status-codes#gateway-close-event-codes>;
|
||||||
///
|
///
|
||||||
/// Supposed to be sent as numbers, though they are sent as string most of the time?
|
/// Supposed to be sent as numbers, though they are sent as string most of the time?
|
||||||
///
|
///
|
||||||
/// Also includes errors when initiating a connection and unexpected opcodes
|
/// Also includes errors when initiating a connection and unexpected opcodes
|
||||||
#[derive(PartialEq, Eq, Default, Clone)]
|
#[derive(PartialEq, Eq, Default, Clone, WebSocketEvent)]
|
||||||
pub GatewayError
|
pub GatewayError
|
||||||
// Errors we have received from the gateway
|
// Errors we have received from the gateway
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -95,4 +100,55 @@ custom_error! {
|
||||||
UnexpectedOpcodeReceived{opcode: u8} = "Received an opcode we weren't expecting to receive: {opcode}",
|
UnexpectedOpcodeReceived{opcode: u8} = "Received an opcode we weren't expecting to receive: {opcode}",
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebSocketEvent for GatewayError {}
|
custom_error! {
|
||||||
|
/// Voice Gateway errors
|
||||||
|
///
|
||||||
|
/// Similar to [GatewayError].
|
||||||
|
///
|
||||||
|
/// See <https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-close-event-codes>;
|
||||||
|
#[derive(Clone, Default, PartialEq, Eq, WebSocketEvent)]
|
||||||
|
pub VoiceGatewayError
|
||||||
|
// Errors we receive
|
||||||
|
#[default]
|
||||||
|
UnknownOpcode = "You sent an invalid opcode",
|
||||||
|
FailedToDecodePayload = "You sent an invalid payload in your identifying to the (Voice) Gateway",
|
||||||
|
NotAuthenticated = "You sent a payload before identifying with the (Voice) Gateway",
|
||||||
|
AuthenticationFailed = "The token you sent in your identify payload is incorrect",
|
||||||
|
AlreadyAuthenticated = "You sent more than one identify payload",
|
||||||
|
SessionNoLongerValid = "Your session is no longer valid",
|
||||||
|
SessionTimeout = "Your session has timed out",
|
||||||
|
ServerNotFound = "We can't find the server you're trying to connect to",
|
||||||
|
UnknownProtocol = "We didn't recognize the protocol you sent",
|
||||||
|
Disconnected = "Channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped. Should not reconnect.",
|
||||||
|
VoiceServerCrashed = "The server crashed, try resuming",
|
||||||
|
UnknownEncryptionMode = "Server failed to decrypt data",
|
||||||
|
|
||||||
|
// Errors when initiating a gateway connection
|
||||||
|
CannotConnect{error: String} = "Cannot connect due to a tungstenite error: {error}",
|
||||||
|
NonHelloOnInitiate{opcode: u8} = "Received non hello on initial gateway connection ({opcode}), something is definitely wrong",
|
||||||
|
|
||||||
|
// Other misc errors
|
||||||
|
UnexpectedOpcodeReceived{opcode: u8} = "Received an opcode we weren't expecting to receive: {opcode}",
|
||||||
|
}
|
||||||
|
|
||||||
|
custom_error! {
|
||||||
|
/// Voice UDP errors.
|
||||||
|
#[derive(Clone, PartialEq, Eq, WebSocketEvent)]
|
||||||
|
pub VoiceUdpError
|
||||||
|
|
||||||
|
// General errors
|
||||||
|
BrokenSocket{error: String} = "Could not write / read from UDP socket: {error}",
|
||||||
|
NoData = "We have not set received the necessary data to perform this operation.",
|
||||||
|
|
||||||
|
// Encryption errors
|
||||||
|
EncryptionModeNotImplemented{encryption_mode: String} = "Voice encryption mode {encryption_mode} is not yet implemented.",
|
||||||
|
NoKey = "Tried to encrypt / decrypt rtp data, but no key has been received yet",
|
||||||
|
FailedEncryption = "Tried to encrypt rtp data, but failed. Most likely this is an issue chorus' nonce generation. Please open an issue on the chorus github: https://github.com/polyphony-chat/chorus/issues/new",
|
||||||
|
FailedDecryption = "Tried to decrypt rtp data, but failed. Most likely this is an issue chorus' nonce generation. Please open an issue on the chorus github: https://github.com/polyphony-chat/chorus/issues/new",
|
||||||
|
FailedNonceGeneration{error: String} = "Tried to generate nonce, but failed due to error: {error}.",
|
||||||
|
|
||||||
|
// Errors when initiating a socket connection
|
||||||
|
CannotBind{error: String} = "Cannot bind socket due to a UDP error: {error}",
|
||||||
|
CannotConnect{error: String} = "Cannot connect due to a UDP error: {error}",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
|
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
|
||||||
pub mod tungstenite;
|
pub mod tungstenite;
|
||||||
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
|
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
stream::{SplitSink, SplitStream},
|
stream::{SplitSink, SplitStream},
|
||||||
StreamExt,
|
StreamExt,
|
||||||
|
@ -23,8 +27,16 @@ impl TungsteniteBackend {
|
||||||
websocket_url: &str,
|
websocket_url: &str,
|
||||||
) -> Result<(TungsteniteSink, TungsteniteStream), crate::errors::GatewayError> {
|
) -> Result<(TungsteniteSink, TungsteniteStream), crate::errors::GatewayError> {
|
||||||
let mut roots = rustls::RootCertStore::empty();
|
let mut roots = rustls::RootCertStore::empty();
|
||||||
for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs")
|
let certs = rustls_native_certs::load_native_certs();
|
||||||
{
|
|
||||||
|
if let Err(e) = certs {
|
||||||
|
log::error!("Failed to load platform native certs! {:?}", e);
|
||||||
|
return Err(GatewayError::CannotConnect {
|
||||||
|
error: format!("{:?}", e),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for cert in certs.unwrap() {
|
||||||
roots.add(&rustls::Certificate(cert.0)).unwrap();
|
roots.add(&rustls::Certificate(cert.0)).unwrap();
|
||||||
}
|
}
|
||||||
let (websocket_stream, _) = match connect_async_tls_with_config(
|
let (websocket_stream, _) = match connect_async_tls_with_config(
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
stream::{SplitSink, SplitStream},
|
stream::{SplitSink, SplitStream},
|
||||||
StreamExt,
|
StreamExt,
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::types;
|
use crate::types;
|
||||||
|
|
||||||
|
@ -42,6 +46,8 @@ pub struct Session {
|
||||||
pub ready: GatewayEvent<types::GatewayReady>,
|
pub ready: GatewayEvent<types::GatewayReady>,
|
||||||
pub ready_supplemental: GatewayEvent<types::GatewayReadySupplemental>,
|
pub ready_supplemental: GatewayEvent<types::GatewayReadySupplemental>,
|
||||||
pub replace: GatewayEvent<types::SessionsReplace>,
|
pub replace: GatewayEvent<types::SessionsReplace>,
|
||||||
|
pub reconnect: GatewayEvent<types::GatewayReconnect>,
|
||||||
|
pub invalid: GatewayEvent<types::GatewayInvalidSession>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
@ -5,13 +9,14 @@ use log::*;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
use self::event::Events;
|
use super::events::Events;
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::{Sink, Stream};
|
use super::{Sink, Stream};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
self, AutoModerationRule, AutoModerationRuleUpdate, Channel, ChannelCreate, ChannelDelete,
|
self, AutoModerationRule, AutoModerationRuleUpdate, Channel, ChannelCreate, ChannelDelete,
|
||||||
ChannelUpdate, Guild, GuildRoleCreate, GuildRoleUpdate, JsonField, RoleObject, SourceUrlField,
|
ChannelUpdate, GatewayInvalidSession, GatewayReconnect, Guild, GuildRoleCreate,
|
||||||
ThreadUpdate, UpdateMessage, WebSocketEvent,
|
GuildRoleUpdate, JsonField, RoleObject, SourceUrlField, ThreadUpdate, UpdateMessage,
|
||||||
|
WebSocketEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -21,6 +26,7 @@ pub struct Gateway {
|
||||||
websocket_send: Arc<Mutex<Sink>>,
|
websocket_send: Arc<Mutex<Sink>>,
|
||||||
websocket_receive: Stream,
|
websocket_receive: Stream,
|
||||||
kill_send: tokio::sync::broadcast::Sender<()>,
|
kill_send: tokio::sync::broadcast::Sender<()>,
|
||||||
|
kill_receive: tokio::sync::broadcast::Receiver<()>,
|
||||||
store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
|
store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
|
||||||
url: String,
|
url: String,
|
||||||
}
|
}
|
||||||
|
@ -70,6 +76,7 @@ impl Gateway {
|
||||||
websocket_send: shared_websocket_send.clone(),
|
websocket_send: shared_websocket_send.clone(),
|
||||||
websocket_receive,
|
websocket_receive,
|
||||||
kill_send: kill_send.clone(),
|
kill_send: kill_send.clone(),
|
||||||
|
kill_receive: kill_send.subscribe(),
|
||||||
store: store.clone(),
|
store: store.clone(),
|
||||||
url: websocket_url.clone(),
|
url: websocket_url.clone(),
|
||||||
};
|
};
|
||||||
|
@ -94,14 +101,21 @@ impl Gateway {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main gateway listener task;
|
/// The main gateway listener task;
|
||||||
///
|
|
||||||
/// Can only be stopped by closing the websocket, cannot be made to listen for kill
|
|
||||||
pub async fn gateway_listen_task(&mut self) {
|
pub async fn gateway_listen_task(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
let msg = self.websocket_receive.next().await;
|
let msg;
|
||||||
|
|
||||||
|
tokio::select! {
|
||||||
|
Ok(_) = self.kill_receive.recv() => {
|
||||||
|
log::trace!("GW: Closing listener task");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
message = self.websocket_receive.next() => {
|
||||||
|
msg = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PRETTYFYME: Remove inline conditional compiling
|
// PRETTYFYME: Remove inline conditional compiling
|
||||||
// This if chain can be much better but if let is unstable on stable rust
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
if let Some(Ok(message)) = msg {
|
if let Some(Ok(message)) = msg {
|
||||||
self.handle_message(message.into()).await;
|
self.handle_message(message.into()).await;
|
||||||
|
@ -335,10 +349,42 @@ impl Gateway {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
GATEWAY_RECONNECT => {
|
GATEWAY_RECONNECT => {
|
||||||
todo!()
|
trace!("GW: Received Reconnect");
|
||||||
|
|
||||||
|
let reconnect = GatewayReconnect {};
|
||||||
|
|
||||||
|
self.events
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.session
|
||||||
|
.reconnect
|
||||||
|
.notify(reconnect)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
GATEWAY_INVALID_SESSION => {
|
GATEWAY_INVALID_SESSION => {
|
||||||
todo!()
|
trace!("GW: Received Invalid Session");
|
||||||
|
|
||||||
|
let mut resumable: bool = false;
|
||||||
|
|
||||||
|
if let Some(raw_value) = gateway_payload.event_data {
|
||||||
|
if let Ok(deserialized) = serde_json::from_str(raw_value.get()) {
|
||||||
|
resumable = deserialized;
|
||||||
|
} else {
|
||||||
|
warn!("Failed to parse part of INVALID_SESSION ('{}' as bool), assuming non-resumable", raw_value.get());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("Failed to parse part of INVALID_SESSION ('d' missing), assuming non-resumable");
|
||||||
|
}
|
||||||
|
|
||||||
|
let invalid_session = GatewayInvalidSession { resumable };
|
||||||
|
|
||||||
|
self.events
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.session
|
||||||
|
.invalid
|
||||||
|
.notify(invalid_session)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
// Starts our heartbeat
|
// Starts our heartbeat
|
||||||
// We should have already handled this in gateway init
|
// We should have already handled this in gateway init
|
||||||
|
@ -394,165 +440,3 @@ impl Gateway {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod event {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Events {
|
|
||||||
pub application: Application,
|
|
||||||
pub auto_moderation: AutoModeration,
|
|
||||||
pub session: Session,
|
|
||||||
pub message: Message,
|
|
||||||
pub user: User,
|
|
||||||
pub relationship: Relationship,
|
|
||||||
pub channel: Channel,
|
|
||||||
pub thread: Thread,
|
|
||||||
pub guild: Guild,
|
|
||||||
pub invite: Invite,
|
|
||||||
pub integration: Integration,
|
|
||||||
pub interaction: Interaction,
|
|
||||||
pub stage_instance: StageInstance,
|
|
||||||
pub call: Call,
|
|
||||||
pub voice: Voice,
|
|
||||||
pub webhooks: Webhooks,
|
|
||||||
pub gateway_identify_payload: GatewayEvent<types::GatewayIdentifyPayload>,
|
|
||||||
pub gateway_resume: GatewayEvent<types::GatewayResume>,
|
|
||||||
pub error: GatewayEvent<GatewayError>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Application {
|
|
||||||
pub command_permissions_update: GatewayEvent<types::ApplicationCommandPermissionsUpdate>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct AutoModeration {
|
|
||||||
pub rule_create: GatewayEvent<types::AutoModerationRuleCreate>,
|
|
||||||
pub rule_update: GatewayEvent<types::AutoModerationRuleUpdate>,
|
|
||||||
pub rule_delete: GatewayEvent<types::AutoModerationRuleDelete>,
|
|
||||||
pub action_execution: GatewayEvent<types::AutoModerationActionExecution>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Session {
|
|
||||||
pub ready: GatewayEvent<types::GatewayReady>,
|
|
||||||
pub ready_supplemental: GatewayEvent<types::GatewayReadySupplemental>,
|
|
||||||
pub replace: GatewayEvent<types::SessionsReplace>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct StageInstance {
|
|
||||||
pub create: GatewayEvent<types::StageInstanceCreate>,
|
|
||||||
pub update: GatewayEvent<types::StageInstanceUpdate>,
|
|
||||||
pub delete: GatewayEvent<types::StageInstanceDelete>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Message {
|
|
||||||
pub create: GatewayEvent<types::MessageCreate>,
|
|
||||||
pub update: GatewayEvent<types::MessageUpdate>,
|
|
||||||
pub delete: GatewayEvent<types::MessageDelete>,
|
|
||||||
pub delete_bulk: GatewayEvent<types::MessageDeleteBulk>,
|
|
||||||
pub reaction_add: GatewayEvent<types::MessageReactionAdd>,
|
|
||||||
pub reaction_remove: GatewayEvent<types::MessageReactionRemove>,
|
|
||||||
pub reaction_remove_all: GatewayEvent<types::MessageReactionRemoveAll>,
|
|
||||||
pub reaction_remove_emoji: GatewayEvent<types::MessageReactionRemoveEmoji>,
|
|
||||||
pub ack: GatewayEvent<types::MessageACK>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct User {
|
|
||||||
pub update: GatewayEvent<types::UserUpdate>,
|
|
||||||
pub guild_settings_update: GatewayEvent<types::UserGuildSettingsUpdate>,
|
|
||||||
pub presence_update: GatewayEvent<types::PresenceUpdate>,
|
|
||||||
pub typing_start: GatewayEvent<types::TypingStartEvent>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Relationship {
|
|
||||||
pub add: GatewayEvent<types::RelationshipAdd>,
|
|
||||||
pub remove: GatewayEvent<types::RelationshipRemove>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Channel {
|
|
||||||
pub create: GatewayEvent<types::ChannelCreate>,
|
|
||||||
pub update: GatewayEvent<types::ChannelUpdate>,
|
|
||||||
pub unread_update: GatewayEvent<types::ChannelUnreadUpdate>,
|
|
||||||
pub delete: GatewayEvent<types::ChannelDelete>,
|
|
||||||
pub pins_update: GatewayEvent<types::ChannelPinsUpdate>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Thread {
|
|
||||||
pub create: GatewayEvent<types::ThreadCreate>,
|
|
||||||
pub update: GatewayEvent<types::ThreadUpdate>,
|
|
||||||
pub delete: GatewayEvent<types::ThreadDelete>,
|
|
||||||
pub list_sync: GatewayEvent<types::ThreadListSync>,
|
|
||||||
pub member_update: GatewayEvent<types::ThreadMemberUpdate>,
|
|
||||||
pub members_update: GatewayEvent<types::ThreadMembersUpdate>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Guild {
|
|
||||||
pub create: GatewayEvent<types::GuildCreate>,
|
|
||||||
pub update: GatewayEvent<types::GuildUpdate>,
|
|
||||||
pub delete: GatewayEvent<types::GuildDelete>,
|
|
||||||
pub audit_log_entry_create: GatewayEvent<types::GuildAuditLogEntryCreate>,
|
|
||||||
pub ban_add: GatewayEvent<types::GuildBanAdd>,
|
|
||||||
pub ban_remove: GatewayEvent<types::GuildBanRemove>,
|
|
||||||
pub emojis_update: GatewayEvent<types::GuildEmojisUpdate>,
|
|
||||||
pub stickers_update: GatewayEvent<types::GuildStickersUpdate>,
|
|
||||||
pub integrations_update: GatewayEvent<types::GuildIntegrationsUpdate>,
|
|
||||||
pub member_add: GatewayEvent<types::GuildMemberAdd>,
|
|
||||||
pub member_remove: GatewayEvent<types::GuildMemberRemove>,
|
|
||||||
pub member_update: GatewayEvent<types::GuildMemberUpdate>,
|
|
||||||
pub members_chunk: GatewayEvent<types::GuildMembersChunk>,
|
|
||||||
pub role_create: GatewayEvent<types::GuildRoleCreate>,
|
|
||||||
pub role_update: GatewayEvent<types::GuildRoleUpdate>,
|
|
||||||
pub role_delete: GatewayEvent<types::GuildRoleDelete>,
|
|
||||||
pub role_scheduled_event_create: GatewayEvent<types::GuildScheduledEventCreate>,
|
|
||||||
pub role_scheduled_event_update: GatewayEvent<types::GuildScheduledEventUpdate>,
|
|
||||||
pub role_scheduled_event_delete: GatewayEvent<types::GuildScheduledEventDelete>,
|
|
||||||
pub role_scheduled_event_user_add: GatewayEvent<types::GuildScheduledEventUserAdd>,
|
|
||||||
pub role_scheduled_event_user_remove: GatewayEvent<types::GuildScheduledEventUserRemove>,
|
|
||||||
pub passive_update_v1: GatewayEvent<types::PassiveUpdateV1>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Invite {
|
|
||||||
pub create: GatewayEvent<types::InviteCreate>,
|
|
||||||
pub delete: GatewayEvent<types::InviteDelete>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Integration {
|
|
||||||
pub create: GatewayEvent<types::IntegrationCreate>,
|
|
||||||
pub update: GatewayEvent<types::IntegrationUpdate>,
|
|
||||||
pub delete: GatewayEvent<types::IntegrationDelete>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Interaction {
|
|
||||||
pub create: GatewayEvent<types::InteractionCreate>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Call {
|
|
||||||
pub create: GatewayEvent<types::CallCreate>,
|
|
||||||
pub update: GatewayEvent<types::CallUpdate>,
|
|
||||||
pub delete: GatewayEvent<types::CallDelete>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Voice {
|
|
||||||
pub state_update: GatewayEvent<types::VoiceStateUpdate>,
|
|
||||||
pub server_update: GatewayEvent<types::VoiceServerUpdate>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Webhooks {
|
|
||||||
pub update: GatewayEvent<types::WebhooksUpdate>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use futures_util::SinkExt;
|
use futures_util::SinkExt;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use super::{event::Events, *};
|
use super::{events::Events, *};
|
||||||
use crate::types::{self, Composite};
|
use crate::types::{self, Composite};
|
||||||
|
|
||||||
/// Represents a handle to a Gateway connection. A Gateway connection will create observable
|
/// Represents a handle to a Gateway connection. A Gateway connection will create observable
|
||||||
|
@ -40,10 +44,19 @@ impl GatewayHandle {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursively observes a [`Shared`] object, by making sure all [`Composite `] fields within
|
||||||
|
/// that object and its children are being watched.
|
||||||
|
///
|
||||||
|
/// Observing means, that if new information arrives about the observed object or its children,
|
||||||
|
/// the object automatically gets updated, without you needing to request new information about
|
||||||
|
/// the object in question from the API, which is expensive and can lead to rate limiting.
|
||||||
|
///
|
||||||
|
/// The [`Shared`] object returned by this method points to a different object than the one
|
||||||
|
/// being supplied as a &self function argument.
|
||||||
pub async fn observe<T: Updateable + Clone + Debug + Composite<T>>(
|
pub async fn observe<T: Updateable + Clone + Debug + Composite<T>>(
|
||||||
&self,
|
&self,
|
||||||
object: Arc<RwLock<T>>,
|
object: Shared<T>,
|
||||||
) -> Arc<RwLock<T>> {
|
) -> Shared<T> {
|
||||||
let mut store = self.store.lock().await;
|
let mut store = self.store.lock().await;
|
||||||
let id = object.read().unwrap().id();
|
let id = object.read().unwrap().id();
|
||||||
if let Some(channel) = store.get(&id) {
|
if let Some(channel) = store.get(&id) {
|
||||||
|
@ -84,7 +97,7 @@ impl GatewayHandle {
|
||||||
/// with all of its observable fields being observed.
|
/// with all of its observable fields being observed.
|
||||||
pub async fn observe_and_into_inner<T: Updateable + Clone + Debug + Composite<T>>(
|
pub async fn observe_and_into_inner<T: Updateable + Clone + Debug + Composite<T>>(
|
||||||
&self,
|
&self,
|
||||||
object: Arc<RwLock<T>>,
|
object: Shared<T>,
|
||||||
) -> T {
|
) -> T {
|
||||||
let channel = self.observe(object.clone()).await;
|
let channel = self.observe(object.clone()).await;
|
||||||
let object = channel.read().unwrap().clone();
|
let object = channel.read().unwrap().clone();
|
||||||
|
@ -160,7 +173,7 @@ impl GatewayHandle {
|
||||||
|
|
||||||
/// Closes the websocket connection and stops all gateway tasks;
|
/// Closes the websocket connection and stops all gateway tasks;
|
||||||
///
|
///
|
||||||
/// Esentially pulls the plug on the gateway, leaving it possible to resume;
|
/// Essentially pulls the plug on the gateway, leaving it possible to resume;
|
||||||
pub async fn close(&self) {
|
pub async fn close(&self) {
|
||||||
self.kill_send.send(()).unwrap();
|
self.kill_send.send(()).unwrap();
|
||||||
self.websocket_send.lock().await.close().await.unwrap();
|
self.websocket_send.lock().await.close().await.unwrap();
|
||||||
|
|
|
@ -1,9 +1,24 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use futures_util::SinkExt;
|
use futures_util::SinkExt;
|
||||||
use log::*;
|
use log::*;
|
||||||
use std::time::{self, Duration, Instant};
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use tokio::time::Instant;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use wasmtimer::std::Instant;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use tokio::time::sleep_until;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use wasmtimer::tokio::sleep_until;
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use tokio::sync::mpsc::{Receiver, Sender};
|
use tokio::sync::mpsc::{Receiver, Sender};
|
||||||
|
|
||||||
use safina_timer::sleep_until;
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
|
@ -11,7 +26,7 @@ use super::*;
|
||||||
use crate::types;
|
use crate::types;
|
||||||
|
|
||||||
/// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms
|
/// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms
|
||||||
const HEARTBEAT_ACK_TIMEOUT: u64 = 2000;
|
pub const HEARTBEAT_ACK_TIMEOUT: u64 = 2000;
|
||||||
|
|
||||||
/// Handles sending heartbeats to the gateway in another thread
|
/// Handles sending heartbeats to the gateway in another thread
|
||||||
#[allow(dead_code)] // FIXME: Remove this, once HeartbeatHandler is used
|
#[allow(dead_code)] // FIXME: Remove this, once HeartbeatHandler is used
|
||||||
|
@ -57,18 +72,11 @@ impl HeartbeatHandler {
|
||||||
mut receive: Receiver<HeartbeatThreadCommunication>,
|
mut receive: Receiver<HeartbeatThreadCommunication>,
|
||||||
mut kill_receive: tokio::sync::broadcast::Receiver<()>,
|
mut kill_receive: tokio::sync::broadcast::Receiver<()>,
|
||||||
) {
|
) {
|
||||||
let mut last_heartbeat_timestamp: Instant = time::Instant::now();
|
let mut last_heartbeat_timestamp: Instant = Instant::now();
|
||||||
let mut last_heartbeat_acknowledged = true;
|
let mut last_heartbeat_acknowledged = true;
|
||||||
let mut last_seq_number: Option<u64> = None;
|
let mut last_seq_number: Option<u64> = None;
|
||||||
|
|
||||||
safina_timer::start_timer_thread();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if kill_receive.try_recv().is_ok() {
|
|
||||||
trace!("GW: Closing heartbeat task");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeout = if last_heartbeat_acknowledged {
|
let timeout = if last_heartbeat_acknowledged {
|
||||||
heartbeat_interval
|
heartbeat_interval
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,6 +110,10 @@ impl HeartbeatHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(_) = kill_receive.recv() => {
|
||||||
|
log::trace!("GW: Closing heartbeat task");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_send {
|
if should_send {
|
||||||
|
@ -119,11 +131,11 @@ impl HeartbeatHandler {
|
||||||
let send_result = websocket_tx.lock().await.send(msg.into()).await;
|
let send_result = websocket_tx.lock().await.send(msg.into()).await;
|
||||||
if send_result.is_err() {
|
if send_result.is_err() {
|
||||||
// We couldn't send, the websocket is broken
|
// We couldn't send, the websocket is broken
|
||||||
warn!("GW: Couldnt send heartbeat, websocket seems broken");
|
warn!("GW: Couldn't send heartbeat, websocket seems broken");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_heartbeat_timestamp = time::Instant::now();
|
last_heartbeat_timestamp = Instant::now();
|
||||||
last_heartbeat_acknowledged = false;
|
last_heartbeat_acknowledged = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use crate::types;
|
use crate::types;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Represents a messsage received from the gateway. This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError].
|
/// Represents a message received from the gateway. This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError].
|
||||||
/// This struct is used internally when handling messages.
|
/// This struct is used internally when handling messages.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GatewayMessage(pub String);
|
pub struct GatewayMessage(pub String);
|
||||||
|
|
||||||
impl GatewayMessage {
|
impl GatewayMessage {
|
||||||
/// Parses the message as an error;
|
/// Parses the message as an error;
|
||||||
/// Returns the error if succesfully parsed, None if the message isn't an error
|
/// Returns the error if successfully parsed, None if the message isn't an error
|
||||||
pub fn error(&self) -> Option<GatewayError> {
|
pub fn error(&self) -> Option<GatewayError> {
|
||||||
// Some error strings have dots on the end, which we don't care about
|
// Some error strings have dots on the end, which we don't care about
|
||||||
let processed_content = self.0.to_lowercase().replace('.', "");
|
let processed_content = self.0.to_lowercase().replace('.', "");
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
pub mod backends;
|
pub mod backends;
|
||||||
|
@ -94,6 +98,12 @@ pub struct GatewayEvent<T: WebSocketEvent> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: WebSocketEvent> GatewayEvent<T> {
|
impl<T: WebSocketEvent> GatewayEvent<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
observers: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the GatewayEvent is observed by at least one Observer.
|
/// Returns true if the GatewayEvent is observed by at least one Observer.
|
||||||
pub fn is_observed(&self) -> bool {
|
pub fn is_observed(&self) -> bool {
|
||||||
!self.observers.is_empty()
|
!self.observers.is_empty()
|
||||||
|
@ -116,9 +126,17 @@ impl<T: WebSocketEvent> GatewayEvent<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Notifies the observers of the GatewayEvent.
|
/// Notifies the observers of the GatewayEvent.
|
||||||
async fn notify(&self, new_event_data: T) {
|
pub(crate) async fn notify(&self, new_event_data: T) {
|
||||||
for observer in &self.observers {
|
for observer in &self.observers {
|
||||||
observer.update(&new_event_data).await;
|
observer.update(&new_event_data).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type alias for [`Arc<RwLock<T>>`], used to make the public facing API concerned with
|
||||||
|
/// Composite structs more ergonomic.
|
||||||
|
/// ## Note
|
||||||
|
///
|
||||||
|
/// While `T` does not have to implement `Composite` to be used with `Shared`,
|
||||||
|
/// the primary use of `Shared` is with types that implement `Composite`.
|
||||||
|
pub type Shared<T> = Arc<RwLock<T>>;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//! Instance and ChorusUser objects.
|
//! Instance and ChorusUser objects.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -9,7 +13,7 @@ use reqwest::Client;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::errors::ChorusResult;
|
use crate::errors::ChorusResult;
|
||||||
use crate::gateway::{Gateway, GatewayHandle};
|
use crate::gateway::{Gateway, GatewayHandle, Shared};
|
||||||
use crate::ratelimiter::ChorusRequest;
|
use crate::ratelimiter::ChorusRequest;
|
||||||
use crate::types::types::subconfigs::limits::rates::RateLimits;
|
use crate::types::types::subconfigs::limits::rates::RateLimits;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
|
@ -19,6 +23,7 @@ use crate::UrlBundle;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
/// The [`Instance`]; what you will be using to perform all sorts of actions on the Spacebar server.
|
/// The [`Instance`]; what you will be using to perform all sorts of actions on the Spacebar server.
|
||||||
|
///
|
||||||
/// If `limits_information` is `None`, then the instance will not be rate limited.
|
/// If `limits_information` is `None`, then the instance will not be rate limited.
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
pub urls: UrlBundle,
|
pub urls: UrlBundle,
|
||||||
|
@ -36,8 +41,6 @@ impl PartialEq for Instance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Instance {}
|
|
||||||
|
|
||||||
impl std::hash::Hash for Instance {
|
impl std::hash::Hash for Instance {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.urls.hash(state);
|
self.urls.hash(state);
|
||||||
|
@ -92,8 +95,17 @@ impl PartialEq for LimitsInformation {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle). To create an Instance from one singular url, use [`Instance::from_root_url()`].
|
pub(crate) fn clone_limits_if_some(&self) -> Option<HashMap<LimitType, Limit>> {
|
||||||
pub async fn new(urls: UrlBundle) -> ChorusResult<Instance> {
|
if self.limits_information.is_some() {
|
||||||
|
return Some(self.limits_information.as_ref().unwrap().ratelimits.clone());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle).
|
||||||
|
///
|
||||||
|
/// To create an Instance from one singular url, use [`Instance::new()`].
|
||||||
|
pub async fn from_url_bundle(urls: UrlBundle) -> 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;
|
||||||
|
|
||||||
|
@ -123,21 +135,12 @@ impl Instance {
|
||||||
Ok(instance)
|
Ok(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clone_limits_if_some(&self) -> Option<HashMap<LimitType, Limit>> {
|
|
||||||
if self.limits_information.is_some() {
|
|
||||||
return Some(self.limits_information.as_ref().unwrap().ratelimits.clone());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
||||||
/// Shorthand for `Instance::new(UrlBundle::from_root_domain(root_domain).await?)`.
|
///
|
||||||
// RAGC: Can we really call this a conversion?
|
/// Shorthand for `Instance::from_url_bundle(UrlBundle::from_root_domain(root_domain).await?)`.
|
||||||
// Would with_root_url be better? Not really I think, because with is for more details
|
pub async fn new(root_url: &str) -> ChorusResult<Instance> {
|
||||||
// Where are this is with.. less? (or rather with other ones)
|
|
||||||
pub async fn from_root_url(root_url: &str) -> ChorusResult<Instance> {
|
|
||||||
let urls = UrlBundle::from_root_url(root_url).await?;
|
let urls = UrlBundle::from_root_url(root_url).await?;
|
||||||
Instance::new(urls).await
|
Instance::from_url_bundle(urls).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_limited(api_url: &str) -> ChorusResult<Option<LimitsConfiguration>> {
|
pub async fn is_limited(api_url: &str) -> ChorusResult<Option<LimitsConfiguration>> {
|
||||||
|
@ -174,11 +177,11 @@ impl fmt::Display for Token {
|
||||||
/// It is used for most authenticated actions on a Spacebar server.
|
/// It is used for most authenticated actions on a Spacebar server.
|
||||||
/// It also has its own [Gateway] connection.
|
/// It also has its own [Gateway] connection.
|
||||||
pub struct ChorusUser {
|
pub struct ChorusUser {
|
||||||
pub belongs_to: Arc<RwLock<Instance>>,
|
pub belongs_to: Shared<Instance>,
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub limits: Option<HashMap<LimitType, Limit>>,
|
pub limits: Option<HashMap<LimitType, Limit>>,
|
||||||
pub settings: Arc<RwLock<UserSettings>>,
|
pub settings: Shared<UserSettings>,
|
||||||
pub object: Arc<RwLock<User>>,
|
pub object: Shared<User>,
|
||||||
pub gateway: GatewayHandle,
|
pub gateway: GatewayHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,14 +236,14 @@ impl ChorusUser {
|
||||||
/// Creates a new [ChorusUser] from existing data.
|
/// Creates a new [ChorusUser] from existing data.
|
||||||
///
|
///
|
||||||
/// # Notes
|
/// # Notes
|
||||||
/// This isn't the prefered way to create a ChorusUser.
|
/// This isn't the preferred way to create a ChorusUser.
|
||||||
/// See [Instance::login_account] and [Instance::register_account] instead.
|
/// See [Instance::login_account] and [Instance::register_account] instead.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
belongs_to: Arc<RwLock<Instance>>,
|
belongs_to: Shared<Instance>,
|
||||||
token: String,
|
token: String,
|
||||||
limits: Option<HashMap<LimitType, Limit>>,
|
limits: Option<HashMap<LimitType, Limit>>,
|
||||||
settings: Arc<RwLock<UserSettings>>,
|
settings: Shared<UserSettings>,
|
||||||
object: Arc<RwLock<User>>,
|
object: Shared<User>,
|
||||||
gateway: GatewayHandle,
|
gateway: GatewayHandle,
|
||||||
) -> ChorusUser {
|
) -> ChorusUser {
|
||||||
ChorusUser {
|
ChorusUser {
|
||||||
|
@ -258,7 +261,7 @@ 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: Arc<RwLock<Instance>>, token: String) -> ChorusUser {
|
pub(crate) async fn shell(instance: Shared<Instance>, token: String) -> 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();
|
||||||
|
|
27
src/lib.rs
27
src/lib.rs
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Chorus combines all the required functionalities of a user-centric Spacebar library into one package.
|
Chorus combines all the required functionalities of a user-centric Spacebar library into one package.
|
||||||
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
|
||||||
|
@ -44,7 +48,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 seperate thread. Depending on
|
// Each user connects to the Gateway. The 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)
|
||||||
|
@ -128,7 +132,10 @@ pub mod instance;
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
pub mod ratelimiter;
|
pub mod ratelimiter;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
#[cfg(feature = "client")]
|
#[cfg(all(
|
||||||
|
feature = "client",
|
||||||
|
any(feature = "voice_udp", feature = "voice_gateway")
|
||||||
|
))]
|
||||||
pub mod voice;
|
pub mod voice;
|
||||||
|
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
@ -137,6 +144,12 @@ pub mod voice;
|
||||||
/// # Notes
|
/// # Notes
|
||||||
/// All the urls can be found on the /api/policies/instance/domains endpoint of a spacebar server
|
/// All the urls can be found on the /api/policies/instance/domains endpoint of a spacebar server
|
||||||
pub struct UrlBundle {
|
pub struct UrlBundle {
|
||||||
|
/// The root url of an Instance. Usually, this would be the url where `.well-known/spacebar` can
|
||||||
|
/// be located under. If the instance you are connecting to for some reason does not have a
|
||||||
|
/// `.well-known` set up (for example, if it is a local/testing instance), you can use the api
|
||||||
|
/// url as a substitute.
|
||||||
|
/// Ex: `https://spacebar.chat`
|
||||||
|
pub root: String,
|
||||||
/// The api's url.
|
/// The api's url.
|
||||||
/// Ex: `https://old.server.spacebar.chat/api`
|
/// Ex: `https://old.server.spacebar.chat/api`
|
||||||
pub api: String,
|
pub api: String,
|
||||||
|
@ -151,8 +164,9 @@ 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(api: String, wss: String, cdn: String) -> Self {
|
pub fn new(root: String, api: String, wss: String, cdn: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
root: UrlBundle::parse_url(root),
|
||||||
api: UrlBundle::parse_url(api),
|
api: UrlBundle::parse_url(api),
|
||||||
wss: UrlBundle::parse_url(wss),
|
wss: UrlBundle::parse_url(wss),
|
||||||
cdn: UrlBundle::parse_url(cdn),
|
cdn: UrlBundle::parse_url(cdn),
|
||||||
|
@ -237,7 +251,12 @@ impl UrlBundle {
|
||||||
.json::<types::types::domains_configuration::Domains>()
|
.json::<types::types::domains_configuration::Domains>()
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(UrlBundle::new(body.api_endpoint, body.gateway, body.cdn))
|
Ok(UrlBundle::new(
|
||||||
|
url.to_string(),
|
||||||
|
body.api_endpoint,
|
||||||
|
body.gateway,
|
||||||
|
body.cdn,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(ChorusError::RequestFailed {
|
Err(ChorusError::RequestFailed {
|
||||||
url: url.to_string(),
|
url: url.to_string(),
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//! Ratelimiter and request handling functionality.
|
//! Ratelimiter and request handling functionality.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::defaults::{guild::GuildDefaults, user::UserDefaults};
|
use crate::types::config::types::subconfigs::defaults::{guild::GuildDefaults, user::UserDefaults};
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Eq, PartialEq, Hash, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Eq, PartialEq, Hash, Clone, Debug)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::email::{
|
use crate::types::config::types::subconfigs::email::{
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::kafka::KafkaBroker;
|
use crate::types::config::types::subconfigs::kafka::KafkaBroker;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::limits::{
|
use crate::types::config::types::subconfigs::limits::{
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
pub mod api_configuration;
|
pub mod api_configuration;
|
||||||
pub mod cdn_configuration;
|
pub mod cdn_configuration;
|
||||||
pub mod defaults_configuration;
|
pub mod defaults_configuration;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::region::Region;
|
use crate::types::config::types::subconfigs::region::Region;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::register::{
|
use crate::types::config::types::subconfigs::register::{
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use rand::Fill;
|
use rand::Fill;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{ExplicitContentFilterLevel, MessageNotificationLevel};
|
use crate::types::{ExplicitContentFilterLevel, MessageNotificationLevel};
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
pub mod guild;
|
pub mod guild;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
pub mod mailgun;
|
pub mod mailgun;
|
||||||
pub mod mailjet;
|
pub mod mailjet;
|
||||||
pub mod sendgrid;
|
pub mod sendgrid;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::utils::Snowflake;
|
use crate::types::utils::Snowflake;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
pub mod autojoin;
|
pub mod autojoin;
|
||||||
pub mod discovery;
|
pub mod discovery;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
pub mod channel;
|
pub mod channel;
|
||||||
pub mod global;
|
pub mod global;
|
||||||
pub mod guild;
|
pub mod guild;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::limits::ratelimits::RateLimitOptions;
|
use crate::types::config::types::subconfigs::limits::ratelimits::RateLimitOptions;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::config::types::subconfigs::limits::ratelimits::{
|
use crate::types::config::types::subconfigs::limits::ratelimits::{
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod defaults;
|
pub mod defaults;
|
||||||
pub mod email;
|
pub mod email;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue