Merge branch 'main' into perpetual/gateway-dev
This commit is contained in:
commit
7f726deb19
|
@ -1,4 +1,4 @@
|
||||||
name: Rust
|
name: Build and Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -10,7 +10,7 @@ env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_test:
|
rust:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
name: Clippy check
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
|
||||||
|
# Make sure CI fails on all warnings, including Clippy lints
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: "-Dwarnings"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
clippy_check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Run Clippy
|
||||||
|
run: cargo clippy --all-targets --all-features
|
|
@ -29,9 +29,6 @@ openssl = "0.10.52"
|
||||||
base64 = "0.21.2"
|
base64 = "0.21.2"
|
||||||
hostname = "0.3.1"
|
hostname = "0.3.1"
|
||||||
bitflags = { version = "2.2.1", features = ["serde"] }
|
bitflags = { version = "2.2.1", features = ["serde"] }
|
||||||
atomic = "0.5.3"
|
|
||||||
bigdecimal = "0.3.1"
|
|
||||||
num-bigint = "0.4.3"
|
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
poem = { version = "1.3.55", optional = true }
|
poem = { version = "1.3.55", optional = true }
|
||||||
sqlx = { git = "https://github.com/zert3x/sqlx", branch="feature/skip", features = ["mysql", "sqlite", "json", "chrono", "ipnetwork", "runtime-tokio-native-tls", "any"], optional = true }
|
sqlx = { git = "https://github.com/zert3x/sqlx", branch="feature/skip", features = ["mysql", "sqlite", "json", "chrono", "ipnetwork", "runtime-tokio-native-tls", "any"], optional = true }
|
||||||
|
|
29
README.md
29
README.md
|
@ -5,6 +5,7 @@
|
||||||
[![Contributors][contributors-shield]][contributors-url]
|
[![Contributors][contributors-shield]][contributors-url]
|
||||||
[![Forks][forks-shield]][forks-url]
|
[![Forks][forks-shield]][forks-url]
|
||||||
[![Issues][issues-shield]][issues-url]
|
[![Issues][issues-shield]][issues-url]
|
||||||
|
<img src="https://img.shields.io/static/v1?label=Status&message=Early%20Development&color=blue">
|
||||||
|
|
||||||
</br>
|
</br>
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
@ -30,7 +31,19 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Progress Tracker/Roadmap:
|
## About
|
||||||
|
|
||||||
|
Chorus is a Rust library that allows developers to interact with multiple Spacebar-compatible APIs and Gateways simultaneously. The library provides a simple and efficient way to communicate with these services, making it easier for developers to build applications that rely on them. Chorus is open-source and welcomes contributions from the community.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
If you would like to contribute, please feel free to open an Issue with the idea you have, or a
|
||||||
|
Pull Request. Please keep our [contribution guidelines](https://github.com/polyphony-chat/.github/blob/main/CONTRIBUTION_GUIDELINES.md) in mind. Your contribution might not be
|
||||||
|
accepted, if it violates these guidelines or [our Code of Conduct](https://github.com/polyphony-chat/.github/blob/main/CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Progress Tracker/Roadmap</summary>
|
||||||
|
|
||||||
### Core Functionality
|
### Core Functionality
|
||||||
- [x] Rate Limiter (hint: couldn't be fully tested due to [an Issue with the Spacebar Server](https://github.com/spacebarchat/server/issues/1022))
|
- [x] Rate Limiter (hint: couldn't be fully tested due to [an Issue with the Spacebar Server](https://github.com/spacebarchat/server/issues/1022))
|
||||||
- [x] [Login (the conventional way)](https://github.com/polyphony-chat/chorus/issues/1)
|
- [x] [Login (the conventional way)](https://github.com/polyphony-chat/chorus/issues/1)
|
||||||
|
@ -58,8 +71,8 @@
|
||||||
### User Management
|
### User Management
|
||||||
- [ ] [User profile customization](https://github.com/polyphony-chat/chorus/issues/41)
|
- [ ] [User profile customization](https://github.com/polyphony-chat/chorus/issues/41)
|
||||||
- [x] Gettings users and user profiles
|
- [x] Gettings users and user profiles
|
||||||
- [ ] [Friend requests](https://github.com/polyphony-chat/chorus/issues/92)
|
- [x] [Friend requests](https://github.com/polyphony-chat/chorus/issues/92)
|
||||||
- [ ] [Blocking users](https://github.com/polyphony-chat/chorus/issues/92)
|
- [x] [Blocking users](https://github.com/polyphony-chat/chorus/issues/92)
|
||||||
- [ ] User presence (online, offline, idle, etc.)
|
- [ ] User presence (online, offline, idle, etc.)
|
||||||
- [ ] User status (custom status, etc.)
|
- [ ] User status (custom status, etc.)
|
||||||
- [x] Account deletion
|
- [x] Account deletion
|
||||||
|
@ -93,9 +106,6 @@
|
||||||
- [x] Sending rich content in messages (links, images, videos)
|
- [x] Sending rich content in messages (links, images, videos)
|
||||||
- [ ] Customizing embed appearance (title, description, color, fields)
|
- [ ] Customizing embed appearance (title, description, color, fields)
|
||||||
|
|
||||||
### Notifications and Push Notifications
|
|
||||||
- [ ] Notification settings management
|
|
||||||
|
|
||||||
### Webhooks
|
### Webhooks
|
||||||
- [ ] Webhook creation and management
|
- [ ] Webhook creation and management
|
||||||
- [ ] Handling incoming webhook events
|
- [ ] Handling incoming webhook events
|
||||||
|
@ -107,8 +117,10 @@
|
||||||
|
|
||||||
[Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust
|
[Rust]: https://img.shields.io/badge/Rust-orange?style=plastic&logo=rust
|
||||||
[Rust-url]: https://www.rust-lang.org/
|
[Rust-url]: https://www.rust-lang.org/
|
||||||
[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/rust.yml?style=flat
|
[build-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/build_and_test.yml?style=flat
|
||||||
[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/rust.yml
|
[build-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/build_and_test.yml
|
||||||
|
[clippy-shield]: https://img.shields.io/github/actions/workflow/status/polyphony-chat/chorus/clippy.yml?style=flat
|
||||||
|
[clippy-url]: https://github.com/polyphony-chat/chorus/blob/main/.github/workflows/clippy.yml
|
||||||
[contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat
|
[contributors-shield]: https://img.shields.io/github/contributors/polyphony-chat/chorus.svg?style=flat
|
||||||
[contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors
|
[contributors-url]: https://github.com/polyphony-chat/chorus/graphs/contributors
|
||||||
[forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat
|
[forks-shield]: https://img.shields.io/github/forks/polyphony-chat/chorus.svg?style=flat
|
||||||
|
@ -121,3 +133,4 @@
|
||||||
[license-url]: https://github.com/polyphony-chat/chorus/blob/master/LICENSE
|
[license-url]: https://github.com/polyphony-chat/chorus/blob/master/LICENSE
|
||||||
[Discord]: https://dcbadge.vercel.app/api/server/m3FpcapGDD?style=flat
|
[Discord]: https://dcbadge.vercel.app/api/server/m3FpcapGDD?style=flat
|
||||||
[Discord-invite]: https://discord.com/invite/m3FpcapGDD
|
[Discord-invite]: https://discord.com/invite/m3FpcapGDD
|
||||||
|
</details>
|
|
@ -3,8 +3,8 @@ use chorus::{
|
||||||
gateway::{Gateway, Observer},
|
gateway::{Gateway, Observer},
|
||||||
types::{GatewayIdentifyPayload, GatewayReady},
|
types::{GatewayIdentifyPayload, GatewayReady},
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::{sync::Arc, time::Duration};
|
||||||
use tokio::{self, sync::Mutex};
|
use tokio::{self, time::sleep};
|
||||||
|
|
||||||
// This example creates a simple gateway connection and a basic observer struct
|
// This example creates a simple gateway connection and a basic observer struct
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ pub struct ExampleObserver {}
|
||||||
// One struct can be an observer of multiple websocketevents, if needed
|
// One struct can be an observer of multiple websocketevents, if needed
|
||||||
impl Observer<GatewayReady> for ExampleObserver {
|
impl Observer<GatewayReady> for ExampleObserver {
|
||||||
// After we subscribe to an event this function is called every time we receive it
|
// After we subscribe to an event this function is called every time we receive it
|
||||||
fn update(&mut self, _data: &GatewayReady) {
|
fn update(&self, _data: &GatewayReady) {
|
||||||
println!("Observed Ready!");
|
println!("Observed Ready!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,8 @@ async fn main() {
|
||||||
// Create an instance of our observer
|
// Create an instance of our observer
|
||||||
let observer = ExampleObserver {};
|
let observer = ExampleObserver {};
|
||||||
|
|
||||||
// Because observers have to reside in between the main and gateway thread, (they have to be shared between both) we need to put them in an Arc<Mutex>
|
// Share ownership of the observer with the gateway
|
||||||
let shared_observer = Arc::new(Mutex::new(observer));
|
let shared_observer = Arc::new(observer);
|
||||||
|
|
||||||
// Subscribe our observer to the Ready event on this gateway
|
// Subscribe our observer to the Ready event on this gateway
|
||||||
// From now on observer.update(data) will be called every time we receive the Ready event
|
// From now on observer.update(data) will be called every time we receive the Ready event
|
||||||
|
@ -44,8 +44,7 @@ async fn main() {
|
||||||
.await
|
.await
|
||||||
.session
|
.session
|
||||||
.ready
|
.ready
|
||||||
.subscribe(shared_observer)
|
.subscribe(shared_observer);
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Authenticate so we will receive any events
|
// Authenticate so we will receive any events
|
||||||
let token = "SecretToken".to_string();
|
let token = "SecretToken".to_string();
|
||||||
|
@ -54,5 +53,7 @@ async fn main() {
|
||||||
gateway.send_identify(identify).await;
|
gateway.send_identify(identify).await;
|
||||||
|
|
||||||
// Do something on the main thread so we don't quit
|
// Do something on the main thread so we don't quit
|
||||||
loop {}
|
loop {
|
||||||
|
sleep(Duration::MAX).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use chorus::{self, gateway::Gateway, types::GatewayIdentifyPayload};
|
use chorus::{self, gateway::Gateway, types::GatewayIdentifyPayload};
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
/// This example creates a simple gateway connection and a session with an Identify event
|
/// This example creates a simple gateway connection and a session with an Identify event
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -26,5 +29,7 @@ async fn main() {
|
||||||
gateway.send_identify(identify).await;
|
gateway.send_identify(identify).await;
|
||||||
|
|
||||||
// Do something on the main thread so we don't quit
|
// Do something on the main thread so we don't quit
|
||||||
loop {}
|
loop {
|
||||||
|
sleep(Duration::MAX).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ impl Instance {
|
||||||
) -> Result<UserMeta, ChorusLibError> {
|
) -> Result<UserMeta, ChorusLibError> {
|
||||||
let json_schema = json!(login_schema);
|
let json_schema = json!(login_schema);
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let endpoint_url = self.urls.get_api().to_string() + "/auth/login";
|
let endpoint_url = self.urls.api.clone() + "/auth/login";
|
||||||
let request_builder = client.post(endpoint_url).body(json_schema.to_string());
|
let request_builder = client.post(endpoint_url).body(json_schema.to_string());
|
||||||
// We do not have a user yet, and the UserRateLimits will not be affected by a login
|
// We do not have a user yet, and the UserRateLimits will not be affected by a login
|
||||||
// request (since login is an instance wide limit), which is why we are just cloning the
|
// request (since login is an instance wide limit), which is why we are just cloning the
|
||||||
|
|
|
@ -25,7 +25,7 @@ impl Instance {
|
||||||
) -> Result<UserMeta, ChorusLibError> {
|
) -> Result<UserMeta, ChorusLibError> {
|
||||||
let json_schema = json!(register_schema);
|
let json_schema = json!(register_schema);
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let endpoint_url = self.urls.get_api().to_string() + "/auth/register";
|
let endpoint_url = self.urls.api.clone() + "/auth/register";
|
||||||
let request_builder = client.post(endpoint_url).body(json_schema.to_string());
|
let request_builder = client.post(endpoint_url).body(json_schema.to_string());
|
||||||
// We do not have a user yet, and the UserRateLimits will not be affected by a login
|
// We do not have a user yet, and the UserRateLimits will not be affected by a login
|
||||||
// request (since register is an instance wide limit), which is why we are just cloning
|
// request (since register is an instance wide limit), which is why we are just cloning
|
||||||
|
@ -59,11 +59,10 @@ impl Instance {
|
||||||
return Err(ChorusLibError::InvalidFormBodyError { error_type, error });
|
return Err(ChorusLibError::InvalidFormBodyError { error_type, error });
|
||||||
}
|
}
|
||||||
let user_object = self.get_user(token.clone(), None).await.unwrap();
|
let user_object = self.get_user(token.clone(), None).await.unwrap();
|
||||||
let settings =
|
let settings = UserMeta::get_settings(&token, &self.urls.api, &mut self.limits)
|
||||||
UserMeta::get_settings(&token, &self.urls.get_api().to_string(), &mut self.limits)
|
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let user: UserMeta = UserMeta::new(
|
let user = UserMeta::new(
|
||||||
Rc::new(RefCell::new(self.clone())),
|
Rc::new(RefCell::new(self.clone())),
|
||||||
token.clone(),
|
token.clone(),
|
||||||
cloned_limits,
|
cloned_limits,
|
||||||
|
|
|
@ -10,9 +10,7 @@ use crate::{
|
||||||
|
|
||||||
impl Channel {
|
impl Channel {
|
||||||
pub async fn get(user: &mut UserMeta, channel_id: &str) -> Result<Channel, ChorusLibError> {
|
pub async fn get(user: &mut UserMeta, channel_id: &str) -> Result<Channel, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow_mut();
|
let url = user.belongs_to.borrow_mut().urls.api.clone();
|
||||||
let url = belongs_to.urls.get_api().to_string();
|
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.get(format!("{}/channels/{}/", url, channel_id))
|
.get(format!("{}/channels/{}/", url, channel_id))
|
||||||
.bearer_auth(user.token());
|
.bearer_auth(user.token());
|
||||||
|
@ -44,24 +42,19 @@ impl Channel {
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// An `Option` that contains an `ChorusLibError` if an error occurred during the request, or `None` if the request was successful.
|
/// A `Result` that contains a `ChorusLibError` if an error occurred during the request, or `()` if the request was successful.
|
||||||
pub async fn delete(self, user: &mut UserMeta) -> Option<ChorusLibError> {
|
pub async fn delete(self, user: &mut UserMeta) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow_mut();
|
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.delete(format!(
|
.delete(format!(
|
||||||
"{}/channels/{}/",
|
"{}/channels/{}/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow_mut().urls.api,
|
||||||
self.id.to_string()
|
self.id
|
||||||
))
|
))
|
||||||
.bearer_auth(user.token());
|
.bearer_auth(user.token());
|
||||||
drop(belongs_to);
|
|
||||||
let response =
|
let response =
|
||||||
common::handle_request(request, user, crate::api::limits::LimitType::Channel).await;
|
common::handle_request_as_result(request, user, crate::api::limits::LimitType::Channel)
|
||||||
if response.is_err() {
|
.await;
|
||||||
return Some(response.err().unwrap());
|
response
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modifies a channel.
|
/// Modifies a channel.
|
||||||
|
@ -83,16 +76,14 @@ impl Channel {
|
||||||
channel_id: &str,
|
channel_id: &str,
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
) -> Result<Channel, ChorusLibError> {
|
) -> Result<Channel, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.patch(format!(
|
.patch(format!(
|
||||||
"{}/channels/{}/",
|
"{}/channels/{}/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
channel_id
|
channel_id
|
||||||
))
|
))
|
||||||
.bearer_auth(user.token())
|
.bearer_auth(user.token())
|
||||||
.body(to_string(&modify_data).unwrap());
|
.body(to_string(&modify_data).unwrap());
|
||||||
drop(belongs_to);
|
|
||||||
let channel = common::deserialize_response::<Channel>(
|
let channel = common::deserialize_response::<Channel>(
|
||||||
request,
|
request,
|
||||||
user,
|
user,
|
||||||
|
|
|
@ -25,9 +25,7 @@ impl Message {
|
||||||
message: &mut MessageSendSchema,
|
message: &mut MessageSendSchema,
|
||||||
files: Option<Vec<PartialDiscordFileAttachment>>,
|
files: Option<Vec<PartialDiscordFileAttachment>>,
|
||||||
) -> Result<Message, crate::errors::ChorusLibError> {
|
) -> Result<Message, crate::errors::ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
let url_api = user.belongs_to.borrow().urls.api.clone();
|
||||||
let url_api = belongs_to.urls.get_api().to_string();
|
|
||||||
drop(belongs_to);
|
|
||||||
|
|
||||||
if files.is_none() {
|
if files.is_none() {
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
|
|
|
@ -2,7 +2,7 @@ use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::handle_request,
|
api::{handle_request, handle_request_as_result},
|
||||||
errors::ChorusLibError,
|
errors::ChorusLibError,
|
||||||
instance::UserMeta,
|
instance::UserMeta,
|
||||||
types::{self, PermissionOverwrite},
|
types::{self, PermissionOverwrite},
|
||||||
|
@ -19,35 +19,30 @@ impl types::Channel {
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// This function returns [`None`] if the request is successful, otherwise it returns a [`ChorusLibError`] instance.
|
/// This function returns a result that is either [`Ok(())`] if the request is successful, or an [`Err(ChorusLibError)`].
|
||||||
pub async fn edit_permissions(
|
pub async fn edit_permissions(
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
channel_id: &str,
|
channel_id: &str,
|
||||||
overwrite: PermissionOverwrite,
|
overwrite: PermissionOverwrite,
|
||||||
) -> Option<ChorusLibError> {
|
) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow_mut();
|
let url = {
|
||||||
let url = format!(
|
format!(
|
||||||
"{}/channels/{}/permissions/{}",
|
"{}/channels/{}/permissions/{}",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow_mut().urls.api,
|
||||||
channel_id,
|
channel_id,
|
||||||
overwrite.id
|
overwrite.id
|
||||||
);
|
)
|
||||||
drop(belongs_to);
|
};
|
||||||
let body = match to_string(&overwrite) {
|
let body = match to_string(&overwrite) {
|
||||||
Ok(string) => string,
|
Ok(string) => string,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Some(ChorusLibError::FormCreationError {
|
return Err(ChorusLibError::FormCreationError {
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let request = Client::new().put(url).bearer_auth(user.token()).body(body);
|
let request = Client::new().put(url).bearer_auth(user.token()).body(body);
|
||||||
match handle_request(request, user, crate::api::limits::LimitType::Channel).await {
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await
|
||||||
Ok(_) => None,
|
|
||||||
Err(e) => Some(ChorusLibError::InvalidResponseError {
|
|
||||||
error: e.to_string(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes a permission overwrite for a channel.
|
/// Deletes a permission overwrite for a channel.
|
||||||
|
@ -60,26 +55,19 @@ impl types::Channel {
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// This function returns [`None`] if the request is successful, otherwise it returns a [`ChorusLibError`] instance.
|
/// This function returns a Result that is either [`Ok(())`] if the request is successfulm or an [`Err(ChorusLibError)`].
|
||||||
pub async fn delete_permission(
|
pub async fn delete_permission(
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
channel_id: &str,
|
channel_id: &str,
|
||||||
overwrite_id: &str,
|
overwrite_id: &str,
|
||||||
) -> Option<ChorusLibError> {
|
) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow_mut();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/permissions/{}",
|
"{}/channels/{}/permissions/{}",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow_mut().urls.api,
|
||||||
channel_id,
|
channel_id,
|
||||||
overwrite_id
|
overwrite_id
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().delete(url).bearer_auth(user.token());
|
let request = Client::new().delete(url).bearer_auth(user.token());
|
||||||
match handle_request(request, user, crate::api::limits::LimitType::Channel).await {
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await
|
||||||
Ok(_) => None,
|
|
||||||
Err(e) => Some(ChorusLibError::InvalidResponseError {
|
|
||||||
error: e.to_string(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{handle_request, handle_request_as_option},
|
api::{handle_request, handle_request_as_result},
|
||||||
errors::ChorusLibError,
|
errors::ChorusLibError,
|
||||||
instance::UserMeta,
|
instance::UserMeta,
|
||||||
types,
|
types,
|
||||||
|
@ -24,28 +24,21 @@ impl ReactionMeta {
|
||||||
* `user` - A mutable reference to a [`UserMeta`] instance.
|
* `user` - A mutable reference to a [`UserMeta`] instance.
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
An `Option` [`crate::errors::ChorusLibError`] if something went wrong.
|
A `Result` [`()`] [`crate::errors::ChorusLibError`] if something went wrong.
|
||||||
Fires a `Message Reaction Remove All` Gateway event.
|
Fires a `Message Reaction Remove All` Gateway event.
|
||||||
|
|
||||||
# Reference
|
# Reference
|
||||||
See [https://discord.com/developers/docs/resources/channel#delete-all-reactions](https://discord.com/developers/docs/resources/channel#delete-all-reactions)
|
See [https://discord.com/developers/docs/resources/channel#delete-all-reactions](https://discord.com/developers/docs/resources/channel#delete-all-reactions)
|
||||||
*/
|
*/
|
||||||
pub async fn delete_all(&self, user: &mut UserMeta) -> Option<ChorusLibError> {
|
pub async fn delete_all(&self, user: &mut UserMeta) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/",
|
"{}/channels/{}/messages/{}/reactions/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id
|
self.message_id
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().delete(url).bearer_auth(user.token());
|
let request = Client::new().delete(url).bearer_auth(user.token());
|
||||||
match handle_request(request, user, crate::api::limits::LimitType::Channel).await {
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await
|
||||||
Ok(_) => None,
|
|
||||||
Err(e) => Some(ChorusLibError::InvalidResponseError {
|
|
||||||
error: e.to_string(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,28 +51,21 @@ impl ReactionMeta {
|
||||||
* `user` - A mutable reference to a [`UserMeta`] instance.
|
* `user` - A mutable reference to a [`UserMeta`] instance.
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
A [`crate::errors::ChorusLibError`] if something went wrong.
|
A Result that is [`Err(crate::errors::ChorusLibError)`] if something went wrong.
|
||||||
|
|
||||||
# Reference
|
# Reference
|
||||||
See [https://discord.com/developers/docs/resources/channel#get-reactions](https://discord.com/developers/docs/resources/channel#get-reactions)
|
See [https://discord.com/developers/docs/resources/channel#get-reactions](https://discord.com/developers/docs/resources/channel#get-reactions)
|
||||||
*/
|
*/
|
||||||
pub async fn get(&self, emoji: &str, user: &mut UserMeta) -> Option<ChorusLibError> {
|
pub async fn get(&self, emoji: &str, user: &mut UserMeta) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/",
|
"{}/channels/{}/messages/{}/reactions/{}/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id,
|
self.message_id,
|
||||||
emoji
|
emoji
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().get(url).bearer_auth(user.token());
|
let request = Client::new().get(url).bearer_auth(user.token());
|
||||||
match handle_request(request, user, crate::api::limits::LimitType::Channel).await {
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await
|
||||||
Ok(_) => None,
|
|
||||||
Err(e) => Some(ChorusLibError::InvalidResponseError {
|
|
||||||
error: e.to_string(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,29 +79,26 @@ impl ReactionMeta {
|
||||||
* `user` - A mutable reference to a [`UserMeta`] instance.
|
* `user` - A mutable reference to a [`UserMeta`] instance.
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
A [`crate::errors::ChorusLibError`] if something went wrong.
|
A Result that is [`Err(crate::errors::ChorusLibError)`] if something went wrong.
|
||||||
Fires a `Message Reaction Remove Emoji` Gateway event.
|
Fires a `Message Reaction Remove Emoji` Gateway event.
|
||||||
|
|
||||||
# Reference
|
# Reference
|
||||||
See [https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji](https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji)
|
See [https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji](https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji)
|
||||||
*/
|
*/
|
||||||
pub async fn delete_emoji(&self, emoji: &str, user: &mut UserMeta) -> Option<ChorusLibError> {
|
pub async fn delete_emoji(
|
||||||
let belongs_to = user.belongs_to.borrow();
|
&self,
|
||||||
|
emoji: &str,
|
||||||
|
user: &mut UserMeta,
|
||||||
|
) -> Result<(), ChorusLibError> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/",
|
"{}/channels/{}/messages/{}/reactions/{}/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id,
|
self.message_id,
|
||||||
emoji
|
emoji
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().delete(url).bearer_auth(user.token());
|
let request = Client::new().delete(url).bearer_auth(user.token());
|
||||||
match handle_request(request, user, crate::api::limits::LimitType::Channel).await {
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await
|
||||||
Ok(_) => None,
|
|
||||||
Err(e) => Some(ChorusLibError::InvalidResponseError {
|
|
||||||
error: e.to_string(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,25 +115,21 @@ impl ReactionMeta {
|
||||||
* `user` - A mutable reference to a [`UserMeta`] instance.
|
* `user` - A mutable reference to a [`UserMeta`] instance.
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
A `Result` containing a [`reqwest::Response`] or a [`crate::errors::ChorusLibError`].
|
A `Result` containing [`()`] or a [`crate::errors::ChorusLibError`].
|
||||||
Returns a 204 empty response on success.
|
|
||||||
Fires a Message Reaction Add Gateway event.
|
|
||||||
|
|
||||||
# Reference
|
# Reference
|
||||||
See [https://discord.com/developers/docs/resources/channel#create-reaction](https://discord.com/developers/docs/resources/channel#create-reaction)
|
See [https://discord.com/developers/docs/resources/channel#create-reaction](https://discord.com/developers/docs/resources/channel#create-reaction)
|
||||||
*/
|
*/
|
||||||
pub async fn create(&self, emoji: &str, user: &mut UserMeta) -> Option<ChorusLibError> {
|
pub async fn create(&self, emoji: &str, user: &mut UserMeta) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/@me/",
|
"{}/channels/{}/messages/{}/reactions/{}/@me/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id,
|
self.message_id,
|
||||||
emoji
|
emoji
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().put(url).bearer_auth(user.token());
|
let request = Client::new().put(url).bearer_auth(user.token());
|
||||||
handle_request_as_option(request, user, crate::api::limits::LimitType::Channel).await
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,25 +142,22 @@ impl ReactionMeta {
|
||||||
* `user` - A mutable reference to a [`UserMeta`] instance.
|
* `user` - A mutable reference to a [`UserMeta`] instance.
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
A `Result` containing a [`reqwest::Response`] or a [`crate::errors::ChorusLibError`].
|
A `Result` containing [`()`] or a [`crate::errors::ChorusLibError`].
|
||||||
Returns a 204 empty response on success.
|
|
||||||
Fires a `Message Reaction Remove` Gateway event.
|
Fires a `Message Reaction Remove` Gateway event.
|
||||||
|
|
||||||
# Reference
|
# Reference
|
||||||
See [https://discord.com/developers/docs/resources/channel#delete-own-reaction](https://discord.com/developers/docs/resources/channel#delete-own-reaction)
|
See [https://discord.com/developers/docs/resources/channel#delete-own-reaction](https://discord.com/developers/docs/resources/channel#delete-own-reaction)
|
||||||
*/
|
*/
|
||||||
pub async fn remove(&self, emoji: &str, user: &mut UserMeta) -> Option<ChorusLibError> {
|
pub async fn remove(&self, emoji: &str, user: &mut UserMeta) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/@me/",
|
"{}/channels/{}/messages/{}/reactions/{}/@me/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id,
|
self.message_id,
|
||||||
emoji
|
emoji
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().delete(url).bearer_auth(user.token());
|
let request = Client::new().delete(url).bearer_auth(user.token());
|
||||||
handle_request_as_option(request, user, crate::api::limits::LimitType::Channel).await
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -197,8 +173,7 @@ impl ReactionMeta {
|
||||||
* `user` - A mutable reference to a [`UserMeta`] instance.
|
* `user` - A mutable reference to a [`UserMeta`] instance.
|
||||||
|
|
||||||
# Returns
|
# Returns
|
||||||
A `Result` containing a [`reqwest::Response`] or a [`crate::errors::ChorusLibError`].
|
A `Result` containing [`()`] or a [`crate::errors::ChorusLibError`].
|
||||||
Returns a 204 empty response on success.
|
|
||||||
Fires a Message Reaction Remove Gateway event.
|
Fires a Message Reaction Remove Gateway event.
|
||||||
|
|
||||||
# Reference
|
# Reference
|
||||||
|
@ -209,18 +184,16 @@ impl ReactionMeta {
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
emoji: &str,
|
emoji: &str,
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
) -> Option<ChorusLibError> {
|
) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/channels/{}/messages/{}/reactions/{}/{}",
|
"{}/channels/{}/messages/{}/reactions/{}/{}",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
self.channel_id,
|
self.channel_id,
|
||||||
self.message_id,
|
self.message_id,
|
||||||
emoji,
|
emoji,
|
||||||
user_id
|
user_id
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().delete(url).bearer_auth(user.token());
|
let request = Client::new().delete(url).bearer_auth(user.token());
|
||||||
handle_request_as_option(request, user, crate::api::limits::LimitType::Channel).await
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Channel).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,30 +12,25 @@ pub async fn handle_request(
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
limit_type: LimitType,
|
limit_type: LimitType,
|
||||||
) -> Result<reqwest::Response, crate::errors::ChorusLibError> {
|
) -> Result<reqwest::Response, crate::errors::ChorusLibError> {
|
||||||
let mut belongs_to = user.belongs_to.borrow_mut();
|
LimitedRequester::send_request(
|
||||||
match LimitedRequester::send_request(
|
|
||||||
request,
|
request,
|
||||||
limit_type,
|
limit_type,
|
||||||
&mut belongs_to.limits,
|
&mut user.belongs_to.borrow_mut().limits,
|
||||||
&mut user.limits,
|
&mut user.limits,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
|
||||||
Ok(response) => return Ok(response),
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a request to wherever it needs to go. Returns [`None`] on success and
|
/// Sends a request to wherever it needs to go. Returns [`Ok(())`] on success and
|
||||||
/// [`Some(ChorusLibError)`] on failure.
|
/// [`Err(ChorusLibError)`] on failure.
|
||||||
pub async fn handle_request_as_option(
|
pub async fn handle_request_as_result(
|
||||||
request: RequestBuilder,
|
request: RequestBuilder,
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
limit_type: LimitType,
|
limit_type: LimitType,
|
||||||
) -> Option<ChorusLibError> {
|
) -> Result<(), ChorusLibError> {
|
||||||
match handle_request(request, user, limit_type).await {
|
match handle_request(request, user, limit_type).await {
|
||||||
Ok(_) => None,
|
Ok(_) => Ok(()),
|
||||||
Err(e) => Some(ChorusLibError::InvalidResponseError {
|
Err(e) => Err(ChorusLibError::InvalidResponseError {
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -53,7 +48,7 @@ pub async fn deserialize_response<T: for<'a> Deserialize<'a>>(
|
||||||
return Err(ChorusLibError::InvalidResponseError {
|
return Err(ChorusLibError::InvalidResponseError {
|
||||||
error: format!(
|
error: format!(
|
||||||
"Error while trying to process the HTTP response into a String: {}",
|
"Error while trying to process the HTTP response into a String: {}",
|
||||||
e.to_string()
|
e
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -64,7 +59,7 @@ pub async fn deserialize_response<T: for<'a> Deserialize<'a>>(
|
||||||
return Err(ChorusLibError::InvalidResponseError {
|
return Err(ChorusLibError::InvalidResponseError {
|
||||||
error: format!(
|
error: format!(
|
||||||
"Error while trying to deserialize the JSON response into T: {}",
|
"Error while trying to deserialize the JSON response into T: {}",
|
||||||
e.to_string()
|
e
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use serde_json::to_string;
|
||||||
|
|
||||||
use crate::api::deserialize_response;
|
use crate::api::deserialize_response;
|
||||||
use crate::api::handle_request;
|
use crate::api::handle_request;
|
||||||
use crate::api::handle_request_as_option;
|
use crate::api::handle_request_as_result;
|
||||||
use crate::api::limits::Limits;
|
use crate::api::limits::Limits;
|
||||||
use crate::errors::ChorusLibError;
|
use crate::errors::ChorusLibError;
|
||||||
use crate::instance::UserMeta;
|
use crate::instance::UserMeta;
|
||||||
|
@ -32,9 +32,7 @@ impl Guild {
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
guild_create_schema: GuildCreateSchema,
|
guild_create_schema: GuildCreateSchema,
|
||||||
) -> Result<Guild, ChorusLibError> {
|
) -> Result<Guild, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
let url = format!("{}/guilds/", user.belongs_to.borrow().urls.api);
|
||||||
let url = format!("{}/guilds/", belongs_to.urls.get_api());
|
|
||||||
drop(belongs_to);
|
|
||||||
let request = reqwest::Client::new()
|
let request = reqwest::Client::new()
|
||||||
.post(url.clone())
|
.post(url.clone())
|
||||||
.bearer_auth(user.token.clone())
|
.bearer_auth(user.token.clone())
|
||||||
|
@ -52,7 +50,7 @@ impl Guild {
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// An `Option` containing an `ChorusLibError` if an error occurred during the request, otherwise `None`.
|
/// An `Result` containing an `ChorusLibError` if an error occurred during the request, otherwise `()`.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -66,14 +64,16 @@ impl Guild {
|
||||||
/// None => println!("Guild deleted successfully"),
|
/// None => println!("Guild deleted successfully"),
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn delete(user: &mut UserMeta, guild_id: &str) -> Option<ChorusLibError> {
|
pub async fn delete(user: &mut UserMeta, guild_id: &str) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
let url = format!(
|
||||||
let url = format!("{}/guilds/{}/delete/", belongs_to.urls.get_api(), guild_id);
|
"{}/guilds/{}/delete/",
|
||||||
drop(belongs_to);
|
user.belongs_to.borrow().urls.api,
|
||||||
|
guild_id
|
||||||
|
);
|
||||||
let request = reqwest::Client::new()
|
let request = reqwest::Client::new()
|
||||||
.post(url.clone())
|
.post(url.clone())
|
||||||
.bearer_auth(user.token.clone());
|
.bearer_auth(user.token.clone());
|
||||||
handle_request_as_option(request, user, crate::api::limits::LimitType::Guild).await
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Guild).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a request to create a new channel in the guild.
|
/// Sends a request to create a new channel in the guild.
|
||||||
|
@ -97,7 +97,7 @@ impl Guild {
|
||||||
let mut belongs_to = user.belongs_to.borrow_mut();
|
let mut belongs_to = user.belongs_to.borrow_mut();
|
||||||
Channel::_create(
|
Channel::_create(
|
||||||
&user.token,
|
&user.token,
|
||||||
&format!("{}", belongs_to.urls.get_api()),
|
&format!("{}", belongs_to.urls.api),
|
||||||
&self.id.to_string(),
|
&self.id.to_string(),
|
||||||
schema,
|
schema,
|
||||||
&mut user.limits,
|
&mut user.limits,
|
||||||
|
@ -116,15 +116,13 @@ impl Guild {
|
||||||
/// * `limits_instance` - A mutable reference to a `Limits` struct containing the instance's rate limits.
|
/// * `limits_instance` - A mutable reference to a `Limits` struct containing the instance's rate limits.
|
||||||
///
|
///
|
||||||
pub async fn channels(&self, user: &mut UserMeta) -> Result<Vec<Channel>, ChorusLibError> {
|
pub async fn channels(&self, user: &mut UserMeta) -> Result<Vec<Channel>, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"{}/guilds/{}/channels/",
|
"{}/guilds/{}/channels/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
self.id.to_string()
|
self.id
|
||||||
))
|
))
|
||||||
.bearer_auth(user.token());
|
.bearer_auth(user.token());
|
||||||
drop(belongs_to);
|
|
||||||
let result = handle_request(request, user, crate::api::limits::LimitType::Channel)
|
let result = handle_request(request, user, crate::api::limits::LimitType::Channel)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -159,7 +157,7 @@ impl Guild {
|
||||||
pub async fn get(user: &mut UserMeta, guild_id: &str) -> Result<Guild, ChorusLibError> {
|
pub async fn get(user: &mut UserMeta, guild_id: &str) -> Result<Guild, ChorusLibError> {
|
||||||
let mut belongs_to = user.belongs_to.borrow_mut();
|
let mut belongs_to = user.belongs_to.borrow_mut();
|
||||||
Guild::_get(
|
Guild::_get(
|
||||||
&format!("{}", belongs_to.urls.get_api()),
|
&format!("{}", belongs_to.urls.api),
|
||||||
guild_id,
|
guild_id,
|
||||||
&user.token,
|
&user.token,
|
||||||
&mut user.limits,
|
&mut user.limits,
|
||||||
|
@ -219,7 +217,7 @@ impl Channel {
|
||||||
let mut belongs_to = user.belongs_to.borrow_mut();
|
let mut belongs_to = user.belongs_to.borrow_mut();
|
||||||
Channel::_create(
|
Channel::_create(
|
||||||
&user.token,
|
&user.token,
|
||||||
&format!("{}", belongs_to.urls.get_api()),
|
&format!("{}", belongs_to.urls.api),
|
||||||
guild_id,
|
guild_id,
|
||||||
schema,
|
schema,
|
||||||
&mut user.limits,
|
&mut user.limits,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{deserialize_response, handle_request_as_option},
|
api::{deserialize_response, handle_request_as_result},
|
||||||
errors::ChorusLibError,
|
errors::ChorusLibError,
|
||||||
instance::UserMeta,
|
instance::UserMeta,
|
||||||
types,
|
types,
|
||||||
|
@ -24,14 +24,12 @@ impl types::GuildMember {
|
||||||
guild_id: &str,
|
guild_id: &str,
|
||||||
member_id: &str,
|
member_id: &str,
|
||||||
) -> Result<types::GuildMember, ChorusLibError> {
|
) -> Result<types::GuildMember, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/members/{}/",
|
"{}/guilds/{}/members/{}/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
member_id
|
member_id
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().get(url).bearer_auth(user.token());
|
let request = Client::new().get(url).bearer_auth(user.token());
|
||||||
deserialize_response::<types::GuildMember>(
|
deserialize_response::<types::GuildMember>(
|
||||||
request,
|
request,
|
||||||
|
@ -52,24 +50,22 @@ impl types::GuildMember {
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// An `Option` containing a `ChorusLibError` if the request fails, or `None` if the request succeeds.
|
/// An `Result` containing a `ChorusLibError` if the request fails, or `()` if the request succeeds.
|
||||||
pub async fn add_role(
|
pub async fn add_role(
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
guild_id: &str,
|
guild_id: &str,
|
||||||
member_id: &str,
|
member_id: &str,
|
||||||
role_id: &str,
|
role_id: &str,
|
||||||
) -> Option<ChorusLibError> {
|
) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/members/{}/roles/{}/",
|
"{}/guilds/{}/members/{}/roles/{}/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
member_id,
|
member_id,
|
||||||
role_id
|
role_id
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().put(url).bearer_auth(user.token());
|
let request = Client::new().put(url).bearer_auth(user.token());
|
||||||
handle_request_as_option(request, user, crate::api::limits::LimitType::Guild).await
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Guild).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a role from a guild member.
|
/// Removes a role from a guild member.
|
||||||
|
@ -83,23 +79,21 @@ impl types::GuildMember {
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// An `Option` containing a `ChorusLibError` if the request fails, or `None` if the request succeeds.
|
/// A `Result` containing a `ChorusLibError` if the request fails, or `()` if the request succeeds.
|
||||||
pub async fn remove_role(
|
pub async fn remove_role(
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
guild_id: &str,
|
guild_id: &str,
|
||||||
member_id: &str,
|
member_id: &str,
|
||||||
role_id: &str,
|
role_id: &str,
|
||||||
) -> Option<crate::errors::ChorusLibError> {
|
) -> Result<(), crate::errors::ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/members/{}/roles/{}/",
|
"{}/guilds/{}/members/{}/roles/{}/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
member_id,
|
member_id,
|
||||||
role_id
|
role_id
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().delete(url).bearer_auth(user.token());
|
let request = Client::new().delete(url).bearer_auth(user.token());
|
||||||
handle_request_as_option(request, user, crate::api::limits::LimitType::Guild).await
|
handle_request_as_result(request, user, crate::api::limits::LimitType::Guild).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,11 @@ impl types::RoleObject {
|
||||||
user: &mut UserMeta,
|
user: &mut UserMeta,
|
||||||
guild_id: &str,
|
guild_id: &str,
|
||||||
) -> Result<Option<Vec<RoleObject>>, ChorusLibError> {
|
) -> Result<Option<Vec<RoleObject>>, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
let url = format!(
|
||||||
let url = format!("{}/guilds/{}/roles/", belongs_to.urls.get_api(), guild_id);
|
"{}/guilds/{}/roles/",
|
||||||
drop(belongs_to);
|
user.belongs_to.borrow().urls.api,
|
||||||
|
guild_id
|
||||||
|
);
|
||||||
let request = Client::new().get(url).bearer_auth(user.token());
|
let request = Client::new().get(url).bearer_auth(user.token());
|
||||||
let roles = deserialize_response::<Vec<RoleObject>>(
|
let roles = deserialize_response::<Vec<RoleObject>>(
|
||||||
request,
|
request,
|
||||||
|
@ -64,14 +66,12 @@ impl types::RoleObject {
|
||||||
guild_id: &str,
|
guild_id: &str,
|
||||||
role_id: &str,
|
role_id: &str,
|
||||||
) -> Result<RoleObject, ChorusLibError> {
|
) -> Result<RoleObject, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/roles/{}/",
|
"{}/guilds/{}/roles/{}/",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
role_id
|
role_id
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().get(url).bearer_auth(user.token());
|
let request = Client::new().get(url).bearer_auth(user.token());
|
||||||
deserialize_response(request, user, crate::api::limits::LimitType::Guild).await
|
deserialize_response(request, user, crate::api::limits::LimitType::Guild).await
|
||||||
}
|
}
|
||||||
|
@ -96,17 +96,16 @@ impl types::RoleObject {
|
||||||
guild_id: &str,
|
guild_id: &str,
|
||||||
role_create_schema: RoleCreateModifySchema,
|
role_create_schema: RoleCreateModifySchema,
|
||||||
) -> Result<RoleObject, ChorusLibError> {
|
) -> Result<RoleObject, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
let url = format!(
|
||||||
let url = format!("{}/guilds/{}/roles/", belongs_to.urls.get_api(), guild_id);
|
"{}/guilds/{}/roles/",
|
||||||
drop(belongs_to);
|
user.belongs_to.borrow().urls.api,
|
||||||
let body = match to_string::<RoleCreateModifySchema>(&role_create_schema) {
|
guild_id
|
||||||
Ok(string) => string,
|
);
|
||||||
Err(e) => {
|
let body = to_string::<RoleCreateModifySchema>(&role_create_schema).map_err(|e| {
|
||||||
return Err(ChorusLibError::FormCreationError {
|
ChorusLibError::FormCreationError {
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
})?;
|
||||||
let request = Client::new().post(url).bearer_auth(user.token()).body(body);
|
let request = Client::new().post(url).bearer_auth(user.token()).body(body);
|
||||||
deserialize_response(request, user, crate::api::limits::LimitType::Guild).await
|
deserialize_response(request, user, crate::api::limits::LimitType::Guild).await
|
||||||
}
|
}
|
||||||
|
@ -131,17 +130,16 @@ impl types::RoleObject {
|
||||||
guild_id: &str,
|
guild_id: &str,
|
||||||
role_position_update_schema: types::RolePositionUpdateSchema,
|
role_position_update_schema: types::RolePositionUpdateSchema,
|
||||||
) -> Result<RoleObject, ChorusLibError> {
|
) -> Result<RoleObject, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
let url = format!(
|
||||||
let url = format!("{}/guilds/{}/roles/", belongs_to.urls.get_api(), guild_id);
|
"{}/guilds/{}/roles/",
|
||||||
let body = match to_string(&role_position_update_schema) {
|
user.belongs_to.borrow().urls.api,
|
||||||
Ok(body) => body,
|
guild_id
|
||||||
Err(e) => {
|
);
|
||||||
return Err(ChorusLibError::FormCreationError {
|
let body = to_string(&role_position_update_schema).map_err(|e| {
|
||||||
|
ChorusLibError::FormCreationError {
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
})?;
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.patch(url)
|
.patch(url)
|
||||||
.bearer_auth(user.token())
|
.bearer_auth(user.token())
|
||||||
|
@ -172,22 +170,17 @@ impl types::RoleObject {
|
||||||
role_id: &str,
|
role_id: &str,
|
||||||
role_create_schema: RoleCreateModifySchema,
|
role_create_schema: RoleCreateModifySchema,
|
||||||
) -> Result<RoleObject, ChorusLibError> {
|
) -> Result<RoleObject, ChorusLibError> {
|
||||||
let belongs_to = user.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/guilds/{}/roles/{}",
|
"{}/guilds/{}/roles/{}",
|
||||||
belongs_to.urls.get_api(),
|
user.belongs_to.borrow().urls.api,
|
||||||
guild_id,
|
guild_id,
|
||||||
role_id
|
role_id
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
let body = to_string::<RoleCreateModifySchema>(&role_create_schema).map_err(|e| {
|
||||||
let body = match to_string::<RoleCreateModifySchema>(&role_create_schema) {
|
ChorusLibError::FormCreationError {
|
||||||
Ok(string) => string,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(ChorusLibError::FormCreationError {
|
|
||||||
error: e.to_string(),
|
error: e.to_string(),
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
})?;
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.patch(url)
|
.patch(url)
|
||||||
.bearer_auth(user.token())
|
.bearer_auth(user.token())
|
||||||
|
|
|
@ -15,7 +15,7 @@ impl Instance {
|
||||||
&self,
|
&self,
|
||||||
) -> Result<GeneralConfiguration, ChorusLibError> {
|
) -> Result<GeneralConfiguration, ChorusLibError> {
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let endpoint_url = self.urls.get_api().to_string() + "/policies/instance/";
|
let endpoint_url = self.urls.api.clone() + "/policies/instance/";
|
||||||
let request = match client.get(&endpoint_url).send().await {
|
let request = match client.get(&endpoint_url).send().await {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -33,7 +33,6 @@ impl Instance {
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = request.text().await.unwrap();
|
let body = request.text().await.unwrap();
|
||||||
let instance_policies_schema: GeneralConfiguration = from_str(&body).unwrap();
|
Ok(from_str::<GeneralConfiguration>(&body).unwrap())
|
||||||
Ok(instance_policies_schema)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,31 +195,31 @@ pub mod limits {
|
||||||
|
|
||||||
pub fn get_limit_ref(&self, limit_type: &LimitType) -> &Limit {
|
pub fn get_limit_ref(&self, limit_type: &LimitType) -> &Limit {
|
||||||
match limit_type {
|
match limit_type {
|
||||||
&LimitType::AbsoluteMessage => self.limit_absolute_messages,
|
LimitType::AbsoluteMessage => self.limit_absolute_messages,
|
||||||
&LimitType::AbsoluteRegister => self.limit_absolute_register,
|
LimitType::AbsoluteRegister => self.limit_absolute_register,
|
||||||
&LimitType::AuthLogin => self.limit_auth_login,
|
LimitType::AuthLogin => self.limit_auth_login,
|
||||||
&LimitType::AuthRegister => self.limit_auth_register,
|
LimitType::AuthRegister => self.limit_auth_register,
|
||||||
&LimitType::Channel => self.limit_channel,
|
LimitType::Channel => self.limit_channel,
|
||||||
&LimitType::Error => self.limit_error,
|
LimitType::Error => self.limit_error,
|
||||||
&LimitType::Global => self.limit_global,
|
LimitType::Global => self.limit_global,
|
||||||
&LimitType::Guild => self.limit_guild,
|
LimitType::Guild => self.limit_guild,
|
||||||
&LimitType::Ip => self.limit_ip,
|
LimitType::Ip => self.limit_ip,
|
||||||
&LimitType::Webhook => self.limit_webhook,
|
LimitType::Webhook => self.limit_webhook,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_limit_mut_ref(&mut self, limit_type: &LimitType) -> &mut Limit {
|
pub fn get_limit_mut_ref(&mut self, limit_type: &LimitType) -> &mut Limit {
|
||||||
match limit_type {
|
match limit_type {
|
||||||
&LimitType::AbsoluteMessage => self.limit_absolute_messages,
|
LimitType::AbsoluteMessage => self.limit_absolute_messages,
|
||||||
&LimitType::AbsoluteRegister => self.limit_absolute_register,
|
LimitType::AbsoluteRegister => self.limit_absolute_register,
|
||||||
&LimitType::AuthLogin => self.limit_auth_login,
|
LimitType::AuthLogin => self.limit_auth_login,
|
||||||
&LimitType::AuthRegister => self.limit_auth_register,
|
LimitType::AuthRegister => self.limit_auth_register,
|
||||||
&LimitType::Channel => self.limit_channel,
|
LimitType::Channel => self.limit_channel,
|
||||||
&LimitType::Error => self.limit_error,
|
LimitType::Error => self.limit_error,
|
||||||
&LimitType::Global => self.limit_global,
|
LimitType::Global => self.limit_global,
|
||||||
&LimitType::Guild => self.limit_guild,
|
LimitType::Guild => self.limit_guild,
|
||||||
&LimitType::Ip => self.limit_ip,
|
LimitType::Ip => self.limit_ip,
|
||||||
&LimitType::Webhook => self.limit_webhook,
|
LimitType::Webhook => self.limit_webhook,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,31 +256,31 @@ pub mod limits {
|
||||||
|
|
||||||
pub fn get_limit_ref(&self, limit_type: &LimitType) -> &Limit {
|
pub fn get_limit_ref(&self, limit_type: &LimitType) -> &Limit {
|
||||||
match limit_type {
|
match limit_type {
|
||||||
&LimitType::AbsoluteMessage => &self.limit_absolute_messages,
|
LimitType::AbsoluteMessage => &self.limit_absolute_messages,
|
||||||
&LimitType::AbsoluteRegister => &self.limit_absolute_register,
|
LimitType::AbsoluteRegister => &self.limit_absolute_register,
|
||||||
&LimitType::AuthLogin => &self.limit_auth_login,
|
LimitType::AuthLogin => &self.limit_auth_login,
|
||||||
&LimitType::AuthRegister => &self.limit_auth_register,
|
LimitType::AuthRegister => &self.limit_auth_register,
|
||||||
&LimitType::Channel => &self.limit_channel,
|
LimitType::Channel => &self.limit_channel,
|
||||||
&LimitType::Error => &self.limit_error,
|
LimitType::Error => &self.limit_error,
|
||||||
&LimitType::Global => &self.limit_global,
|
LimitType::Global => &self.limit_global,
|
||||||
&LimitType::Guild => &self.limit_guild,
|
LimitType::Guild => &self.limit_guild,
|
||||||
&LimitType::Ip => &self.limit_ip,
|
LimitType::Ip => &self.limit_ip,
|
||||||
&LimitType::Webhook => &self.limit_webhook,
|
LimitType::Webhook => &self.limit_webhook,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_limit_mut_ref(&mut self, limit_type: &LimitType) -> &mut Limit {
|
pub fn get_limit_mut_ref(&mut self, limit_type: &LimitType) -> &mut Limit {
|
||||||
match limit_type {
|
match limit_type {
|
||||||
&LimitType::AbsoluteMessage => &mut self.limit_absolute_messages,
|
LimitType::AbsoluteMessage => &mut self.limit_absolute_messages,
|
||||||
&LimitType::AbsoluteRegister => &mut self.limit_absolute_register,
|
LimitType::AbsoluteRegister => &mut self.limit_absolute_register,
|
||||||
&LimitType::AuthLogin => &mut self.limit_auth_login,
|
LimitType::AuthLogin => &mut self.limit_auth_login,
|
||||||
&LimitType::AuthRegister => &mut self.limit_auth_register,
|
LimitType::AuthRegister => &mut self.limit_auth_register,
|
||||||
&LimitType::Channel => &mut self.limit_channel,
|
LimitType::Channel => &mut self.limit_channel,
|
||||||
&LimitType::Error => &mut self.limit_error,
|
LimitType::Error => &mut self.limit_error,
|
||||||
&LimitType::Global => &mut self.limit_global,
|
LimitType::Global => &mut self.limit_global,
|
||||||
&LimitType::Guild => &mut self.limit_guild,
|
LimitType::Guild => &mut self.limit_guild,
|
||||||
&LimitType::Ip => &mut self.limit_ip,
|
LimitType::Ip => &mut self.limit_ip,
|
||||||
&LimitType::Webhook => &mut self.limit_webhook,
|
LimitType::Webhook => &mut self.limit_webhook,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ pub mod limits {
|
||||||
/// TODO: Change this to return a Result and handle the errors properly.
|
/// TODO: Change this to return a Result and handle the errors properly.
|
||||||
pub async fn check_limits(api_url: String) -> Limits {
|
pub async fn check_limits(api_url: String) -> Limits {
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let url_parsed = crate::URLBundle::parse_url(api_url) + "/policies/instance/limits";
|
let url_parsed = crate::UrlBundle::parse_url(api_url) + "/policies/instance/limits";
|
||||||
let result = client
|
let result = client
|
||||||
.get(url_parsed)
|
.get(url_parsed)
|
||||||
.send()
|
.send()
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{api::deserialize_response, errors::ChorusLibError, instance::UserMeta, types};
|
use crate::{
|
||||||
|
api::{deserialize_response, handle_request_as_result},
|
||||||
|
errors::ChorusLibError,
|
||||||
|
instance::UserMeta,
|
||||||
|
types::{self, CreateUserRelationshipSchema, RelationshipType},
|
||||||
|
};
|
||||||
|
|
||||||
impl UserMeta {
|
impl UserMeta {
|
||||||
/// Retrieves the mutual relationships between the authenticated user and the specified user.
|
/// Retrieves the mutual relationships between the authenticated user and the specified user.
|
||||||
|
@ -10,24 +16,134 @@ impl UserMeta {
|
||||||
/// * `user_id` - A string slice that holds the ID of the user to retrieve the mutual relationships with.
|
/// * `user_id` - A string slice that holds the ID of the user to retrieve the mutual relationships with.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// This function returns a [`Option<Vec<Result<PublicUser, ChorusLibError>>>`].
|
/// This function returns a [`Result<Vec<PublicUser>, ChorusLibError>`].
|
||||||
pub async fn get_mutual_relationships(
|
pub async fn get_mutual_relationships(
|
||||||
&mut self,
|
&mut self,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
) -> Result<Option<Vec<types::PublicUser>>, ChorusLibError> {
|
) -> Result<Vec<types::PublicUser>, ChorusLibError> {
|
||||||
let belongs_to = self.belongs_to.borrow();
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}/users/{}/relationships/",
|
"{}/users/{}/relationships/",
|
||||||
belongs_to.urls.get_api(),
|
self.belongs_to.borrow().urls.api,
|
||||||
user_id
|
user_id
|
||||||
);
|
);
|
||||||
drop(belongs_to);
|
|
||||||
let request = Client::new().get(url).bearer_auth(self.token());
|
let request = Client::new().get(url).bearer_auth(self.token());
|
||||||
deserialize_response::<Option<Vec<types::PublicUser>>>(
|
deserialize_response::<Vec<types::PublicUser>>(
|
||||||
request,
|
request,
|
||||||
self,
|
self,
|
||||||
crate::api::limits::LimitType::Global,
|
crate::api::limits::LimitType::Global,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the authenticated user's relationships.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// This function returns a [`Result<Vec<types::Relationship>, ChorusLibError>`].
|
||||||
|
pub async fn get_relationships(&mut self) -> Result<Vec<types::Relationship>, ChorusLibError> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/users/@me/relationships/",
|
||||||
|
self.belongs_to.borrow().urls.api
|
||||||
|
);
|
||||||
|
let request = Client::new().get(url).bearer_auth(self.token());
|
||||||
|
deserialize_response::<Vec<types::Relationship>>(
|
||||||
|
request,
|
||||||
|
self,
|
||||||
|
crate::api::limits::LimitType::Global,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a friend request to a user.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `schema` - A [`FriendRequestSendSchema`] struct that holds the information about the friend request to be sent.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// This function returns a [`Result`] that holds a [`ChorusLibError`] if the request fails.
|
||||||
|
pub async fn send_friend_request(
|
||||||
|
&mut self,
|
||||||
|
schema: types::FriendRequestSendSchema,
|
||||||
|
) -> Result<(), ChorusLibError> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/users/@me/relationships/",
|
||||||
|
self.belongs_to.borrow().urls.api
|
||||||
|
);
|
||||||
|
let body = to_string(&schema).unwrap();
|
||||||
|
let request = Client::new().post(url).bearer_auth(self.token()).body(body);
|
||||||
|
handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modifies the relationship between the authenticated user and the specified user.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `user_id` - A string slice that holds the ID of the user to modify the relationship with.
|
||||||
|
/// * `relationship_type` - A [`RelationshipType`] enum that specifies the type of relationship to modify.
|
||||||
|
/// * [`RelationshipType::None`]: Removes the relationship between the two users.
|
||||||
|
/// * [`RelationshipType::Friends`] | [`RelationshipType::Incoming`] | [`RelationshipType::Outgoing`]:
|
||||||
|
/// Either accepts an incoming friend request, or sends a new friend request, if there is no
|
||||||
|
/// incoming friend request from the specified `user_id`.
|
||||||
|
/// * [`RelationshipType::Blocked`]: Blocks the specified user_id.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// This function returns an [`Result`] that holds a [`ChorusLibError`] if the request fails.
|
||||||
|
pub async fn modify_user_relationship(
|
||||||
|
&mut self,
|
||||||
|
user_id: &str,
|
||||||
|
relationship_type: RelationshipType,
|
||||||
|
) -> Result<(), ChorusLibError> {
|
||||||
|
let api_url = self.belongs_to.borrow().urls.api.clone();
|
||||||
|
match relationship_type {
|
||||||
|
RelationshipType::None => {
|
||||||
|
let request = Client::new()
|
||||||
|
.delete(format!("{}/users/@me/relationships/{}/", api_url, user_id))
|
||||||
|
.bearer_auth(self.token());
|
||||||
|
handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await
|
||||||
|
}
|
||||||
|
RelationshipType::Friends | RelationshipType::Incoming | RelationshipType::Outgoing => {
|
||||||
|
let body = CreateUserRelationshipSchema {
|
||||||
|
relationship_type: None, // Selecting 'None' here will accept an incoming FR or send a new FR.
|
||||||
|
from_friend_suggestion: None,
|
||||||
|
friend_token: None,
|
||||||
|
};
|
||||||
|
let request = Client::new()
|
||||||
|
.put(format!("{}/users/@me/relationships/{}/", api_url, user_id))
|
||||||
|
.bearer_auth(self.token())
|
||||||
|
.body(to_string(&body).unwrap());
|
||||||
|
handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await
|
||||||
|
}
|
||||||
|
RelationshipType::Blocked => {
|
||||||
|
let body = CreateUserRelationshipSchema {
|
||||||
|
relationship_type: Some(RelationshipType::Blocked),
|
||||||
|
from_friend_suggestion: None,
|
||||||
|
friend_token: None,
|
||||||
|
};
|
||||||
|
let request = Client::new()
|
||||||
|
.put(format!("{}/users/@me/relationships/{}/", api_url, user_id))
|
||||||
|
.bearer_auth(self.token())
|
||||||
|
.body(to_string(&body).unwrap());
|
||||||
|
handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await
|
||||||
|
}
|
||||||
|
RelationshipType::Suggestion | RelationshipType::Implicit => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the relationship between the authenticated user and the specified user.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `user_id` - A string slice that holds the ID of the user to remove the relationship with.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// This function returns a [`Result`] that holds a [`ChorusLibError`] if the request fails.
|
||||||
|
pub async fn remove_relationship(&mut self, user_id: &str) -> Result<(), ChorusLibError> {
|
||||||
|
let url = format!(
|
||||||
|
"{}/users/@me/relationships/{}/",
|
||||||
|
self.belongs_to.borrow().urls.api,
|
||||||
|
user_id
|
||||||
|
);
|
||||||
|
let request = Client::new().delete(url).bearer_auth(self.token());
|
||||||
|
handle_request_as_result(request, self, crate::api::limits::LimitType::Global).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use reqwest::Client;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{deserialize_response, handle_request_as_option, limits::Limits},
|
api::{deserialize_response, handle_request_as_result, limits::Limits},
|
||||||
errors::ChorusLibError,
|
errors::ChorusLibError,
|
||||||
instance::{Instance, UserMeta},
|
instance::{Instance, UserMeta},
|
||||||
limit::LimitedRequester,
|
limit::LimitedRequester,
|
||||||
|
@ -52,10 +52,7 @@ impl UserMeta {
|
||||||
return Err(ChorusLibError::PasswordRequiredError);
|
return Err(ChorusLibError::PasswordRequiredError);
|
||||||
}
|
}
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.patch(format!(
|
.patch(format!("{}/users/@me/", self.belongs_to.borrow().urls.api))
|
||||||
"{}/users/@me/",
|
|
||||||
self.belongs_to.borrow_mut().urls.get_api()
|
|
||||||
))
|
|
||||||
.body(to_string(&modify_schema).unwrap())
|
.body(to_string(&modify_schema).unwrap())
|
||||||
.bearer_auth(self.token());
|
.bearer_auth(self.token());
|
||||||
let user_updated =
|
let user_updated =
|
||||||
|
@ -74,14 +71,15 @@ impl UserMeta {
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// Returns `None` if the user was successfully deleted, or an `ChorusLibError` if an error occurred.
|
/// Returns `()` if the user was successfully deleted, or a `ChorusLibError` if an error occurred.
|
||||||
pub async fn delete(mut self) -> Option<ChorusLibError> {
|
pub async fn delete(mut self) -> Result<(), ChorusLibError> {
|
||||||
let belongs_to = self.belongs_to.borrow();
|
|
||||||
let request = Client::new()
|
let request = Client::new()
|
||||||
.post(format!("{}/users/@me/delete/", belongs_to.urls.get_api()))
|
.post(format!(
|
||||||
|
"{}/users/@me/delete/",
|
||||||
|
self.belongs_to.borrow().urls.api
|
||||||
|
))
|
||||||
.bearer_auth(self.token());
|
.bearer_auth(self.token());
|
||||||
drop(belongs_to);
|
handle_request_as_result(request, &mut self, crate::api::limits::LimitType::Ip).await
|
||||||
handle_request_as_option(request, &mut self, crate::api::limits::LimitType::Ip).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +88,7 @@ impl User {
|
||||||
let mut belongs_to = user.belongs_to.borrow_mut();
|
let mut belongs_to = user.belongs_to.borrow_mut();
|
||||||
User::_get(
|
User::_get(
|
||||||
&user.token(),
|
&user.token(),
|
||||||
&format!("{}", belongs_to.urls.get_api()),
|
&format!("{}", belongs_to.urls.api),
|
||||||
&mut belongs_to.limits,
|
&mut belongs_to.limits,
|
||||||
id,
|
id,
|
||||||
)
|
)
|
||||||
|
@ -103,12 +101,11 @@ impl User {
|
||||||
limits_instance: &mut Limits,
|
limits_instance: &mut Limits,
|
||||||
id: Option<&String>,
|
id: Option<&String>,
|
||||||
) -> Result<User, ChorusLibError> {
|
) -> Result<User, ChorusLibError> {
|
||||||
let url: String;
|
let url = if id.is_none() {
|
||||||
if id.is_none() {
|
format!("{}/users/@me/", url_api)
|
||||||
url = format!("{}/users/@me/", url_api);
|
|
||||||
} else {
|
} else {
|
||||||
url = format!("{}/users/{}", url_api, id.unwrap());
|
format!("{}/users/{}", url_api, id.unwrap())
|
||||||
}
|
};
|
||||||
let request = reqwest::Client::new().get(url).bearer_auth(token);
|
let request = reqwest::Client::new().get(url).bearer_auth(token);
|
||||||
let mut cloned_limits = limits_instance.clone();
|
let mut cloned_limits = limits_instance.clone();
|
||||||
match LimitedRequester::send_request(
|
match LimitedRequester::send_request(
|
||||||
|
@ -166,12 +163,6 @@ impl Instance {
|
||||||
token: String,
|
token: String,
|
||||||
id: Option<&String>,
|
id: Option<&String>,
|
||||||
) -> Result<User, ChorusLibError> {
|
) -> Result<User, ChorusLibError> {
|
||||||
User::_get(
|
User::_get(&token, &self.urls.api, &mut self.limits, id).await
|
||||||
&token,
|
|
||||||
&self.urls.get_api().to_string(),
|
|
||||||
&mut self.limits,
|
|
||||||
id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,14 @@ custom_error! {
|
||||||
CantGetInfoError{error:String} = "Something seems to be wrong with the instance. Cannot get information about the instance: {error}",
|
CantGetInfoError{error:String} = "Something seems to be wrong with the instance. Cannot get information about the instance: {error}",
|
||||||
InvalidFormBodyError{error_type: String, error:String} = "The server responded with: {error_type}: {error}",
|
InvalidFormBodyError{error_type: String, error:String} = "The server responded with: {error_type}: {error}",
|
||||||
RateLimited{bucket:String} = "Ratelimited on Bucket {bucket}",
|
RateLimited{bucket:String} = "Ratelimited on Bucket {bucket}",
|
||||||
MultipartCreationError{error: String} = "Got an error whilst creating the form: {}",
|
MultipartCreationError{error: String} = "Got an error whilst creating the form: {error}",
|
||||||
FormCreationError{error: String} = "Got an error whilst creating the form: {}",
|
FormCreationError{error: String} = "Got an error whilst creating the form: {error}",
|
||||||
TokenExpired = "Token expired, invalid or not found.",
|
TokenExpired = "Token expired, invalid or not found.",
|
||||||
NoPermission = "You do not have the permissions needed to perform this action.",
|
NoPermission = "You do not have the permissions needed to perform this action.",
|
||||||
NotFound{error: String} = "The provided resource hasn't been found: {}",
|
NotFound{error: String} = "The provided resource hasn't been found: {error}",
|
||||||
PasswordRequiredError = "You need to provide your current password to authenticate for this action.",
|
PasswordRequiredError = "You need to provide your current password to authenticate for this action.",
|
||||||
InvalidResponseError{error: String} = "The response is malformed and cannot be processed. Error: {}",
|
InvalidResponseError{error: String} = "The response is malformed and cannot be processed. Error: {error}",
|
||||||
|
InvalidArgumentsError{error: String} = "Invalid arguments were provided. Error: {error}"
|
||||||
}
|
}
|
||||||
|
|
||||||
custom_error! {
|
custom_error! {
|
||||||
|
|
252
src/gateway.rs
252
src/gateway.rs
|
@ -3,6 +3,7 @@ use crate::errors::ObserverError;
|
||||||
use crate::gateway::events::Events;
|
use crate::gateway::events::Events;
|
||||||
use crate::types;
|
use crate::types;
|
||||||
use crate::types::WebSocketEvent;
|
use crate::types::WebSocketEvent;
|
||||||
|
use std::any::Any;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use futures_util::stream::SplitSink;
|
use futures_util::stream::SplitSink;
|
||||||
|
@ -72,11 +73,9 @@ const GATEWAY_LAZY_REQUEST: u8 = 14;
|
||||||
/// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms
|
/// The amount of time we wait for a heartbeat ack before resending our heartbeat in ms
|
||||||
const HEARTBEAT_ACK_TIMEOUT: u128 = 2000;
|
const HEARTBEAT_ACK_TIMEOUT: u128 = 2000;
|
||||||
|
|
||||||
|
/// Represents a messsage received from the gateway. This will be either a [GatewayReceivePayload], containing events, or a [GatewayError].
|
||||||
|
/// This struct is used internally when handling messages.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
/**
|
|
||||||
Represents a messsage received from the gateway. This will be either a [GatewayReceivePayload], containing events, or a [GatewayError].
|
|
||||||
This struct is used internally when handling messages.
|
|
||||||
*/
|
|
||||||
pub struct GatewayMessage {
|
pub struct GatewayMessage {
|
||||||
/// The message we received from the server
|
/// The message we received from the server
|
||||||
message: tokio_tungstenite::tungstenite::Message,
|
message: tokio_tungstenite::tungstenite::Message,
|
||||||
|
@ -94,60 +93,36 @@ impl GatewayMessage {
|
||||||
let content = self.message.to_string();
|
let content = self.message.to_string();
|
||||||
|
|
||||||
// Some error strings have dots on the end, which we don't care about
|
// Some error strings have dots on the end, which we don't care about
|
||||||
let processed_content = content.clone().to_lowercase().replace(".", "");
|
let processed_content = content.to_lowercase().replace('.', "");
|
||||||
|
|
||||||
match processed_content.as_str() {
|
match processed_content.as_str() {
|
||||||
"unknown error" | "4000" => {
|
"unknown error" | "4000" => Some(GatewayError::UnknownError),
|
||||||
return Some(GatewayError::UnknownError);
|
"unknown opcode" | "4001" => Some(GatewayError::UnknownOpcodeError),
|
||||||
}
|
|
||||||
"unknown opcode" | "4001" => {
|
|
||||||
return Some(GatewayError::UnknownOpcodeError);
|
|
||||||
}
|
|
||||||
"decode error" | "error while decoding payload" | "4002" => {
|
"decode error" | "error while decoding payload" | "4002" => {
|
||||||
return Some(GatewayError::DecodeError);
|
Some(GatewayError::DecodeError)
|
||||||
}
|
|
||||||
"not authenticated" | "4003" => {
|
|
||||||
return Some(GatewayError::NotAuthenticatedError);
|
|
||||||
}
|
|
||||||
"authentication failed" | "4004" => {
|
|
||||||
return Some(GatewayError::AuthenticationFailedError);
|
|
||||||
}
|
|
||||||
"already authenticated" | "4005" => {
|
|
||||||
return Some(GatewayError::AlreadyAuthenticatedError);
|
|
||||||
}
|
|
||||||
"invalid seq" | "4007" => {
|
|
||||||
return Some(GatewayError::InvalidSequenceNumberError);
|
|
||||||
}
|
|
||||||
"rate limited" | "4008" => {
|
|
||||||
return Some(GatewayError::RateLimitedError);
|
|
||||||
}
|
|
||||||
"session timed out" | "4009" => {
|
|
||||||
return Some(GatewayError::SessionTimedOutError);
|
|
||||||
}
|
|
||||||
"invalid shard" | "4010" => {
|
|
||||||
return Some(GatewayError::InvalidShardError);
|
|
||||||
}
|
|
||||||
"sharding required" | "4011" => {
|
|
||||||
return Some(GatewayError::ShardingRequiredError);
|
|
||||||
}
|
|
||||||
"invalid api version" | "4012" => {
|
|
||||||
return Some(GatewayError::InvalidAPIVersionError);
|
|
||||||
}
|
}
|
||||||
|
"not authenticated" | "4003" => Some(GatewayError::NotAuthenticatedError),
|
||||||
|
"authentication failed" | "4004" => Some(GatewayError::AuthenticationFailedError),
|
||||||
|
"already authenticated" | "4005" => Some(GatewayError::AlreadyAuthenticatedError),
|
||||||
|
"invalid seq" | "4007" => Some(GatewayError::InvalidSequenceNumberError),
|
||||||
|
"rate limited" | "4008" => Some(GatewayError::RateLimitedError),
|
||||||
|
"session timed out" | "4009" => Some(GatewayError::SessionTimedOutError),
|
||||||
|
"invalid shard" | "4010" => Some(GatewayError::InvalidShardError),
|
||||||
|
"sharding required" | "4011" => Some(GatewayError::ShardingRequiredError),
|
||||||
|
"invalid api version" | "4012" => Some(GatewayError::InvalidAPIVersionError),
|
||||||
"invalid intent(s)" | "invalid intent" | "4013" => {
|
"invalid intent(s)" | "invalid intent" | "4013" => {
|
||||||
return Some(GatewayError::InvalidIntentsError);
|
Some(GatewayError::InvalidIntentsError)
|
||||||
}
|
}
|
||||||
"disallowed intent(s)" | "disallowed intents" | "4014" => {
|
"disallowed intent(s)" | "disallowed intents" | "4014" => {
|
||||||
return Some(GatewayError::DisallowedIntentsError);
|
Some(GatewayError::DisallowedIntentsError)
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether or not the message is an error
|
/// Returns whether or not the message is an error
|
||||||
pub fn is_error(&self) -> bool {
|
pub fn is_error(&self) -> bool {
|
||||||
return self.error().is_some();
|
self.error().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the message as a payload;
|
/// Parses the message as a payload;
|
||||||
|
@ -168,17 +143,15 @@ impl GatewayMessage {
|
||||||
|
|
||||||
/// Returns whether or not the message is empty
|
/// Returns whether or not the message is empty
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
return self.message.is_empty();
|
self.message.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a handle to a Gateway connection. A Gateway connection will create observable
|
||||||
|
/// [`GatewayEvents`](GatewayEvent), which you can subscribe to. Gateway events include all currently
|
||||||
|
/// implemented [Types] with the trait [`WebSocketEvent`]
|
||||||
|
/// Using this handle you can also send Gateway Events directly.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/**
|
|
||||||
Represents a handle to a Gateway connection. A Gateway connection will create observable
|
|
||||||
[`GatewayEvents`](GatewayEvent), which you can subscribe to. Gateway events include all currently
|
|
||||||
implemented [Types] with the trait [`WebSocketEvent`]
|
|
||||||
Using this handle you can also send Gateway Events directly.
|
|
||||||
*/
|
|
||||||
pub struct GatewayHandle {
|
pub struct GatewayHandle {
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub events: Arc<Mutex<Events>>,
|
pub events: Arc<Mutex<Events>>,
|
||||||
|
@ -308,6 +281,7 @@ pub struct Gateway {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gateway {
|
impl Gateway {
|
||||||
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub async fn new(websocket_url: String) -> Result<GatewayHandle, GatewayError> {
|
pub async fn new(websocket_url: String) -> Result<GatewayHandle, GatewayError> {
|
||||||
let (websocket_stream, _) = match connect_async_tls_with_config(
|
let (websocket_stream, _) = match connect_async_tls_with_config(
|
||||||
&websocket_url,
|
&websocket_url,
|
||||||
|
@ -371,13 +345,13 @@ impl Gateway {
|
||||||
gateway.gateway_listen_task().await;
|
gateway.gateway_listen_task().await;
|
||||||
});
|
});
|
||||||
|
|
||||||
return Ok(GatewayHandle {
|
Ok(GatewayHandle {
|
||||||
url: websocket_url.clone(),
|
url: websocket_url.clone(),
|
||||||
events: shared_events,
|
events: shared_events,
|
||||||
websocket_send: shared_websocket_send.clone(),
|
websocket_send: shared_websocket_send.clone(),
|
||||||
handle,
|
handle,
|
||||||
kill_send: kill_send.clone(),
|
kill_send: kill_send.clone(),
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main gateway listener task;
|
/// The main gateway listener task;
|
||||||
|
@ -388,15 +362,11 @@ impl Gateway {
|
||||||
let msg = self.websocket_receive.next().await;
|
let msg = self.websocket_receive.next().await;
|
||||||
|
|
||||||
// This if chain can be much better but if let is unstable on stable rust
|
// This if chain can be much better but if let is unstable on stable rust
|
||||||
if msg.as_ref().is_some() {
|
if let Some(Ok(message)) = msg {
|
||||||
if msg.as_ref().unwrap().is_ok() {
|
self.handle_message(GatewayMessage::from_tungstenite_message(message))
|
||||||
let msg_unwrapped = msg.unwrap().unwrap();
|
|
||||||
self.handle_message(GatewayMessage::from_tungstenite_message(msg_unwrapped))
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// We couldn't receive the next message or it was an error, something is wrong with the websocket, close
|
// We couldn't receive the next message or it was an error, something is wrong with the websocket, close
|
||||||
println!("GW: Websocket is broken, stopping gateway");
|
println!("GW: Websocket is broken, stopping gateway");
|
||||||
|
@ -422,8 +392,8 @@ impl Gateway {
|
||||||
return Err(data_deserialize_result.err().unwrap());
|
return Err(data_deserialize_result.err().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
event.update_data(data_deserialize_result.unwrap()).await;
|
event.notify(data_deserialize_result.unwrap()).await;
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This handles a message as a websocket event and updates its events along with the events' observers
|
/// This handles a message as a websocket event and updates its events along with the events' observers
|
||||||
|
@ -444,11 +414,9 @@ impl Gateway {
|
||||||
if msg.is_error() {
|
if msg.is_error() {
|
||||||
println!("GW: Received error, connection will close..");
|
println!("GW: Received error, connection will close..");
|
||||||
|
|
||||||
let error = msg.error();
|
let _error = msg.error();
|
||||||
|
|
||||||
match error {
|
{}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.close().await;
|
self.close().await;
|
||||||
return;
|
return;
|
||||||
|
@ -1399,13 +1367,7 @@ impl Gateway {
|
||||||
sessions: result.unwrap(),
|
sessions: result.unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.events
|
self.events.lock().await.session.replace.notify(data).await;
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.session
|
|
||||||
.replace
|
|
||||||
.update_data(data)
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
"USER_UPDATE" => {
|
"USER_UPDATE" => {
|
||||||
let event = &mut self.events.lock().await.user.update;
|
let event = &mut self.events.lock().await.user.update;
|
||||||
|
@ -1561,9 +1523,7 @@ impl Gateway {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Handles sending heartbeats to the gateway in another thread
|
||||||
Handles sending heartbeats to the gateway in another thread
|
|
||||||
*/
|
|
||||||
struct HeartbeatHandler {
|
struct HeartbeatHandler {
|
||||||
/// The heartbeat interval in milliseconds
|
/// The heartbeat interval in milliseconds
|
||||||
pub heartbeat_interval: u128,
|
pub heartbeat_interval: u128,
|
||||||
|
@ -1698,10 +1658,8 @@ impl HeartbeatHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Used for communications between the heartbeat and gateway thread.
|
||||||
Used for communications between the heartbeat and gateway thread.
|
/// Either signifies a sequence number update, a heartbeat ACK or a Heartbeat request by the server
|
||||||
Either signifies a sequence number update, a heartbeat ACK or a Heartbeat request by the server
|
|
||||||
*/
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct HeartbeatThreadCommunication {
|
struct HeartbeatThreadCommunication {
|
||||||
/// The opcode for the communication we received, if relevant
|
/// The opcode for the communication we received, if relevant
|
||||||
|
@ -1710,89 +1668,47 @@ struct HeartbeatThreadCommunication {
|
||||||
sequence_number: Option<u64>,
|
sequence_number: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Trait which defines the behavior of an Observer. An Observer is an object which is subscribed to
|
||||||
Trait which defines the behavior of an Observer. An Observer is an object which is subscribed to
|
/// an Observable. The Observer is notified when the Observable's data changes.
|
||||||
an Observable. The Observer is notified when the Observable's data changes.
|
/// In this case, the Observable is a [`GatewayEvent`], which is a wrapper around a WebSocketEvent.
|
||||||
In this case, the Observable is a [`GatewayEvent`], which is a wrapper around a WebSocketEvent.
|
/// Note that `Debug` is used to tell `Observer`s apart when unsubscribing.
|
||||||
*/
|
pub trait Observer<T>: Sync + Send + std::fmt::Debug {
|
||||||
pub trait Observer<T: types::WebSocketEvent>: std::fmt::Debug {
|
fn update(&self, data: &T);
|
||||||
fn update(&mut self, data: &T);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GatewayEvent is a wrapper around a WebSocketEvent. It is used to notify the observers of a
|
/// GatewayEvent is a wrapper around a WebSocketEvent. It is used to notify the observers of a
|
||||||
change in the WebSocketEvent. GatewayEvents are observable.
|
/// change in the WebSocketEvent. GatewayEvents are observable.
|
||||||
*/
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct GatewayEvent<T: types::WebSocketEvent> {
|
pub struct GatewayEvent<T: WebSocketEvent> {
|
||||||
observers: Vec<Arc<Mutex<dyn Observer<T> + Sync + Send>>>,
|
observers: Vec<Arc<dyn Observer<T>>>,
|
||||||
pub event_data: T,
|
|
||||||
pub is_observed: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: types::WebSocketEvent> GatewayEvent<T> {
|
impl<T: WebSocketEvent> GatewayEvent<T> {
|
||||||
fn new(event_data: T) -> Self {
|
/// Returns true if the GatewayEvent is observed by at least one Observer.
|
||||||
Self {
|
|
||||||
is_observed: false,
|
|
||||||
observers: Vec::new(),
|
|
||||||
event_data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Returns true if the GatewayEvent is observed by at least one Observer.
|
|
||||||
*/
|
|
||||||
pub fn is_observed(&self) -> bool {
|
pub fn is_observed(&self) -> bool {
|
||||||
self.is_observed
|
!self.observers.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Subscribes an Observer to the GatewayEvent.
|
||||||
Subscribes an Observer to the GatewayEvent. Returns an error if the GatewayEvent is already
|
pub fn subscribe(&mut self, observable: Arc<dyn Observer<T>>) {
|
||||||
observed.
|
|
||||||
# Errors
|
|
||||||
Returns an error if the GatewayEvent is already observed.
|
|
||||||
Error type: [`ObserverError::AlreadySubscribedError`]
|
|
||||||
*/
|
|
||||||
pub fn subscribe(
|
|
||||||
&mut self,
|
|
||||||
observable: Arc<Mutex<dyn Observer<T> + Sync + Send>>,
|
|
||||||
) -> Result<(), ObserverError> {
|
|
||||||
if self.is_observed {
|
|
||||||
return Err(ObserverError::AlreadySubscribedError);
|
|
||||||
}
|
|
||||||
self.is_observed = true;
|
|
||||||
self.observers.push(observable);
|
self.observers.push(observable);
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Unsubscribes an Observer from the GatewayEvent.
|
||||||
Unsubscribes an Observer from the GatewayEvent.
|
pub fn unsubscribe(&mut self, observable: &dyn Observer<T>) {
|
||||||
*/
|
|
||||||
pub fn unsubscribe(&mut self, observable: Arc<Mutex<dyn Observer<T> + Sync + Send>>) {
|
|
||||||
// .retain()'s closure retains only those elements of the vector, which have a different
|
// .retain()'s closure retains only those elements of the vector, which have a different
|
||||||
// pointer value than observable.
|
// pointer value than observable.
|
||||||
// The usage of the debug format to compare the generic T of observers is quite stupid, but the only thing to compare between them is T and if T == T they are the same
|
// The usage of the debug format to compare the generic T of observers is quite stupid, but the only thing to compare between them is T and if T == T they are the same
|
||||||
// anddd there is no way to do that without using format
|
// anddd there is no way to do that without using format
|
||||||
|
let to_remove = format!("{:?}", observable);
|
||||||
self.observers
|
self.observers
|
||||||
.retain(|obs| !(format!("{:?}", obs) == format!("{:?}", &observable)));
|
.retain(|obs| format!("{:?}", obs) != to_remove);
|
||||||
self.is_observed = !self.observers.is_empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Notifies the observers of the GatewayEvent.
|
||||||
Updates the GatewayEvent's data and notifies the observers.
|
async fn notify(&self, new_event_data: T) {
|
||||||
*/
|
|
||||||
async fn update_data(&mut self, new_event_data: T) {
|
|
||||||
self.event_data = new_event_data;
|
|
||||||
self.notify().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Notifies the observers of the GatewayEvent.
|
|
||||||
*/
|
|
||||||
async fn notify(&self) {
|
|
||||||
for observer in &self.observers {
|
for observer in &self.observers {
|
||||||
let mut observer_lock = observer.lock().await;
|
observer.update(&new_event_data);
|
||||||
observer_lock.update(&self.event_data);
|
|
||||||
drop(observer_lock);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1961,23 +1877,23 @@ mod events {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod example {
|
mod example {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::sync::atomic::{AtomicI32, Ordering::Relaxed};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Consumer;
|
struct Consumer {
|
||||||
|
name: String,
|
||||||
|
events_received: AtomicI32,
|
||||||
|
}
|
||||||
|
|
||||||
impl Observer<types::GatewayResume> for Consumer {
|
impl Observer<types::GatewayResume> for Consumer {
|
||||||
fn update(&mut self, data: &types::GatewayResume) {
|
fn update(&self, _data: &types::GatewayResume) {
|
||||||
println!("{}", data.token)
|
self.events_received.fetch_add(1, Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_observer_behavior() {
|
async fn test_observer_behavior() {
|
||||||
let mut event = GatewayEvent::new(types::GatewayResume {
|
let mut event = GatewayEvent::default();
|
||||||
token: "start".to_string(),
|
|
||||||
session_id: "start".to_string(),
|
|
||||||
seq: "start".to_string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let new_data = types::GatewayResume {
|
let new_data = types::GatewayResume {
|
||||||
token: "token_3276ha37am3".to_string(),
|
token: "token_3276ha37am3".to_string(),
|
||||||
|
@ -1985,25 +1901,23 @@ mod example {
|
||||||
seq: "3".to_string(),
|
seq: "3".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let consumer = Consumer;
|
let consumer = Arc::new(Consumer {
|
||||||
let arc_mut_consumer = Arc::new(Mutex::new(consumer));
|
name: "first".into(),
|
||||||
|
events_received: 0.into(),
|
||||||
|
});
|
||||||
|
event.subscribe(consumer.clone());
|
||||||
|
|
||||||
event.subscribe(arc_mut_consumer.clone()).unwrap();
|
let second_consumer = Arc::new(Consumer {
|
||||||
|
name: "second".into(),
|
||||||
|
events_received: 0.into(),
|
||||||
|
});
|
||||||
|
event.subscribe(second_consumer.clone());
|
||||||
|
|
||||||
event.notify().await;
|
event.notify(new_data.clone()).await;
|
||||||
|
event.unsubscribe(&*consumer);
|
||||||
|
event.notify(new_data).await;
|
||||||
|
|
||||||
event.update_data(new_data).await;
|
assert_eq!(consumer.events_received.load(Relaxed), 1);
|
||||||
|
assert_eq!(second_consumer.events_received.load(Relaxed), 2);
|
||||||
let second_consumer = Consumer;
|
|
||||||
let arc_mut_second_consumer = Arc::new(Mutex::new(second_consumer));
|
|
||||||
|
|
||||||
match event.subscribe(arc_mut_second_consumer.clone()).err() {
|
|
||||||
None => assert!(false),
|
|
||||||
Some(err) => println!("You cannot subscribe twice: {}", err),
|
|
||||||
}
|
|
||||||
|
|
||||||
event.unsubscribe(arc_mut_consumer.clone());
|
|
||||||
|
|
||||||
event.subscribe(arc_mut_second_consumer.clone()).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,14 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::api::limits::Limits;
|
use crate::api::limits::Limits;
|
||||||
use crate::errors::{ChorusLibError, FieldFormatError};
|
use crate::errors::{ChorusLibError, FieldFormatError};
|
||||||
use crate::types::{GeneralConfiguration, User, UserSettings};
|
use crate::types::{GeneralConfiguration, User, UserSettings};
|
||||||
use crate::URLBundle;
|
use crate::UrlBundle;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
/**
|
/**
|
||||||
The [`Instance`] what you will be using to perform all sorts of actions on the Spacebar server.
|
The [`Instance`] what you will be using to perform all sorts of actions on the Spacebar server.
|
||||||
*/
|
*/
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
pub urls: URLBundle,
|
pub urls: UrlBundle,
|
||||||
pub instance_info: GeneralConfiguration,
|
pub instance_info: GeneralConfiguration,
|
||||||
pub limits: Limits,
|
pub limits: Limits,
|
||||||
}
|
}
|
||||||
|
@ -26,20 +26,11 @@ impl Instance {
|
||||||
/// * `requester` - The [`LimitedRequester`] that will be used to make requests to the Spacebar server.
|
/// * `requester` - The [`LimitedRequester`] that will be used to make requests to the Spacebar server.
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// * [`InstanceError`] - If the instance cannot be created.
|
/// * [`InstanceError`] - If the instance cannot be created.
|
||||||
pub async fn new(urls: URLBundle) -> Result<Instance, ChorusLibError> {
|
pub async fn new(urls: UrlBundle) -> Result<Instance, ChorusLibError> {
|
||||||
let mut instance = Instance {
|
let mut instance = Instance {
|
||||||
urls: urls.clone(),
|
urls: urls.clone(),
|
||||||
instance_info: GeneralConfiguration::new(
|
// Will be overwritten in the next step
|
||||||
// This is okay, because the instance_info will be overwritten by the instance_policies_schema() function.
|
instance_info: GeneralConfiguration::default(),
|
||||||
"".to_string(),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
limits: Limits::check_limits(urls.api).await,
|
limits: Limits::check_limits(urls.api).await,
|
||||||
};
|
};
|
||||||
instance.instance_info = match instance.general_configuration_schema().await {
|
instance.instance_info = match instance.general_configuration_schema().await {
|
||||||
|
|
36
src/lib.rs
36
src/lib.rs
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(clippy::module_inception)]
|
||||||
|
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
|
@ -16,18 +18,18 @@ pub mod voice;
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
||||||
/// A URLBundle is a struct which bundles together the API-, Gateway- and CDN-URLs of a Spacebar
|
/// A URLBundle is a struct which bundles together the API-, Gateway- and CDN-URLs of a Spacebar
|
||||||
/// instance.
|
/// instance.
|
||||||
pub struct URLBundle {
|
pub struct UrlBundle {
|
||||||
pub api: String,
|
pub api: String,
|
||||||
pub wss: String,
|
pub wss: String,
|
||||||
pub cdn: String,
|
pub cdn: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl URLBundle {
|
impl UrlBundle {
|
||||||
pub fn new(api: String, wss: String, cdn: String) -> Self {
|
pub fn new(api: String, wss: String, cdn: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
api: URLBundle::parse_url(api),
|
api: UrlBundle::parse_url(api),
|
||||||
wss: URLBundle::parse_url(wss),
|
wss: UrlBundle::parse_url(wss),
|
||||||
cdn: URLBundle::parse_url(cdn),
|
cdn: UrlBundle::parse_url(cdn),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,13 +44,13 @@ impl URLBundle {
|
||||||
let url = match Url::parse(&url) {
|
let url = match Url::parse(&url) {
|
||||||
Ok(url) => {
|
Ok(url) => {
|
||||||
if url.scheme() == "localhost" {
|
if url.scheme() == "localhost" {
|
||||||
return URLBundle::parse_url(format!("http://{}", url));
|
return UrlBundle::parse_url(format!("http://{}", url));
|
||||||
}
|
}
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
Err(ParseError::RelativeUrlWithoutBase) => {
|
Err(ParseError::RelativeUrlWithoutBase) => {
|
||||||
let url_fmt = format!("http://{}", url);
|
let url_fmt = format!("http://{}", url);
|
||||||
return URLBundle::parse_url(url_fmt);
|
return UrlBundle::parse_url(url_fmt);
|
||||||
}
|
}
|
||||||
Err(_) => panic!("Invalid URL"),
|
Err(_) => panic!("Invalid URL"),
|
||||||
};
|
};
|
||||||
|
@ -59,18 +61,6 @@ impl URLBundle {
|
||||||
}
|
}
|
||||||
url_string
|
url_string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_api(&self) -> &str {
|
|
||||||
&self.api
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cdn(&self) -> &str {
|
|
||||||
&self.cdn
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_wss(&self) -> &str {
|
|
||||||
&self.wss
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -79,13 +69,13 @@ mod lib {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_url() {
|
fn test_parse_url() {
|
||||||
let mut result = URLBundle::parse_url(String::from("localhost:3000/"));
|
let mut result = UrlBundle::parse_url(String::from("localhost:3000/"));
|
||||||
assert_eq!(result, String::from("http://localhost:3000"));
|
assert_eq!(result, String::from("http://localhost:3000"));
|
||||||
result = URLBundle::parse_url(String::from("https://some.url.com/"));
|
result = UrlBundle::parse_url(String::from("https://some.url.com/"));
|
||||||
assert_eq!(result, String::from("https://some.url.com"));
|
assert_eq!(result, String::from("https://some.url.com"));
|
||||||
result = URLBundle::parse_url(String::from("https://some.url.com/"));
|
result = UrlBundle::parse_url(String::from("https://some.url.com/"));
|
||||||
assert_eq!(result, String::from("https://some.url.com"));
|
assert_eq!(result, String::from("https://some.url.com"));
|
||||||
result = URLBundle::parse_url(String::from("https://some.url.com"));
|
result = UrlBundle::parse_url(String::from("https://some.url.com"));
|
||||||
assert_eq!(result, String::from("https://some.url.com"));
|
assert_eq!(result, String::from("https://some.url.com"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
25
src/limit.rs
25
src/limit.rs
|
@ -82,13 +82,11 @@ impl LimitedRequester {
|
||||||
);
|
);
|
||||||
if !response.status().is_success() {
|
if !response.status().is_success() {
|
||||||
match response.status().as_u16() {
|
match response.status().as_u16() {
|
||||||
401 => return Err(ChorusLibError::TokenExpired),
|
401 => Err(ChorusLibError::TokenExpired),
|
||||||
403 => return Err(ChorusLibError::TokenExpired),
|
403 => Err(ChorusLibError::TokenExpired),
|
||||||
_ => {
|
_ => Err(ChorusLibError::ReceivedErrorCodeError {
|
||||||
return Err(ChorusLibError::ReceivedErrorCodeError {
|
|
||||||
error_code: response.status().as_str().to_string(),
|
error_code: response.status().as_str().to_string(),
|
||||||
});
|
}),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(response)
|
Ok(response)
|
||||||
|
@ -258,13 +256,13 @@ impl LimitedRequester {
|
||||||
mod rate_limit {
|
mod rate_limit {
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
|
|
||||||
use crate::{api::limits::Config, URLBundle};
|
use crate::{api::limits::Config, UrlBundle};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn run_into_limit() {
|
async fn run_into_limit() {
|
||||||
let urls = URLBundle::new(
|
let urls = UrlBundle::new(
|
||||||
String::from("http://localhost:3001/api/"),
|
String::from("http://localhost:3001/api/"),
|
||||||
String::from("wss://localhost:3001/"),
|
String::from("wss://localhost:3001/"),
|
||||||
String::from("http://localhost:3001/cdn"),
|
String::from("http://localhost:3001/cdn"),
|
||||||
|
@ -286,19 +284,12 @@ mod rate_limit {
|
||||||
.await,
|
.await,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if request.is_some() {
|
assert!(matches!(request, Some(Err(_))));
|
||||||
match request.unwrap() {
|
|
||||||
Ok(_) => assert!(false),
|
|
||||||
Err(_) => assert!(true),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert!(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_send_request() {
|
async fn test_send_request() {
|
||||||
let urls = URLBundle::new(
|
let urls = UrlBundle::new(
|
||||||
String::from("http://localhost:3001/api/"),
|
String::from("http://localhost:3001/api/"),
|
||||||
String::from("wss://localhost:3001/"),
|
String::from("wss://localhost:3001/"),
|
||||||
String::from("http://localhost:3001/cdn"),
|
String::from("http://localhost:3001/cdn"),
|
||||||
|
|
|
@ -89,15 +89,15 @@ fn generate_pairs(obj: &Value, key: &str) -> Vec<ConfigEntity> {
|
||||||
fn pairs_to_config(pairs: Vec<ConfigEntity>) -> ConfigValue {
|
fn pairs_to_config(pairs: Vec<ConfigEntity>) -> ConfigValue {
|
||||||
let mut value = Value::Object(Map::new());
|
let mut value = Value::Object(Map::new());
|
||||||
|
|
||||||
for p in pairs {
|
for pair in pairs {
|
||||||
let keys: Vec<&str> = p.key.split('_').collect();
|
let keys: Vec<&str> = pair.key.split('_').collect();
|
||||||
let mut path = vec![];
|
let mut path = vec![];
|
||||||
|
|
||||||
for (i, &key) in keys.iter().enumerate() {
|
for (i, &key) in keys.iter().enumerate() {
|
||||||
path.push(key);
|
path.push(key);
|
||||||
|
|
||||||
if i == keys.len() - 1 {
|
if i == keys.len() - 1 {
|
||||||
insert_into(&mut value, &path, p.value.clone().unwrap_or(Value::Null));
|
insert_into(&mut value, &path, pair.value.clone().unwrap_or(Value::Null));
|
||||||
} else if keys[i + 1].parse::<usize>().is_ok() {
|
} else if keys[i + 1].parse::<usize>().is_ok() {
|
||||||
if !path_exists(&value, &path) {
|
if !path_exists(&value, &path) {
|
||||||
insert_into(&mut value, &path, Value::Array(Vec::new()));
|
insert_into(&mut value, &path, Value::Array(Vec::new()));
|
||||||
|
@ -182,6 +182,7 @@ mod test {
|
||||||
let pairs = generate_pairs(&v, "");
|
let pairs = generate_pairs(&v, "");
|
||||||
|
|
||||||
let cfg = pairs_to_config(pairs);
|
let cfg = pairs_to_config(pairs);
|
||||||
|
|
||||||
assert_eq!(cfg, c)
|
assert_eq!(cfg, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,27 +31,3 @@ impl Default for GeneralConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeneralConfiguration {
|
|
||||||
pub fn new(
|
|
||||||
instance_name: String,
|
|
||||||
instance_description: Option<String>,
|
|
||||||
front_page: Option<String>,
|
|
||||||
tos_page: Option<String>,
|
|
||||||
correspondence_email: Option<String>,
|
|
||||||
correspondence_user_id: Option<String>,
|
|
||||||
image: Option<String>,
|
|
||||||
instance_id: Option<Snowflake>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
instance_name,
|
|
||||||
instance_description,
|
|
||||||
front_page,
|
|
||||||
tos_page,
|
|
||||||
correspondence_email,
|
|
||||||
correspondence_user_id,
|
|
||||||
image,
|
|
||||||
instance_id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
#[cfg(feature = "sqlx")]
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use bitflags::{bitflags, Flags};
|
use bitflags::bitflags;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::types::Snowflake;
|
||||||
|
|
||||||
use super::PublicUser;
|
use super::PublicUser;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialEq, Eq)]
|
||||||
/// See https://discord-userdoccers.vercel.app/resources/user#relationship-structure
|
/// See https://discord-userdoccers.vercel.app/resources/user#relationship-structure
|
||||||
pub struct Relationship {
|
pub struct Relationship {
|
||||||
pub id: Snowflake,
|
pub id: Snowflake,
|
||||||
|
@ -17,7 +17,7 @@ pub struct Relationship {
|
||||||
pub since: Option<DateTime<Utc>>,
|
pub since: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default)]
|
#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Default, Eq, PartialEq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
/// See https://discord-userdoccers.vercel.app/resources/user#relationship-type
|
/// See https://discord-userdoccers.vercel.app/resources/user#relationship-type
|
||||||
pub enum RelationshipType {
|
pub enum RelationshipType {
|
||||||
|
|
|
@ -108,6 +108,7 @@ impl PermissionFlags {
|
||||||
self.contains(permission) || self.contains(PermissionFlags::ADMINISTRATOR)
|
self.contains(permission) || self.contains(PermissionFlags::ADMINISTRATOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::inherent_to_string)]
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
self.bits().to_string()
|
self.bits().to_string()
|
||||||
}
|
}
|
||||||
|
@ -129,7 +130,7 @@ impl PermissionFlags {
|
||||||
pub fn from_vec(flags: Vec<PermissionFlags>) -> String {
|
pub fn from_vec(flags: Vec<PermissionFlags>) -> String {
|
||||||
let mut permissions: PermissionFlags = Default::default();
|
let mut permissions: PermissionFlags = Default::default();
|
||||||
for flag in flags.iter() {
|
for flag in flags.iter() {
|
||||||
permissions = permissions | flag.clone();
|
permissions |= flag.clone();
|
||||||
}
|
}
|
||||||
permissions.to_string()
|
permissions.to_string()
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,16 +51,18 @@ impl GatewayIdentifyPayload {
|
||||||
impl GatewayIdentifyPayload {
|
impl GatewayIdentifyPayload {
|
||||||
/// Creates an identify payload with the same default capabilities as the official client
|
/// Creates an identify payload with the same default capabilities as the official client
|
||||||
pub fn default_w_client_capabilities() -> Self {
|
pub fn default_w_client_capabilities() -> Self {
|
||||||
let mut def = Self::default();
|
Self {
|
||||||
def.capabilities = Some(8189); // Default capabilities for a client
|
capabilities: Some(8189), // Default capabilities for a client
|
||||||
def
|
..Self::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an identify payload with all possible capabilities
|
/// Creates an identify payload with all possible capabilities
|
||||||
pub fn default_w_all_capabilities() -> Self {
|
pub fn default_w_all_capabilities() -> Self {
|
||||||
let mut def = Self::default();
|
Self {
|
||||||
def.capabilities = Some(i32::MAX); // Since discord uses bitwise for capabilities, this has almost every bit as 1, so all capabilities
|
capabilities: Some(i32::MAX), // Since discord uses bitwise for capabilities, this has almost every bit as 1, so all capabilities
|
||||||
def
|
..Self::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,22 +150,18 @@ impl GatewayIdentifyConnectionProps {
|
||||||
|
|
||||||
/// Returns the most common connection props so we can't be tracked
|
/// Returns the most common connection props so we can't be tracked
|
||||||
pub fn common() -> Self {
|
pub fn common() -> Self {
|
||||||
let mut default = Self::minimal();
|
Self {
|
||||||
|
|
||||||
// See https://www.useragents.me/#most-common-desktop-useragents
|
// See https://www.useragents.me/#most-common-desktop-useragents
|
||||||
// 25% of the web
|
// 25% of the web
|
||||||
//default.browser_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36".to_string();
|
//default.browser_user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36".to_string();
|
||||||
default.browser = String::from("Chrome");
|
browser: String::from("Chrome"),
|
||||||
default.browser_version = String::from("113.0.0.0");
|
browser_version: String::from("113.0.0.0"),
|
||||||
|
system_locale: String::from("en-US"),
|
||||||
default.system_locale = String::from("en-US");
|
os: String::from("Windows"),
|
||||||
|
os_version: Some(String::from("10")),
|
||||||
default.os = String::from("Windows");
|
client_build_number: 199933,
|
||||||
default.os_version = Some(String::from("10"));
|
release_channel: String::from("stable"),
|
||||||
|
..Self::minimal()
|
||||||
default.client_build_number = 199933;
|
}
|
||||||
default.release_channel = String::from("stable");
|
|
||||||
|
|
||||||
return default;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::types::{events::WebSocketEvent, UserStatus};
|
use crate::types::{events::WebSocketEvent, UserStatus};
|
||||||
use crate::types::{Activity, PublicUser, Snowflake};
|
use crate::types::{Activity, ClientStatusObject, PublicUser, Snowflake};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
||||||
|
@ -26,12 +26,4 @@ pub struct PresenceUpdate {
|
||||||
pub client_status: ClientStatusObject,
|
pub client_status: ClientStatusObject,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
|
|
||||||
/// See https://discord.com/developers/docs/topics/gateway-events#client-status-object
|
|
||||||
pub struct ClientStatusObject {
|
|
||||||
pub desktop: Option<String>,
|
|
||||||
pub mobile: Option<String>,
|
|
||||||
pub web: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WebSocketEvent for PresenceUpdate {}
|
impl WebSocketEvent for PresenceUpdate {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::types::events::WebSocketEvent;
|
use crate::types::events::WebSocketEvent;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Default)]
|
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
||||||
pub struct GatewayResume {
|
pub struct GatewayResume {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub session_id: String,
|
pub session_id: String,
|
||||||
|
|
|
@ -116,9 +116,39 @@ pub struct RegisterSchema {
|
||||||
promotional_email_opt_in: Option<bool>,
|
promotional_email_opt_in: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RegisterSchemaOptions {
|
||||||
|
pub username: String,
|
||||||
|
pub password: Option<String>,
|
||||||
|
pub consent: bool,
|
||||||
|
pub email: Option<String>,
|
||||||
|
pub fingerprint: Option<String>,
|
||||||
|
pub invite: Option<String>,
|
||||||
|
pub date_of_birth: Option<String>,
|
||||||
|
pub gift_code_sku_id: Option<String>,
|
||||||
|
pub captcha_key: Option<String>,
|
||||||
|
pub promotional_email_opt_in: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
impl RegisterSchema {
|
impl RegisterSchema {
|
||||||
|
pub fn builder(username: impl Into<String>, consent: bool) -> RegisterSchemaOptions {
|
||||||
|
RegisterSchemaOptions {
|
||||||
|
username: username.into(),
|
||||||
|
password: None,
|
||||||
|
consent,
|
||||||
|
email: None,
|
||||||
|
fingerprint: None,
|
||||||
|
invite: None,
|
||||||
|
date_of_birth: None,
|
||||||
|
gift_code_sku_id: None,
|
||||||
|
captcha_key: None,
|
||||||
|
promotional_email_opt_in: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterSchemaOptions {
|
||||||
/**
|
/**
|
||||||
Returns a new [`Result<RegisterSchema, FieldFormatError>`].
|
Create a new [`RegisterSchema`].
|
||||||
## Arguments
|
## Arguments
|
||||||
All but "String::username" and "bool::consent" are optional.
|
All but "String::username" and "bool::consent" are optional.
|
||||||
|
|
||||||
|
@ -129,47 +159,36 @@ impl RegisterSchema {
|
||||||
|
|
||||||
These constraints have been defined [in the Spacebar-API](https://docs.spacebar.chat/routes/)
|
These constraints have been defined [in the Spacebar-API](https://docs.spacebar.chat/routes/)
|
||||||
*/
|
*/
|
||||||
pub fn new(
|
pub fn build(self) -> Result<RegisterSchema, FieldFormatError> {
|
||||||
username: String,
|
let username = AuthUsername::new(self.username)?.username;
|
||||||
password: Option<String>,
|
|
||||||
consent: bool,
|
|
||||||
email: Option<String>,
|
|
||||||
fingerprint: Option<String>,
|
|
||||||
invite: Option<String>,
|
|
||||||
date_of_birth: Option<String>,
|
|
||||||
gift_code_sku_id: Option<String>,
|
|
||||||
captcha_key: Option<String>,
|
|
||||||
promotional_email_opt_in: Option<bool>,
|
|
||||||
) -> Result<RegisterSchema, FieldFormatError> {
|
|
||||||
let username = AuthUsername::new(username)?.username;
|
|
||||||
|
|
||||||
let email = if let Some(email) = email {
|
let email = if let Some(email) = self.email {
|
||||||
Some(AuthEmail::new(email)?.email)
|
Some(AuthEmail::new(email)?.email)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let password = if let Some(password) = password {
|
let password = if let Some(password) = self.password {
|
||||||
Some(AuthPassword::new(password)?.password)
|
Some(AuthPassword::new(password)?.password)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if !consent {
|
if !self.consent {
|
||||||
return Err(FieldFormatError::ConsentError);
|
return Err(FieldFormatError::ConsentError);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RegisterSchema {
|
Ok(RegisterSchema {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
consent,
|
consent: self.consent,
|
||||||
email,
|
email,
|
||||||
fingerprint,
|
fingerprint: self.fingerprint,
|
||||||
invite,
|
invite: self.invite,
|
||||||
date_of_birth,
|
date_of_birth: self.date_of_birth,
|
||||||
gift_code_sku_id,
|
gift_code_sku_id: self.gift_code_sku_id,
|
||||||
captcha_key,
|
captcha_key: self.captcha_key,
|
||||||
promotional_email_opt_in,
|
promotional_email_opt_in: self.promotional_email_opt_in,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,47 +4,18 @@ use crate::types::entities::{
|
||||||
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
|
AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct MessageSendSchema {
|
pub struct MessageSendSchema {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
message_type: Option<i32>,
|
pub message_type: Option<i32>,
|
||||||
content: Option<String>,
|
pub content: Option<String>,
|
||||||
nonce: Option<String>,
|
pub nonce: Option<String>,
|
||||||
tts: Option<bool>,
|
pub tts: Option<bool>,
|
||||||
embeds: Option<Vec<Embed>>,
|
pub embeds: Option<Vec<Embed>>,
|
||||||
allowed_mentions: Option<AllowedMention>,
|
pub allowed_mentions: Option<AllowedMention>,
|
||||||
message_reference: Option<MessageReference>,
|
pub message_reference: Option<MessageReference>,
|
||||||
components: Option<Vec<Component>>,
|
pub components: Option<Vec<Component>>,
|
||||||
sticker_ids: Option<Vec<String>>,
|
pub sticker_ids: Option<Vec<String>>,
|
||||||
pub attachments: Option<Vec<PartialDiscordFileAttachment>>,
|
pub attachments: Option<Vec<PartialDiscordFileAttachment>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a new() method for MessageSendSchema
|
|
||||||
impl MessageSendSchema {
|
|
||||||
pub fn new(
|
|
||||||
message_type: Option<i32>,
|
|
||||||
content: Option<String>,
|
|
||||||
nonce: Option<String>,
|
|
||||||
tts: Option<bool>,
|
|
||||||
embeds: Option<Vec<Embed>>,
|
|
||||||
allowed_mentions: Option<AllowedMention>,
|
|
||||||
message_reference: Option<MessageReference>,
|
|
||||||
components: Option<Vec<Component>>,
|
|
||||||
sticker_ids: Option<Vec<String>>,
|
|
||||||
attachments: Option<Vec<PartialDiscordFileAttachment>>,
|
|
||||||
) -> MessageSendSchema {
|
|
||||||
MessageSendSchema {
|
|
||||||
message_type,
|
|
||||||
content,
|
|
||||||
nonce,
|
|
||||||
tts,
|
|
||||||
embeds,
|
|
||||||
allowed_mentions,
|
|
||||||
message_reference,
|
|
||||||
components,
|
|
||||||
sticker_ids,
|
|
||||||
attachments,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -65,18 +65,7 @@ mod schemas_tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn consent_false() {
|
fn consent_false() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
RegisterSchema::new(
|
RegisterSchema::builder("Test", false).build(),
|
||||||
"Test".to_string(),
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
Err(FieldFormatError::ConsentError)
|
Err(FieldFormatError::ConsentError)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -91,18 +80,11 @@ mod schemas_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn valid_email() {
|
fn valid_email() {
|
||||||
let reg = RegisterSchema::new(
|
let reg = RegisterSchemaOptions {
|
||||||
"Testy".to_string(),
|
email: Some("me@mail.de".to_string()),
|
||||||
None,
|
..RegisterSchema::builder("Testy", true)
|
||||||
true,
|
}
|
||||||
Some("me@mail.de".to_string()),
|
.build();
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
assert_ne!(reg, Err(FieldFormatError::EmailError));
|
assert_ne!(reg, Err(FieldFormatError::EmailError));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,25 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::types::RelationshipType;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
|
pub struct FriendRequestSendSchema {
|
||||||
|
pub username: String,
|
||||||
|
pub discriminator: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the schema for the Create User Relationship route.
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * relationship_type: The [`RelationshipType`] to create (defaults to -1, which accepts an existing or creates a new friend request)
|
||||||
|
/// * from_friend_suggestion: Whether the relationship was created from a friend suggestion (default false)
|
||||||
|
/// * friend_token: The friend token of the user to add a direct friend relationship to
|
||||||
|
///
|
||||||
|
/// See: [https://discord-userdoccers.vercel.app/resources/user#create-user-relationship](https://discord-userdoccers.vercel.app/resources/user#create-user-relationship)
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
|
pub struct CreateUserRelationshipSchema {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub relationship_type: Option<RelationshipType>,
|
||||||
|
pub from_friend_suggestion: Option<bool>,
|
||||||
|
pub friend_token: Option<String>,
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
pub use regexes::*;
|
pub use regexes::*;
|
||||||
pub use rights::Rights;
|
pub use rights::Rights;
|
||||||
pub use snowflake::{DeconstructedSnowflake, Snowflake};
|
pub use snowflake::Snowflake;
|
||||||
|
|
||||||
pub mod jwt;
|
pub mod jwt;
|
||||||
mod regexes;
|
mod regexes;
|
||||||
|
|
|
@ -1,22 +1,41 @@
|
||||||
use std::fmt::Display;
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
use atomic::Atomic;
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
use bigdecimal::{Num, ToPrimitive, Zero};
|
|
||||||
use num_bigint::{BigInt, ToBigInt};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
#[cfg(feature = "sqlx")]
|
#[cfg(feature = "sqlx")]
|
||||||
use sqlx::Type;
|
use sqlx::Type;
|
||||||
|
|
||||||
|
/// 2015-01-01
|
||||||
const EPOCH: i64 = 1420070400000;
|
const EPOCH: i64 = 1420070400000;
|
||||||
static WORKER_ID: u128 = 0;
|
|
||||||
static PROCESS_ID: u128 = 1;
|
/// Unique identifier including a timestamp.
|
||||||
lazy_static::lazy_static! {
|
/// See https://discord.com/developers/docs/reference#snowflakes
|
||||||
static ref INCREMENT: Atomic<u128> = Atomic::default();
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
}
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
#[cfg_attr(feature = "sqlx", derive(Type))]
|
#[cfg_attr(feature = "sqlx", derive(Type))]
|
||||||
#[cfg_attr(feature = "sqlx", sqlx(transparent))]
|
#[cfg_attr(feature = "sqlx", sqlx(transparent))]
|
||||||
pub struct Snowflake(String);
|
pub struct Snowflake(u64);
|
||||||
|
|
||||||
|
impl Snowflake {
|
||||||
|
pub fn generate() -> Self {
|
||||||
|
const WORKER_ID: u64 = 0;
|
||||||
|
const PROCESS_ID: u64 = 1;
|
||||||
|
static INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
let time = (Utc::now().naive_utc().timestamp_millis() - EPOCH) << 22;
|
||||||
|
let worker = WORKER_ID << 17;
|
||||||
|
let process = PROCESS_ID << 12;
|
||||||
|
let increment = INCREMENT.fetch_add(1, Ordering::Relaxed) as u64 % 32;
|
||||||
|
|
||||||
|
Self(time as u64 | worker | process | increment)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn timestamp(self) -> DateTime<Utc> {
|
||||||
|
Utc.timestamp_millis_opt((self.0 >> 22) as i64 + EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Snowflake {
|
impl Default for Snowflake {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -30,131 +49,59 @@ impl Display for Snowflake {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Snowflake {
|
impl serde::Serialize for Snowflake {
|
||||||
pub fn to_binary(&self) -> String {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
let self_len = self.0.len();
|
where
|
||||||
let high = self.0[..self_len - 10].parse::<u64>().unwrap_or(0);
|
S: serde::Serializer,
|
||||||
let low = self.0[self_len - 10..].parse::<u64>().unwrap();
|
{
|
||||||
let mut low = low;
|
serializer.serialize_str(&self.0.to_string())
|
||||||
let mut high = high;
|
|
||||||
let mut bin = Vec::with_capacity(64);
|
|
||||||
|
|
||||||
while low > 0 || high > 0 {
|
|
||||||
bin.push((low & 1) as u8);
|
|
||||||
low >>= 1;
|
|
||||||
|
|
||||||
if high > 0 {
|
|
||||||
low += 5_000_000_000 * (high % 2);
|
|
||||||
high >>= 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bin.iter()
|
impl<'de> serde::Deserialize<'de> for Snowflake {
|
||||||
.rev()
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
.map(|b| char::from_digit(*b as u32, 10).unwrap())
|
where
|
||||||
.collect()
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct SnowflakeVisitor;
|
||||||
|
impl<'de> serde::de::Visitor<'de> for SnowflakeVisitor {
|
||||||
|
type Value = Snowflake;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("snowflake string")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_binary(num: &str) -> String {
|
fn visit_str<E>(self, value: &str) -> Result<Snowflake, E>
|
||||||
let mut num = BigInt::from_str_radix(num, 2).unwrap();
|
where
|
||||||
let mut dec = Vec::with_capacity(18);
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
let ten = 10.to_bigint().unwrap();
|
match value.parse() {
|
||||||
let _two = 2.to_bigint().unwrap();
|
Ok(value) => Ok(Snowflake(value)),
|
||||||
let _thirty_two = 32.to_bigint().unwrap();
|
Err(_) => Err(serde::de::Error::custom("")),
|
||||||
|
|
||||||
while num.bits() > 50 {
|
|
||||||
let high: BigInt = &num >> 32;
|
|
||||||
let low: BigInt = (high.clone() % &ten) << 32 | &num & BigInt::from((1u64 << 32) - 1);
|
|
||||||
|
|
||||||
let next: BigInt = low.clone() % &ten;
|
|
||||||
dec.push(next.to_u8().unwrap());
|
|
||||||
num = (high / &ten) << 32 | (low / &ten);
|
|
||||||
}
|
|
||||||
|
|
||||||
while !num.is_zero() {
|
|
||||||
dec.push((num.clone() % &ten).to_u8().unwrap());
|
|
||||||
num /= &ten;
|
|
||||||
}
|
|
||||||
|
|
||||||
dec.iter()
|
|
||||||
.rev()
|
|
||||||
.map(|d| char::from_digit(*d as u32, 10).unwrap())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_worker_process() -> u128 {
|
|
||||||
let time = (chrono::Utc::now().naive_utc().timestamp_millis() - EPOCH) << 22;
|
|
||||||
let worker = WORKER_ID << 17;
|
|
||||||
let process = PROCESS_ID << 12;
|
|
||||||
let increment = INCREMENT.load(atomic::Ordering::Relaxed);
|
|
||||||
|
|
||||||
INCREMENT.store(increment + 1, atomic::Ordering::Relaxed);
|
|
||||||
|
|
||||||
time as u128 | worker | process | increment
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate() -> Self {
|
|
||||||
Self(Self::generate_worker_process().to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deconstruct(&self) -> DeconstructedSnowflake {
|
|
||||||
let binary = format!("{:0>64}", self.to_binary());
|
|
||||||
|
|
||||||
let ts = i64::from_str_radix(&binary[0..42], 2).unwrap() + EPOCH;
|
|
||||||
let wid = u64::from_str_radix(&binary[42..47], 2).unwrap();
|
|
||||||
let pid = u64::from_str_radix(&binary[47..52], 2).unwrap();
|
|
||||||
let increment = BigInt::from_str_radix(&binary[52..64], 2).unwrap();
|
|
||||||
|
|
||||||
DeconstructedSnowflake {
|
|
||||||
timestamp: ts,
|
|
||||||
worker_id: wid,
|
|
||||||
process_id: pid,
|
|
||||||
increment,
|
|
||||||
binary,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
deserializer.deserialize_str(SnowflakeVisitor)
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
}
|
||||||
pub struct DeconstructedSnowflake {
|
|
||||||
pub timestamp: i64,
|
|
||||||
pub worker_id: u64,
|
|
||||||
pub process_id: u64,
|
|
||||||
pub increment: BigInt,
|
|
||||||
pub binary: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
use super::Snowflake;
|
use super::Snowflake;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new_snowflake() {
|
fn generate() {
|
||||||
let snow = Snowflake::generate();
|
let snow_1 = Snowflake::generate();
|
||||||
println!("{snow}");
|
let snow_2 = Snowflake::generate();
|
||||||
|
assert!(snow_1.0 < snow_2.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn snowflake_to_binary() {
|
fn timestamp() {
|
||||||
let snowflake = super::Snowflake("1104339392517902336".to_string());
|
let snow: Snowflake = serde_json::from_str("\"175928847299117063\"").unwrap();
|
||||||
|
let timestamp = "2016-04-30 11:18:25.796Z".parse::<DateTime<Utc>>().unwrap();
|
||||||
let bin = snowflake.to_binary();
|
assert_eq!(snow.timestamp(), timestamp);
|
||||||
println!("{bin}");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn binary_to_snowflake() {
|
|
||||||
let snowflake = super::Snowflake::from_binary(
|
|
||||||
"111101010011011001101101001110010010100000000001000000000000",
|
|
||||||
);
|
|
||||||
println!("{snowflake}");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_deconstruct() {
|
|
||||||
let new = super::Snowflake::generate();
|
|
||||||
|
|
||||||
println!("{:?}", new.deconstruct());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
use chorus::types;
|
use chorus::types::{RegisterSchema, RegisterSchemaOptions};
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_registration() {
|
async fn test_registration() {
|
||||||
let mut bundle = common::setup().await;
|
let mut bundle = common::setup().await;
|
||||||
let reg = types::RegisterSchema::new(
|
let reg = RegisterSchemaOptions {
|
||||||
"Hiiii".to_string(),
|
date_of_birth: Some("2000-01-01".to_string()),
|
||||||
None,
|
..RegisterSchema::builder("Hiiii", true)
|
||||||
true,
|
}
|
||||||
None,
|
.build()
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some("2000-01-01".to_string()),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
bundle.instance.register_account(®).await.unwrap();
|
bundle.instance.register_account(®).await.unwrap();
|
||||||
common::teardown(bundle).await;
|
common::teardown(bundle).await;
|
||||||
|
|
|
@ -21,7 +21,7 @@ async fn get_channel() {
|
||||||
async fn delete_channel() {
|
async fn delete_channel() {
|
||||||
let mut bundle = common::setup().await;
|
let mut bundle = common::setup().await;
|
||||||
let result = bundle.channel.clone().delete(&mut bundle.user).await;
|
let result = bundle.channel.clone().delete(&mut bundle.user).await;
|
||||||
assert!(result.is_none());
|
assert!(result.is_ok());
|
||||||
common::teardown(bundle).await
|
common::teardown(bundle).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,14 +72,16 @@ async fn modify_channel() {
|
||||||
bundle.channel.id.to_string().as_str(),
|
bundle.channel.id.to_string().as_str(),
|
||||||
permission_override.clone(),
|
permission_override.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Channel::delete_permission(
|
Channel::delete_permission(
|
||||||
&mut bundle.user,
|
&mut bundle.user,
|
||||||
bundle.channel.id.to_string().as_str(),
|
bundle.channel.id.to_string().as_str(),
|
||||||
&permission_override.id,
|
&permission_override.id,
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
common::teardown(bundle).await
|
common::teardown(bundle).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,14 @@ use chorus::{
|
||||||
instance::{Instance, UserMeta},
|
instance::{Instance, UserMeta},
|
||||||
types::{
|
types::{
|
||||||
Channel, ChannelCreateSchema, Guild, GuildCreateSchema, RegisterSchema,
|
Channel, ChannelCreateSchema, Guild, GuildCreateSchema, RegisterSchema,
|
||||||
RoleCreateModifySchema, RoleObject,
|
RegisterSchemaOptions, RoleCreateModifySchema, RoleObject,
|
||||||
},
|
},
|
||||||
URLBundle,
|
UrlBundle,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TestBundle {
|
pub struct TestBundle {
|
||||||
pub urls: URLBundle,
|
pub urls: UrlBundle,
|
||||||
pub user: UserMeta,
|
pub user: UserMeta,
|
||||||
pub instance: Instance,
|
pub instance: Instance,
|
||||||
pub guild: Guild,
|
pub guild: Guild,
|
||||||
|
@ -19,25 +19,18 @@ pub struct TestBundle {
|
||||||
|
|
||||||
// Set up a test by creating an Instance and a User. Reduces Test boilerplate.
|
// Set up a test by creating an Instance and a User. Reduces Test boilerplate.
|
||||||
pub async fn setup() -> TestBundle {
|
pub async fn setup() -> TestBundle {
|
||||||
let urls = URLBundle::new(
|
let urls = UrlBundle::new(
|
||||||
"http://localhost:3001/api".to_string(),
|
"http://localhost:3001/api".to_string(),
|
||||||
"ws://localhost:3001".to_string(),
|
"ws://localhost:3001".to_string(),
|
||||||
"http://localhost:3001".to_string(),
|
"http://localhost:3001".to_string(),
|
||||||
);
|
);
|
||||||
let mut instance = Instance::new(urls.clone()).await.unwrap();
|
let mut instance = Instance::new(urls.clone()).await.unwrap();
|
||||||
// Requires the existance of the below user.
|
// Requires the existance of the below user.
|
||||||
let reg = RegisterSchema::new(
|
let reg = RegisterSchemaOptions {
|
||||||
"integrationtestuser".to_string(),
|
date_of_birth: Some("2000-01-01".to_string()),
|
||||||
None,
|
..RegisterSchema::builder("integrationtestuser", true)
|
||||||
true,
|
}
|
||||||
None,
|
.build()
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some("2000-01-01".to_string()),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let guild_create_schema = GuildCreateSchema {
|
let guild_create_schema = GuildCreateSchema {
|
||||||
name: Some("Test-Guild!".to_string()),
|
name: Some("Test-Guild!".to_string()),
|
||||||
|
@ -100,6 +93,7 @@ pub async fn setup() -> TestBundle {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Teardown method to clean up after a test.
|
// Teardown method to clean up after a test.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn teardown(mut bundle: TestBundle) {
|
pub async fn teardown(mut bundle: TestBundle) {
|
||||||
Guild::delete(&mut bundle.user, &bundle.guild.id.to_string()).await;
|
Guild::delete(&mut bundle.user, &bundle.guild.id.to_string()).await;
|
||||||
bundle.user.delete().await;
|
bundle.user.delete().await;
|
||||||
|
|
|
@ -20,10 +20,9 @@ async fn guild_creation_deletion() {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
match Guild::delete(&mut bundle.user, &guild.id.to_string()).await {
|
assert!(Guild::delete(&mut bundle.user, &guild.id.to_string())
|
||||||
None => assert!(true),
|
.await
|
||||||
Some(_) => assert!(false),
|
.is_ok());
|
||||||
}
|
|
||||||
common::teardown(bundle).await
|
common::teardown(bundle).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ async fn add_remove_role() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !role_found {
|
if !role_found {
|
||||||
assert!(false)
|
panic!()
|
||||||
}
|
}
|
||||||
chorus::types::GuildMember::remove_role(&mut bundle.user, guild_id, user_id, role_id).await;
|
chorus::types::GuildMember::remove_role(&mut bundle.user, guild_id, user_id, role_id).await;
|
||||||
let member = chorus::types::GuildMember::get(&mut bundle.user, guild_id, user_id)
|
let member = chorus::types::GuildMember::get(&mut bundle.user, guild_id, user_id)
|
||||||
|
@ -28,11 +28,11 @@ async fn add_remove_role() {
|
||||||
if role != role_id {
|
if role != role_id {
|
||||||
role_found = false;
|
role_found = false;
|
||||||
} else {
|
} else {
|
||||||
assert!(false);
|
panic!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if role_found {
|
if role_found {
|
||||||
assert!(false)
|
panic!()
|
||||||
}
|
}
|
||||||
common::teardown(bundle).await
|
common::teardown(bundle).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,10 @@ mod common;
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn send_message() {
|
async fn send_message() {
|
||||||
let mut bundle = common::setup().await;
|
let mut bundle = common::setup().await;
|
||||||
let mut message = types::MessageSendSchema::new(
|
let mut message = types::MessageSendSchema {
|
||||||
None,
|
content: Some("A Message!".to_string()),
|
||||||
Some("A Message!".to_string()),
|
..Default::default()
|
||||||
None,
|
};
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
let _ = bundle
|
let _ = bundle
|
||||||
.user
|
.user
|
||||||
.send_message(&mut message, bundle.channel.id.to_string(), None)
|
.send_message(&mut message, bundle.channel.id.to_string(), None)
|
||||||
|
@ -53,18 +45,11 @@ async fn send_message_attachment() {
|
||||||
content: buffer,
|
content: buffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut message = types::MessageSendSchema::new(
|
let mut message = types::MessageSendSchema {
|
||||||
None,
|
content: Some("trans rights now".to_string()),
|
||||||
Some("trans rights now".to_string()),
|
attachments: Some(vec![attachment.clone()]),
|
||||||
None,
|
..Default::default()
|
||||||
None,
|
};
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(vec![attachment.clone()]),
|
|
||||||
);
|
|
||||||
|
|
||||||
let vec_attach = vec![attachment.clone()];
|
let vec_attach = vec![attachment.clone()];
|
||||||
let _arg = Some(&vec_attach);
|
let _arg = Some(&vec_attach);
|
||||||
|
@ -79,3 +64,20 @@ async fn send_message_attachment() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
common::teardown(bundle).await
|
common::teardown(bundle).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn read_messages() {
|
||||||
|
let mut bundle = common::setup().await;
|
||||||
|
|
||||||
|
// First create some messages to read
|
||||||
|
let mut message = types::MessageSendSchema {
|
||||||
|
content: Some("A Message!".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let _ = bundle
|
||||||
|
.user
|
||||||
|
.send_message(&mut message, bundle.channel.id.to_string(), None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
common::teardown(bundle).await
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
use chorus::types::{self, RegisterSchema, RegisterSchemaOptions, Relationship, RelationshipType};
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_get_mutual_relationships() {
|
||||||
|
let register_schema = RegisterSchemaOptions {
|
||||||
|
date_of_birth: Some("2000-01-01".to_string()),
|
||||||
|
..RegisterSchema::builder("integrationtestuser2", true)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut bundle = common::setup().await;
|
||||||
|
let belongs_to = &mut bundle.instance;
|
||||||
|
let user = &mut bundle.user;
|
||||||
|
let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap();
|
||||||
|
let friend_request_schema = types::FriendRequestSendSchema {
|
||||||
|
username: user.object.username.clone(),
|
||||||
|
discriminator: Some(user.object.discriminator.clone()),
|
||||||
|
};
|
||||||
|
other_user.send_friend_request(friend_request_schema).await;
|
||||||
|
let relationships = user
|
||||||
|
.get_mutual_relationships(&other_user.object.id.to_string())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
println!("{:?}", relationships);
|
||||||
|
common::teardown(bundle).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_get_relationships() {
|
||||||
|
let register_schema = RegisterSchemaOptions {
|
||||||
|
date_of_birth: Some("2000-01-01".to_string()),
|
||||||
|
..RegisterSchema::builder("integrationtestuser2", true)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut bundle = common::setup().await;
|
||||||
|
let belongs_to = &mut bundle.instance;
|
||||||
|
let user = &mut bundle.user;
|
||||||
|
let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap();
|
||||||
|
let friend_request_schema = types::FriendRequestSendSchema {
|
||||||
|
username: user.object.username.clone(),
|
||||||
|
discriminator: Some(user.object.discriminator.clone()),
|
||||||
|
};
|
||||||
|
other_user.send_friend_request(friend_request_schema).await;
|
||||||
|
let relationships = user.get_relationships().await.unwrap();
|
||||||
|
assert_eq!(relationships.get(0).unwrap().id, other_user.object.id);
|
||||||
|
common::teardown(bundle).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_modify_relationship_friends() {
|
||||||
|
let register_schema = RegisterSchemaOptions {
|
||||||
|
date_of_birth: Some("2000-01-01".to_string()),
|
||||||
|
..RegisterSchema::builder("integrationtestuser2", true)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut bundle = common::setup().await;
|
||||||
|
let belongs_to = &mut bundle.instance;
|
||||||
|
let user = &mut bundle.user;
|
||||||
|
let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap();
|
||||||
|
other_user
|
||||||
|
.modify_user_relationship(
|
||||||
|
&user.object.id.to_string(),
|
||||||
|
types::RelationshipType::Friends,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let relationships = user.get_relationships().await.unwrap();
|
||||||
|
assert_eq!(relationships.get(0).unwrap().id, other_user.object.id);
|
||||||
|
assert_eq!(
|
||||||
|
relationships.get(0).unwrap().relationship_type,
|
||||||
|
RelationshipType::Incoming
|
||||||
|
);
|
||||||
|
let relationships = other_user.get_relationships().await.unwrap();
|
||||||
|
assert_eq!(relationships.get(0).unwrap().id, user.object.id);
|
||||||
|
assert_eq!(
|
||||||
|
relationships.get(0).unwrap().relationship_type,
|
||||||
|
RelationshipType::Outgoing
|
||||||
|
);
|
||||||
|
user.modify_user_relationship(
|
||||||
|
other_user.object.id.to_string().as_str(),
|
||||||
|
RelationshipType::Friends,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(
|
||||||
|
other_user
|
||||||
|
.get_relationships()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.relationship_type,
|
||||||
|
RelationshipType::Friends
|
||||||
|
);
|
||||||
|
user.remove_relationship(other_user.object.id.to_string().as_str())
|
||||||
|
.await;
|
||||||
|
assert_eq!(
|
||||||
|
other_user.get_relationships().await.unwrap(),
|
||||||
|
Vec::<Relationship>::new()
|
||||||
|
);
|
||||||
|
common::teardown(bundle).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_modify_relationship_block() {
|
||||||
|
let register_schema = RegisterSchemaOptions {
|
||||||
|
date_of_birth: Some("2000-01-01".to_string()),
|
||||||
|
..RegisterSchema::builder("integrationtestuser2", true)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut bundle = common::setup().await;
|
||||||
|
let belongs_to = &mut bundle.instance;
|
||||||
|
let user = &mut bundle.user;
|
||||||
|
let mut other_user = belongs_to.register_account(®ister_schema).await.unwrap();
|
||||||
|
other_user
|
||||||
|
.modify_user_relationship(
|
||||||
|
&user.object.id.to_string(),
|
||||||
|
types::RelationshipType::Blocked,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let relationships = user.get_relationships().await.unwrap();
|
||||||
|
assert_eq!(relationships, Vec::<Relationship>::new());
|
||||||
|
let relationships = other_user.get_relationships().await.unwrap();
|
||||||
|
assert_eq!(relationships.get(0).unwrap().id, user.object.id);
|
||||||
|
assert_eq!(
|
||||||
|
relationships.get(0).unwrap().relationship_type,
|
||||||
|
RelationshipType::Blocked
|
||||||
|
);
|
||||||
|
other_user
|
||||||
|
.remove_relationship(user.object.id.to_string().as_str())
|
||||||
|
.await;
|
||||||
|
assert_eq!(
|
||||||
|
other_user.get_relationships().await.unwrap(),
|
||||||
|
Vec::<Relationship>::new()
|
||||||
|
);
|
||||||
|
common::teardown(bundle).await
|
||||||
|
}
|
|
@ -25,10 +25,7 @@ async fn create_and_get_roles() {
|
||||||
let expected = types::RoleObject::get_all(&mut bundle.user, &guild_id)
|
let expected = types::RoleObject::get_all(&mut bundle.user, &guild_id)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()[2]
|
||||||
.iter()
|
|
||||||
.nth(2)
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
assert_eq!(role, expected);
|
assert_eq!(role, expected);
|
||||||
|
|
Loading…
Reference in New Issue