Compare commits

...

59 Commits

Author SHA1 Message Date
Flori 29ada27125
Release 0.15.0 (#487)
## Release 0.15.0

### TODO:
- [x] Merge #457  
- [x] Merge #489 

## Changes

- Primitive voice support: #457
- Cleaner code
    - Replace `Arc<RwLock<T>>` with `Shared<T>`: #466 
    - More, better documentation
    - More tests
    - Fix some typos
    - Make gateway `close()` code cleaner
- Remove `#[derive(Eq)]` from types which falsely had this trait derived
- Change license from AGPL-3 to MPL-2.0: #477 
- Bump `whoami`, `mio`, `h2` to fix CVEs
- Bump versions of used GitHub Actions
2024-04-16 18:07:41 +02:00
kozabrada123 171b46c4d7
Limit test actions to 30 minutes (#489)
fix: limit all tests to 30 minutes
2024-04-16 17:40:38 +02:00
bitfl0wer d2761079cf
change version to 0.15.0 2024-04-16 17:38:36 +02:00
kozabrada123 56b2381716
Primitive voice implementation (feature/voice) (#457)
* Add Webrtc Identify & Ready

* Add more webrtc typings

* Attempt an untested voice gateway implementation

* fmt

* Merge with main

* Same allow as for voice as normal gateway

* Test error observer

* Minor updates

* More derives

* Even more derives

* Small types update

* e

* Minor doc fixes

* Modernise voice gateway

* Add default impl for voicegatewayerror

* Make voice event fields pub

* Event updates via the scientific method

* ??

* Fix bad request in voice gateway init

* Voice gateway updates

* Fix error failing to 'deserialize' properly

* Update voice identify

* Clarify FIXME related to #430

* Update to v7

* Create seperate voice_gateway.rs and voice_udp.rs

* Restructure voice to new module

* fix: deserialization error in speaking bitflags

* feat: kinda janky ip discovery impl

* feat: return ip discovery data + minor update

* feat: packet parsing!

* fix: voice works again

* feat: add voice_media_sink_wants

(comitting uncommited changes to merge)

* chore: rename events/webrtc to events/voice_gateway

* Add UdpHandle

* chore: clippy + other misc updates

* fix: attempt to fix failing wasm build

* chore: yes clippy, that is indeed an unneeded return statement

* feat: add VoiceData struct

* feat: add VoiceData reference to UdpHandler

* feat: decryption?

* chore: formatting

* feat: add ssrc definition (op 12)

* feat: add untested sending & asbtract nonce generation

* feat: Public api! (sorta)

* small updates

* feat: add sequence number

* chore: yes

* feat: merge VoiceHandler into official development

* chore: yes clippy, you are special

* fix: duplicated gateway events

* feat: first try at vgw wasm compat

* fix: blunder

* fix: gateway connect using wrong url

* fix: properly using encrypted data, bad practice for buffer creation

* chore: split voice udp

* feat: udp error handling, create udp/backends

* fix: its the same

* chore: clarify UDP on WASM

* api: split voice gateway and udp features, test for voice gateway in WASM

* feat: new encryption modes, minor code quality

* docs: document voice encryption modes

* chore: unused imports

* chore: update getrandom version to match wasm version

* chore: update on packet size FIXME

* drop buf asap

* Okay can't do that actually

* tests: add nonce test

* normal tests work?

* docs: fix doc warning, fix incorrect refrences to 'webrtc'

* chore: json isn't a doc test

* tests: better gateway auth test

* testing tests

* update voice heartbeat, fix the new test issue

* committed too much

* fix: unused import

* fix: use ip discovery address as string, not as Vec<u8>

* chore: less obnoxious logging

* chore: better unimplemented voice modes handling

* chore: remove unused variable

* chore: use matches macro

* add voice examples, make gateway ones clearer

* rename voice example

* chore: remove unused VoiceHandler

* fix: implement gateway Reconnect and InvalidSession

* Typo

Co-authored-by: Flori <39242991+bitfl0wer@users.noreply.github.com>

* Fix a bunch of typos

Co-authored-by: Flori <39242991+bitfl0wer@users.noreply.github.com>

* fix: error handling while loading native certs

* fix: guh

* use be for nonce bytes

* fix: refactor gw and vgw closures

* remove outdated docs

---------

Co-authored-by: Flori <39242991+bitfl0wer@users.noreply.github.com>
2024-04-16 17:18:21 +02:00
kozabrada123 a55e3120af
Fix code scanning alerts on #487 (#488)
fix codescanning alerts on 0.15.0
2024-04-16 15:54:30 +02:00
bitfl0wer f8ba59c43e
bump version, add rust-version 2024-04-16 15:13:27 +02:00
kozabrada123 0aeda51878
Bump whoami to fix stack buffer overflow (#485)
his was done since whoami versions < 1.5.0 are vulnerable to a stack
buffer overflow.
2024-04-16 08:31:14 +02:00
kozabrada123 854c9820ef
Ignore unused imports for pub use (#484) 2024-03-15 17:08:46 +01:00
kozabrada123 d5cebf0fef
Minor docs updates, add Get Private Channels (#483)
* feat: add get_private_channels

* minor docs update and reorder
2024-03-15 16:13:51 +01:00
kozabrada123 2b69035ec7
Bump mio to fix RUSTSEC-2024-0019 (#482) 2024-03-14 20:25:24 +01:00
kozabrada123 51c676c000
Fix broken luna.gitlab.io links (#480)
docs: fix broken luna.gitlab.io links
2024-02-25 09:54:06 +01:00
Flori 73c3d030f1
Move contribution guidelines to CONTRIBUTING.md (#478)
Moves the contribution guidelines from README.md to CONTRIBUTING.md, as
per @striezel's suggestion.
2024-02-04 22:30:08 +01:00
bitfl0wer 9f76afb6bb
Move contribution guidelines to CONTRIBUTING.md 2024-02-01 11:52:29 +01:00
Flori 82fd55b20c
License change (#477) 2024-01-31 23:59:42 +01:00
kozabrada123 5dc1dee27a
Update github/codeql-action to v3 (#476) 2024-01-31 23:15:08 +01:00
Dirk Stolle 362dd57504
Replace unmaintained actions-rs/toolchain by dtolnay/rust-toolchain (#473) 2024-01-31 23:02:10 +01:00
kozabrada123 36a72416d6
Fix example in readme for #456 (#475) 2024-01-31 22:53:13 +01:00
Dirk Stolle d3e5df65d4
Update h2 to 0.3.24 to fix vulnerability RUSTSEC-2024-0003 (#474)
The update fixes a resource exhaustion vulnerability in h2 which
may lead to Denial of Service. For more information on that see
<https://rustsec.org/advisories/RUSTSEC-2024-0003>.
2024-01-31 22:47:46 +01:00
Dirk Stolle 908e995949
Bump actions/setup-node in GHA workflow to v4 (#472) 2024-01-31 22:43:54 +01:00
Dirk Stolle f0686892e0
Fix a few typos (#471) 2024-01-31 22:27:53 +01:00
bitfl0wer 28cdd43b9b
Include license header everywhere 2024-01-30 17:19:34 +01:00
bitfl0wer 3700347503
Change license to Mozilla Public License v2.0 2024-01-30 10:03:14 +01:00
bitfl0wer 234304465b
Change license to Mozilla Public License v2.0 2024-01-30 10:00:05 +01:00
Flori 7a7c468bd0
Coverage (#468)
Up the reported coverage on coveralls by adding some missing tests,
especially for types. Also removes/replaces some old, faulty or
non-idiomatic code.

While unit testing might seem a bit bloaty (especially with this many
additions), I'd argue that writing somewhat sensible tests is the only
good way to somewhat reliably keep things from breaking in larger
projects/bigger codebases, such as these.

Most of these tests should, hopefully, be of acceptable quality,
although I do admit that when writing tests, I sometimes just like to
turn my brain off
2024-01-24 23:44:31 +01:00
bitfl0wer 262a52a8e9
exclude trivial id() functions from coverage 2024-01-24 23:26:59 +01:00
bitfl0wer 3040dcc46b
Add comment about test_self_updating_structs 2024-01-24 23:02:16 +01:00
bitfl0wer c950288df1
extend self updating structs test 2024-01-24 23:01:38 +01:00
bitfl0wer e073ff26c4
remove hit limit test 2024-01-24 18:51:10 +01:00
bitfl0wer 3ffb124cd4
Add test for get_limit_config 2024-01-24 12:32:08 +01:00
bitfl0wer 57e6cb438d
Add test to hit ratelimit 2024-01-24 12:21:46 +01:00
bitfl0wer 970f5b8b4f
Remove PartialOrd from Emoji because unneccessary 2024-01-23 23:53:08 +01:00
bitfl0wer 98f42aa03b
Add Message PartialEq Test 2024-01-23 23:42:00 +01:00
bitfl0wer 9cc7ede763
Add partial_eq test for relationship.rs/Relationship 2024-01-23 23:31:35 +01:00
bitfl0wer a2b6d4e407
Remove old/redundant code from attachment.rs 2024-01-23 23:11:37 +01:00
bitfl0wer 41a0e2fe27
Add to_public_user test 2024-01-23 23:06:24 +01:00
bitfl0wer 00c70501c4
Rename to_public_user into into_public_user 2024-01-23 23:06:14 +01:00
bitfl0wer 97ab757633
Add unit tests for guild.rs entities 2024-01-23 21:13:15 +01:00
bitfl0wer 7434690027
Remove Eq fromn Guild as it is not Eq 2024-01-23 21:05:01 +01:00
bitfl0wer 577a399a7b
Create tests from to_str and from_str for GuildFeatures 2024-01-23 20:50:19 +01:00
bitfl0wer 11df180446
APPEND: Remove unused imports 2024-01-23 19:08:35 +01:00
bitfl0wer 0923de59a4
Replace usage of Arc<RwLock<...>> in public APIs with Shared<...> 2024-01-23 19:07:23 +01:00
bitfl0wer 8846159ffd
Remove impl Eq from types that didn't qualify for Eq 2024-01-23 18:43:06 +01:00
bitfl0wer 013687c810
write unit tests for config and entities 2024-01-22 20:57:29 +01:00
bitfl0wer c6e7724650
rustfmt 2024-01-22 20:57:17 +01:00
bitfl0wer 74fc954d1a
Fix broken behaviour for ConfigEntity 2024-01-22 20:57:08 +01:00
Flori fe8106d2a1
"Self updating structs" API improvements (#467)
This PR slightly improves the ergonomics of working with self-updating
structs, by making the changes as documented in #466.
2024-01-22 15:19:24 +01:00
bitfl0wer 21699e5899
Loosen bounds on IntoShared<T> 2024-01-22 15:00:46 +01:00
bitfl0wer 5372d2c475
Make IntoShared trait with blanket implementation 2024-01-22 14:56:23 +01:00
bitfl0wer 29f3ee802a
Fix errors by moving into_shared out of Composite 2024-01-22 14:50:33 +01:00
bitfl0wer 6637f14b18
Replace Arc, Rwlock with Shared 2024-01-21 20:24:17 +01:00
bitfl0wer a571a9e137
Write documentation for observe 2024-01-21 20:13:00 +01:00
bitfl0wer 57214fd2fe
Add documentation for into_shared 2024-01-21 17:15:11 +01:00
bitfl0wer ca58767372
Rename to_shared to into_shared 2024-01-21 17:10:24 +01:00
bitfl0wer 2a7cae30b8
Define public method `to_shared` for dyn Composite 2024-01-21 17:07:54 +01:00
bitfl0wer 0660e25bdb
rustfmt 2024-01-21 17:07:30 +01:00
bitfl0wer 36ac6c1e5e
Replace use of Arc<RwLock<T>> with Shared<T> 2024-01-21 17:07:19 +01:00
bitfl0wer 315fe8e33b
Define type alias `Shared` 2024-01-21 17:06:43 +01:00
kozabrada123 8e25f401a5
Minor instance updates (#465)
make Instance::from_url_bundle pub, update Instance docs
2024-01-20 13:15:13 +01:00
Flori 85e494dd4a
merge main into dev (#464) 2024-01-19 21:55:29 +01:00
230 changed files with 5591 additions and 1291 deletions

View File

@ -13,13 +13,14 @@ jobs:
linux: linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 30
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Clone spacebar server - name: Clone spacebar server
run: | run: |
git clone https://github.com/bitfl0wer/server.git git clone https://github.com/bitfl0wer/server.git
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
cache: 'npm' cache: 'npm'
@ -52,7 +53,7 @@ jobs:
# - name: Clone spacebar server # - name: Clone spacebar server
# run: | # run: |
# git clone https://github.com/bitfl0wer/server.git # git clone https://github.com/bitfl0wer/server.git
# - uses: actions/setup-node@v3 # - uses: actions/setup-node@v4
# with: # with:
# node-version: 18 # node-version: 18
# cache: 'npm' # cache: 'npm'
@ -75,12 +76,13 @@ jobs:
# SAFARIDRIVER=$(which safaridriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt" --no-fail-fast # SAFARIDRIVER=$(which safaridriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt" --no-fail-fast
wasm-gecko: wasm-gecko:
runs-on: macos-latest runs-on: macos-latest
timeout-minutes: 30
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Clone spacebar server - name: Clone spacebar server
run: | run: |
git clone https://github.com/bitfl0wer/server.git git clone https://github.com/bitfl0wer/server.git
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
cache: 'npm' cache: 'npm'
@ -100,15 +102,16 @@ jobs:
rustup target add wasm32-unknown-unknown rustup target add wasm32-unknown-unknown
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force
GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt" GECKODRIVER=$(which geckodriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"
wasm-chrome: wasm-chrome:
runs-on: macos-latest runs-on: macos-latest
timeout-minutes: 30
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Clone spacebar server - name: Clone spacebar server
run: | run: |
git clone https://github.com/bitfl0wer/server.git git clone https://github.com/bitfl0wer/server.git
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
cache: 'npm' cache: 'npm'
@ -128,4 +131,4 @@ jobs:
rustup target add wasm32-unknown-unknown rustup target add wasm32-unknown-unknown
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force cargo binstall --no-confirm wasm-bindgen-cli --version "0.2.88" --force
CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt" CHROMEDRIVER=$(which chromedriver) cargo test --target wasm32-unknown-unknown --no-default-features --features="client, rt, voice_gateway"

View File

@ -29,12 +29,9 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: dtolnay/rust-toolchain@stable
with: with:
profile: minimal
toolchain: stable
components: clippy components: clippy
override: true
- name: Install required cargo - name: Install required cargo
run: cargo install clippy-sarif sarif-fmt run: cargo install clippy-sarif sarif-fmt
@ -47,7 +44,7 @@ jobs:
continue-on-error: true continue-on-error: true
- name: Upload analysis results to GitHub - name: Upload analysis results to GitHub
uses: github/codeql-action/upload-sarif@v2 uses: github/codeql-action/upload-sarif@v3
with: with:
sarif_file: rust-clippy-results.sarif sarif_file: rust-clippy-results.sarif
wait-for-processing: true wait-for-processing: true

10
CONTRIBUTING.md Normal file
View File

@ -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.

155
Cargo.lock generated
View File

@ -17,6 +17,16 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.8.7" version = "0.8.7"
@ -199,14 +209,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chorus" name = "chorus"
version = "0.14.0" version = "0.15.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"base64 0.21.7", "base64 0.21.7",
"bitflags 2.4.1", "bitflags 2.4.1",
"chorus-macros", "chorus-macros",
"chrono", "chrono",
"crypto_secretbox",
"custom_error", "custom_error",
"discortp",
"futures-util", "futures-util",
"getrandom", "getrandom",
"hostname", "hostname",
@ -264,6 +276,17 @@ dependencies = [
"windows-targets 0.48.5", "windows-targets 0.48.5",
] ]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
"zeroize",
]
[[package]] [[package]]
name = "console_error_panic_hook" name = "console_error_panic_hook"
version = "0.1.7" version = "0.1.7"
@ -342,9 +365,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [ dependencies = [
"generic-array", "generic-array",
"rand_core",
"typenum", "typenum",
] ]
[[package]]
name = "crypto_secretbox"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d6cf87adf719ddf43a805e92c6870a531aedda35ff640442cbaf8674e141e1"
dependencies = [
"aead",
"cipher",
"generic-array",
"poly1305",
"salsa20",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "custom_error" name = "custom_error"
version = "1.9.2" version = "1.9.2"
@ -425,6 +464,16 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "discortp"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524b9439c09174aede2c88d58cfc6b83575b06569d1af4d07562f76595b2896b"
dependencies = [
"pnet_macros",
"pnet_macros_support",
]
[[package]] [[package]]
name = "dotenvy" name = "dotenvy"
version = "0.15.7" version = "0.15.7"
@ -643,6 +692,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [ dependencies = [
"typenum", "typenum",
"version_check", "version_check",
"zeroize",
] ]
[[package]] [[package]]
@ -666,9 +716,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.23" version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -923,6 +973,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "ipnet" name = "ipnet"
version = "2.9.0" version = "2.9.0"
@ -1085,9 +1144,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.10" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [ dependencies = [
"libc", "libc",
"wasi", "wasi",
@ -1123,6 +1182,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "no-std-net"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -1217,6 +1282,12 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.62" version = "0.10.62"
@ -1363,6 +1434,36 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
[[package]]
name = "pnet_base"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d3a993d49e5fd5d4d854d6999d4addca1f72d86c65adf224a36757161c02b6"
dependencies = [
"no-std-net",
]
[[package]]
name = "pnet_macros"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48dd52a5211fac27e7acb14cfc9f30ae16ae0e956b7b779c8214c74559cef4c3"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn 1.0.109",
]
[[package]]
name = "pnet_macros_support"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89de095dc7739349559913aed1ef6a11e73ceade4897dadc77c5e09de6740750"
dependencies = [
"pnet_base",
]
[[package]] [[package]]
name = "poem" name = "poem"
version = "1.3.59" version = "1.3.59"
@ -1406,6 +1507,17 @@ dependencies = [
"syn 2.0.48", "syn 2.0.48",
] ]
[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]] [[package]]
name = "powerfmt" name = "powerfmt"
version = "0.2.0" version = "0.2.0"
@ -1688,6 +1800,15 @@ version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "salsa20"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
dependencies = [
"cipher",
]
[[package]] [[package]]
name = "schannel" name = "schannel"
version = "0.1.23" version = "0.1.23"
@ -2526,6 +2647,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle",
]
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.7.1" version = "0.7.1"
@ -2588,6 +2719,12 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.89" version = "0.2.89"
@ -2705,9 +2842,13 @@ dependencies = [
[[package]] [[package]]
name = "whoami" name = "whoami"
version = "1.4.1" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e"
dependencies = [
"redox_syscall",
"wasite",
]
[[package]] [[package]]
name = "wildmatch" name = "wildmatch"

View File

@ -1,13 +1,14 @@
[package] [package]
name = "chorus" name = "chorus"
description = "A library for interacting with multiple Spacebar-compatible Instances at once." description = "A library for interacting with multiple Spacebar-compatible Instances at once."
version = "0.14.0" version = "0.15.0"
license = "AGPL-3.0" license = "MPL-2.0"
edition = "2021" edition = "2021"
repository = "https://github.com/polyphony-chat/chorus" repository = "https://github.com/polyphony-chat/chorus"
readme = "README.md" readme = "README.md"
keywords = ["spacebar", "discord", "polyphony"] keywords = ["spacebar", "discord", "polyphony"]
website = ["https://discord.com/invite/m3FpcapGDD"] website = ["https://discord.com/invite/m3FpcapGDD"]
rust-version = "1.67.1"
[features] [features]
@ -16,6 +17,9 @@ backend = ["dep:poem", "dep:sqlx"]
rt-multi-thread = ["tokio/rt-multi-thread"] rt-multi-thread = ["tokio/rt-multi-thread"]
rt = ["tokio/rt"] rt = ["tokio/rt"]
client = [] client = []
voice = ["voice_udp", "voice_gateway"]
voice_udp = ["dep:discortp", "dep:crypto_secretbox"]
voice_gateway = []
[dependencies] [dependencies]
tokio = { version = "1.35.1", features = ["macros", "sync"] } tokio = { version = "1.35.1", features = ["macros", "sync"] }
@ -24,10 +28,7 @@ serde_json = { version = "1.0.111", features = ["raw_value"] }
serde-aux = "4.3.1" serde-aux = "4.3.1"
serde_with = "3.4.0" serde_with = "3.4.0"
serde_repr = "0.1.18" serde_repr = "0.1.18"
reqwest = { features = [ reqwest = { features = ["multipart", "json"], version = "0.11.23" }
"multipart",
"json",
], version = "0.11.23" }
url = "2.5.0" url = "2.5.0"
chrono = { version = "0.4.31", features = ["serde"] } chrono = { version = "0.4.31", features = ["serde"] }
regex = "1.10.2" regex = "1.10.2"
@ -52,6 +53,8 @@ sqlx = { version = "0.7.3", features = [
"runtime-tokio-native-tls", "runtime-tokio-native-tls",
"any", "any",
], optional = true } ], optional = true }
discortp = { version = "0.5.0", optional = true, features = ["rtp", "discord", "demux"] }
crypto_secretbox = { version = "0.1.1", optional = true }
rand = "0.8.5" rand = "0.8.5"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
@ -63,6 +66,7 @@ tokio-tungstenite = { version = "0.20.1", features = [
] } ] }
native-tls = "0.2.11" native-tls = "0.2.11"
hostname = "0.3.1" hostname = "0.3.1"
getrandom = { version = "0.2.12" }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2.12", features = ["js"] } getrandom = { version = "0.2.12", features = ["js"] }

1034
LICENSE

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,7 @@ To get started with Chorus, import it into your project by adding the following
```toml ```toml
[dependencies] [dependencies]
chorus = "0.14.0" chorus = "0.15.0"
``` ```
### Establishing a Connection ### Establishing a Connection
@ -53,16 +53,10 @@ To connect to a Spacebar compatible server, you need to create an [`Instance`](h
```rs ```rs
use chorus::instance::Instance; use chorus::instance::Instance;
use chorus::UrlBundle;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let bundle = UrlBundle::new( let instance = Instance::new("https://example.com")
"https://example.com/api".to_string(),
"wss://example.com/".to_string(),
"https://example.com/cdn".to_string(),
);
let instance = Instance::new(bundle)
.await .await
.expect("Failed to connect to the Spacebar server"); .expect("Failed to connect to the Spacebar server");
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique. // You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
@ -87,7 +81,7 @@ let login_schema = LoginSchema {
password: "Correct-Horse-Battery-Staple".to_string(), password: "Correct-Horse-Battery-Staple".to_string(),
..Default::default() ..Default::default()
}; };
// Each user connects to the Gateway. The Gateway connection lives on a seperate thread. Depending on // Each user connects to the Gateway. The Gateway connection lives on a separate thread. Depending on
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads. // the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
let user = instance let user = instance
.login_account(login_schema) .login_account(login_schema)
@ -125,7 +119,7 @@ like "proxy connection checking" are already disabled on this version, which oth
### wasm ### wasm
To test for wasm, you will need to `cargo install wasm-pack`. You can then run To test for wasm, you will need to `cargo install wasm-pack`. You can then run
`wasm-pack test --<chrome/firefox/safari> --headless -- --target wasm32-unknown-unknown --features="rt, client" --no-default-features` `wasm-pack test --<chrome/firefox/safari> --headless -- --target wasm32-unknown-unknown --features="rt, client, voice_gateway" --no-default-features`
to run the tests for wasm. to run the tests for wasm.
## Versioning ## Versioning
@ -134,11 +128,7 @@ This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read
## Contributing ## Contributing
Chorus is currently missing voice support and a lot of API endpoints, many of which should be trivial to implement, See [CONTRIBUTING.md](./CONTRIBUTING.md).
ever since [we streamlined the process of doing so](https://github.com/polyphony-chat/chorus/discussions/401).
If you'd like to contribute new functionality, check out [The 'Meta'-issues.](https://github.com/polyphony-chat/chorus/issues?q=is%3Aissue+label%3A%22Type%3A+Meta%22+) They contain a comprehensive list of all features which are yet missing for full Discord.com compatibility.
Please feel free to open an Issue with the idea you have, or a Pull Request. Please keep our [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) in mind. Your contribution might not be accepted if it violates these guidelines or [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md).
<details> <details>
<summary>Progress Tracker/Roadmap</summary> <summary>Progress Tracker/Roadmap</summary>

View File

@ -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 proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed}; use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed};

View File

@ -1,3 +1,16 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This example showcase how to properly use gateway observers.
//
// To properly run it, you will need to change the token below.
const TOKEN: &str = "";
/// Find the gateway websocket url of the server we want to connect to
const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/";
use async_trait::async_trait; use async_trait::async_trait;
use chorus::gateway::Gateway; use chorus::gateway::Gateway;
use chorus::{ use chorus::{
@ -32,11 +45,10 @@ impl Observer<GatewayReady> for ExampleObserver {
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() { async fn main() {
// Find the gateway websocket url of the server we want to connect to let gateway_websocket_url = GATEWAY_URL.to_string();
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
// Initiate the gateway connection // Initiate the gateway connection
let gateway = Gateway::spawn(websocket_url_spacebar).await.unwrap(); let gateway = Gateway::spawn(gateway_websocket_url).await.unwrap();
// Create an instance of our observer // Create an instance of our observer
let observer = ExampleObserver {}; let observer = ExampleObserver {};
@ -55,7 +67,7 @@ async fn main() {
.subscribe(shared_observer); .subscribe(shared_observer);
// Authenticate so we will receive any events // Authenticate so we will receive any events
let token = "SecretToken".to_string(); let token = TOKEN.to_string();
let mut identify = GatewayIdentifyPayload::common(); let mut identify = GatewayIdentifyPayload::common();
identify.token = token; identify.token = token;
gateway.send_identify(identify).await; gateway.send_identify(identify).await;

View File

@ -1,3 +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/.
// This example showcases how to initiate a gateway connection manually
// (e. g. not through ChorusUser)
//
// To properly run it, you will need to modify the token below.
const TOKEN: &str = "";
/// Find the gateway websocket url of the server we want to connect to
const GATEWAY_URL: &str = "wss://gateway.old.server.spacebar.chat/";
use std::time::Duration; use std::time::Duration;
use chorus::gateway::Gateway; use chorus::gateway::Gateway;
@ -11,16 +25,15 @@ use wasmtimer::tokio::sleep;
/// This example creates a simple gateway connection and a session with an Identify event /// This example creates a simple gateway connection and a session with an Identify event
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() { async fn main() {
// Find the gateway websocket url of the server we want to connect to let gateway_websocket_url = GATEWAY_URL.to_string();
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
// Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another // Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another
let gateway = Gateway::spawn(websocket_url_spacebar).await.unwrap(); let gateway = Gateway::spawn(gateway_websocket_url).await.unwrap();
// At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated // At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated
// Get a token for an account on the server // Get a token for an account on the server
let token = "SecretToken".to_string(); let token = TOKEN.to_string();
// Create an identify event // Create an identify event
// An Identify event is how the server authenticates us and gets info about our os and browser, along with our intents / capabilities // An Identify event is how the server authenticates us and gets info about our os and browser, along with our intents / capabilities

View File

@ -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 chorus::instance::Instance; use chorus::instance::Instance;
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]

View File

@ -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 chorus::instance::Instance; use chorus::instance::Instance;
use chorus::types::LoginSchema; use chorus::types::LoginSchema;
@ -13,7 +17,7 @@ async fn main() {
password: "Correct-Horse-Battery-Staple".to_string(), password: "Correct-Horse-Battery-Staple".to_string(),
..Default::default() ..Default::default()
}; };
// Each user connects to the Gateway. The Gateway connection lives on a seperate thread. Depending on // Each user connects to the Gateway. The Gateway connection lives on a separate thread. Depending on
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads. // the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
let user = instance let user = instance
.login_account(login_schema) .login_account(login_schema)

View File

@ -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 = "*"

View File

@ -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();
}*/
}
}

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use reqwest::Client; use reqwest::Client;

View File

@ -1,6 +1,13 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
#[allow(unused_imports)]
pub use login::*; pub use login::*;
#[allow(unused_imports)]
pub use register::*; pub use register::*;
use crate::gateway::Gateway; use crate::gateway::Gateway;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use reqwest::Client; use reqwest::Client;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use reqwest::Client; use reqwest::Client;
use serde_json::to_string; use serde_json::to_string;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use http::header::CONTENT_DISPOSITION; use http::header::CONTENT_DISPOSITION;
use http::HeaderMap; use http::HeaderMap;
use reqwest::{multipart, Client}; use reqwest::{multipart, Client};
@ -36,7 +40,7 @@ impl Message {
chorus_request.deserialize_response::<Message>(user).await chorus_request.deserialize_response::<Message>(user).await
} else { } else {
for (index, attachment) in message.attachments.iter_mut().enumerate() { for (index, attachment) in message.attachments.iter_mut().enumerate() {
attachment.get_mut(index).unwrap().set_id(index as i16); attachment.get_mut(index).unwrap().id = Some(index as i16);
} }
let mut form = reqwest::multipart::Form::new(); let mut form = reqwest::multipart::Form::new();
let payload_json = to_string(&message).unwrap(); let payload_json = to_string(&message).unwrap();
@ -45,8 +49,8 @@ impl Message {
form = form.part("payload_json", payload_field); form = form.part("payload_json", payload_field);
for (index, attachment) in message.attachments.unwrap().into_iter().enumerate() { for (index, attachment) in message.attachments.unwrap().into_iter().enumerate() {
let (attachment_content, current_attachment) = attachment.move_content(); let attachment_content = attachment.content;
let (attachment_filename, _) = current_attachment.move_filename(); let attachment_filename = attachment.filename;
let part_name = format!("files[{}]", index); let part_name = format!("files[{}]", index);
let content_disposition = format!( let content_disposition = format!(
"form-data; name=\"{}\"'; filename=\"{}\"", "form-data; name=\"{}\"'; filename=\"{}\"",

View File

@ -1,3 +1,8 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#![allow(unused_imports)]
pub use channels::*; pub use channels::*;
pub use messages::*; pub use messages::*;
pub use permissions::*; pub use permissions::*;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use reqwest::Client; use reqwest::Client;
use serde_json::to_string; use serde_json::to_string;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::{ use crate::{
errors::ChorusResult, errors::ChorusResult,
instance::ChorusUser, instance::ChorusUser,

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use reqwest::Client; use reqwest::Client;
use serde_json::from_str; use serde_json::from_str;
use serde_json::to_string; use serde_json::to_string;
@ -14,6 +18,25 @@ use crate::types::{
use crate::types::{GuildBan, Snowflake}; use crate::types::{GuildBan, Snowflake};
impl Guild { impl Guild {
/// Fetches a guild by its id.
///
/// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild>
pub async fn get(guild_id: Snowflake, user: &mut ChorusUser) -> ChorusResult<Guild> {
let chorus_request = ChorusRequest {
request: Client::new()
.get(format!(
"{}/guilds/{}",
user.belongs_to.read().unwrap().urls.api,
guild_id
))
.header("Authorization", user.token()),
limit_type: LimitType::Guild(guild_id),
};
let response = chorus_request.deserialize_response::<Guild>(user).await?;
Ok(response)
}
/// Creates a new guild. /// Creates a new guild.
/// ///
/// # Reference /// # Reference
@ -34,6 +57,35 @@ impl Guild {
chorus_request.deserialize_response::<Guild>(user).await chorus_request.deserialize_response::<Guild>(user).await
} }
/// Modify a guild's settings.
///
/// Requires the [MANAGE_GUILD](crate::types::PermissionFlags::MANAGE_GUILD) permission.
///
/// Returns the updated guild.
///
/// # Reference
/// <https://discord-userdoccers.vercel.app/resources/guild#modify-guild>
pub async fn modify(
guild_id: Snowflake,
schema: GuildModifySchema,
user: &mut ChorusUser,
) -> ChorusResult<Guild> {
let chorus_request = ChorusRequest {
request: Client::new()
.patch(format!(
"{}/guilds/{}",
user.belongs_to.read().unwrap().urls.api,
guild_id,
))
.header("Authorization", user.token())
.header("Content-Type", "application/json")
.body(to_string(&schema).unwrap()),
limit_type: LimitType::Guild(guild_id),
};
let response = chorus_request.deserialize_response::<Guild>(user).await?;
Ok(response)
}
/// Deletes a guild by its id. /// Deletes a guild by its id.
/// ///
/// User must be the owner. /// User must be the owner.
@ -123,77 +175,11 @@ impl Guild {
}; };
} }
/// Fetches a guild by its id. /// Returns a guild preview object for the given guild ID.
///
/// If the user is not in the guild, the guild must be discoverable.
/// ///
/// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild>
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)
}
/// Returns a guild preview object for the given guild ID. If the user is not in the guild, the guild must be discoverable.
/// # Reference: /// # Reference:
///
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-preview> /// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-preview>
pub async fn get_preview( pub async fn get_preview(
guild_id: Snowflake, guild_id: Snowflake,
@ -270,7 +256,9 @@ impl Guild {
request.deserialize_response::<Vec<GuildMember>>(user).await request.deserialize_response::<Vec<GuildMember>>(user).await
} }
/// Removes a member from a guild. Requires the KICK_MEMBERS permission. Returns a 204 empty response on success. /// Removes a member from a guild.
///
/// Requires the [KICK_MEMBERS](crate::types::PermissionFlags::KICK_MEMBERS) permission.
/// ///
/// # Reference /// # Reference
/// See <https://discord-userdoccers.vercel.app/resources/guild#remove-guild-member> /// See <https://discord-userdoccers.vercel.app/resources/guild#remove-guild-member>
@ -383,7 +371,9 @@ impl Guild {
.await .await
} }
/// Returns a list of ban objects for the guild. Requires the `BAN_MEMBERS` permission. /// Returns a list of ban objects for the guild.
///
/// Requires the [BAN_MEMBERS](crate::types::PermissionFlags::BAN_MEMBERS) permission.
/// ///
/// # Reference: /// # Reference:
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-bans> /// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-bans>
@ -413,7 +403,9 @@ impl Guild {
request.deserialize_response::<Vec<GuildBan>>(user).await request.deserialize_response::<Vec<GuildBan>>(user).await
} }
/// Returns a ban object for the given user. Requires the `BAN_MEMBERS` permission. /// Returns a ban object for the given user.
///
/// Requires the [BAN_MEMBERS](crate::types::PermissionFlags::BAN_MEMBERS) permission.
/// ///
/// # Reference: /// # Reference:
/// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-ban> /// See <https://discord-userdoccers.vercel.app/resources/guild#get-guild-ban>
@ -441,7 +433,39 @@ impl Guild {
request.deserialize_response::<GuildBan>(user).await request.deserialize_response::<GuildBan>(user).await
} }
/// Removes the ban for a user. Requires the BAN_MEMBERS permissions. Returns a 204 empty response on success. /// Creates a ban from the guild.
///
/// Requires the [BAN_MEMBERS](crate::types::PermissionFlags::BAN_MEMBERS) permission.
///
pub async fn create_ban(
guild_id: Snowflake,
user_id: Snowflake,
audit_log_reason: Option<String>,
schema: GuildBanCreateSchema,
user: &mut ChorusUser,
) -> ChorusResult<()> {
// FIXME: Return GuildBan instead of (). Requires <https://github.com/spacebarchat/server/issues/1096> to be resolved.
let request = ChorusRequest::new(
http::Method::PUT,
format!(
"{}/guilds/{}/bans/{}",
user.belongs_to.read().unwrap().urls.api,
guild_id,
user_id
)
.as_str(),
Some(to_string(&schema).unwrap()),
audit_log_reason.as_deref(),
None,
Some(user),
LimitType::Guild(guild_id),
);
request.handle_request_as_result(user).await
}
/// Removes the ban for a user.
///
/// Requires the [BAN_MEMBERS](crate::types::PermissionFlags::BAN_MEMBERS) permission.
/// ///
/// # Reference: /// # Reference:
/// See <https://discord-userdoccers.vercel.app/resources/guild#delete-guild-ban> /// See <https://discord-userdoccers.vercel.app/resources/guild#delete-guild-ban>

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use reqwest::Client; use reqwest::Client;
use crate::{ use crate::{

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::errors::ChorusResult; use crate::errors::ChorusResult;
use crate::instance::ChorusUser; use crate::instance::ChorusUser;
use crate::types::{Guild, Message, MessageSearchQuery, Snowflake}; use crate::types::{Guild, Message, MessageSearchQuery, Snowflake};
@ -9,7 +13,7 @@ impl Guild {
/// permission to be present on the current user. /// permission to be present on the current user.
/// ///
/// If the guild/channel you are searching is not yet indexed, the endpoint will return a 202 accepted response. /// If the guild/channel you are searching is not yet indexed, the endpoint will return a 202 accepted response.
/// In this case, the method will return a [`ChorusError::InvalidResponse`] error. /// In this case, the method will return a [`ChorusError::InvalidResponse`](crate::errors::ChorusError::InvalidResponse) error.
/// ///
/// # Reference: /// # Reference:
/// See <https://discord-userdoccers.vercel.app/resources/message#search-messages> /// See <https://discord-userdoccers.vercel.app/resources/message#search-messages>

View File

@ -1,3 +1,8 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#![allow(unused_imports)]
pub use guilds::*; pub use guilds::*;
pub use messages::*; pub use messages::*;
pub use roles::*; pub use roles::*;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use reqwest::Client; use reqwest::Client;
use serde_json::to_string; use serde_json::to_string;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use reqwest::Client; use reqwest::Client;
use serde_json::to_string; use serde_json::to_string;

View File

@ -1,4 +1,10 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//! All of the API's endpoints. //! All of the API's endpoints.
#![allow(unused_imports)]
pub use channels::messages::*; pub use channels::messages::*;
pub use guilds::*; pub use guilds::*;
pub use invites::*; pub use invites::*;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde_json::from_str; use serde_json::from_str;
use crate::errors::{ChorusError, ChorusResult}; use crate::errors::{ChorusError, ChorusResult};

View File

@ -1,3 +1,8 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#![allow(unused_imports)]
pub use instance::*; pub use instance::*;
pub mod instance; pub mod instance;

View File

@ -1 +1,5 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
pub mod instance; pub mod instance;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use reqwest::Client; use reqwest::Client;
use serde_json::to_string; use serde_json::to_string;
@ -9,6 +13,26 @@ use crate::{
}; };
impl ChorusUser { impl ChorusUser {
/// Fetches a list of private channels the user is in.
///
/// # Reference:
/// See <https://docs.discord.sex/resources/channel#get-private-channels>
pub async fn get_private_channels(&mut self) -> ChorusResult<Vec<Channel>> {
let url = format!(
"{}/users/@me/channels",
self.belongs_to.read().unwrap().urls.api
);
ChorusRequest {
request: Client::new()
.get(url)
.header("Authorization", self.token())
.header("Content-Type", "application/json"),
limit_type: LimitType::Global,
}
.deserialize_response::<Vec<Channel>>(self)
.await
}
/// Creates a DM channel or group DM channel. /// Creates a DM channel or group DM channel.
/// ///
/// One recipient creates or returns an existing DM channel, /// One recipient creates or returns an existing DM channel,

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use reqwest::Client; use reqwest::Client;
use serde_json::to_string; use serde_json::to_string;

View File

@ -1,3 +1,8 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#![allow(unused_imports)]
pub use channels::*; pub use channels::*;
pub use guilds::*; pub use guilds::*;
pub use relationships::*; pub use relationships::*;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use reqwest::Client; use reqwest::Client;
use serde_json::to_string; use serde_json::to_string;
@ -15,7 +19,7 @@ impl ChorusUser {
/// Retrieves a list of mutual friends between the authenticated user and a given user. /// Retrieves a list of mutual friends between the authenticated user and a given user.
/// ///
/// # Reference /// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#get-users-peer-id-relationships> /// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#get-userspeer_idrelationships>
pub async fn get_mutual_relationships( pub async fn get_mutual_relationships(
&mut self, &mut self,
user_id: Snowflake, user_id: Snowflake,
@ -37,7 +41,7 @@ impl ChorusUser {
/// Retrieves the user's relationships. /// Retrieves the user's relationships.
/// ///
/// # Reference /// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#get-users-me-relationships> /// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#get-usersmerelationships>
pub async fn get_relationships(&mut self) -> ChorusResult<Vec<types::Relationship>> { pub async fn get_relationships(&mut self) -> ChorusResult<Vec<types::Relationship>> {
let url = format!( let url = format!(
"{}/users/@me/relationships", "{}/users/@me/relationships",
@ -55,7 +59,7 @@ impl ChorusUser {
/// Sends a friend request to a user. /// Sends a friend request to a user.
/// ///
/// # Reference /// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#post-users-me-relationships> /// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#post-usersmerelationships>
pub async fn send_friend_request( pub async fn send_friend_request(
&mut self, &mut self,
schema: FriendRequestSendSchema, schema: FriendRequestSendSchema,
@ -132,7 +136,7 @@ impl ChorusUser {
/// Removes the relationship between the authenticated user and a given user. /// Removes the relationship between the authenticated user and a given user.
/// ///
/// # Reference /// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#delete-users-me-relationships-peer-id> /// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#delete-usersmerelationshipspeer_id>
pub async fn remove_relationship(&mut self, user_id: Snowflake) -> ChorusResult<()> { pub async fn remove_relationship(&mut self, user_id: Snowflake) -> ChorusResult<()> {
let url = format!( let url = format!(
"{}/users/@me/relationships/{}", "{}/users/@me/relationships/{}",

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use reqwest::Client; use reqwest::Client;
@ -113,7 +117,7 @@ impl User {
/// Gets the user's settings. /// Gets the user's settings.
/// ///
/// # Reference /// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/user_settings.html#get-users-me-settings> /// See <https://luna.gitlab.io/discord-unofficial-docs/docs/user_settings.html#get-usersmesettings>
pub async fn get_settings( pub async fn get_settings(
token: &String, token: &String,
url_api: &String, url_api: &String,

View File

@ -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/.
//! Contains all the errors that can be returned by the library. //! Contains all the errors that can be returned by the library.
use custom_error::custom_error; use custom_error::custom_error;
@ -17,7 +21,7 @@ custom_error! {
/// Server did not respond. /// Server did not respond.
NoResponse = "Did not receive a response from the Server.", NoResponse = "Did not receive a response from the Server.",
/// Reqwest returned an Error instead of a Response object. /// Reqwest returned an Error instead of a Response object.
RequestFailed{url:String, error: String} = "An error occured while trying to GET from {url}: {error}", RequestFailed{url:String, error: String} = "An error occurred while trying to GET from {url}: {error}",
/// Response received, however, it was not of the successful responses type. Used when no other, special case applies. /// Response received, however, it was not of the successful responses type. Used when no other, special case applies.
ReceivedErrorCode{error_code: u16, error: String} = "Received the following error code while requesting from the route: {error_code}", ReceivedErrorCode{error_code: u16, error: String} = "Received the following error code while requesting from the route: {error_code}",
/// Used when there is likely something wrong with the instance, the request was directed to. /// Used when there is likely something wrong with the instance, the request was directed to.
@ -63,7 +67,7 @@ custom_error! {
} }
custom_error! { custom_error! {
/// For errors we receive from the gateway, see https://discord-userdoccers.vercel.app/topics/opcodes-and-status-codes#gateway-close-event-codes; /// For errors we receive from the gateway, see <https://discord-userdoccers.vercel.app/topics/opcodes-and-status-codes#gateway-close-event-codes>;
/// ///
/// Supposed to be sent as numbers, though they are sent as string most of the time? /// Supposed to be sent as numbers, though they are sent as string most of the time?
/// ///
@ -96,3 +100,59 @@ custom_error! {
} }
impl WebSocketEvent for GatewayError {} impl WebSocketEvent for GatewayError {}
custom_error! {
/// Voice Gateway errors
///
/// Similar to [GatewayError].
///
/// See <https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-close-event-codes>;
#[derive(Clone, Default, PartialEq, Eq)]
pub VoiceGatewayError
// Errors we receive
#[default]
UnknownOpcode = "You sent an invalid opcode",
FailedToDecodePayload = "You sent an invalid payload in your identifying to the (Voice) Gateway",
NotAuthenticated = "You sent a payload before identifying with the (Voice) Gateway",
AuthenticationFailed = "The token you sent in your identify payload is incorrect",
AlreadyAuthenticated = "You sent more than one identify payload",
SessionNoLongerValid = "Your session is no longer valid",
SessionTimeout = "Your session has timed out",
ServerNotFound = "We can't find the server you're trying to connect to",
UnknownProtocol = "We didn't recognize the protocol you sent",
Disconnected = "Channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped. Should not reconnect.",
VoiceServerCrashed = "The server crashed, try resuming",
UnknownEncryptionMode = "Server failed to decrypt data",
// Errors when initiating a gateway connection
CannotConnect{error: String} = "Cannot connect due to a tungstenite error: {error}",
NonHelloOnInitiate{opcode: u8} = "Received non hello on initial gateway connection ({opcode}), something is definitely wrong",
// Other misc errors
UnexpectedOpcodeReceived{opcode: u8} = "Received an opcode we weren't expecting to receive: {opcode}",
}
impl WebSocketEvent for VoiceGatewayError {}
custom_error! {
/// Voice UDP errors.
#[derive(Clone, PartialEq, Eq)]
pub VoiceUdpError
// General errors
BrokenSocket{error: String} = "Could not write / read from UDP socket: {error}",
NoData = "We have not set received the necessary data to perform this operation.",
// Encryption errors
EncryptionModeNotImplemented{encryption_mode: String} = "Voice encryption mode {encryption_mode} is not yet implemented.",
NoKey = "Tried to encrypt / decrypt rtp data, but no key has been received yet",
FailedEncryption = "Tried to encrypt rtp data, but failed. Most likely this is an issue chorus' nonce generation. Please open an issue on the chorus github: https://github.com/polyphony-chat/chorus/issues/new",
FailedDecryption = "Tried to decrypt rtp data, but failed. Most likely this is an issue chorus' nonce generation. Please open an issue on the chorus github: https://github.com/polyphony-chat/chorus/issues/new",
FailedNonceGeneration{error: String} = "Tried to generate nonce, but failed due to error: {error}.",
// Errors when initiating a socket connection
CannotBind{error: String} = "Cannot bind socket due to a UDP error: {error}",
CannotConnect{error: String} = "Cannot connect due to a UDP error: {error}",
}
impl WebSocketEvent for VoiceUdpError {}

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))] #[cfg(all(not(target_arch = "wasm32"), feature = "client"))]
pub mod tungstenite; pub mod tungstenite;
#[cfg(all(not(target_arch = "wasm32"), feature = "client"))] #[cfg(all(not(target_arch = "wasm32"), feature = "client"))]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use futures_util::{ use futures_util::{
stream::{SplitSink, SplitStream}, stream::{SplitSink, SplitStream},
StreamExt, StreamExt,
@ -23,8 +27,16 @@ impl TungsteniteBackend {
websocket_url: &str, websocket_url: &str,
) -> Result<(TungsteniteSink, TungsteniteStream), crate::errors::GatewayError> { ) -> Result<(TungsteniteSink, TungsteniteStream), crate::errors::GatewayError> {
let mut roots = rustls::RootCertStore::empty(); let mut roots = rustls::RootCertStore::empty();
for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") let certs = rustls_native_certs::load_native_certs();
{
if let Err(e) = certs {
log::error!("Failed to load platform native certs! {:?}", e);
return Err(GatewayError::CannotConnect {
error: format!("{:?}", e),
});
}
for cert in certs.unwrap() {
roots.add(&rustls::Certificate(cert.0)).unwrap(); roots.add(&rustls::Certificate(cert.0)).unwrap();
} }
let (websocket_stream, _) = match connect_async_tls_with_config( let (websocket_stream, _) = match connect_async_tls_with_config(

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use futures_util::{ use futures_util::{
stream::{SplitSink, SplitStream}, stream::{SplitSink, SplitStream},
StreamExt, StreamExt,

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use super::*; use super::*;
use crate::types; use crate::types;
@ -42,6 +46,8 @@ pub struct Session {
pub ready: GatewayEvent<types::GatewayReady>, pub ready: GatewayEvent<types::GatewayReady>,
pub ready_supplemental: GatewayEvent<types::GatewayReadySupplemental>, pub ready_supplemental: GatewayEvent<types::GatewayReadySupplemental>,
pub replace: GatewayEvent<types::SessionsReplace>, pub replace: GatewayEvent<types::SessionsReplace>,
pub reconnect: GatewayEvent<types::GatewayReconnect>,
pub invalid: GatewayEvent<types::GatewayInvalidSession>,
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::time::Duration; use std::time::Duration;
use futures_util::{SinkExt, StreamExt}; use futures_util::{SinkExt, StreamExt};
@ -5,13 +9,14 @@ use log::*;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::task; use tokio::task;
use self::event::Events; use super::events::Events;
use super::*; use super::*;
use super::{Sink, Stream}; use super::{Sink, Stream};
use crate::types::{ use crate::types::{
self, AutoModerationRule, AutoModerationRuleUpdate, Channel, ChannelCreate, ChannelDelete, self, AutoModerationRule, AutoModerationRuleUpdate, Channel, ChannelCreate, ChannelDelete,
ChannelUpdate, Guild, GuildRoleCreate, GuildRoleUpdate, JsonField, RoleObject, SourceUrlField, ChannelUpdate, GatewayInvalidSession, GatewayReconnect, Guild, GuildRoleCreate,
ThreadUpdate, UpdateMessage, WebSocketEvent, GuildRoleUpdate, JsonField, RoleObject, SourceUrlField, ThreadUpdate, UpdateMessage,
WebSocketEvent,
}; };
#[derive(Debug)] #[derive(Debug)]
@ -21,6 +26,7 @@ pub struct Gateway {
websocket_send: Arc<Mutex<Sink>>, websocket_send: Arc<Mutex<Sink>>,
websocket_receive: Stream, websocket_receive: Stream,
kill_send: tokio::sync::broadcast::Sender<()>, kill_send: tokio::sync::broadcast::Sender<()>,
kill_receive: tokio::sync::broadcast::Receiver<()>,
store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>, store: Arc<Mutex<HashMap<Snowflake, Arc<RwLock<ObservableObject>>>>>,
url: String, url: String,
} }
@ -70,6 +76,7 @@ impl Gateway {
websocket_send: shared_websocket_send.clone(), websocket_send: shared_websocket_send.clone(),
websocket_receive, websocket_receive,
kill_send: kill_send.clone(), kill_send: kill_send.clone(),
kill_receive: kill_send.subscribe(),
store: store.clone(), store: store.clone(),
url: websocket_url.clone(), url: websocket_url.clone(),
}; };
@ -94,14 +101,21 @@ impl Gateway {
} }
/// The main gateway listener task; /// The main gateway listener task;
///
/// Can only be stopped by closing the websocket, cannot be made to listen for kill
pub async fn gateway_listen_task(&mut self) { pub async fn gateway_listen_task(&mut self) {
loop { loop {
let msg = self.websocket_receive.next().await; let msg;
tokio::select! {
Ok(_) = self.kill_receive.recv() => {
log::trace!("GW: Closing listener task");
break;
}
message = self.websocket_receive.next() => {
msg = message;
}
}
// PRETTYFYME: Remove inline conditional compiling // PRETTYFYME: Remove inline conditional compiling
// This if chain can be much better but if let is unstable on stable rust
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
if let Some(Ok(message)) = msg { if let Some(Ok(message)) = msg {
self.handle_message(message.into()).await; self.handle_message(message.into()).await;
@ -335,10 +349,42 @@ impl Gateway {
.unwrap(); .unwrap();
} }
GATEWAY_RECONNECT => { GATEWAY_RECONNECT => {
todo!() trace!("GW: Received Reconnect");
let reconnect = GatewayReconnect {};
self.events
.lock()
.await
.session
.reconnect
.notify(reconnect)
.await;
} }
GATEWAY_INVALID_SESSION => { GATEWAY_INVALID_SESSION => {
todo!() trace!("GW: Received Invalid Session");
let mut resumable: bool = false;
if let Some(raw_value) = gateway_payload.event_data {
if let Ok(deserialized) = serde_json::from_str(raw_value.get()) {
resumable = deserialized;
} else {
warn!("Failed to parse part of INVALID_SESSION ('{}' as bool), assuming non-resumable", raw_value.get());
}
} else {
warn!("Failed to parse part of INVALID_SESSION ('d' missing), assuming non-resumable");
}
let invalid_session = GatewayInvalidSession { resumable };
self.events
.lock()
.await
.session
.invalid
.notify(invalid_session)
.await;
} }
// Starts our heartbeat // Starts our heartbeat
// We should have already handled this in gateway init // We should have already handled this in gateway init
@ -394,165 +440,3 @@ impl Gateway {
} }
} }
} }
pub mod event {
use super::*;
#[derive(Default, Debug)]
pub struct Events {
pub application: Application,
pub auto_moderation: AutoModeration,
pub session: Session,
pub message: Message,
pub user: User,
pub relationship: Relationship,
pub channel: Channel,
pub thread: Thread,
pub guild: Guild,
pub invite: Invite,
pub integration: Integration,
pub interaction: Interaction,
pub stage_instance: StageInstance,
pub call: Call,
pub voice: Voice,
pub webhooks: Webhooks,
pub gateway_identify_payload: GatewayEvent<types::GatewayIdentifyPayload>,
pub gateway_resume: GatewayEvent<types::GatewayResume>,
pub error: GatewayEvent<GatewayError>,
}
#[derive(Default, Debug)]
pub struct Application {
pub command_permissions_update: GatewayEvent<types::ApplicationCommandPermissionsUpdate>,
}
#[derive(Default, Debug)]
pub struct AutoModeration {
pub rule_create: GatewayEvent<types::AutoModerationRuleCreate>,
pub rule_update: GatewayEvent<types::AutoModerationRuleUpdate>,
pub rule_delete: GatewayEvent<types::AutoModerationRuleDelete>,
pub action_execution: GatewayEvent<types::AutoModerationActionExecution>,
}
#[derive(Default, Debug)]
pub struct Session {
pub ready: GatewayEvent<types::GatewayReady>,
pub ready_supplemental: GatewayEvent<types::GatewayReadySupplemental>,
pub replace: GatewayEvent<types::SessionsReplace>,
}
#[derive(Default, Debug)]
pub struct StageInstance {
pub create: GatewayEvent<types::StageInstanceCreate>,
pub update: GatewayEvent<types::StageInstanceUpdate>,
pub delete: GatewayEvent<types::StageInstanceDelete>,
}
#[derive(Default, Debug)]
pub struct Message {
pub create: GatewayEvent<types::MessageCreate>,
pub update: GatewayEvent<types::MessageUpdate>,
pub delete: GatewayEvent<types::MessageDelete>,
pub delete_bulk: GatewayEvent<types::MessageDeleteBulk>,
pub reaction_add: GatewayEvent<types::MessageReactionAdd>,
pub reaction_remove: GatewayEvent<types::MessageReactionRemove>,
pub reaction_remove_all: GatewayEvent<types::MessageReactionRemoveAll>,
pub reaction_remove_emoji: GatewayEvent<types::MessageReactionRemoveEmoji>,
pub ack: GatewayEvent<types::MessageACK>,
}
#[derive(Default, Debug)]
pub struct User {
pub update: GatewayEvent<types::UserUpdate>,
pub guild_settings_update: GatewayEvent<types::UserGuildSettingsUpdate>,
pub presence_update: GatewayEvent<types::PresenceUpdate>,
pub typing_start: GatewayEvent<types::TypingStartEvent>,
}
#[derive(Default, Debug)]
pub struct Relationship {
pub add: GatewayEvent<types::RelationshipAdd>,
pub remove: GatewayEvent<types::RelationshipRemove>,
}
#[derive(Default, Debug)]
pub struct Channel {
pub create: GatewayEvent<types::ChannelCreate>,
pub update: GatewayEvent<types::ChannelUpdate>,
pub unread_update: GatewayEvent<types::ChannelUnreadUpdate>,
pub delete: GatewayEvent<types::ChannelDelete>,
pub pins_update: GatewayEvent<types::ChannelPinsUpdate>,
}
#[derive(Default, Debug)]
pub struct Thread {
pub create: GatewayEvent<types::ThreadCreate>,
pub update: GatewayEvent<types::ThreadUpdate>,
pub delete: GatewayEvent<types::ThreadDelete>,
pub list_sync: GatewayEvent<types::ThreadListSync>,
pub member_update: GatewayEvent<types::ThreadMemberUpdate>,
pub members_update: GatewayEvent<types::ThreadMembersUpdate>,
}
#[derive(Default, Debug)]
pub struct Guild {
pub create: GatewayEvent<types::GuildCreate>,
pub update: GatewayEvent<types::GuildUpdate>,
pub delete: GatewayEvent<types::GuildDelete>,
pub audit_log_entry_create: GatewayEvent<types::GuildAuditLogEntryCreate>,
pub ban_add: GatewayEvent<types::GuildBanAdd>,
pub ban_remove: GatewayEvent<types::GuildBanRemove>,
pub emojis_update: GatewayEvent<types::GuildEmojisUpdate>,
pub stickers_update: GatewayEvent<types::GuildStickersUpdate>,
pub integrations_update: GatewayEvent<types::GuildIntegrationsUpdate>,
pub member_add: GatewayEvent<types::GuildMemberAdd>,
pub member_remove: GatewayEvent<types::GuildMemberRemove>,
pub member_update: GatewayEvent<types::GuildMemberUpdate>,
pub members_chunk: GatewayEvent<types::GuildMembersChunk>,
pub role_create: GatewayEvent<types::GuildRoleCreate>,
pub role_update: GatewayEvent<types::GuildRoleUpdate>,
pub role_delete: GatewayEvent<types::GuildRoleDelete>,
pub role_scheduled_event_create: GatewayEvent<types::GuildScheduledEventCreate>,
pub role_scheduled_event_update: GatewayEvent<types::GuildScheduledEventUpdate>,
pub role_scheduled_event_delete: GatewayEvent<types::GuildScheduledEventDelete>,
pub role_scheduled_event_user_add: GatewayEvent<types::GuildScheduledEventUserAdd>,
pub role_scheduled_event_user_remove: GatewayEvent<types::GuildScheduledEventUserRemove>,
pub passive_update_v1: GatewayEvent<types::PassiveUpdateV1>,
}
#[derive(Default, Debug)]
pub struct Invite {
pub create: GatewayEvent<types::InviteCreate>,
pub delete: GatewayEvent<types::InviteDelete>,
}
#[derive(Default, Debug)]
pub struct Integration {
pub create: GatewayEvent<types::IntegrationCreate>,
pub update: GatewayEvent<types::IntegrationUpdate>,
pub delete: GatewayEvent<types::IntegrationDelete>,
}
#[derive(Default, Debug)]
pub struct Interaction {
pub create: GatewayEvent<types::InteractionCreate>,
}
#[derive(Default, Debug)]
pub struct Call {
pub create: GatewayEvent<types::CallCreate>,
pub update: GatewayEvent<types::CallUpdate>,
pub delete: GatewayEvent<types::CallDelete>,
}
#[derive(Default, Debug)]
pub struct Voice {
pub state_update: GatewayEvent<types::VoiceStateUpdate>,
pub server_update: GatewayEvent<types::VoiceServerUpdate>,
}
#[derive(Default, Debug)]
pub struct Webhooks {
pub update: GatewayEvent<types::WebhooksUpdate>,
}
}

View File

@ -1,9 +1,13 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use futures_util::SinkExt; use futures_util::SinkExt;
use log::*; use log::*;
use std::fmt::Debug; use std::fmt::Debug;
use super::{event::Events, *}; use super::{events::Events, *};
use crate::types::{self, Composite}; use crate::types::{self, Composite};
/// Represents a handle to a Gateway connection. A Gateway connection will create observable /// Represents a handle to a Gateway connection. A Gateway connection will create observable
@ -40,10 +44,19 @@ impl GatewayHandle {
.unwrap(); .unwrap();
} }
/// Recursively observes a [`Shared`] object, by making sure all [`Composite `] fields within
/// that object and its children are being watched.
///
/// Observing means, that if new information arrives about the observed object or its children,
/// the object automatically gets updated, without you needing to request new information about
/// the object in question from the API, which is expensive and can lead to rate limiting.
///
/// The [`Shared`] object returned by this method points to a different object than the one
/// being supplied as a &self function argument.
pub async fn observe<T: Updateable + Clone + Debug + Composite<T>>( pub async fn observe<T: Updateable + Clone + Debug + Composite<T>>(
&self, &self,
object: Arc<RwLock<T>>, object: Shared<T>,
) -> Arc<RwLock<T>> { ) -> Shared<T> {
let mut store = self.store.lock().await; let mut store = self.store.lock().await;
let id = object.read().unwrap().id(); let id = object.read().unwrap().id();
if let Some(channel) = store.get(&id) { if let Some(channel) = store.get(&id) {
@ -84,7 +97,7 @@ impl GatewayHandle {
/// with all of its observable fields being observed. /// with all of its observable fields being observed.
pub async fn observe_and_into_inner<T: Updateable + Clone + Debug + Composite<T>>( pub async fn observe_and_into_inner<T: Updateable + Clone + Debug + Composite<T>>(
&self, &self,
object: Arc<RwLock<T>>, object: Shared<T>,
) -> T { ) -> T {
let channel = self.observe(object.clone()).await; let channel = self.observe(object.clone()).await;
let object = channel.read().unwrap().clone(); let object = channel.read().unwrap().clone();
@ -160,7 +173,7 @@ impl GatewayHandle {
/// Closes the websocket connection and stops all gateway tasks; /// Closes the websocket connection and stops all gateway tasks;
/// ///
/// Esentially pulls the plug on the gateway, leaving it possible to resume; /// Essentially pulls the plug on the gateway, leaving it possible to resume;
pub async fn close(&self) { pub async fn close(&self) {
self.kill_send.send(()).unwrap(); self.kill_send.send(()).unwrap();
self.websocket_send.lock().await.close().await.unwrap(); self.websocket_send.lock().await.close().await.unwrap();

View File

@ -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::SinkExt; use futures_util::SinkExt;
use log::*; use log::*;
@ -22,7 +26,7 @@ use super::*;
use crate::types; use crate::types;
/// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms /// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms
const HEARTBEAT_ACK_TIMEOUT: u64 = 2000; pub const HEARTBEAT_ACK_TIMEOUT: u64 = 2000;
/// Handles sending heartbeats to the gateway in another thread /// Handles sending heartbeats to the gateway in another thread
#[allow(dead_code)] // FIXME: Remove this, once HeartbeatHandler is used #[allow(dead_code)] // FIXME: Remove this, once HeartbeatHandler is used
@ -73,11 +77,6 @@ impl HeartbeatHandler {
let mut last_seq_number: Option<u64> = None; let mut last_seq_number: Option<u64> = None;
loop { loop {
if kill_receive.try_recv().is_ok() {
trace!("GW: Closing heartbeat task");
break;
}
let timeout = if last_heartbeat_acknowledged { let timeout = if last_heartbeat_acknowledged {
heartbeat_interval heartbeat_interval
} else { } else {
@ -111,6 +110,10 @@ impl HeartbeatHandler {
} }
} }
} }
Ok(_) = kill_receive.recv() => {
log::trace!("GW: Closing heartbeat task");
break;
}
} }
if should_send { if should_send {
@ -128,7 +131,7 @@ impl HeartbeatHandler {
let send_result = websocket_tx.lock().await.send(msg.into()).await; let send_result = websocket_tx.lock().await.send(msg.into()).await;
if send_result.is_err() { if send_result.is_err() {
// We couldn't send, the websocket is broken // We couldn't send, the websocket is broken
warn!("GW: Couldnt send heartbeat, websocket seems broken"); warn!("GW: Couldn't send heartbeat, websocket seems broken");
break; break;
} }

View File

@ -1,15 +1,19 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use crate::types; use crate::types;
use super::*; use super::*;
/// Represents a messsage received from the gateway. This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError]. /// Represents a message received from the gateway. This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError].
/// This struct is used internally when handling messages. /// This struct is used internally when handling messages.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct GatewayMessage(pub String); pub struct GatewayMessage(pub String);
impl GatewayMessage { impl GatewayMessage {
/// Parses the message as an error; /// Parses the message as an error;
/// Returns the error if succesfully parsed, None if the message isn't an error /// Returns the error if successfully parsed, None if the message isn't an error
pub fn error(&self) -> Option<GatewayError> { pub fn error(&self) -> Option<GatewayError> {
// Some error strings have dots on the end, which we don't care about // Some error strings have dots on the end, which we don't care about
let processed_content = self.0.to_lowercase().replace('.', ""); let processed_content = self.0.to_lowercase().replace('.', "");

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use async_trait::async_trait; use async_trait::async_trait;
pub mod backends; pub mod backends;
@ -94,6 +98,12 @@ pub struct GatewayEvent<T: WebSocketEvent> {
} }
impl<T: WebSocketEvent> GatewayEvent<T> { impl<T: WebSocketEvent> GatewayEvent<T> {
pub fn new() -> Self {
Self {
observers: Vec::new(),
}
}
/// Returns true if the GatewayEvent is observed by at least one Observer. /// Returns true if the GatewayEvent is observed by at least one Observer.
pub fn is_observed(&self) -> bool { pub fn is_observed(&self) -> bool {
!self.observers.is_empty() !self.observers.is_empty()
@ -116,9 +126,17 @@ impl<T: WebSocketEvent> GatewayEvent<T> {
} }
/// Notifies the observers of the GatewayEvent. /// Notifies the observers of the GatewayEvent.
async fn notify(&self, new_event_data: T) { pub(crate) async fn notify(&self, new_event_data: T) {
for observer in &self.observers { for observer in &self.observers {
observer.update(&new_event_data).await; observer.update(&new_event_data).await;
} }
} }
} }
/// A type alias for [`Arc<RwLock<T>>`], used to make the public facing API concerned with
/// Composite structs more ergonomic.
/// ## Note
///
/// While `T` does not have to implement `Composite` to be used with `Shared`,
/// the primary use of `Shared` is with types that implement `Composite`.
pub type Shared<T> = Arc<RwLock<T>>;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//! Instance and ChorusUser objects. //! Instance and ChorusUser objects.
use std::collections::HashMap; use std::collections::HashMap;
@ -9,7 +13,7 @@ use reqwest::Client;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::errors::ChorusResult; use crate::errors::ChorusResult;
use crate::gateway::{Gateway, GatewayHandle}; use crate::gateway::{Gateway, GatewayHandle, Shared};
use crate::ratelimiter::ChorusRequest; use crate::ratelimiter::ChorusRequest;
use crate::types::types::subconfigs::limits::rates::RateLimits; use crate::types::types::subconfigs::limits::rates::RateLimits;
use crate::types::{ use crate::types::{
@ -19,6 +23,7 @@ use crate::UrlBundle;
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
/// The [`Instance`]; what you will be using to perform all sorts of actions on the Spacebar server. /// The [`Instance`]; what you will be using to perform all sorts of actions on the Spacebar server.
///
/// If `limits_information` is `None`, then the instance will not be rate limited. /// If `limits_information` is `None`, then the instance will not be rate limited.
pub struct Instance { pub struct Instance {
pub urls: UrlBundle, pub urls: UrlBundle,
@ -36,8 +41,6 @@ impl PartialEq for Instance {
} }
} }
impl Eq for Instance {}
impl std::hash::Hash for Instance { impl std::hash::Hash for Instance {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.urls.hash(state); self.urls.hash(state);
@ -72,8 +75,17 @@ impl PartialEq for LimitsInformation {
} }
impl Instance { impl Instance {
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle). To create an Instance from one singular url, use [`Instance::from_root_url()`]. pub(crate) fn clone_limits_if_some(&self) -> Option<HashMap<LimitType, Limit>> {
async fn from_url_bundle(urls: UrlBundle) -> ChorusResult<Instance> { if self.limits_information.is_some() {
return Some(self.limits_information.as_ref().unwrap().ratelimits.clone());
}
None
}
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle).
///
/// To create an Instance from one singular url, use [`Instance::new()`].
pub async fn from_url_bundle(urls: UrlBundle) -> ChorusResult<Instance> {
let is_limited: Option<LimitsConfiguration> = Instance::is_limited(&urls.api).await?; let is_limited: Option<LimitsConfiguration> = Instance::is_limited(&urls.api).await?;
let limit_information; let limit_information;
@ -103,17 +115,9 @@ impl Instance {
Ok(instance) Ok(instance)
} }
pub(crate) fn clone_limits_if_some(&self) -> Option<HashMap<LimitType, Limit>> {
if self.limits_information.is_some() {
return Some(self.limits_information.as_ref().unwrap().ratelimits.clone());
}
None
}
/// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url. /// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url.
/// Shorthand for `Instance::new(UrlBundle::from_root_domain(root_domain).await?)`.
/// ///
/// If `limited` is `true`, then Chorus will track and enforce rate limits for this instance. /// Shorthand for `Instance::from_url_bundle(UrlBundle::from_root_domain(root_domain).await?)`.
pub async fn new(root_url: &str) -> ChorusResult<Instance> { pub async fn new(root_url: &str) -> ChorusResult<Instance> {
let urls = UrlBundle::from_root_url(root_url).await?; let urls = UrlBundle::from_root_url(root_url).await?;
Instance::from_url_bundle(urls).await Instance::from_url_bundle(urls).await
@ -153,11 +157,11 @@ impl fmt::Display for Token {
/// It is used for most authenticated actions on a Spacebar server. /// It is used for most authenticated actions on a Spacebar server.
/// It also has its own [Gateway] connection. /// It also has its own [Gateway] connection.
pub struct ChorusUser { pub struct ChorusUser {
pub belongs_to: Arc<RwLock<Instance>>, pub belongs_to: Shared<Instance>,
pub token: String, pub token: String,
pub limits: Option<HashMap<LimitType, Limit>>, pub limits: Option<HashMap<LimitType, Limit>>,
pub settings: Arc<RwLock<UserSettings>>, pub settings: Shared<UserSettings>,
pub object: Arc<RwLock<User>>, pub object: Shared<User>,
pub gateway: GatewayHandle, pub gateway: GatewayHandle,
} }
@ -169,8 +173,6 @@ impl PartialEq for ChorusUser {
} }
} }
impl Eq for ChorusUser {}
impl ChorusUser { impl ChorusUser {
pub fn token(&self) -> String { pub fn token(&self) -> String {
self.token.clone() self.token.clone()
@ -183,14 +185,14 @@ impl ChorusUser {
/// Creates a new [ChorusUser] from existing data. /// Creates a new [ChorusUser] from existing data.
/// ///
/// # Notes /// # Notes
/// This isn't the prefered way to create a ChorusUser. /// This isn't the preferred way to create a ChorusUser.
/// See [Instance::login_account] and [Instance::register_account] instead. /// See [Instance::login_account] and [Instance::register_account] instead.
pub fn new( pub fn new(
belongs_to: Arc<RwLock<Instance>>, belongs_to: Shared<Instance>,
token: String, token: String,
limits: Option<HashMap<LimitType, Limit>>, limits: Option<HashMap<LimitType, Limit>>,
settings: Arc<RwLock<UserSettings>>, settings: Shared<UserSettings>,
object: Arc<RwLock<User>>, object: Shared<User>,
gateway: GatewayHandle, gateway: GatewayHandle,
) -> ChorusUser { ) -> ChorusUser {
ChorusUser { ChorusUser {
@ -208,7 +210,7 @@ impl ChorusUser {
/// registering or logging in to the Instance, where you do not yet have a User object, but still /// registering or logging in to the Instance, where you do not yet have a User object, but still
/// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify /// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify
/// first. /// first.
pub(crate) async fn shell(instance: Arc<RwLock<Instance>>, token: String) -> ChorusUser { pub(crate) async fn shell(instance: Shared<Instance>, token: String) -> ChorusUser {
let settings = Arc::new(RwLock::new(UserSettings::default())); let settings = Arc::new(RwLock::new(UserSettings::default()));
let object = Arc::new(RwLock::new(User::default())); let object = Arc::new(RwLock::new(User::default()));
let wss_url = instance.read().unwrap().urls.wss.clone(); let wss_url = instance.read().unwrap().urls.wss.clone();

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
/*! /*!
Chorus combines all the required functionalities of a user-centric Spacebar library into one package. Chorus combines all the required functionalities of a user-centric Spacebar library into one package.
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
@ -44,7 +48,7 @@ let login_schema = LoginSchema {
password: "Correct-Horse-Battery-Staple".to_string(), password: "Correct-Horse-Battery-Staple".to_string(),
..Default::default() ..Default::default()
}; };
// Each user connects to the Gateway. The Gateway connection lives on a seperate thread. Depending on // Each user connects to the Gateway. The Gateway connection lives on a separate thread. Depending on
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads. // the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
let user = instance let user = instance
.login_account(login_schema) .login_account(login_schema)
@ -128,7 +132,10 @@ pub mod instance;
#[cfg(feature = "client")] #[cfg(feature = "client")]
pub mod ratelimiter; pub mod ratelimiter;
pub mod types; pub mod types;
#[cfg(feature = "client")] #[cfg(all(
feature = "client",
any(feature = "voice_udp", feature = "voice_gateway")
))]
pub mod voice; pub mod voice;
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//! Ratelimiter and request handling functionality. //! Ratelimiter and request handling functionality.
use std::collections::HashMap; use std::collections::HashMap;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{Map, Value}; use serde_json::{Map, Value};

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::defaults::{guild::GuildDefaults, user::UserDefaults}; use crate::types::config::types::subconfigs::defaults::{guild::GuildDefaults, user::UserDefaults};

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Eq, PartialEq, Hash, Clone, Debug)] #[derive(Deserialize, Serialize, Eq, PartialEq, Hash, Clone, Debug)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::email::{ use crate::types::config::types::subconfigs::email::{

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
use std::io::Write; use std::io::Write;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::kafka::KafkaBroker; use crate::types::config::types::subconfigs::kafka::KafkaBroker;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::limits::{ use crate::types::config::types::subconfigs::limits::{

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
pub mod api_configuration; pub mod api_configuration;
pub mod cdn_configuration; pub mod cdn_configuration;
pub mod defaults_configuration; pub mod defaults_configuration;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::region::Region; use crate::types::config::types::subconfigs::region::Region;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::register::{ use crate::types::config::types::subconfigs::register::{

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use base64::Engine; use base64::Engine;
use rand::Fill; use rand::Fill;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::ffi::OsString; use std::ffi::OsString;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{ExplicitContentFilterLevel, MessageNotificationLevel}; use crate::types::{ExplicitContentFilterLevel, MessageNotificationLevel};

View File

@ -1,2 +1,6 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
pub mod guild; pub mod guild;
pub mod user; pub mod user;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
pub mod mailgun; pub mod mailgun;
pub mod mailjet; pub mod mailjet;
pub mod sendgrid; pub mod sendgrid;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake; use crate::types::utils::Snowflake;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,2 +1,6 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
pub mod autojoin; pub mod autojoin;
pub mod discovery; pub mod discovery;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
pub mod channel; pub mod channel;
pub mod global; pub mod global;
pub mod guild; pub mod guild;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::limits::ratelimits::RateLimitOptions; use crate::types::config::types::subconfigs::limits::ratelimits::RateLimitOptions;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub mod auth; pub mod auth;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::limits::ratelimits::{ use crate::types::config::types::subconfigs::limits::ratelimits::{

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
pub mod client; pub mod client;
pub mod defaults; pub mod defaults;
pub mod email; pub mod email;

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -1,3 +1,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -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 use date_of_birth::DateOfBirthConfiguration; pub use date_of_birth::DateOfBirthConfiguration;
pub use email::RegistrationEmailConfiguration; pub use email::RegistrationEmailConfiguration;
pub use password::PasswordConfiguration; pub use password::PasswordConfiguration;

Some files were not shown because too many files have changed in this diff Show More