Merge stuff

This commit is contained in:
kozabrada123 2023-07-21 14:10:50 +02:00
commit 44d9655e8e
36 changed files with 3344 additions and 1238 deletions

View File

@ -16,19 +16,21 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install dependencies - name: Clone spacebar server
run: | run: |
sudo apt-get update
sudo apt-get install -y git python3 build-essential
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt-get install -y nodejs
git clone https://github.com/bitfl0wer/server.git git clone https://github.com/bitfl0wer/server.git
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
cache-dependency-path: server/package-lock.json
- name: Prepare and start Spacebar server - name: Prepare and start Spacebar server
run: | run: |
npm install npm install
npm run setup npm run setup
npm run start & npm run start &
working-directory: ./server working-directory: ./server
- uses: Swatinem/rust-cache@v2
- name: Build - name: Build
run: cargo build --verbose run: cargo build --verbose
- name: Run tests - name: Run tests

View File

@ -1,20 +0,0 @@
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

53
.github/workflows/rust-clippy.yml vendored Normal file
View File

@ -0,0 +1,53 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# rust-clippy is a tool that runs a bunch of lints to catch common
# mistakes in your Rust code and help improve your Rust code.
# More details at https://github.com/rust-lang/rust-clippy
# and https://rust-lang.github.io/rust-clippy/
name: rust-clippy analyze
on:
push:
branches: [ "main", "preserve/*" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
jobs:
rust-clippy-analyze:
name: Run rust-clippy analyzing
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1
with:
profile: minimal
toolchain: stable
components: clippy
override: true
- name: Install required cargo
run: cargo install clippy-sarif sarif-fmt
- name: Run rust-clippy
run:
cargo clippy
--all-features
--message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt
continue-on-error: true
- name: Upload analysis results to GitHub
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: rust-clippy-results.sarif
wait-for-processing: true

11
.gitignore vendored
View File

@ -2,19 +2,18 @@
# will have compiled files and executables # will have compiled files and executables
/target/ /target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.rs.bk
# Added by cargo # Added by cargo
/target /target
### # IDE specific folders and configs
.vscode/** .vscode/**
.idea/** .idea/**
# macOS
**/.DS_Store

2552
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,34 +10,34 @@ backend = ["poem", "sqlx"]
client = [] client = []
[dependencies] [dependencies]
tokio = {version = "1.28.1"} tokio = {version = "1.29.1", features = ["macros"]}
serde = {version = "1.0.163", features = ["derive"]} serde = {version = "1.0.171", features = ["derive"]}
serde_json = {version= "1.0.96", features = ["raw_value"]} serde_json = {version= "1.0.103", features = ["raw_value"]}
serde-aux = "4.2.0" serde-aux = "4.2.0"
serde_with = "3.0.0" serde_with = "3.0.0"
serde_repr = "0.1.12" serde_repr = "0.1.14"
reqwest = {version = "0.11.16", features = ["multipart"]} reqwest = {version = "0.11.18", features = ["multipart"]}
url = "2.3.1" url = "2.4.0"
chrono = {version = "0.4.24", features = ["serde"]} chrono = {version = "0.4.26", features = ["serde"]}
regex = "1.7.3" regex = "1.9.1"
custom_error = "1.9.2" custom_error = "1.9.2"
native-tls = "0.2.11" native-tls = "0.2.11"
tokio-tungstenite = {version = "0.19.0", features = ["native-tls"]} tokio-tungstenite = {version = "0.19.0", features = ["native-tls"]}
futures-util = "0.3.28" futures-util = "0.3.28"
http = "0.2.9" http = "0.2.9"
openssl = "0.10.52" openssl = "0.10.55"
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.3.3", features = ["serde"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
poem = { version = "1.3.55", optional = true } poem = { version = "1.3.56", 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 }
thiserror = "1.0.40" thiserror = "1.0.43"
jsonwebtoken = "8.3.0" jsonwebtoken = "8.3.0"
log = "0.4.19" log = "0.4.19"
async-trait = "0.1.71" async-trait = "0.1.71"
[dev-dependencies] [dev-dependencies]
tokio = {version = "1.28.1", features = ["full"]} tokio = {version = "1.29.1", features = ["full"]}
lazy_static = "1.4.0" lazy_static = "1.4.0"
rusty-hook = "0.11.2" rusty-hook = "0.11.2"

View File

@ -56,9 +56,9 @@ accepted, if it violates these guidelines or [our Code of Conduct](https://githu
- [x] Channel creation - [x] Channel creation
- [x] Channel deletion - [x] Channel deletion
- [x] [Channel management (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/48) - [x] [Channel management (name, description, icon, etc.)](https://github.com/polyphony-chat/chorus/issues/48)
- [ ] [Join and Leave Guilds](https://github.com/polyphony-chat/chorus/issues/45) - [x] [Join and Leave Guilds](https://github.com/polyphony-chat/chorus/issues/45)
- [ ] [Start DMs](https://github.com/polyphony-chat/chorus/issues/45) - [x] [Start DMs](https://github.com/polyphony-chat/chorus/issues/45)
- [ ] [Group DM creation, deletion and member management](https://github.com/polyphony-chat/chorus/issues/89) - [x] [Group DM creation, deletion and member management](https://github.com/polyphony-chat/chorus/issues/89)
- [ ] [Deleting messages](https://github.com/polyphony-chat/chorus/issues/91) - [ ] [Deleting messages](https://github.com/polyphony-chat/chorus/issues/91)
- [ ] [Message threads](https://github.com/polyphony-chat/chorus/issues/90) - [ ] [Message threads](https://github.com/polyphony-chat/chorus/issues/90)
- [x] [Reactions](https://github.com/polyphony-chat/chorus/issues/85) - [x] [Reactions](https://github.com/polyphony-chat/chorus/issues/85)

View File

@ -1,6 +1,7 @@
use reqwest::Client; use reqwest::Client;
use serde_json::to_string; use serde_json::to_string;
use crate::types::AddChannelRecipientSchema;
use crate::{ use crate::{
api::LimitType, api::LimitType,
errors::{ChorusError, ChorusResult}, errors::{ChorusError, ChorusResult},
@ -83,4 +84,54 @@ impl Channel {
.deserialize_response::<Vec<Message>>(user) .deserialize_response::<Vec<Message>>(user)
.await .await
} }
/// # Reference:
/// Read: <https://discord-userdoccers.vercel.app/resources/channel#add-channel-recipient>
pub async fn add_channel_recipient(
&self,
recipient_id: Snowflake,
user: &mut UserMeta,
add_channel_recipient_schema: Option<AddChannelRecipientSchema>,
) -> ChorusResult<()> {
let mut request = Client::new()
.put(format!(
"{}/channels/{}/recipients/{}/",
user.belongs_to.borrow().urls.api,
self.id,
recipient_id
))
.bearer_auth(user.token());
if let Some(schema) = add_channel_recipient_schema {
request = request.body(to_string(&schema).unwrap());
}
ChorusRequest {
request,
limit_type: LimitType::Channel(self.id),
}
.handle_request_as_result(user)
.await
}
/// # Reference:
/// Read: <https://discord-userdoccers.vercel.app/resources/channel#remove-channel-recipient>
pub async fn remove_channel_recipient(
&self,
recipient_id: Snowflake,
user: &mut UserMeta,
) -> ChorusResult<()> {
let request = Client::new()
.delete(format!(
"{}/channels/{}/recipients/{}/",
user.belongs_to.borrow().urls.api,
self.id,
recipient_id
))
.bearer_auth(user.token());
ChorusRequest {
request,
limit_type: LimitType::Channel(self.id),
}
.handle_request_as_result(user)
.await
}
} }

View File

@ -7,29 +7,23 @@ use crate::api::LimitType;
use crate::errors::ChorusResult; use crate::errors::ChorusResult;
use crate::instance::UserMeta; use crate::instance::UserMeta;
use crate::ratelimiter::ChorusRequest; use crate::ratelimiter::ChorusRequest;
use crate::types::{Message, MessageSendSchema, PartialDiscordFileAttachment, Snowflake}; use crate::types::{Message, MessageSendSchema, Snowflake};
impl Message { impl Message {
/// Sends a message to the Spacebar server. /// Sends a message to the Spacebar server.
/// # Arguments
/// * `url_api` - The URL of the Spacebar server's API.
/// * `message` - The [`Message`] that will be sent to the Spacebar server.
/// # Errors
/// * [`crate::errors::ChorusError`] - If the message cannot be sent.
pub async fn send( pub async fn send(
user: &mut UserMeta, user: &mut UserMeta,
channel_id: Snowflake, channel_id: Snowflake,
message: &mut MessageSendSchema, mut message: MessageSendSchema,
files: Option<Vec<PartialDiscordFileAttachment>>,
) -> ChorusResult<Message> { ) -> ChorusResult<Message> {
let url_api = user.belongs_to.borrow().urls.api.clone(); let url_api = user.belongs_to.borrow().urls.api.clone();
if files.is_none() { if message.attachments.is_none() {
let chorus_request = ChorusRequest { let chorus_request = ChorusRequest {
request: Client::new() request: Client::new()
.post(format!("{}/channels/{}/messages/", url_api, channel_id)) .post(format!("{}/channels/{}/messages/", url_api, channel_id))
.bearer_auth(user.token()) .bearer_auth(user.token())
.body(to_string(message).unwrap()), .body(to_string(&message).unwrap()),
limit_type: LimitType::Channel(channel_id), limit_type: LimitType::Channel(channel_id),
}; };
chorus_request.deserialize_response::<Message>(user).await chorus_request.deserialize_response::<Message>(user).await
@ -38,12 +32,12 @@ impl Message {
attachment.get_mut(index).unwrap().set_id(index as i16); attachment.get_mut(index).unwrap().set_id(index as i16);
} }
let mut form = reqwest::multipart::Form::new(); let mut form = reqwest::multipart::Form::new();
let payload_json = to_string(message).unwrap(); let payload_json = to_string(&message).unwrap();
let payload_field = reqwest::multipart::Part::text(payload_json); let payload_field = reqwest::multipart::Part::text(payload_json);
form = form.part("payload_json", payload_field); form = form.part("payload_json", payload_field);
for (index, attachment) in files.unwrap().into_iter().enumerate() { for (index, attachment) in message.attachments.unwrap().into_iter().enumerate() {
let (attachment_content, current_attachment) = attachment.move_content(); let (attachment_content, current_attachment) = attachment.move_content();
let (attachment_filename, _) = current_attachment.move_filename(); let (attachment_filename, _) = current_attachment.move_filename();
let part_name = format!("files[{}]", index); let part_name = format!("files[{}]", index);
@ -75,19 +69,13 @@ impl Message {
impl UserMeta { impl UserMeta {
/// Sends a message to the Spacebar server. /// Sends a message to the Spacebar server.
/// # Arguments
/// * `url_api` - The URL of the Spacebar server's API.
/// * `message` - The [`Message`] that will be sent to the Spacebar server.
/// # Errors
/// * [`crate::errors::ChorusError`] - If the message cannot be sent.
/// # Notes /// # Notes
/// Shorthand call for Message::send() /// Shorthand call for Message::send()
pub async fn send_message( pub async fn send_message(
&mut self, &mut self,
message: &mut MessageSendSchema, message: MessageSendSchema,
channel_id: Snowflake, channel_id: Snowflake,
files: Option<Vec<PartialDiscordFileAttachment>>, ) -> ChorusResult<Message, crate::errors::ChorusError> {
) -> ChorusResult<Message> { Message::send(self, channel_id, message).await
Message::send(self, channel_id, message, files).await
} }
} }

73
src/api/invites/mod.rs Normal file
View File

@ -0,0 +1,73 @@
use reqwest::Client;
use serde_json::to_string;
use crate::errors::ChorusResult;
use crate::instance::UserMeta;
use crate::ratelimiter::ChorusRequest;
use crate::types::{CreateChannelInviteSchema, GuildInvite, Invite, Snowflake};
impl UserMeta {
/// # Arguments
/// - invite_code: The invite code to accept the invite for.
/// - session_id: The session ID that is accepting the invite, required for guest invites.
///
/// # Reference:
/// Read <https://discord-userdoccers.vercel.app/resources/invite#accept-invite>
pub async fn accept_invite(
&mut self,
invite_code: &str,
session_id: Option<&str>,
) -> ChorusResult<Invite> {
let mut request = ChorusRequest {
request: Client::new()
.post(format!(
"{}/invites/{}/",
self.belongs_to.borrow().urls.api,
invite_code
))
.bearer_auth(self.token()),
limit_type: super::LimitType::Global,
};
if session_id.is_some() {
request.request = request
.request
.body(to_string(session_id.unwrap()).unwrap());
}
request.deserialize_response::<Invite>(self).await
}
/// Note: Spacebar does not yet implement this endpoint.
pub async fn create_user_invite(&mut self, code: Option<&str>) -> ChorusResult<Invite> {
ChorusRequest {
request: Client::new()
.post(format!(
"{}/users/@me/invites/",
self.belongs_to.borrow().urls.api
))
.body(to_string(&code).unwrap())
.bearer_auth(self.token()),
limit_type: super::LimitType::Global,
}
.deserialize_response::<Invite>(self)
.await
}
pub async fn create_guild_invite(
&mut self,
create_channel_invite_schema: CreateChannelInviteSchema,
channel_id: Snowflake,
) -> ChorusResult<GuildInvite> {
ChorusRequest {
request: Client::new()
.post(format!(
"{}/channels/{}/invites/",
self.belongs_to.borrow().urls.api,
channel_id
))
.bearer_auth(self.token())
.body(to_string(&create_channel_invite_schema).unwrap()),
limit_type: super::LimitType::Channel(channel_id),
}
.deserialize_response::<GuildInvite>(self)
.await
}
}

View File

@ -1,10 +1,13 @@
pub use channels::messages::*; pub use channels::messages::*;
pub use guilds::*; pub use guilds::*;
pub use invites::*;
pub use policies::instance::instance::*; pub use policies::instance::instance::*;
pub use policies::instance::ratelimits::*; pub use policies::instance::ratelimits::*;
pub use users::*;
pub mod auth; pub mod auth;
pub mod channels; pub mod channels;
pub mod guilds; pub mod guilds;
pub mod invites;
pub mod policies; pub mod policies;
pub mod users; pub mod users;

View File

@ -1,11 +1,13 @@
use std::hash::Hash; use std::hash::Hash;
use serde::{Deserialize, Serialize};
use crate::types::Snowflake; use crate::types::Snowflake;
/// The different types of ratelimits that can be applied to a request. Includes "Baseline"-variants /// The different types of ratelimits that can be applied to a request. Includes "Baseline"-variants
/// for when the Snowflake is not yet known. /// for when the Snowflake is not yet known.
/// See <https://discord.com/developers/docs/topics/rate-limits#rate-limits> for more information. /// See <https://discord.com/developers/docs/topics/rate-limits#rate-limits> for more information.
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default, Hash)] #[derive(Clone, Copy, Eq, PartialEq, Debug, Default, Hash, Serialize, Deserialize)]
pub enum LimitType { pub enum LimitType {
AuthRegister, AuthRegister,
AuthLogin, AuthLogin,
@ -23,7 +25,7 @@ pub enum LimitType {
/// A struct that represents the current ratelimits, either instance-wide or user-wide. /// A struct that represents the current ratelimits, either instance-wide or user-wide.
/// See <https://discord.com/developers/docs/topics/rate-limits#rate-limits> for more information. /// See <https://discord.com/developers/docs/topics/rate-limits#rate-limits> for more information.
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Limit { pub struct Limit {
pub bucket: LimitType, pub bucket: LimitType,
pub limit: u64, pub limit: u64,

32
src/api/users/channels.rs Normal file
View File

@ -0,0 +1,32 @@
use reqwest::Client;
use serde_json::to_string;
use crate::{
api::LimitType,
errors::ChorusResult,
instance::UserMeta,
ratelimiter::ChorusRequest,
types::{Channel, PrivateChannelCreateSchema},
};
impl UserMeta {
/// Creates a DM channel or group DM channel.
///
/// # Reference:
/// Read <https://discord-userdoccers.vercel.app/resources/channel#create-private-channel>
pub async fn create_private_channel(
&mut self,
create_private_channel_schema: PrivateChannelCreateSchema,
) -> ChorusResult<Channel> {
let url = format!("{}/users/@me/channels", self.belongs_to.borrow().urls.api);
ChorusRequest {
request: Client::new()
.post(url)
.bearer_auth(self.token())
.body(to_string(&create_private_channel_schema).unwrap()),
limit_type: LimitType::Global,
}
.deserialize_response::<Channel>(self)
.await
}
}

30
src/api/users/guilds.rs Normal file
View File

@ -0,0 +1,30 @@
use reqwest::Client;
use serde_json::to_string;
use crate::errors::ChorusResult;
use crate::instance::UserMeta;
use crate::ratelimiter::ChorusRequest;
use crate::types::Snowflake;
impl UserMeta {
/// # Arguments:
/// - lurking: Whether the user is lurking in the guild
///
/// # Reference:
/// Read <https://discord-userdoccers.vercel.app/resources/guild#leave-guild>
pub async fn leave_guild(&mut self, guild_id: &Snowflake, lurking: bool) -> ChorusResult<()> {
ChorusRequest {
request: Client::new()
.delete(format!(
"{}/users/@me/guilds/{}/",
self.belongs_to.borrow().urls.api,
guild_id
))
.bearer_auth(self.token())
.body(to_string(&lurking).unwrap()),
limit_type: crate::api::LimitType::Guild(*guild_id),
}
.handle_request_as_result(self)
.await
}
}

View File

@ -1,5 +1,9 @@
pub use channels::*;
pub use guilds::*;
pub use relationships::*; pub use relationships::*;
pub use users::*; pub use users::*;
pub mod channels;
pub mod guilds;
pub mod relationships; pub mod relationships;
pub mod users; pub mod users;

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ pub struct Instance {
pub client: Client, pub client: Client,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LimitsInformation { pub struct LimitsInformation {
pub ratelimits: HashMap<LimitType, Limit>, pub ratelimits: HashMap<LimitType, Limit>,
pub configuration: RateLimits, pub configuration: RateLimits,
@ -81,7 +81,7 @@ impl fmt::Display for Token {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct UserMeta { pub struct UserMeta {
pub belongs_to: Rc<RefCell<Instance>>, pub belongs_to: Rc<RefCell<Instance>>,
pub token: String, pub token: String,

View File

@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use log; use log::{self, debug};
use reqwest::{Client, RequestBuilder, Response}; use reqwest::{Client, RequestBuilder, Response};
use serde::Deserialize; use serde::Deserialize;
use serde_json::from_str; use serde_json::from_str;
@ -37,7 +37,10 @@ impl ChorusRequest {
.execute(self.request.build().unwrap()) .execute(self.request.build().unwrap())
.await .await
{ {
Ok(result) => result, Ok(result) => {
debug!("Request successful: {:?}", result);
result
}
Err(error) => { Err(error) => {
log::warn!("Request failed: {:?}", error); log::warn!("Request failed: {:?}", error);
return Err(ChorusError::RequestFailed { return Err(ChorusError::RequestFailed {
@ -430,6 +433,7 @@ impl ChorusRequest {
user: &mut UserMeta, user: &mut UserMeta,
) -> ChorusResult<T> { ) -> ChorusResult<T> {
let response = self.send_request(user).await?; let response = self.send_request(user).await?;
debug!("Got response: {:?}", response);
let response_text = match response.text().await { let response_text = match response.text().await {
Ok(string) => string, Ok(string) => string,
Err(e) => { Err(e) => {
@ -446,8 +450,8 @@ impl ChorusRequest {
Err(e) => { Err(e) => {
return Err(ChorusError::InvalidResponse { return Err(ChorusError::InvalidResponse {
error: format!( error: format!(
"Error while trying to deserialize the JSON response into T: {}", "Error while trying to deserialize the JSON response into requested type T: {}. JSON Response: {}",
e e, response_text
), ),
}) })
} }

View File

@ -10,7 +10,7 @@ use sqlx::{
database::{HasArguments, HasValueRef}, database::{HasArguments, HasValueRef},
encode::IsNull, encode::IsNull,
error::BoxDynError, error::BoxDynError,
Decode, Encode, MySql, Decode, MySql,
}; };
use crate::types::config::types::subconfigs::guild::{ use crate::types::config::types::subconfigs::guild::{
@ -139,7 +139,7 @@ pub enum GuildFeatures {
InvitesClosed, InvitesClosed,
} }
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, Eq)]
pub struct GuildFeaturesList(Vec<GuildFeatures>); pub struct GuildFeaturesList(Vec<GuildFeatures>);
impl Deref for GuildFeaturesList { impl Deref for GuildFeaturesList {

View File

@ -11,58 +11,58 @@ use crate::types::{
#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct Channel { pub struct Channel {
pub id: Snowflake,
pub created_at: Option<chrono::DateTime<Utc>>,
#[serde(rename = "type")]
pub channel_type: ChannelType,
pub guild_id: Option<Snowflake>,
pub position: Option<i32>,
#[cfg(feature = "sqlx")]
pub permission_overwrites: Option<sqlx::types::Json<Vec<PermissionOverwrite>>>,
#[cfg(not(feature = "sqlx"))]
pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
pub name: Option<String>,
pub topic: Option<String>,
pub nsfw: Option<bool>,
pub last_message_id: Option<Snowflake>,
pub bitrate: Option<i32>,
pub user_limit: Option<i32>,
pub rate_limit_per_user: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub recipients: Option<Vec<User>>,
pub icon: Option<String>,
pub owner_id: Option<Snowflake>,
pub application_id: Option<Snowflake>, pub application_id: Option<Snowflake>,
pub managed: Option<bool>,
pub parent_id: Option<Snowflake>,
pub last_pin_timestamp: Option<String>,
pub rtc_region: Option<String>,
pub video_quality_mode: Option<i32>,
pub message_count: Option<i32>,
pub member_count: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub thread_metadata: Option<ThreadMetadata>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member: Option<ThreadMember>,
pub default_auto_archive_duration: Option<i32>,
pub permissions: Option<String>,
pub flags: Option<i32>,
pub total_message_sent: Option<i32>,
#[cfg(feature = "sqlx")]
pub available_tags: Option<sqlx::types::Json<Vec<Tag>>>,
#[cfg(not(feature = "sqlx"))]
pub available_tags: Option<Vec<Tag>>,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub applied_tags: Option<sqlx::types::Json<Vec<String>>>, pub applied_tags: Option<sqlx::types::Json<Vec<String>>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
pub applied_tags: Option<Vec<String>>, pub applied_tags: Option<Vec<String>>,
#[cfg(feature = "sqlx")] #[cfg(feature = "sqlx")]
pub available_tags: Option<sqlx::types::Json<Vec<Tag>>>,
#[cfg(not(feature = "sqlx"))]
pub available_tags: Option<Vec<Tag>>,
pub bitrate: Option<i32>,
#[serde(rename = "type")]
pub channel_type: ChannelType,
pub created_at: Option<chrono::DateTime<Utc>>,
pub default_auto_archive_duration: Option<i32>,
pub default_forum_layout: Option<i32>,
#[cfg(feature = "sqlx")]
pub default_reaction_emoji: Option<sqlx::types::Json<DefaultReaction>>, pub default_reaction_emoji: Option<sqlx::types::Json<DefaultReaction>>,
#[cfg(not(feature = "sqlx"))] #[cfg(not(feature = "sqlx"))]
pub default_reaction_emoji: Option<DefaultReaction>, pub default_reaction_emoji: Option<DefaultReaction>,
pub default_thread_rate_limit_per_user: Option<i32>,
pub default_sort_order: Option<i32>, pub default_sort_order: Option<i32>,
pub default_forum_layout: Option<i32>, pub default_thread_rate_limit_per_user: Option<i32>,
pub flags: Option<i32>,
pub guild_id: Option<Snowflake>,
pub icon: Option<String>,
pub id: Snowflake,
pub last_message_id: Option<Snowflake>,
pub last_pin_timestamp: Option<String>,
pub managed: Option<bool>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub member: Option<ThreadMember>,
pub member_count: Option<i32>,
pub message_count: Option<i32>,
pub name: Option<String>,
pub nsfw: Option<bool>,
pub owner_id: Option<Snowflake>,
pub parent_id: Option<Snowflake>,
#[cfg(feature = "sqlx")]
pub permission_overwrites: Option<sqlx::types::Json<Vec<PermissionOverwrite>>>,
#[cfg(not(feature = "sqlx"))]
pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
pub permissions: Option<String>,
pub position: Option<i32>,
pub rate_limit_per_user: Option<i32>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub recipients: Option<Vec<User>>,
pub rtc_region: Option<String>,
#[cfg_attr(feature = "sqlx", sqlx(skip))]
pub thread_metadata: Option<ThreadMetadata>,
pub topic: Option<String>,
pub total_message_sent: Option<i32>,
pub user_limit: Option<i32>,
pub video_quality_mode: Option<i32>,
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)] #[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
@ -74,7 +74,7 @@ pub struct Tag {
pub emoji_name: Option<String>, pub emoji_name: Option<String>,
} }
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd)]
pub struct PermissionOverwrite { pub struct PermissionOverwrite {
pub id: Snowflake, pub id: Snowflake,
#[serde(rename = "type")] #[serde(rename = "type")]

View File

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use crate::types::entities::User; use crate::types::entities::User;
use crate::types::Snowflake; use crate::types::Snowflake;
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, Default)] #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, Default)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct Emoji { pub struct Emoji {
pub id: Option<Snowflake>, pub id: Option<Snowflake>,

View File

@ -91,7 +91,7 @@ pub struct Guild {
} }
/// See <https://docs.spacebar.chat/routes/#get-/guilds/-guild_id-/bans/-user-> /// See <https://docs.spacebar.chat/routes/#get-/guilds/-guild_id-/bans/-user->
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct GuildBan { pub struct GuildBan {
pub user_id: Snowflake, pub user_id: Snowflake,

View File

@ -0,0 +1,75 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::{Snowflake, WelcomeScreenObject};
use super::guild::GuildScheduledEvent;
use super::{Application, Channel, GuildMember, User};
/// Represents a code that when used, adds a user to a guild or group DM channel, or creates a relationship between two users.
/// See <https://discord-userdoccers.vercel.app/resources/invite#invite-object>
#[derive(Debug, Serialize, Deserialize)]
pub struct Invite {
pub approximate_member_count: Option<i32>,
pub approximate_presence_count: Option<i32>,
pub channel: Option<Channel>,
pub code: String,
pub created_at: Option<DateTime<Utc>>,
pub expires_at: Option<DateTime<Utc>>,
pub flags: Option<i32>,
pub guild: Option<InviteGuild>,
pub guild_id: Option<Snowflake>,
pub guild_scheduled_event: Option<GuildScheduledEvent>,
#[serde(rename = "type")]
pub invite_type: Option<i32>,
pub inviter: Option<User>,
pub max_age: Option<i32>,
pub max_uses: Option<i32>,
pub stage_instance: Option<InviteStageInstance>,
pub target_application: Option<Application>,
pub target_type: Option<i32>,
pub target_user: Option<User>,
pub temporary: Option<bool>,
pub uses: Option<i32>,
}
/// The guild an invite is for.
/// See <https://discord-userdoccers.vercel.app/resources/invite#invite-guild-object>
#[derive(Debug, Serialize, Deserialize)]
pub struct InviteGuild {
pub id: Snowflake,
pub name: String,
pub icon: Option<String>,
pub splash: Option<String>,
pub verification_level: i32,
pub features: Vec<String>,
pub vanity_url_code: Option<String>,
pub description: Option<String>,
pub banner: Option<String>,
pub premium_subscription_count: Option<i32>,
#[serde(rename = "nsfw")]
#[serde(skip_serializing_if = "Option::is_none")]
pub nsfw_deprecated: Option<bool>,
pub nsfw_level: NSFWLevel,
pub welcome_screen: Option<WelcomeScreenObject>,
}
/// See <https://discord-userdoccers.vercel.app/resources/guild#nsfw-level> for an explanation on what
/// the levels mean.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum NSFWLevel {
Default = 0,
Explicit = 1,
Safe = 2,
AgeRestricted = 3,
}
/// See <https://discord-userdoccers.vercel.app/resources/invite#invite-stage-instance-object>
#[derive(Debug, Serialize, Deserialize)]
pub struct InviteStageInstance {
pub members: Vec<GuildMember>,
pub participant_count: i32,
pub speaker_count: i32,
pub topic: String,
}

View File

@ -8,6 +8,7 @@ pub use emoji::*;
pub use guild::*; pub use guild::*;
pub use guild_member::*; pub use guild_member::*;
pub use integration::*; pub use integration::*;
pub use invite::*;
pub use message::*; pub use message::*;
pub use relationship::*; pub use relationship::*;
pub use role::*; pub use role::*;
@ -31,6 +32,7 @@ mod emoji;
mod guild; mod guild;
mod guild_member; mod guild_member;
mod integration; mod integration;
mod invite;
mod message; mod message;
mod relationship; mod relationship;
mod role; mod role;

View File

@ -43,9 +43,9 @@ pub struct User {
pub bio: Option<String>, pub bio: Option<String>,
pub theme_colors: Option<Vec<u8>>, pub theme_colors: Option<Vec<u8>>,
pub phone: Option<String>, pub phone: Option<String>,
pub nsfw_allowed: bool, pub nsfw_allowed: Option<bool>,
pub premium: bool, pub premium: Option<bool>,
pub purchased_flags: i32, pub purchased_flags: Option<i32>,
pub premium_usage_flags: Option<i32>, pub premium_usage_flags: Option<i32>,
pub disabled: Option<bool>, pub disabled: Option<bool>,
} }

View File

@ -14,8 +14,7 @@ impl WebSocketEvent for GatewayHello {}
/// Contains info on how often the client should send heartbeats to the server; /// Contains info on how often the client should send heartbeats to the server;
pub struct HelloData { pub struct HelloData {
/// How often a client should send heartbeats, in milliseconds /// How often a client should send heartbeats, in milliseconds
// u128 because std used u128s for milliseconds pub heartbeat_interval: u64,
pub heartbeat_interval: u128,
} }
impl WebSocketEvent for HelloData {} impl WebSocketEvent for HelloData {}

View File

@ -1,8 +1,9 @@
use bitflags::bitflags;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::{entities::PermissionOverwrite, Snowflake}; use crate::types::{entities::PermissionOverwrite, Snowflake};
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, Default, PartialEq, PartialOrd)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct ChannelCreateSchema { pub struct ChannelCreateSchema {
pub name: String, pub name: String,
@ -26,7 +27,7 @@ pub struct ChannelCreateSchema {
pub video_quality_mode: Option<i32>, pub video_quality_mode: Option<i32>,
} }
#[derive(Debug, Deserialize, Serialize, Clone, Default)] #[derive(Debug, Deserialize, Serialize, Clone, Default, PartialEq, PartialOrd)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct ChannelModifySchema { pub struct ChannelModifySchema {
pub name: Option<String>, pub name: Option<String>,
@ -48,7 +49,7 @@ pub struct ChannelModifySchema {
pub video_quality_mode: Option<i32>, pub video_quality_mode: Option<i32>,
} }
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
pub struct GetChannelMessagesSchema { pub struct GetChannelMessagesSchema {
/// Between 1 and 100, defaults to 50. /// Between 1 and 100, defaults to 50.
pub limit: Option<i32>, pub limit: Option<i32>,
@ -56,7 +57,7 @@ pub struct GetChannelMessagesSchema {
pub anchor: ChannelMessagesAnchor, pub anchor: ChannelMessagesAnchor,
} }
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub enum ChannelMessagesAnchor { pub enum ChannelMessagesAnchor {
Before(Snowflake), Before(Snowflake),
@ -94,3 +95,56 @@ impl GetChannelMessagesSchema {
} }
} }
} }
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)]
pub struct CreateChannelInviteSchema {
pub flags: Option<InviteFlags>,
pub max_age: Option<u32>,
pub max_uses: Option<u8>,
pub temporary: Option<bool>,
pub unique: Option<bool>,
pub validate: Option<String>,
pub target_type: Option<InviteType>,
pub target_user_id: Option<Snowflake>,
pub target_application_id: Option<Snowflake>,
}
impl Default for CreateChannelInviteSchema {
fn default() -> Self {
Self {
flags: None,
max_age: Some(86400),
max_uses: Some(0),
temporary: Some(false),
unique: Some(false),
validate: None,
target_type: None,
target_user_id: None,
target_application_id: None,
}
}
}
bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
pub struct InviteFlags: u64 {
const GUEST = 1 << 0;
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum InviteType {
#[default]
Stream = 1,
EmbeddedApplication = 2,
RoleSubscriptions = 3,
CreatorPage = 4,
}
/// See <https://discord-userdoccers.vercel.app/resources/channel#add-channel-recipient>
#[derive(Debug, Deserialize, Serialize, Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
pub struct AddChannelRecipientSchema {
pub access_token: Option<String>,
pub nick: Option<String>,
}

View File

@ -1,5 +1,9 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::types::Snowflake;
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
pub struct UserModifySchema { pub struct UserModifySchema {
@ -14,3 +18,17 @@ pub struct UserModifySchema {
pub email: Option<String>, pub email: Option<String>,
pub discriminator: Option<i16>, pub discriminator: Option<i16>,
} }
/// # Attributes:
/// - recipients: The users to include in the private channel
/// - access_tokens: The access tokens of users that have granted your app the `gdm.join` scope. Only usable for OAuth2 requests (which can only create group DMs).
/// - nicks: A mapping of user IDs to their respective nicknames. Only usable for OAuth2 requests (which can only create group DMs).
///
/// # Reference:
/// Read: <https://discord-userdoccers.vercel.app/resources/channel#json-params>
#[derive(Debug, Deserialize, Serialize)]
pub struct PrivateChannelCreateSchema {
pub recipients: Option<Vec<Snowflake>>,
pub access_tokens: Option<Vec<String>>,
pub nicks: Option<HashMap<Snowflake, String>>,
}

View File

@ -12,7 +12,7 @@ const EPOCH: i64 = 1420070400000;
/// Unique identifier including a timestamp. /// Unique identifier including a timestamp.
/// See <https://discord.com/developers/docs/reference#snowflakes> /// See <https://discord.com/developers/docs/reference#snowflakes>
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[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(u64); pub struct Snowflake(u64);

View File

@ -1,6 +1,6 @@
use chorus::types::{ use chorus::types::{
self, Channel, GetChannelMessagesSchema, MessageSendSchema, PermissionFlags, self, Channel, GetChannelMessagesSchema, MessageSendSchema, PermissionFlags,
PermissionOverwrite, Snowflake, PermissionOverwrite, PrivateChannelCreateSchema, RelationshipType, Snowflake,
}; };
mod common; mod common;
@ -89,12 +89,11 @@ async fn get_channel_messages() {
let _ = bundle let _ = bundle
.user .user
.send_message( .send_message(
&mut MessageSendSchema { MessageSendSchema {
content: Some("A Message!".to_string()), content: Some("A Message!".to_string()),
..Default::default() ..Default::default()
}, },
bundle.channel.id, bundle.channel.id,
None,
) )
.await .await
.unwrap(); .unwrap();
@ -136,3 +135,81 @@ async fn get_channel_messages() {
common::teardown(bundle).await common::teardown(bundle).await
} }
#[tokio::test]
async fn create_dm() {
let mut bundle = common::setup().await;
let other_user = bundle.create_user("integrationtestuser2").await;
let user = &mut bundle.user;
let private_channel_create_schema = PrivateChannelCreateSchema {
recipients: Some(Vec::from([other_user.object.id])),
access_tokens: None,
nicks: None,
};
let dm_channel = user
.create_private_channel(private_channel_create_schema)
.await
.unwrap();
assert!(dm_channel.recipients.is_some());
assert_eq!(
dm_channel.recipients.as_ref().unwrap().get(0).unwrap().id,
other_user.object.id
);
assert_eq!(
dm_channel.recipients.as_ref().unwrap().get(1).unwrap().id,
user.object.id
);
common::teardown(bundle).await;
}
// #[tokio::test]
// This test currently is broken due to an issue with the Spacebar Server.
#[allow(dead_code)]
async fn remove_add_person_from_to_dm() {
let mut bundle = common::setup().await;
let mut other_user = bundle.create_user("integrationtestuser2").await;
let mut third_user = bundle.create_user("integrationtestuser3").await;
let user = &mut bundle.user;
let private_channel_create_schema = PrivateChannelCreateSchema {
recipients: Some(Vec::from([other_user.object.id, third_user.object.id])),
access_tokens: None,
nicks: None,
};
let dm_channel = user
.create_private_channel(private_channel_create_schema)
.await
.unwrap(); // Creates the Channel and stores the response Channel object
dm_channel
.remove_channel_recipient(other_user.object.id, user)
.await
.unwrap();
assert!(dm_channel.recipients.as_ref().unwrap().get(1).is_none());
other_user
.modify_user_relationship(user.object.id, RelationshipType::Friends)
.await
.unwrap();
user.modify_user_relationship(other_user.object.id, RelationshipType::Friends)
.await
.unwrap();
third_user
.modify_user_relationship(user.object.id, RelationshipType::Friends)
.await
.unwrap();
user.modify_user_relationship(third_user.object.id, RelationshipType::Friends)
.await
.unwrap();
// Users 1-2 and 1-3 are now friends
dm_channel
.add_channel_recipient(other_user.object.id, user, None)
.await
.unwrap();
assert!(dm_channel.recipients.is_some());
assert_eq!(
dm_channel.recipients.as_ref().unwrap().get(0).unwrap().id,
other_user.object.id
);
assert_eq!(
dm_channel.recipients.as_ref().unwrap().get(1).unwrap().id,
user.object.id
);
}

View File

@ -7,8 +7,9 @@ use chorus::{
UrlBundle, UrlBundle,
}; };
#[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
pub struct TestBundle { pub(crate) struct TestBundle {
pub urls: UrlBundle, pub urls: UrlBundle,
pub user: UserMeta, pub user: UserMeta,
pub instance: Instance, pub instance: Instance,
@ -17,8 +18,24 @@ pub struct TestBundle {
pub channel: Channel, pub channel: Channel,
} }
impl TestBundle {
#[allow(unused)]
pub(crate) async fn create_user(&mut self, username: &str) -> UserMeta {
let register_schema = RegisterSchema {
username: username.to_string(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()),
..Default::default()
};
self.instance
.register_account(&register_schema)
.await
.unwrap()
}
}
// 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(crate) 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(),
@ -93,7 +110,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)] #[allow(dead_code)]
pub async fn teardown(mut bundle: TestBundle) { pub(crate) async fn teardown(mut bundle: TestBundle) {
Guild::delete(&mut bundle.user, bundle.guild.id) Guild::delete(&mut bundle.user, bundle.guild.id)
.await .await
.unwrap(); .unwrap();

25
tests/invites.rs Normal file
View File

@ -0,0 +1,25 @@
use chorus::types::CreateChannelInviteSchema;
mod common;
#[tokio::test]
async fn create_accept_invite() {
let mut bundle = common::setup().await;
let channel = bundle.channel.clone();
let mut user = bundle.user.clone();
let create_channel_invite_schema = CreateChannelInviteSchema::default();
let mut other_user = bundle.create_user("testuser1312").await;
assert!(chorus::types::Guild::get(bundle.guild.id, &mut other_user)
.await
.is_err());
let invite = user
.create_guild_invite(create_channel_invite_schema, channel.id)
.await
.unwrap();
other_user.accept_invite(&invite.code, None).await.unwrap();
assert!(chorus::types::Guild::get(bundle.guild.id, &mut other_user)
.await
.is_ok());
common::teardown(bundle).await;
}

View File

@ -8,13 +8,13 @@ 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 { let message = types::MessageSendSchema {
content: Some("A Message!".to_string()), content: Some("A Message!".to_string()),
..Default::default() ..Default::default()
}; };
let _ = bundle let _ = bundle
.user .user
.send_message(&mut message, bundle.channel.id, None) .send_message(message, bundle.channel.id)
.await .await
.unwrap(); .unwrap();
common::teardown(bundle).await common::teardown(bundle).await
@ -45,7 +45,7 @@ async fn send_message_attachment() {
content: buffer, content: buffer,
}; };
let mut message = types::MessageSendSchema { let message = types::MessageSendSchema {
content: Some("trans rights now".to_string()), content: Some("trans rights now".to_string()),
attachments: Some(vec![attachment.clone()]), attachments: Some(vec![attachment.clone()]),
..Default::default() ..Default::default()
@ -55,11 +55,7 @@ async fn send_message_attachment() {
let _arg = Some(&vec_attach); let _arg = Some(&vec_attach);
bundle bundle
.user .user
.send_message( .send_message(message, bundle.channel.id)
&mut message,
bundle.channel.id,
Some(vec![attachment.clone()]),
)
.await .await
.unwrap(); .unwrap();
common::teardown(bundle).await common::teardown(bundle).await

View File

@ -1,20 +1,12 @@
use chorus::types::{self, RegisterSchema, Relationship, RelationshipType}; use chorus::types::{self, Relationship, RelationshipType};
mod common; mod common;
#[tokio::test] #[tokio::test]
async fn test_get_mutual_relationships() { async fn test_get_mutual_relationships() {
let register_schema = RegisterSchema {
username: "integrationtestuser2".to_string(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()),
..Default::default()
};
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let belongs_to = &mut bundle.instance; let mut other_user = bundle.create_user("integrationtestuser2").await;
let user = &mut bundle.user; let user = &mut bundle.user;
let mut other_user = belongs_to.register_account(&register_schema).await.unwrap();
let friend_request_schema = types::FriendRequestSendSchema { let friend_request_schema = types::FriendRequestSendSchema {
username: user.object.username.clone(), username: user.object.username.clone(),
discriminator: Some(user.object.discriminator.clone()), discriminator: Some(user.object.discriminator.clone()),
@ -30,17 +22,9 @@ async fn test_get_mutual_relationships() {
#[tokio::test] #[tokio::test]
async fn test_get_relationships() { async fn test_get_relationships() {
let register_schema = RegisterSchema {
username: "integrationtestuser2".to_string(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()),
..Default::default()
};
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let belongs_to = &mut bundle.instance; let mut other_user = bundle.create_user("integrationtestuser2").await;
let user = &mut bundle.user; let user = &mut bundle.user;
let mut other_user = belongs_to.register_account(&register_schema).await.unwrap();
let friend_request_schema = types::FriendRequestSendSchema { let friend_request_schema = types::FriendRequestSendSchema {
username: user.object.username.clone(), username: user.object.username.clone(),
discriminator: Some(user.object.discriminator.clone()), discriminator: Some(user.object.discriminator.clone()),
@ -56,17 +40,9 @@ async fn test_get_relationships() {
#[tokio::test] #[tokio::test]
async fn test_modify_relationship_friends() { async fn test_modify_relationship_friends() {
let register_schema = RegisterSchema {
username: "integrationtestuser2".to_string(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()),
..Default::default()
};
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let belongs_to = &mut bundle.instance; let mut other_user = bundle.create_user("integrationtestuser2").await;
let user = &mut bundle.user; let user = &mut bundle.user;
let mut other_user = belongs_to.register_account(&register_schema).await.unwrap();
let _ = other_user let _ = other_user
.modify_user_relationship(user.object.id, types::RelationshipType::Friends) .modify_user_relationship(user.object.id, types::RelationshipType::Friends)
.await; .await;
@ -105,17 +81,9 @@ async fn test_modify_relationship_friends() {
#[tokio::test] #[tokio::test]
async fn test_modify_relationship_block() { async fn test_modify_relationship_block() {
let register_schema = RegisterSchema {
username: "integrationtestuser2".to_string(),
consent: true,
date_of_birth: Some("2000-01-01".to_string()),
..Default::default()
};
let mut bundle = common::setup().await; let mut bundle = common::setup().await;
let belongs_to = &mut bundle.instance; let mut other_user = bundle.create_user("integrationtestuser2").await;
let user = &mut bundle.user; let user = &mut bundle.user;
let mut other_user = belongs_to.register_account(&register_schema).await.unwrap();
let _ = other_user let _ = other_user
.modify_user_relationship(user.object.id, types::RelationshipType::Blocked) .modify_user_relationship(user.object.id, types::RelationshipType::Blocked)
.await; .await;