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

View File

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

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"
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]]
name = "ahash"
version = "0.8.7"
@ -199,14 +209,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chorus"
version = "0.14.0"
version = "0.15.0"
dependencies = [
"async-trait",
"base64 0.21.7",
"bitflags 2.4.1",
"chorus-macros",
"chrono",
"crypto_secretbox",
"custom_error",
"discortp",
"futures-util",
"getrandom",
"hostname",
@ -264,6 +276,17 @@ dependencies = [
"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]]
name = "console_error_panic_hook"
version = "0.1.7"
@ -342,9 +365,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"rand_core",
"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]]
name = "custom_error"
version = "1.9.2"
@ -425,6 +464,16 @@ dependencies = [
"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]]
name = "dotenvy"
version = "0.15.7"
@ -643,6 +692,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
"zeroize",
]
[[package]]
@ -666,9 +716,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "h2"
version = "0.3.23"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
dependencies = [
"bytes",
"fnv",
@ -923,6 +973,15 @@ dependencies = [
"serde",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]]
name = "ipnet"
version = "2.9.0"
@ -1085,9 +1144,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"wasi",
@ -1123,6 +1182,12 @@ dependencies = [
"libc",
]
[[package]]
name = "no-std-net"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65"
[[package]]
name = "nom"
version = "7.1.3"
@ -1217,6 +1282,12 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.62"
@ -1363,6 +1434,36 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "poem"
version = "1.3.59"
@ -1406,6 +1507,17 @@ dependencies = [
"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]]
name = "powerfmt"
version = "0.2.0"
@ -1688,6 +1800,15 @@ version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "salsa20"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
dependencies = [
"cipher",
]
[[package]]
name = "schannel"
version = "0.1.23"
@ -2526,6 +2647,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "untrusted"
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"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]]
name = "wasm-bindgen"
version = "0.2.89"
@ -2705,9 +2842,13 @@ dependencies = [
[[package]]
name = "whoami"
version = "1.4.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50"
checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e"
dependencies = [
"redox_syscall",
"wasite",
]
[[package]]
name = "wildmatch"

View File

@ -1,13 +1,14 @@
[package]
name = "chorus"
description = "A library for interacting with multiple Spacebar-compatible Instances at once."
version = "0.14.0"
license = "AGPL-3.0"
version = "0.15.0"
license = "MPL-2.0"
edition = "2021"
repository = "https://github.com/polyphony-chat/chorus"
readme = "README.md"
keywords = ["spacebar", "discord", "polyphony"]
website = ["https://discord.com/invite/m3FpcapGDD"]
rust-version = "1.67.1"
[features]
@ -16,6 +17,9 @@ backend = ["dep:poem", "dep:sqlx"]
rt-multi-thread = ["tokio/rt-multi-thread"]
rt = ["tokio/rt"]
client = []
voice = ["voice_udp", "voice_gateway"]
voice_udp = ["dep:discortp", "dep:crypto_secretbox"]
voice_gateway = []
[dependencies]
tokio = { version = "1.35.1", features = ["macros", "sync"] }
@ -24,10 +28,7 @@ serde_json = { version = "1.0.111", features = ["raw_value"] }
serde-aux = "4.3.1"
serde_with = "3.4.0"
serde_repr = "0.1.18"
reqwest = { features = [
"multipart",
"json",
], version = "0.11.23" }
reqwest = { features = ["multipart", "json"], version = "0.11.23" }
url = "2.5.0"
chrono = { version = "0.4.31", features = ["serde"] }
regex = "1.10.2"
@ -52,6 +53,8 @@ sqlx = { version = "0.7.3", features = [
"runtime-tokio-native-tls",
"any",
], 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"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
@ -63,6 +66,7 @@ tokio-tungstenite = { version = "0.20.1", features = [
] }
native-tls = "0.2.11"
hostname = "0.3.1"
getrandom = { version = "0.2.12" }
[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2.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
[dependencies]
chorus = "0.14.0"
chorus = "0.15.0"
```
### Establishing a Connection
@ -53,16 +53,10 @@ To connect to a Spacebar compatible server, you need to create an [`Instance`](h
```rs
use chorus::instance::Instance;
use chorus::UrlBundle;
#[tokio::main]
async fn main() {
let bundle = UrlBundle::new(
"https://example.com/api".to_string(),
"wss://example.com/".to_string(),
"https://example.com/cdn".to_string(),
);
let instance = Instance::new(bundle)
let instance = Instance::new("https://example.com")
.await
.expect("Failed to connect to the Spacebar server");
// You can create as many instances of `Instance` as you want, but each `Instance` should likely be unique.
@ -87,7 +81,7 @@ let login_schema = LoginSchema {
password: "Correct-Horse-Battery-Staple".to_string(),
..Default::default()
};
// Each user connects to the Gateway. The Gateway connection lives on a seperate thread. Depending on
// Each user connects to the Gateway. The Gateway connection lives on a separate thread. Depending on
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
let user = instance
.login_account(login_schema)
@ -125,7 +119,7 @@ like "proxy connection checking" are already disabled on this version, which oth
### wasm
To test for wasm, you will need to `cargo install wasm-pack`. You can then run
`wasm-pack test --<chrome/firefox/safari> --headless -- --target wasm32-unknown-unknown --features="rt, client" --no-default-features`
`wasm-pack test --<chrome/firefox/safari> --headless -- --target wasm32-unknown-unknown --features="rt, client, voice_gateway" --no-default-features`
to run the tests for wasm.
## Versioning
@ -134,11 +128,7 @@ This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read
## Contributing
Chorus is currently missing voice support and a lot of API endpoints, many of which should be trivial to implement,
ever since [we streamlined the process of doing so](https://github.com/polyphony-chat/chorus/discussions/401).
If you'd like to contribute new functionality, check out [The 'Meta'-issues.](https://github.com/polyphony-chat/chorus/issues?q=is%3Aissue+label%3A%22Type%3A+Meta%22+) They contain a comprehensive list of all features which are yet missing for full Discord.com compatibility.
Please feel free to open an Issue with the idea you have, or a Pull Request. Please keep our [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) in mind. Your contribution might not be accepted if it violates these guidelines or [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md).
See [CONTRIBUTING.md](./CONTRIBUTING.md).
<details>
<summary>Progress Tracker/Roadmap</summary>

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 quote::quote;
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 chorus::gateway::Gateway;
use chorus::{
@ -32,11 +45,10 @@ impl Observer<GatewayReady> for ExampleObserver {
#[tokio::main(flavor = "current_thread")]
async fn main() {
// Find the gateway websocket url of the server we want to connect to
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
let gateway_websocket_url = GATEWAY_URL.to_string();
// Initiate the gateway connection
let gateway = Gateway::spawn(websocket_url_spacebar).await.unwrap();
let gateway = Gateway::spawn(gateway_websocket_url).await.unwrap();
// Create an instance of our observer
let observer = ExampleObserver {};
@ -55,7 +67,7 @@ async fn main() {
.subscribe(shared_observer);
// Authenticate so we will receive any events
let token = "SecretToken".to_string();
let token = TOKEN.to_string();
let mut identify = GatewayIdentifyPayload::common();
identify.token = token;
gateway.send_identify(identify).await;

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 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
#[tokio::main(flavor = "current_thread")]
async fn main() {
// Find the gateway websocket url of the server we want to connect to
let websocket_url_spacebar = "wss://gateway.old.server.spacebar.chat/".to_string();
let gateway_websocket_url = GATEWAY_URL.to_string();
// Initiate the gateway connection, starting a listener in one thread and a heartbeat handler in another
let gateway = Gateway::spawn(websocket_url_spacebar).await.unwrap();
let gateway = Gateway::spawn(gateway_websocket_url).await.unwrap();
// At this point, we are connected to the server and are sending heartbeats, however we still haven't authenticated
// Get a token for an account on the server
let token = "SecretToken".to_string();
let token = TOKEN.to_string();
// Create an identify event
// An Identify event is how the server authenticates us and gets info about our os and browser, along with our intents / capabilities
@ -31,8 +44,8 @@ async fn main() {
identify.token = token;
// Send off the event
gateway.send_identify(identify).await;
gateway.send_identify(identify).await;
// Do something on the main thread so we don't quit
loop {
sleep(Duration::from_secs(3600)).await;

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

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 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};
#[allow(unused_imports)]
pub use login::*;
#[allow(unused_imports)]
pub use register::*;
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 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 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::HeaderMap;
use reqwest::{multipart, Client};
@ -36,7 +40,7 @@ impl Message {
chorus_request.deserialize_response::<Message>(user).await
} else {
for (index, attachment) in message.attachments.iter_mut().enumerate() {
attachment.get_mut(index).unwrap().set_id(index as i16);
attachment.get_mut(index).unwrap().id = Some(index as i16);
}
let mut form = reqwest::multipart::Form::new();
let payload_json = to_string(&message).unwrap();
@ -45,8 +49,8 @@ impl Message {
form = form.part("payload_json", payload_field);
for (index, attachment) in message.attachments.unwrap().into_iter().enumerate() {
let (attachment_content, current_attachment) = attachment.move_content();
let (attachment_filename, _) = current_attachment.move_filename();
let attachment_content = attachment.content;
let attachment_filename = attachment.filename;
let part_name = format!("files[{}]", index);
let content_disposition = format!(
"form-data; name=\"{}\"'; filename=\"{}\"",

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

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 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::instance::ChorusUser;
use crate::types::{Guild, Message, MessageSearchQuery, Snowflake};
@ -9,7 +13,7 @@ impl Guild {
/// permission to be present on the current user.
///
/// If the guild/channel you are searching is not yet indexed, the endpoint will return a 202 accepted response.
/// In this case, the method will return a [`ChorusError::InvalidResponse`] error.
/// In this case, the method will return a [`ChorusError::InvalidResponse`](crate::errors::ChorusError::InvalidResponse) error.
///
/// # Reference:
/// See <https://discord-userdoccers.vercel.app/resources/message#search-messages>

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 messages::*;
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 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 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.
#![allow(unused_imports)]
pub use channels::messages::*;
pub use guilds::*;
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 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 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;

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 serde_json::to_string;
@ -9,6 +13,26 @@ use crate::{
};
impl ChorusUser {
/// Fetches a list of private channels the user is in.
///
/// # Reference:
/// See <https://docs.discord.sex/resources/channel#get-private-channels>
pub async fn get_private_channels(&mut self) -> ChorusResult<Vec<Channel>> {
let url = format!(
"{}/users/@me/channels",
self.belongs_to.read().unwrap().urls.api
);
ChorusRequest {
request: Client::new()
.get(url)
.header("Authorization", self.token())
.header("Content-Type", "application/json"),
limit_type: LimitType::Global,
}
.deserialize_response::<Vec<Channel>>(self)
.await
}
/// Creates a DM channel or group DM channel.
///
/// One recipient creates or returns an existing DM channel,

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 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 guilds::*;
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 serde_json::to_string;
@ -15,7 +19,7 @@ impl ChorusUser {
/// Retrieves a list of mutual friends between the authenticated user and a given user.
///
/// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#get-users-peer-id-relationships>
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#get-userspeer_idrelationships>
pub async fn get_mutual_relationships(
&mut self,
user_id: Snowflake,
@ -37,7 +41,7 @@ impl ChorusUser {
/// Retrieves the user's relationships.
///
/// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#get-users-me-relationships>
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#get-usersmerelationships>
pub async fn get_relationships(&mut self) -> ChorusResult<Vec<types::Relationship>> {
let url = format!(
"{}/users/@me/relationships",
@ -55,7 +59,7 @@ impl ChorusUser {
/// Sends a friend request to a user.
///
/// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#post-users-me-relationships>
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#post-usersmerelationships>
pub async fn send_friend_request(
&mut self,
schema: FriendRequestSendSchema,
@ -132,7 +136,7 @@ impl ChorusUser {
/// Removes the relationship between the authenticated user and a given user.
///
/// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/relationships.html#delete-users-me-relationships-peer-id>
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/relationships.html#delete-usersmerelationshipspeer_id>
pub async fn remove_relationship(&mut self, user_id: Snowflake) -> ChorusResult<()> {
let url = format!(
"{}/users/@me/relationships/{}",

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 reqwest::Client;
@ -113,7 +117,7 @@ impl User {
/// Gets the user's settings.
///
/// # Reference
/// See <https://luna.gitlab.io/discord-unofficial-docs/user_settings.html#get-users-me-settings>
/// See <https://luna.gitlab.io/discord-unofficial-docs/docs/user_settings.html#get-usersmesettings>
pub async fn get_settings(
token: &String,
url_api: &String,

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.
use custom_error::custom_error;
@ -17,7 +21,7 @@ custom_error! {
/// Server did not respond.
NoResponse = "Did not receive a response from the Server.",
/// Reqwest returned an Error instead of a Response object.
RequestFailed{url:String, error: String} = "An error occured while trying to GET from {url}: {error}",
RequestFailed{url:String, error: String} = "An error occurred while trying to GET from {url}: {error}",
/// Response received, however, it was not of the successful responses type. Used when no other, special case applies.
ReceivedErrorCode{error_code: u16, error: String} = "Received the following error code while requesting from the route: {error_code}",
/// Used when there is likely something wrong with the instance, the request was directed to.
@ -63,7 +67,7 @@ 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?
///
@ -96,3 +100,59 @@ custom_error! {
}
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"))]
pub mod tungstenite;
#[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::{
stream::{SplitSink, SplitStream},
StreamExt,
@ -23,8 +27,16 @@ impl TungsteniteBackend {
websocket_url: &str,
) -> Result<(TungsteniteSink, TungsteniteStream), crate::errors::GatewayError> {
let mut roots = rustls::RootCertStore::empty();
for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs")
{
let certs = rustls_native_certs::load_native_certs();
if let Err(e) = certs {
log::error!("Failed to load platform native certs! {:?}", e);
return Err(GatewayError::CannotConnect {
error: format!("{:?}", e),
});
}
for cert in certs.unwrap() {
roots.add(&rustls::Certificate(cert.0)).unwrap();
}
let (websocket_stream, _) = match connect_async_tls_with_config(

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::{
stream::{SplitSink, SplitStream},
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 crate::types;
@ -42,6 +46,8 @@ pub struct Session {
pub ready: GatewayEvent<types::GatewayReady>,
pub ready_supplemental: GatewayEvent<types::GatewayReadySupplemental>,
pub replace: GatewayEvent<types::SessionsReplace>,
pub reconnect: GatewayEvent<types::GatewayReconnect>,
pub invalid: GatewayEvent<types::GatewayInvalidSession>,
}
#[derive(Default, Debug)]

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

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

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

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 super::*;
/// Represents a messsage received from the gateway. This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError].
/// Represents a message received from the gateway. This will be either a [types::GatewayReceivePayload], containing events, or a [GatewayError].
/// This struct is used internally when handling messages.
#[derive(Clone, Debug)]
pub struct GatewayMessage(pub String);
impl GatewayMessage {
/// Parses the message as an error;
/// Returns the error if succesfully parsed, None if the message isn't an error
/// Returns the error if successfully parsed, None if the message isn't an error
pub fn error(&self) -> Option<GatewayError> {
// Some error strings have dots on the end, which we don't care about
let processed_content = self.0.to_lowercase().replace('.', "");

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;
pub mod backends;
@ -94,6 +98,12 @@ pub struct GatewayEvent<T: WebSocketEvent> {
}
impl<T: WebSocketEvent> GatewayEvent<T> {
pub fn new() -> Self {
Self {
observers: Vec::new(),
}
}
/// Returns true if the GatewayEvent is observed by at least one Observer.
pub fn is_observed(&self) -> bool {
!self.observers.is_empty()
@ -116,9 +126,17 @@ impl<T: WebSocketEvent> GatewayEvent<T> {
}
/// Notifies the observers of the GatewayEvent.
async fn notify(&self, new_event_data: T) {
pub(crate) async fn notify(&self, new_event_data: T) {
for observer in &self.observers {
observer.update(&new_event_data).await;
}
}
}
/// A type alias for [`Arc<RwLock<T>>`], used to make the public facing API concerned with
/// Composite structs more ergonomic.
/// ## Note
///
/// While `T` does not have to implement `Composite` to be used with `Shared`,
/// the primary use of `Shared` is with types that implement `Composite`.
pub type Shared<T> = Arc<RwLock<T>>;

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

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.
The library handles various aspects on your behalf, such as rate limiting, authentication and maintaining
@ -44,7 +48,7 @@ let login_schema = LoginSchema {
password: "Correct-Horse-Battery-Staple".to_string(),
..Default::default()
};
// Each user connects to the Gateway. The Gateway connection lives on a seperate thread. Depending on
// Each user connects to the Gateway. The Gateway connection lives on a separate thread. Depending on
// the runtime feature you choose, this can potentially take advantage of all of your computers' threads.
let user = instance
.login_account(login_schema)
@ -128,7 +132,10 @@ pub mod instance;
#[cfg(feature = "client")]
pub mod ratelimiter;
pub mod types;
#[cfg(feature = "client")]
#[cfg(all(
feature = "client",
any(feature = "voice_udp", feature = "voice_gateway")
))]
pub mod voice;
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]

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.
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_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};
#[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};
#[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 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};
#[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 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};
#[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};
#[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 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};
#[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};
#[cfg(feature = "sqlx")]
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 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 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};
#[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};
#[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 cdn_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};
#[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};
#[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 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 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 rand::Fill;
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 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};
#[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 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 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};
#[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};
#[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};
#[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 mailjet;
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};
#[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};
#[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 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};
#[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 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};
#[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};
#[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};
#[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};
#[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};
#[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 global;
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 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};
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 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 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};
#[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 defaults;
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};
#[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};
#[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};
#[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 email::RegistrationEmailConfiguration;
pub use password::PasswordConfiguration;

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