Compare commits
80 Commits
44ac1c0c85
...
433711409e
Author | SHA1 | Date |
---|---|---|
kozabrada123 | 433711409e | |
kozabrada123 | 2deb63af10 | |
kozabrada123 | 6ad1b862c4 | |
kozabrada123 | a710edc2a1 | |
kozabrada123 | 171b46c4d7 | |
bitfl0wer | d2761079cf | |
kozabrada123 | 56b2381716 | |
kozabrada123 | a55e3120af | |
bitfl0wer | f8ba59c43e | |
kozabrada123 | 0aeda51878 | |
kozabrada123 | 854c9820ef | |
kozabrada123 | d5cebf0fef | |
kozabrada123 | 2b69035ec7 | |
kozabrada123 | 51c676c000 | |
Flori | 73c3d030f1 | |
bitfl0wer | 9f76afb6bb | |
Flori | 82fd55b20c | |
kozabrada123 | 5dc1dee27a | |
Dirk Stolle | 362dd57504 | |
kozabrada123 | 36a72416d6 | |
Dirk Stolle | d3e5df65d4 | |
Dirk Stolle | 908e995949 | |
Dirk Stolle | f0686892e0 | |
bitfl0wer | 28cdd43b9b | |
bitfl0wer | 3700347503 | |
bitfl0wer | 234304465b | |
Flori | 7a7c468bd0 | |
bitfl0wer | 262a52a8e9 | |
bitfl0wer | 3040dcc46b | |
bitfl0wer | c950288df1 | |
bitfl0wer | e073ff26c4 | |
bitfl0wer | 3ffb124cd4 | |
bitfl0wer | 57e6cb438d | |
bitfl0wer | 970f5b8b4f | |
bitfl0wer | 98f42aa03b | |
bitfl0wer | 9cc7ede763 | |
bitfl0wer | a2b6d4e407 | |
bitfl0wer | 41a0e2fe27 | |
bitfl0wer | 00c70501c4 | |
bitfl0wer | 97ab757633 | |
bitfl0wer | 7434690027 | |
bitfl0wer | 577a399a7b | |
bitfl0wer | 11df180446 | |
bitfl0wer | 0923de59a4 | |
bitfl0wer | 8846159ffd | |
bitfl0wer | 013687c810 | |
bitfl0wer | c6e7724650 | |
bitfl0wer | 74fc954d1a | |
Flori | fe8106d2a1 | |
bitfl0wer | 21699e5899 | |
bitfl0wer | 5372d2c475 | |
bitfl0wer | 29f3ee802a | |
bitfl0wer | 6637f14b18 | |
bitfl0wer | a571a9e137 | |
bitfl0wer | 57214fd2fe | |
bitfl0wer | ca58767372 | |
bitfl0wer | 2a7cae30b8 | |
bitfl0wer | 0660e25bdb | |
bitfl0wer | 36ac6c1e5e | |
bitfl0wer | 315fe8e33b | |
kozabrada123 | 8e25f401a5 | |
Flori | 85e494dd4a | |
Flori | d3853700c0 | |
bitfl0wer | 59b6907481 | |
bitfl0wer | f7d31fe57b | |
Flori | dcc626ef10 | |
Flori | 011b214ea1 | |
kozabrada123 | 8a2bc8287e | |
kozabrada123 | 8243f103f9 | |
kozabrada123 | 34cc344c8d | |
kozabrada123 | 72936d4f21 | |
kozabrada123 | 921a3ef9c0 | |
kozabrada123 | c3017df1c2 | |
kozabrada123 | e2b69487aa | |
kozabrada123 | d37415fc13 | |
Flori | 82a3f98db7 | |
bitfl0wer | 7fdb4bae49 | |
bitfl0wer | de42299fd0 | |
Flori | 7a517b3663 | |
bitfl0wer | fd3aad03e3 |
|
@ -13,13 +13,14 @@ jobs:
|
|||
linux:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Clone spacebar server
|
||||
run: |
|
||||
git clone https://github.com/bitfl0wer/server.git
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
|
@ -52,7 +53,7 @@ jobs:
|
|||
# - name: Clone spacebar server
|
||||
# run: |
|
||||
# git clone https://github.com/bitfl0wer/server.git
|
||||
# - uses: actions/setup-node@v3
|
||||
# - uses: actions/setup-node@v4
|
||||
# with:
|
||||
# node-version: 18
|
||||
# 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
|
||||
wasm-gecko:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Clone spacebar server
|
||||
run: |
|
||||
git clone https://github.com/bitfl0wer/server.git
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
|
@ -100,15 +102,16 @@ jobs:
|
|||
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
|
||||
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:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Clone spacebar server
|
||||
run: |
|
||||
git clone https://github.com/bitfl0wer/server.git
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
|
@ -128,4 +131,4 @@ jobs:
|
|||
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
|
||||
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
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: clippy
|
||||
override: true
|
||||
|
||||
- name: Install required cargo
|
||||
run: cargo install clippy-sarif sarif-fmt
|
||||
|
@ -47,7 +44,7 @@ jobs:
|
|||
continue-on-error: true
|
||||
|
||||
- name: Upload analysis results to GitHub
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: rust-clippy-results.sarif
|
||||
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]
|
||||
name = "chorus"
|
||||
description = "A library for interacting with multiple Spacebar-compatible Instances at once."
|
||||
version = "0.13.0"
|
||||
license = "AGPL-3.0"
|
||||
version = "0.15.0"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/polyphony-chat/chorus"
|
||||
readme = "README.md"
|
||||
keywords = ["spacebar", "discord", "polyphony"]
|
||||
website = ["https://discord.com/invite/m3FpcapGDD"]
|
||||
rust-version = "1.67.1"
|
||||
|
||||
|
||||
[features]
|
||||
|
@ -16,34 +17,34 @@ backend = ["dep:poem", "dep:sqlx"]
|
|||
rt-multi-thread = ["tokio/rt-multi-thread"]
|
||||
rt = ["tokio/rt"]
|
||||
client = []
|
||||
voice = ["voice_udp", "voice_gateway"]
|
||||
voice_udp = ["dep:discortp", "dep:crypto_secretbox"]
|
||||
voice_gateway = []
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.34.0", features = ["macros", "sync"] }
|
||||
serde = { version = "1.0.188", features = ["derive", "rc"] }
|
||||
serde_json = { version = "1.0.105", features = ["raw_value"] }
|
||||
serde-aux = "4.2.0"
|
||||
serde_with = "3.3.0"
|
||||
serde_repr = "0.1.16"
|
||||
reqwest = { git = "https://github.com/bitfl0wer/reqwest.git", branch = "wasm-headers", features = [
|
||||
"multipart",
|
||||
"json",
|
||||
], 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
|
||||
url = "2.4.0"
|
||||
chrono = { version = "0.4.26", features = ["serde"] }
|
||||
regex = "1.9.4"
|
||||
tokio = { version = "1.35.1", features = ["macros", "sync"] }
|
||||
serde = { version = "1.0.195", features = ["derive", "rc"] }
|
||||
serde_json = { version = "1.0.111", features = ["raw_value"] }
|
||||
serde-aux = "4.3.1"
|
||||
serde_with = "3.4.0"
|
||||
serde_repr = "0.1.18"
|
||||
reqwest = { features = ["multipart", "json"], version = "0.11.23" }
|
||||
url = "2.5.0"
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
regex = "1.10.2"
|
||||
custom_error = "1.9.2"
|
||||
futures-util = "0.3.28"
|
||||
http = "0.2.9"
|
||||
base64 = "0.21.3"
|
||||
bitflags = { version = "2.4.0", features = ["serde"] }
|
||||
futures-util = "0.3.30"
|
||||
http = "0.2.11"
|
||||
base64 = "0.21.7"
|
||||
bitflags = { version = "2.4.1", features = ["serde"] }
|
||||
lazy_static = "1.4.0"
|
||||
poem = { version = "1.3.57", optional = true }
|
||||
thiserror = "1.0.47"
|
||||
poem = { version = "1.3.59", optional = true }
|
||||
thiserror = "1.0.56"
|
||||
jsonwebtoken = "8.3.0"
|
||||
log = "0.4.20"
|
||||
async-trait = "0.1.73"
|
||||
chorus-macros = "0.2.0"
|
||||
sqlx = { version = "0.7.1", features = [
|
||||
async-trait = "0.1.77"
|
||||
chorus-macros = "0.3.0"
|
||||
sqlx = { version = "0.7.3", features = [
|
||||
"mysql",
|
||||
"sqlite",
|
||||
"json",
|
||||
|
@ -52,11 +53,12 @@ sqlx = { version = "0.7.1", features = [
|
|||
"runtime-tokio-native-tls",
|
||||
"any",
|
||||
], 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"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
rustls = "0.21.8"
|
||||
rustls = "0.21.10"
|
||||
rustls-native-certs = "0.6.3"
|
||||
tokio-tungstenite = { version = "0.20.1", features = [
|
||||
"rustls-tls-native-roots",
|
||||
|
@ -64,13 +66,15 @@ tokio-tungstenite = { version = "0.20.1", features = [
|
|||
] }
|
||||
native-tls = "0.2.11"
|
||||
hostname = "0.3.1"
|
||||
getrandom = { version = "0.2.12" }
|
||||
|
||||
[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"
|
||||
wasm-bindgen-futures = "0.4.38"
|
||||
wasm-bindgen-futures = "0.4.39"
|
||||
wasmtimer = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
wasm-bindgen-test = "0.3.38"
|
||||
wasm-bindgen = "0.2.88"
|
||||
wasm-bindgen-test = "0.3.39"
|
||||
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
|
||||
[dependencies]
|
||||
chorus = "0.13.0"
|
||||
chorus = "0.15.0"
|
||||
```
|
||||
|
||||
### Establishing a Connection
|
||||
|
@ -53,16 +53,10 @@ To connect to a Spacebar compatible server, you need to create an [`Instance`](h
|
|||
|
||||
```rs
|
||||
use chorus::instance::Instance;
|
||||
use chorus::UrlBundle;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let bundle = UrlBundle::new(
|
||||
"https://example.com/api".to_string(),
|
||||
"wss://example.com/".to_string(),
|
||||
"https://example.com/cdn".to_string(),
|
||||
);
|
||||
let instance = Instance::new(bundle)
|
||||
let instance = Instance::new("https://example.com")
|
||||
.await
|
||||
.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.
|
||||
|
@ -87,7 +81,7 @@ let login_schema = LoginSchema {
|
|||
password: "Correct-Horse-Battery-Staple".to_string(),
|
||||
..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.
|
||||
let user = instance
|
||||
.login_account(login_schema)
|
||||
|
@ -125,7 +119,7 @@ like "proxy connection checking" are already disabled on this version, which oth
|
|||
### wasm
|
||||
|
||||
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.
|
||||
|
||||
## Versioning
|
||||
|
@ -134,11 +128,7 @@ This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read
|
|||
|
||||
## Contributing
|
||||
|
||||
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. 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).
|
||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
|
||||
<details>
|
||||
<summary>Progress Tracker/Roadmap</summary>
|
||||
|
|
|
@ -15,7 +15,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "chorus-macros"
|
||||
version = "0.1.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"quote",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "chorus-macros"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0"
|
||||
license = "MPL-2.0"
|
||||
description = "Macros for the chorus crate."
|
||||
|
||||
[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 quote::quote;
|
||||
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)]
|
||||
pub fn updateable_macro_derive(input: TokenStream) -> TokenStream {
|
||||
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 chorus::gateway::Gateway;
|
||||
use chorus::{
|
||||
|
@ -8,6 +21,11 @@ use chorus::{
|
|||
use std::{sync::Arc, time::Duration};
|
||||
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
|
||||
|
||||
// Due to certain limitations all observers must impl debug
|
||||
|
@ -27,11 +45,10 @@ impl Observer<GatewayReady> for ExampleObserver {
|
|||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
// Find the gateway websocket url of the server we want to connect to
|
||||
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
|
||||
let gateway_websocket_url = GATEWAY_URL.to_string();
|
||||
|
||||
// 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
|
||||
let observer = ExampleObserver {};
|
||||
|
@ -50,14 +67,13 @@ async fn main() {
|
|||
.subscribe(shared_observer);
|
||||
|
||||
// Authenticate so we will receive any events
|
||||
let token = "SecretToken".to_string();
|
||||
let token = TOKEN.to_string();
|
||||
let mut identify = GatewayIdentifyPayload::common();
|
||||
identify.token = token;
|
||||
gateway.send_identify(identify).await;
|
||||
safina_timer::start_timer_thread();
|
||||
|
||||
// Do something on the main thread so we don't quit
|
||||
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 chorus::gateway::Gateway;
|
||||
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
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
// Find the gateway websocket url of the server we want to connect to
|
||||
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
|
||||
let gateway_websocket_url = GATEWAY_URL.to_string();
|
||||
|
||||
// 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
|
||||
|
||||
// Get a token for an account on the server
|
||||
let token = "SecretToken".to_string();
|
||||
let token = TOKEN.to_string();
|
||||
|
||||
// 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
|
||||
|
@ -26,10 +44,10 @@ async fn main() {
|
|||
identify.token = token;
|
||||
|
||||
// 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
|
||||
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::UrlBundle;
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
let bundle = UrlBundle::new(
|
||||
"https://example.com/api".to_string(),
|
||||
"wss://example.com/".to_string(),
|
||||
"https://example.com/cdn".to_string(),
|
||||
);
|
||||
let instance = Instance::new(bundle)
|
||||
let instance = Instance::new("https://example.com/")
|
||||
.await
|
||||
.expect("Failed to connect to the Spacebar server");
|
||||
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::types::LoginSchema;
|
||||
use chorus::UrlBundle;
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
let bundle = UrlBundle::new(
|
||||
"https://example.com/api".to_string(),
|
||||
"wss://example.com/".to_string(),
|
||||
"https://example.com/cdn".to_string(),
|
||||
);
|
||||
let mut instance = Instance::new(bundle)
|
||||
let mut instance = Instance::new("https://example.com/")
|
||||
.await
|
||||
.expect("Failed to connect to the Spacebar server");
|
||||
// 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(),
|
||||
..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.
|
||||
let user = instance
|
||||
.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 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};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use login::*;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use register::*;
|
||||
|
||||
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 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 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::HeaderMap;
|
||||
use reqwest::{multipart, Client};
|
||||
|
@ -36,7 +40,7 @@ impl Message {
|
|||
chorus_request.deserialize_response::<Message>(user).await
|
||||
} else {
|
||||
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 payload_json = to_string(&message).unwrap();
|
||||
|
@ -45,8 +49,8 @@ impl Message {
|
|||
form = form.part("payload_json", payload_field);
|
||||
|
||||
for (index, attachment) in message.attachments.unwrap().into_iter().enumerate() {
|
||||
let (attachment_content, current_attachment) = attachment.move_content();
|
||||
let (attachment_filename, _) = current_attachment.move_filename();
|
||||
let attachment_content = attachment.content;
|
||||
let attachment_filename = attachment.filename;
|
||||
let part_name = format!("files[{}]", index);
|
||||
let content_disposition = format!(
|
||||
"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 messages::*;
|
||||
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 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::{
|
||||
errors::ChorusResult,
|
||||
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 serde_json::from_str;
|
||||
use serde_json::to_string;
|
||||
|
@ -14,6 +18,25 @@ use crate::types::{
|
|||
use crate::types::{GuildBan, Snowflake};
|
||||
|
||||
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.
|
||||
///
|
||||
/// # Reference
|
||||
|
@ -34,6 +57,35 @@ impl Guild {
|
|||
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.
|
||||
///
|
||||
/// 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.
|
||||
/// # Reference:
|
||||
///
|
||||
/// # Reference:
|
||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-preview>
|
||||
// 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
|
||||
|
@ -273,7 +257,9 @@ impl Guild {
|
|||
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
|
||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#remove-guild-member>
|
||||
|
@ -386,7 +372,9 @@ impl Guild {
|
|||
.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:
|
||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-bans>
|
||||
|
@ -416,7 +404,9 @@ impl Guild {
|
|||
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:
|
||||
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-ban>
|
||||
|
@ -444,7 +434,39 @@ impl Guild {
|
|||
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:
|
||||
/// 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 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::instance::ChorusUser;
|
||||
use crate::types::{Guild, Message, MessageSearchQuery, Snowflake};
|
||||
|
@ -9,7 +13,7 @@ impl Guild {
|
|||
/// permission to be present on the current user.
|
||||
///
|
||||
/// If the guild/channel you are searching is not yet indexed, the endpoint will return a 202 accepted response.
|
||||
/// In this case, the method will return a [`ChorusError::InvalidResponse`] error.
|
||||
/// In this case, the method will return a [`ChorusError::InvalidResponse`](crate::errors::ChorusError::InvalidResponse) error.
|
||||
///
|
||||
/// # Reference:
|
||||
/// 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 messages::*;
|
||||
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 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 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.
|
||||
|
||||
#![allow(unused_imports)]
|
||||
pub use channels::messages::*;
|
||||
pub use guilds::*;
|
||||
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 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 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;
|
||||
|
|
|
@ -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 serde_json::to_string;
|
||||
|
||||
|
@ -9,6 +13,26 @@ use crate::{
|
|||
};
|
||||
|
||||
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.
|
||||
///
|
||||
/// 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 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 guilds::*;
|
||||
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 serde_json::to_string;
|
||||
|
||||
|
@ -15,7 +19,7 @@ impl ChorusUser {
|
|||
/// Retrieves a list of mutual friends between the authenticated user and a given user.
|
||||
///
|
||||
/// # 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(
|
||||
&mut self,
|
||||
user_id: Snowflake,
|
||||
|
@ -37,7 +41,7 @@ impl ChorusUser {
|
|||
/// Retrieves the user's relationships.
|
||||
///
|
||||
/// # 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>> {
|
||||
let url = format!(
|
||||
"{}/users/@me/relationships",
|
||||
|
@ -55,7 +59,7 @@ impl ChorusUser {
|
|||
/// Sends a friend request to a user.
|
||||
///
|
||||
/// # 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(
|
||||
&mut self,
|
||||
schema: FriendRequestSendSchema,
|
||||
|
@ -132,7 +136,7 @@ impl ChorusUser {
|
|||
/// Removes the relationship between the authenticated user and a given user.
|
||||
///
|
||||
/// # 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<()> {
|
||||
let url = format!(
|
||||
"{}/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 reqwest::Client;
|
||||
|
@ -113,7 +117,7 @@ impl User {
|
|||
/// Fetches the user's settings.
|
||||
///
|
||||
/// # 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(
|
||||
token: &String,
|
||||
url_api: &String,
|
||||
|
|
165
src/errors.rs
165
src/errors.rs
|
@ -1,12 +1,17 @@
|
|||
// 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.
|
||||
use custom_error::custom_error;
|
||||
|
||||
use crate::types::WebSocketEvent;
|
||||
use chorus_macros::WebSocketEvent;
|
||||
|
||||
custom_error! {
|
||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||
pub RegistrationError
|
||||
Consent = "Consent must be 'true' to register.",
|
||||
Consent = "consent must be 'true' to register",
|
||||
}
|
||||
|
||||
pub type ChorusResult<T> = std::result::Result<T, ChorusError>;
|
||||
|
@ -15,33 +20,34 @@ custom_error! {
|
|||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub ChorusError
|
||||
/// Server did not respond.
|
||||
NoResponse = "Did not receive a response from the Server.",
|
||||
NoResponse = "server did not respond",
|
||||
/// 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.
|
||||
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 error code while requesting from the route: {error_code}",
|
||||
/// Used when there is likely something wrong with the instance, the request was directed to.
|
||||
CantGetInformation{error:String} = "Something seems to be wrong with the instance. Cannot get information about the instance: {error}",
|
||||
CantGetInformation{error:String} = "cannot get information about the instance: {error}, something is likely wrong with the instance",
|
||||
/// The requests form body was malformed/invalid.
|
||||
InvalidFormBody{error_type: String, error:String} = "The server responded with: {error_type}: {error}",
|
||||
InvalidFormBody{error_type: String, error:String} = "the server responded with: {error_type}: {error}",
|
||||
/// The request has not been processed by the server due to a relevant rate limit bucket being exhausted.
|
||||
RateLimited{bucket:String} = "Ratelimited on Bucket {bucket}",
|
||||
RateLimited{bucket:String} = "ratelimited on bucket {bucket}",
|
||||
/// The multipart form could not be created.
|
||||
MultipartCreation{error: String} = "Got an error whilst creating the form: {error}",
|
||||
MultipartCreation{error: String} = "got an error whilst creating the form: {error}",
|
||||
/// The regular form could not be created.
|
||||
FormCreation{error: String} = "Got an error whilst creating the form: {error}",
|
||||
FormCreation{error: String} = "got an error whilst creating the form: {error}",
|
||||
/// The token is invalid.
|
||||
TokenExpired = "Token expired, invalid or not found.",
|
||||
TokenExpired = "token expired, invalid or not found",
|
||||
/// No permission
|
||||
NoPermission = "You do not have the permissions needed to perform this action.",
|
||||
NoPermission = "you lack the permissions needed to perform this action",
|
||||
/// Resource not found
|
||||
NotFound{error: String} = "The provided resource hasn't been found: {error}",
|
||||
NotFound{error: String} = "the provided resource wasn't found: {error}",
|
||||
/// Used when you, for example, try to change your spacebar account password without providing your old password for verification.
|
||||
PasswordRequired = "You need to provide your current password to authenticate for this action.",
|
||||
// RAGC: could this be worded a bit better to be more concise?
|
||||
PasswordRequired = "you need to provide your current password to authenticate for this action",
|
||||
/// Malformed or unexpected response.
|
||||
InvalidResponse{error: String} = "The response is malformed and cannot be processed. Error: {error}",
|
||||
InvalidResponse{error: String} = "the response is malformed and cannot be processed: {error}",
|
||||
/// Invalid, insufficient or too many arguments provided.
|
||||
InvalidArguments{error: String} = "Invalid arguments were provided. Error: {error}"
|
||||
InvalidArguments{error: String} = "invalid arguments were provided: {error}"
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for ChorusError {
|
||||
|
@ -57,42 +63,119 @@ impl From<reqwest::Error> for ChorusError {
|
|||
}
|
||||
|
||||
custom_error! {
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub ObserverError
|
||||
AlreadySubscribed = "Each event can only be subscribed to once."
|
||||
}
|
||||
|
||||
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?
|
||||
///
|
||||
/// Also includes errors when initiating a connection and unexpected opcodes
|
||||
#[derive(PartialEq, Eq, Default, Clone)]
|
||||
#[derive(PartialEq, Eq, Default, Clone, WebSocketEvent)]
|
||||
pub GatewayError
|
||||
// Errors we have received from the gateway
|
||||
#[default]
|
||||
Unknown = "We're not sure what went wrong. Try reconnecting?",
|
||||
UnknownOpcode = "You sent an invalid Gateway opcode or an invalid payload for an opcode",
|
||||
Decode = "Gateway server couldn't decode payload",
|
||||
NotAuthenticated = "You sent a payload prior to identifying",
|
||||
AuthenticationFailed = "The account token sent with your identify payload is invalid",
|
||||
AlreadyAuthenticated = "You've already identified, no need to reauthenticate",
|
||||
InvalidSequenceNumber = "The sequence number sent when resuming the session was invalid. Reconnect and start a new session",
|
||||
RateLimited = "You are being rate limited!",
|
||||
SessionTimedOut = "Your session timed out. Reconnect and start a new one",
|
||||
InvalidShard = "You sent us an invalid shard when identifying",
|
||||
ShardingRequired = "The session would have handled too many guilds - you are required to shard your connection in order to connect",
|
||||
InvalidAPIVersion = "You sent an invalid Gateway version",
|
||||
InvalidIntents = "You sent an invalid intent",
|
||||
DisallowedIntents = "You sent a disallowed intent. You may have tried to specify an intent that you have not enabled or are not approved for",
|
||||
/// We're not sure what went wrong. Try reconnecting?
|
||||
Unknown = "unknown error occurred, try reconnecting",
|
||||
/// You sent an invalid opcode or an invalid payload for an opcode
|
||||
UnknownOpcode = "client sent invalid opcode or invalid payload for opcode",
|
||||
/// Gateway server couldn't decode payload
|
||||
Decode = "gateway server failed to decode payload",
|
||||
/// You sent a payload prior to identifying
|
||||
NotAuthenticated = "client sent payload before identifying",
|
||||
/// The account token sent with your identify payload is invalid
|
||||
AuthenticationFailed = "account token in identify is invalid",
|
||||
/// You've already identified, no need to reauthenticate
|
||||
AlreadyAuthenticated = "client sent more than one identify payload",
|
||||
/// The sequence number sent when resuming the session was invalid. Reconnect and start a new session
|
||||
InvalidSequenceNumber = "sequence number when resuming session was invalid.",
|
||||
/// You're being rate limited
|
||||
RateLimited = "you are being rate limited",
|
||||
/// Your session timed out. Reconnect and start a new one
|
||||
SessionTimedOut = "session timed out",
|
||||
/// You sent an invalid shard when identifying
|
||||
InvalidShard = "invalid shard in identify",
|
||||
/// The session would have handled too many guilds - you are required to shard your connection in order to connect
|
||||
ShardingRequired = "sharding is required to connect",
|
||||
/// You sent an invalid Gateway version
|
||||
InvalidAPIVersion = "client sent invalid gateway version",
|
||||
/// You sent an invalid intent
|
||||
InvalidIntents = "invalid intent",
|
||||
/// You sent a disallowed intent.
|
||||
///
|
||||
/// You may have tried to specify an intent that you have not enabled or are not approved for
|
||||
DisallowedIntents = "disallowed (not enabled / approved) intent",
|
||||
|
||||
// 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",
|
||||
CannotConnect{error: String} = "encountered a tungstenite error: {error}",
|
||||
NonHelloOnInitiate{opcode: u8} = "received non hello on initializing connection: {opcode}",
|
||||
|
||||
// Other misc errors
|
||||
UnexpectedOpcodeReceived{opcode: u8} = "Received an opcode we weren't expecting to receive: {opcode}",
|
||||
UnexpectedOpcodeReceived{opcode: u8} = "unexpected opcode received: {opcode}",
|
||||
}
|
||||
|
||||
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]
|
||||
/// You sent an invalid opcode
|
||||
UnknownOpcode = "client sent invalid opcode",
|
||||
/// You sent an invalid payload in your identifying to the (Voice) Gateway
|
||||
FailedToDecodePayload = "server failed to decode payload while identifying",
|
||||
/// You sent a payload before identifying with the (Voice) Gateway
|
||||
NotAuthenticated = "client sent payload before identifying",
|
||||
/// The token you sent in your identify payload is incorrect
|
||||
AuthenticationFailed = "account token in identify is invalid",
|
||||
/// You sent more than one identify payload
|
||||
AlreadyAuthenticated = "client sent more than one identify payload",
|
||||
/// Your session is no longer valid
|
||||
SessionNoLongerValid = "session no longer valid",
|
||||
/// Your session has timed out
|
||||
SessionTimeout = "session timed out",
|
||||
/// Can't find the desired server to connect to
|
||||
ServerNotFound = "desired server not found",
|
||||
/// The server didn't recognize the protocol you sent
|
||||
UnknownProtocol = "unrecognized or unknown protocol",
|
||||
/// Channel was deleted, you were kicked, voice server changed, or the main gateway session
|
||||
/// closed.
|
||||
///
|
||||
/// Should not attempt to reconnect.
|
||||
Disconnected = "disconnected from voice",
|
||||
/// The server crashed, try resuming
|
||||
VoiceServerCrashed = "the voice server crashed",
|
||||
/// Server failed to decrypt data
|
||||
UnknownEncryptionMode = "server failed to decrypt / unknown encryption mode",
|
||||
|
||||
// Errors when initiating a gateway connection
|
||||
CannotConnect{error: String} = "encountered a tungstenite error: {error}",
|
||||
NonHelloOnInitiate{opcode: u8} = "received non hello on initializing connection: {opcode}",
|
||||
|
||||
// Other misc errors
|
||||
UnexpectedOpcodeReceived{opcode: u8} = "unexpected opcode received: {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}",
|
||||
/// We have not yet received the necessary data to perform this operation.
|
||||
NoData = "required data not yet received",
|
||||
|
||||
// Encryption errors
|
||||
EncryptionModeNotImplemented{encryption_mode: String} = "voice encryption mode {encryption_mode} is not yet implemented",
|
||||
NoKey = "could not encrypt / decrypt data: no key received yet",
|
||||
FailedEncryption = "failed to encrypt data (most likely this is an issue in chorus' nonce generation, please open an issue)",
|
||||
FailedDecryption = "failed to decrypt data (most likely this is an issue in chorus' nonce generation, please open an issue)",
|
||||
FailedNonceGeneration{error: String} = "failed to generate nonce: {error}.",
|
||||
|
||||
// Errors when initiating a socket connection
|
||||
CannotBind{error: String} = "failed to bind UDP socket: {error}",
|
||||
CannotConnect{error: String} = "failed to open UDP connection: {error}",
|
||||
}
|
||||
|
||||
impl WebSocketEvent for GatewayError {}
|
||||
|
|
|
@ -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"))]
|
||||
pub mod tungstenite;
|
||||
#[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::{
|
||||
stream::{SplitSink, SplitStream},
|
||||
StreamExt,
|
||||
|
@ -23,8 +27,16 @@ impl TungsteniteBackend {
|
|||
websocket_url: &str,
|
||||
) -> Result<(TungsteniteSink, TungsteniteStream), crate::errors::GatewayError> {
|
||||
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();
|
||||
}
|
||||
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::{
|
||||
stream::{SplitSink, SplitStream},
|
||||
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 crate::types;
|
||||
|
||||
|
@ -42,6 +46,8 @@ pub struct Session {
|
|||
pub ready: GatewayEvent<types::GatewayReady>,
|
||||
pub ready_supplemental: GatewayEvent<types::GatewayReadySupplemental>,
|
||||
pub replace: GatewayEvent<types::SessionsReplace>,
|
||||
pub reconnect: GatewayEvent<types::GatewayReconnect>,
|
||||
pub invalid: GatewayEvent<types::GatewayInvalidSession>,
|
||||
}
|
||||
|
||||
#[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 futures_util::{SinkExt, StreamExt};
|
||||
|
@ -5,13 +9,14 @@ use log::*;
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::task;
|
||||
|
||||
use self::event::Events;
|
||||
use super::events::Events;
|
||||
use super::*;
|
||||
use super::{Sink, Stream};
|
||||
use crate::types::{
|
||||
self, AutoModerationRule, AutoModerationRuleUpdate, Channel, ChannelCreate, ChannelDelete,
|
||||
ChannelUpdate, Guild, GuildRoleCreate, GuildRoleUpdate, JsonField, RoleObject, SourceUrlField,
|
||||
ThreadUpdate, UpdateMessage, WebSocketEvent,
|
||||
ChannelUpdate, GatewayInvalidSession, GatewayReconnect, Guild, GuildRoleCreate,
|
||||
GuildRoleUpdate, JsonField, RoleObject, SourceUrlField, ThreadUpdate, UpdateMessage,
|
||||
WebSocketEvent,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -21,6 +26,7 @@ pub struct Gateway {
|
|||
websocket_send: Arc<Mutex<Sink>>,
|
||||
websocket_receive: Stream,
|
||||
kill_send: tokio::sync::broadcast::Sender<()>,
|
||||
kill_receive: tokio::sync::broadcast::Receiver<()>,
|
||||
store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
|
||||
url: String,
|
||||
}
|
||||
|
@ -70,6 +76,7 @@ impl Gateway {
|
|||
websocket_send: shared_websocket_send.clone(),
|
||||
websocket_receive,
|
||||
kill_send: kill_send.clone(),
|
||||
kill_receive: kill_send.subscribe(),
|
||||
store: store.clone(),
|
||||
url: websocket_url.clone(),
|
||||
};
|
||||
|
@ -94,14 +101,21 @@ impl Gateway {
|
|||
}
|
||||
|
||||
/// 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) {
|
||||
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
|
||||
// This if chain can be much better but if let is unstable on stable rust
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if let Some(Ok(message)) = msg {
|
||||
self.handle_message(message.into()).await;
|
||||
|
@ -335,10 +349,42 @@ impl Gateway {
|
|||
.unwrap();
|
||||
}
|
||||
GATEWAY_RECONNECT => {
|
||||
todo!()
|
||||
trace!("GW: Received Reconnect");
|
||||
|
||||
let reconnect = GatewayReconnect {};
|
||||
|
||||
self.events
|
||||
.lock()
|
||||
.await
|
||||
.session
|
||||
.reconnect
|
||||
.notify(reconnect)
|
||||
.await;
|
||||
}
|
||||
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
|
||||
// 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 log::*;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::{event::Events, *};
|
||||
use super::{events::Events, *};
|
||||
use crate::types::{self, Composite};
|
||||
|
||||
/// Represents a handle to a Gateway connection. A Gateway connection will create observable
|
||||
|
@ -40,10 +44,19 @@ impl GatewayHandle {
|
|||
.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>>(
|
||||
&self,
|
||||
object: Arc<RwLock<T>>,
|
||||
) -> Arc<RwLock<T>> {
|
||||
object: Shared<T>,
|
||||
) -> Shared<T> {
|
||||
let mut store = self.store.lock().await;
|
||||
let id = object.read().unwrap().id();
|
||||
if let Some(channel) = store.get(&id) {
|
||||
|
@ -84,7 +97,7 @@ impl GatewayHandle {
|
|||
/// with all of its observable fields being observed.
|
||||
pub async fn observe_and_into_inner<T: Updateable + Clone + Debug + Composite<T>>(
|
||||
&self,
|
||||
object: Arc<RwLock<T>>,
|
||||
object: Shared<T>,
|
||||
) -> T {
|
||||
let channel = self.observe(object.clone()).await;
|
||||
let object = channel.read().unwrap().clone();
|
||||
|
@ -160,7 +173,7 @@ impl GatewayHandle {
|
|||
|
||||
/// 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) {
|
||||
self.kill_send.send(()).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 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 safina_timer::sleep_until;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::task;
|
||||
|
||||
|
@ -11,7 +26,7 @@ use super::*;
|
|||
use crate::types;
|
||||
|
||||
/// 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
|
||||
#[allow(dead_code)] // FIXME: Remove this, once HeartbeatHandler is used
|
||||
|
@ -57,18 +72,11 @@ impl HeartbeatHandler {
|
|||
mut receive: Receiver<HeartbeatThreadCommunication>,
|
||||
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_seq_number: Option<u64> = None;
|
||||
|
||||
safina_timer::start_timer_thread();
|
||||
|
||||
loop {
|
||||
if kill_receive.try_recv().is_ok() {
|
||||
trace!("GW: Closing heartbeat task");
|
||||
break;
|
||||
}
|
||||
|
||||
let timeout = if last_heartbeat_acknowledged {
|
||||
heartbeat_interval
|
||||
} else {
|
||||
|
@ -102,6 +110,10 @@ impl HeartbeatHandler {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(_) = kill_receive.recv() => {
|
||||
log::trace!("GW: Closing heartbeat task");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if should_send {
|
||||
|
@ -119,11 +131,11 @@ impl HeartbeatHandler {
|
|||
let send_result = websocket_tx.lock().await.send(msg.into()).await;
|
||||
if send_result.is_err() {
|
||||
// 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;
|
||||
}
|
||||
|
||||
last_heartbeat_timestamp = time::Instant::now();
|
||||
last_heartbeat_timestamp = Instant::now();
|
||||
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 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.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GatewayMessage(pub String);
|
||||
|
||||
impl GatewayMessage {
|
||||
/// 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> {
|
||||
// Some error strings have dots on the end, which we don't care about
|
||||
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;
|
||||
|
||||
pub mod backends;
|
||||
|
@ -94,6 +98,12 @@ pub struct GatewayEvent<T: WebSocketEvent> {
|
|||
}
|
||||
|
||||
impl<T: WebSocketEvent> GatewayEvent<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
observers: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the GatewayEvent is observed by at least one Observer.
|
||||
pub fn is_observed(&self) -> bool {
|
||||
!self.observers.is_empty()
|
||||
|
@ -116,9 +126,17 @@ impl<T: WebSocketEvent> GatewayEvent<T> {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
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.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
@ -9,7 +13,7 @@ use reqwest::Client;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::errors::ChorusResult;
|
||||
use crate::gateway::{Gateway, GatewayHandle};
|
||||
use crate::gateway::{Gateway, GatewayHandle, Shared};
|
||||
use crate::ratelimiter::ChorusRequest;
|
||||
use crate::types::types::subconfigs::limits::rates::RateLimits;
|
||||
use crate::types::{
|
||||
|
@ -19,6 +23,7 @@ use crate::UrlBundle;
|
|||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
/// 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.
|
||||
pub struct Instance {
|
||||
pub urls: UrlBundle,
|
||||
|
@ -36,8 +41,6 @@ impl PartialEq for Instance {
|
|||
}
|
||||
}
|
||||
|
||||
impl Eq for Instance {}
|
||||
|
||||
impl std::hash::Hash for Instance {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.urls.hash(state);
|
||||
|
@ -92,8 +95,17 @@ impl PartialEq for LimitsInformation {
|
|||
}
|
||||
|
||||
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 async fn new(urls: UrlBundle) -> ChorusResult<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`] 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 limit_information;
|
||||
|
||||
|
@ -123,21 +135,12 @@ impl 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.
|
||||
/// Shorthand for `Instance::new(UrlBundle::from_root_domain(root_domain).await?)`.
|
||||
// RAGC: Can we really call this a conversion?
|
||||
// Would with_root_url be better? Not really I think, because with is for more details
|
||||
// Where are this is with.. less? (or rather with other ones)
|
||||
pub async fn from_root_url(root_url: &str) -> ChorusResult<Instance> {
|
||||
///
|
||||
/// Shorthand for `Instance::from_url_bundle(UrlBundle::from_root_domain(root_domain).await?)`.
|
||||
pub async fn new(root_url: &str) -> ChorusResult<Instance> {
|
||||
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>> {
|
||||
|
@ -174,11 +177,11 @@ impl fmt::Display for Token {
|
|||
/// It is used for most authenticated actions on a Spacebar server.
|
||||
/// It also has its own [Gateway] connection.
|
||||
pub struct ChorusUser {
|
||||
pub belongs_to: Arc<RwLock<Instance>>,
|
||||
pub belongs_to: Shared<Instance>,
|
||||
pub token: String,
|
||||
pub limits: Option<HashMap<LimitType, Limit>>,
|
||||
pub settings: Arc<RwLock<UserSettings>>,
|
||||
pub object: Arc<RwLock<User>>,
|
||||
pub settings: Shared<UserSettings>,
|
||||
pub object: Shared<User>,
|
||||
pub gateway: GatewayHandle,
|
||||
}
|
||||
|
||||
|
@ -233,14 +236,14 @@ impl ChorusUser {
|
|||
/// Creates a new [ChorusUser] from existing data.
|
||||
///
|
||||
/// # 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.
|
||||
pub fn new(
|
||||
belongs_to: Arc<RwLock<Instance>>,
|
||||
belongs_to: Shared<Instance>,
|
||||
token: String,
|
||||
limits: Option<HashMap<LimitType, Limit>>,
|
||||
settings: Arc<RwLock<UserSettings>>,
|
||||
object: Arc<RwLock<User>>,
|
||||
settings: Shared<UserSettings>,
|
||||
object: Shared<User>,
|
||||
gateway: GatewayHandle,
|
||||
) -> 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
|
||||
/// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify
|
||||
/// 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 object = Arc::new(RwLock::new(User::default()));
|
||||
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.
|
||||
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(),
|
||||
..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.
|
||||
let user = instance
|
||||
.login_account(login_schema)
|
||||
|
@ -128,7 +132,10 @@ pub mod instance;
|
|||
#[cfg(feature = "client")]
|
||||
pub mod ratelimiter;
|
||||
pub mod types;
|
||||
#[cfg(feature = "client")]
|
||||
#[cfg(all(
|
||||
feature = "client",
|
||||
any(feature = "voice_udp", feature = "voice_gateway")
|
||||
))]
|
||||
pub mod voice;
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
|
@ -137,6 +144,12 @@ pub mod voice;
|
|||
/// # Notes
|
||||
/// All the urls can be found on the /api/policies/instance/domains endpoint of a spacebar server
|
||||
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.
|
||||
/// Ex: `https://old.server.spacebar.chat/api`
|
||||
pub api: String,
|
||||
|
@ -151,8 +164,9 @@ pub struct UrlBundle {
|
|||
|
||||
impl UrlBundle {
|
||||
/// 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 {
|
||||
root: UrlBundle::parse_url(root),
|
||||
api: UrlBundle::parse_url(api),
|
||||
wss: UrlBundle::parse_url(wss),
|
||||
cdn: UrlBundle::parse_url(cdn),
|
||||
|
@ -237,7 +251,12 @@ impl UrlBundle {
|
|||
.json::<types::types::domains_configuration::Domains>()
|
||||
.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 {
|
||||
Err(ChorusError::RequestFailed {
|
||||
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.
|
||||
|
||||
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_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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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 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};
|
||||
|
||||
#[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 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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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 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};
|
||||
|
||||
#[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};
|
||||
#[cfg(feature = "sqlx")]
|
||||
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 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 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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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 cdn_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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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 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 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 rand::Fill;
|
||||
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 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};
|
||||
|
||||
#[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 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 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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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 mailjet;
|
||||
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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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 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};
|
||||
|
||||
#[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 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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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 global;
|
||||
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 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};
|
||||
|
||||
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 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 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};
|
||||
|
||||
#[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 defaults;
|
||||
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};
|
||||
|
||||
#[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};
|
||||
|
||||
#[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