Types refactor

This commit is contained in:
kozabrada123 2023-05-26 19:47:41 +02:00
commit 57c69e9dac
237 changed files with 9162 additions and 2769 deletions

View File

@ -5,11 +5,11 @@ license = "AGPL-3"
edition = "2021"
[dependencies]
tokio = {version = "1.27.0", features = ["rt", "macros", "rt-multi-thread"]}
tokio = {version = "1.28.1", features = ["rt", "macros", "rt-multi-thread", "full"]}
serde = {version = "1.0.163", features = ["derive"]}
serde_json = {version= "1.0.96", features = ["raw_value"]}
serde-aux = "4.2.0"
serde_repr = "0.1"
serde_repr = "0.1.12"
reqwest = {version = "0.11.16", features = ["multipart"]}
url = "2.3.1"
chrono = {version = "0.4.24", features = ["serde"]}
@ -19,6 +19,18 @@ native-tls = "0.2.11"
tokio-tungstenite = {version = "0.19.0", features = ["native-tls"]}
futures-util = "0.3.28"
http = "0.2.9"
openssl = "0.10.52"
base64 = "0.21.2"
hostname = "0.3.1"
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"
poem = { version = "1.3.55", optional = true }
sqlx = { version = "0.6.3", features = ["mysql", "sqlite", "json", "chrono", "ipnetwork", "runtime-tokio-native-tls", "any"], optional = true }
thiserror = "1.0.40"
jsonwebtoken = "8.3.0"
[dev-dependencies]
lazy_static = "1.4.0"

View File

@ -6,17 +6,16 @@ pub mod login {
use serde_json::{from_str, json};
use crate::api::limits::LimitType;
use crate::api::schemas::LoginSchema;
use crate::api::types::{ErrorResponse, LoginResult};
use crate::errors::InstanceServerError;
use crate::instance::Instance;
use crate::instance::{Instance, UserMeta};
use crate::limit::LimitedRequester;
use crate::types::{ErrorResponse, LoginResult, LoginSchema};
impl Instance {
pub async fn login_account(
&mut self,
login_schema: &LoginSchema,
) -> Result<crate::api::types::User, InstanceServerError> {
) -> Result<UserMeta, InstanceServerError> {
let mut requester = LimitedRequester::new().await;
let json_schema = json!(login_schema);
let client = Client::new();
@ -59,7 +58,7 @@ pub mod login {
.get_user(login_result.token.clone(), None)
.await
.unwrap();
let user = crate::api::types::User::new(
let user = UserMeta::new(
Rc::new(RefCell::new(self.clone())),
login_result.token,
cloned_limits,

View File

@ -5,10 +5,11 @@ pub mod register {
use serde_json::{from_str, json};
use crate::{
api::{limits::LimitType, schemas::RegisterSchema, types::ErrorResponse, Token},
api::limits::LimitType,
errors::InstanceServerError,
instance::Instance,
instance::{Instance, Token, UserMeta},
limit::LimitedRequester,
types::{ErrorResponse, RegisterSchema},
};
impl Instance {
@ -22,7 +23,7 @@ pub mod register {
pub async fn register_account(
&mut self,
register_schema: &RegisterSchema,
) -> Result<crate::api::types::User, InstanceServerError> {
) -> Result<UserMeta, InstanceServerError> {
let json_schema = json!(register_schema);
let mut limited_requester = LimitedRequester::new().await;
let client = Client::new();
@ -62,14 +63,11 @@ pub mod register {
return Err(InstanceServerError::InvalidFormBodyError { error_type, error });
}
let user_object = self.get_user(token.clone(), None).await.unwrap();
let settings = crate::api::types::User::get_settings(
&token,
&self.urls.get_api().to_string(),
&mut self.limits,
)
let settings =
UserMeta::get_settings(&token, &self.urls.get_api().to_string(), &mut self.limits)
.await
.unwrap();
let user: crate::api::types::User = crate::api::types::User::new(
let user: UserMeta = UserMeta::new(
Rc::new(RefCell::new(self.clone())),
token.clone(),
cloned_limits,
@ -83,9 +81,9 @@ pub mod register {
#[cfg(test)]
mod test {
use crate::api::schemas::{AuthUsername, RegisterSchema};
use crate::instance::Instance;
use crate::limit::LimitedRequester;
use crate::types::RegisterSchema;
use crate::URLBundle;
#[tokio::test]
@ -95,10 +93,10 @@ mod test {
"http://localhost:3001".to_string(),
"http://localhost:3001".to_string(),
);
let limited_requester = LimitedRequester::new().await;
let _limited_requester = LimitedRequester::new().await;
let mut test_instance = Instance::new(urls.clone()).await.unwrap();
let reg = RegisterSchema::new(
AuthUsername::new("Hiiii".to_string()).unwrap(),
"Hiiii".to_string(),
None,
true,
None,

View File

@ -2,19 +2,17 @@ use reqwest::Client;
use serde_json::from_str;
use crate::{
api::{limits::Limits, types},
errors::InstanceServerError,
limit::LimitedRequester,
api::limits::Limits, errors::InstanceServerError, limit::LimitedRequester, types::Channel,
};
impl types::Channel {
impl Channel {
pub async fn get(
token: &str,
url_api: &str,
channel_id: &str,
limits_user: &mut Limits,
limits_instance: &mut Limits,
) -> Result<types::Channel, InstanceServerError> {
) -> Result<Channel, InstanceServerError> {
let request = Client::new()
.get(format!("{}/channels/{}/", url_api, channel_id))
.bearer_auth(token);
@ -32,7 +30,7 @@ impl types::Channel {
Err(e) => return Err(e),
};
let result_text = result.text().await.unwrap();
match from_str::<types::Channel>(&result_text) {
match from_str::<Channel>(&result_text) {
Ok(object) => Ok(object),
Err(e) => Err(InstanceServerError::RequestErrorError {
url: format!("{}/channels/{}/", url_api, channel_id),

View File

@ -5,8 +5,9 @@ pub mod messages {
use serde_json::to_string;
use crate::api::limits::Limits;
use crate::api::types::{Message, PartialDiscordFileAttachment, User};
use crate::instance::UserMeta;
use crate::limit::LimitedRequester;
use crate::types::{Message, MessageSendSchema, PartialDiscordFileAttachment};
impl Message {
/**
@ -23,7 +24,7 @@ pub mod messages {
pub async fn send<'a>(
url_api: String,
channel_id: String,
message: &mut crate::api::schemas::MessageSendSchema,
message: &mut MessageSendSchema,
files: Option<Vec<PartialDiscordFileAttachment>>,
token: String,
limits_user: &mut Limits,
@ -65,7 +66,7 @@ pub mod messages {
let mut header_map = HeaderMap::new();
header_map.insert(CONTENT_DISPOSITION, content_disposition.parse().unwrap());
let mut part = multipart::Part::bytes(attachment_content)
let part = multipart::Part::bytes(attachment_content)
.file_name(attachment_filename)
.headers(header_map);
@ -91,10 +92,10 @@ pub mod messages {
}
}
impl User {
impl UserMeta {
pub async fn send_message(
&mut self,
message: &mut crate::api::schemas::MessageSendSchema,
message: &mut MessageSendSchema,
channel_id: String,
files: Option<Vec<PartialDiscordFileAttachment>>,
) -> Result<reqwest::Response, crate::errors::InstanceServerError> {
@ -116,11 +117,9 @@ pub mod messages {
#[cfg(test)]
mod test {
use crate::{
api::{AuthUsername, LoginSchema},
instance::Instance,
limit::LimitedRequester,
};
use crate::instance::UserMeta;
use crate::types::{LoginSchema, MessageSendSchema, PartialDiscordFileAttachment};
use crate::{instance::Instance};
use std::io::Read;
use std::{cell::RefCell, fs::File};
@ -129,7 +128,7 @@ mod test {
#[tokio::test]
async fn send_message() {
let channel_id = "1106954414356168802".to_string();
let mut message = crate::api::schemas::MessageSendSchema::new(
let mut message = MessageSendSchema::new(
None,
Some("A Message!".to_string()),
None,
@ -149,7 +148,7 @@ mod test {
.await
.unwrap();
let login_schema: LoginSchema = LoginSchema::new(
AuthUsername::new("user@test.xyz".to_string()).unwrap(),
"user@test.xyz".to_string(),
"transrights".to_string(),
None,
None,
@ -162,7 +161,7 @@ mod test {
println!("TOKEN: {}", token);
let settings = login_result.settings;
let limits = instance.limits.clone();
let mut user = crate::api::types::User::new(
let mut user = UserMeta::new(
Rc::new(RefCell::new(instance)),
token,
limits,
@ -185,7 +184,7 @@ mod test {
reader.read_to_end(&mut buffer).unwrap();
let attachment = crate::api::types::PartialDiscordFileAttachment {
let attachment = PartialDiscordFileAttachment {
id: None,
filename: "README.md".to_string(),
description: None,
@ -201,7 +200,7 @@ mod test {
content: buffer,
};
let mut message = crate::api::schemas::MessageSendSchema::new(
let mut message = MessageSendSchema::new(
None,
Some("trans rights now".to_string()),
None,
@ -221,7 +220,7 @@ mod test {
.await
.unwrap();
let login_schema: LoginSchema = LoginSchema::new(
AuthUsername::new("user@test.xyz".to_string()).unwrap(),
"user@test.xyz".to_string(),
"transrights".to_string(),
None,
None,
@ -233,7 +232,7 @@ mod test {
let token = login_result.token;
let settings = login_result.settings;
let limits = instance.limits.clone();
let mut user = crate::api::types::User::new(
let mut user = UserMeta::new(
Rc::new(RefCell::new(instance)),
token,
limits,

View File

@ -3,12 +3,12 @@ use serde_json::from_str;
use serde_json::to_string;
use crate::api::limits::Limits;
use crate::api::schemas;
use crate::api::types;
use crate::errors::InstanceServerError;
use crate::instance::UserMeta;
use crate::limit::LimitedRequester;
use crate::types::{Channel, ChannelCreateSchema, Guild, GuildCreateResponse, GuildCreateSchema};
impl types::Guild {
impl Guild {
/// Creates a new guild with the given parameters.
///
/// # Arguments
@ -38,9 +38,9 @@ impl types::Guild {
/// }
/// ```
pub async fn create(
user: &mut types::User,
user: &mut UserMeta,
url_api: &str,
guild_create_schema: schemas::GuildCreateSchema,
guild_create_schema: GuildCreateSchema,
) -> Result<String, crate::errors::InstanceServerError> {
let url = format!("{}/guilds/", url_api);
let limits_user = user.limits.get_as_mut();
@ -62,7 +62,7 @@ impl types::Guild {
Ok(result) => result,
Err(e) => return Err(e),
};
let id: types::GuildCreateResponse = from_str(&result.text().await.unwrap()).unwrap();
let id: GuildCreateResponse = from_str(&result.text().await.unwrap()).unwrap();
Ok(id.id)
}
@ -91,7 +91,7 @@ impl types::Guild {
/// }
/// ```
pub async fn delete(
user: &mut types::User,
user: &mut UserMeta,
url_api: &str,
guild_id: String,
) -> Option<InstanceServerError> {
@ -134,14 +134,14 @@ impl types::Guild {
&self,
url_api: &str,
token: &str,
schema: schemas::ChannelCreateSchema,
schema: ChannelCreateSchema,
limits_user: &mut Limits,
limits_instance: &mut Limits,
) -> Result<types::Channel, InstanceServerError> {
types::Channel::create(
) -> Result<Channel, InstanceServerError> {
Channel::create(
token,
url_api,
&self.id,
&self.id.to_string(),
schema,
limits_user,
limits_instance,
@ -150,7 +150,7 @@ impl types::Guild {
}
}
impl types::Channel {
impl Channel {
/// Sends a request to create a new channel in a guild.
///
/// # Arguments
@ -169,10 +169,10 @@ impl types::Channel {
token: &str,
url_api: &str,
guild_id: &str,
schema: schemas::ChannelCreateSchema,
schema: ChannelCreateSchema,
limits_user: &mut Limits,
limits_instance: &mut Limits,
) -> Result<types::Channel, InstanceServerError> {
) -> Result<Channel, InstanceServerError> {
let request = Client::new()
.post(format!("{}/guilds/{}/channels/", url_api, guild_id))
.bearer_auth(token)
@ -190,7 +190,7 @@ impl types::Channel {
Ok(result) => result,
Err(e) => return Err(e),
};
match from_str::<types::Channel>(&result.text().await.unwrap()) {
match from_str::<Channel>(&result.text().await.unwrap()) {
Ok(object) => Ok(object),
Err(e) => Err(InstanceServerError::RequestErrorError {
url: format!("{}/guilds/{}/channels/", url_api, guild_id),

View File

@ -2,13 +2,9 @@ pub mod auth;
pub mod channels;
pub mod guilds;
pub mod policies;
pub mod schemas;
pub mod types;
pub mod users;
pub use channels::messages::*;
pub use guilds::*;
pub use policies::instance::instance::*;
pub use policies::instance::limits::*;
pub use schemas::*;
pub use types::*;

View File

@ -2,7 +2,8 @@ use reqwest::Client;
use serde_json::from_str;
use crate::errors::InstanceServerError;
use crate::{api::types::InstancePolicies, instance::Instance};
use crate::instance::Instance;
use crate::types::GeneralConfiguration;
impl Instance {
/**
@ -10,7 +11,9 @@ impl Instance {
# Errors
[`InstanceServerError`] - If the request fails.
*/
pub async fn instance_policies_schema(&self) -> Result<InstancePolicies, InstanceServerError> {
pub async fn general_configuration_schema(
&self,
) -> Result<GeneralConfiguration, InstanceServerError> {
let client = Client::new();
let endpoint_url = self.urls.get_api().to_string() + "/policies/instance/";
let request = match client.get(&endpoint_url).send().await {
@ -30,7 +33,7 @@ impl Instance {
}
let body = request.text().await.unwrap();
let instance_policies_schema: InstancePolicies = from_str(&body).unwrap();
let instance_policies_schema: GeneralConfiguration = from_str(&body).unwrap();
Ok(instance_policies_schema)
}
}
@ -46,9 +49,9 @@ mod instance_policies_schema_test {
"http://localhost:3001".to_string(),
"http://localhost:3001".to_string(),
);
let limited_requester = LimitedRequester::new().await;
let _limited_requester = LimitedRequester::new().await;
let test_instance = Instance::new(urls.clone()).await.unwrap();
let _schema = test_instance.instance_policies_schema().await.unwrap();
let _schema = test_instance.general_configuration_schema().await.unwrap();
}
}

View File

@ -1,432 +0,0 @@
use regex::Regex;
use serde::{Deserialize, Serialize};
use crate::errors::FieldFormatError;
use super::{Channel, Embed};
/**
A struct that represents a well-formed email address.
*/
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AuthEmail {
pub email: String,
}
impl AuthEmail {
/**
Returns a new [`Result<AuthEmail, FieldFormatError>`].
## Arguments
The email address you want to validate.
## Errors
You will receive a [`FieldFormatError`], if:
- The email address is not in a valid format.
*/
pub fn new(email: String) -> Result<AuthEmail, FieldFormatError> {
let regex = Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap();
if !regex.is_match(email.as_str()) {
return Err(FieldFormatError::EmailError);
}
Ok(AuthEmail { email })
}
}
/**
A struct that represents a well-formed username.
## Arguments
Please use new() to create a new instance of this struct.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is not between 2 and 32 characters.
*/
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AuthUsername {
pub username: String,
}
impl AuthUsername {
/**
Returns a new [`Result<AuthUsername, FieldFormatError>`].
## Arguments
The username you want to validate.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is not between 2 and 32 characters.
*/
pub fn new(username: String) -> Result<AuthUsername, FieldFormatError> {
if username.len() < 2 || username.len() > 32 {
Err(FieldFormatError::UsernameError)
} else {
Ok(AuthUsername { username })
}
}
}
/**
A struct that represents a well-formed password.
## Arguments
Please use new() to create a new instance of this struct.
## Errors
You will receive a [`FieldFormatError`], if:
- The password is not between 1 and 72 characters.
*/
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AuthPassword {
pub password: String,
}
impl AuthPassword {
/**
Returns a new [`Result<AuthPassword, FieldFormatError>`].
## Arguments
The password you want to validate.
## Errors
You will receive a [`FieldFormatError`], if:
- The password is not between 1 and 72 characters.
*/
pub fn new(password: String) -> Result<AuthPassword, FieldFormatError> {
if password.is_empty() || password.len() > 72 {
Err(FieldFormatError::PasswordError)
} else {
Ok(AuthPassword { password })
}
}
}
/**
A struct that represents a well-formed register request.
## Arguments
Please use new() to create a new instance of this struct.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is not between 2 and 32 characters.
- The password is not between 1 and 72 characters.
*/
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct RegisterSchema {
username: String,
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>,
}
impl RegisterSchema {
/**
Returns a new [`Result<RegisterSchema, FieldFormatError>`].
## Arguments
All but "String::username" and "bool::consent" are optional.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is less than 2 or more than 32 characters in length
- You supply a `password` which is less than 1 or more than 72 characters in length.
These constraints have been defined [in the Spacebar-API](https://docs.spacebar.chat/routes/)
*/
pub fn new(
username: AuthUsername,
password: Option<AuthPassword>,
consent: bool,
email: Option<AuthEmail>,
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 = username.username;
let email_addr;
if email.is_some() {
email_addr = Some(email.unwrap().email);
} else {
email_addr = None;
}
let has_password;
if password.is_some() {
has_password = Some(password.unwrap().password);
} else {
has_password = None;
}
if !consent {
return Err(FieldFormatError::ConsentError);
}
Ok(RegisterSchema {
username,
password: has_password,
consent,
email: email_addr,
fingerprint,
invite,
date_of_birth,
gift_code_sku_id,
captcha_key,
promotional_email_opt_in,
})
}
}
/**
A struct that represents a well-formed login request.
## Arguments
Please use new() to create a new instance of this struct.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is not between 2 and 32 characters.
- The password is not between 1 and 72 characters.
*/
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct LoginSchema {
login: String,
password: String,
undelete: Option<bool>,
captcha_key: Option<String>,
login_source: Option<String>,
gift_code_sku_id: Option<String>,
}
impl LoginSchema {
/**
Returns a new [`Result<LoginSchema, FieldFormatError>`].
## Arguments
login: The username you want to login with.
password: The password you want to login with.
undelete: Honestly no idea what this is for.
captcha_key: The captcha key you want to login with.
login_source: The login source.
gift_code_sku_id: The gift code sku id.
## Errors
You will receive a [`FieldFormatError`], if:
- The username is less than 2 or more than 32 characters in length
*/
pub fn new(
login: AuthUsername,
password: String,
undelete: Option<bool>,
captcha_key: Option<String>,
login_source: Option<String>,
gift_code_sku_id: Option<String>,
) -> Result<LoginSchema, FieldFormatError> {
let login = login.username;
Ok(LoginSchema {
login,
password,
undelete,
captcha_key,
login_source,
gift_code_sku_id,
})
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct TotpSchema {
code: String,
ticket: String,
gift_code_sku_id: Option<String>,
login_source: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct MessageSendSchema {
#[serde(rename = "type")]
message_type: Option<i32>,
content: Option<String>,
nonce: Option<String>,
tts: Option<bool>,
embeds: Option<Vec<super::Embed>>,
allowed_mentions: Option<super::AllowedMention>,
message_reference: Option<super::MessageReference>,
components: Option<Vec<super::Component>>,
sticker_ids: Option<Vec<String>>,
pub attachments: Option<Vec<super::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<super::AllowedMention>,
message_reference: Option<super::MessageReference>,
components: Option<Vec<super::Component>>,
sticker_ids: Option<Vec<String>>,
attachments: Option<Vec<super::PartialDiscordFileAttachment>>,
) -> MessageSendSchema {
MessageSendSchema {
message_type,
content,
nonce,
tts,
embeds,
allowed_mentions,
message_reference,
components,
sticker_ids,
attachments,
}
}
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct GuildCreateSchema {
pub name: Option<String>,
pub region: Option<String>,
pub icon: Option<String>,
pub channels: Option<Vec<Channel>>,
pub guild_template_code: Option<String>,
pub system_channel_id: Option<String>,
pub rules_channel_id: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct UserModifySchema {
pub username: Option<String>,
pub avatar: Option<String>,
pub bio: Option<String>,
pub accent_color: Option<u64>,
pub banner: Option<String>,
pub current_password: Option<String>,
pub new_password: Option<String>,
pub code: Option<String>,
pub email: Option<String>,
pub discriminator: Option<i16>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
// TODO: Implement in polyphony/types
pub struct ChannelCreateSchema {
pub name: String,
#[serde(rename = "type")]
pub channel_type: Option<u8>,
pub topic: Option<String>,
pub icon: Option<String>,
pub bitrate: Option<i32>,
pub user_limit: Option<i32>,
pub rate_limit_per_user: Option<i32>,
pub position: Option<i32>,
pub permission_overwrites: Option<Vec<crate::api::types::PermissionOverwrite>>,
pub parent_id: Option<String>,
pub id: Option<String>,
pub nsfw: Option<bool>,
pub rtc_region: Option<String>,
pub default_auto_archive_duration: Option<i32>,
pub default_reaction_emoji: Option<String>,
pub flags: Option<i32>,
pub default_thread_rate_limit_per_user: Option<i32>,
pub video_quality_mode: Option<i32>,
}
// I know that some of these tests are... really really basic and unneccessary, but sometimes, I
// just feel like writing tests, so there you go :) -@bitfl0wer
#[cfg(test)]
mod schemas_tests {
use super::*;
use crate::errors::FieldFormatError;
#[test]
fn password_too_short() {
assert_eq!(
AuthPassword::new("".to_string()),
Err(FieldFormatError::PasswordError)
);
}
#[test]
fn password_too_long() {
let mut long_pw = String::new();
for _ in 0..73 {
long_pw += "a";
}
assert_eq!(
AuthPassword::new(long_pw),
Err(FieldFormatError::PasswordError)
);
}
#[test]
fn username_too_short() {
assert_eq!(
AuthUsername::new("T".to_string()),
Err(FieldFormatError::UsernameError)
);
}
#[test]
fn username_too_long() {
let mut long_un = String::new();
for _ in 0..33 {
long_un += "a";
}
assert_eq!(
AuthUsername::new(long_un),
Err(FieldFormatError::UsernameError)
);
}
#[test]
fn consent_false() {
assert_eq!(
RegisterSchema::new(
AuthUsername::new("Test".to_string()).unwrap(),
None,
false,
None,
None,
None,
None,
None,
None,
None,
),
Err(FieldFormatError::ConsentError)
);
}
#[test]
fn invalid_email() {
assert_eq!(
AuthEmail::new("p@p.p".to_string()),
Err(FieldFormatError::EmailError)
)
}
#[test]
fn valid_email() {
let reg = RegisterSchema::new(
AuthUsername::new("Testy".to_string()).unwrap(),
None,
true,
Some(AuthEmail::new("me@mail.de".to_string()).unwrap()),
None,
None,
None,
None,
None,
None,
);
assert_ne!(reg, Err(FieldFormatError::EmailError));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,17 +2,14 @@ use reqwest::Client;
use serde_json::{from_str, to_string};
use crate::{
api::{
limits::Limits,
types::{User, UserObject},
UserModifySchema, UserSettings,
},
api::limits::Limits,
errors::InstanceServerError,
instance::Instance,
instance::{Instance, UserMeta},
limit::LimitedRequester,
types::{User, UserModifySchema, UserSettings},
};
impl User {
impl UserMeta {
/**
Get a user object by id, or get the current user.
# Arguments
@ -28,7 +25,7 @@ impl User {
url_api: &String,
id: Option<&String>,
instance_limits: &mut Limits,
) -> Result<UserObject, InstanceServerError> {
) -> Result<User, InstanceServerError> {
let url: String;
if id.is_none() {
url = format!("{}/users/@me/", url_api);
@ -49,7 +46,7 @@ impl User {
{
Ok(result) => {
let result_text = result.text().await.unwrap();
Ok(serde_json::from_str::<UserObject>(&result_text).unwrap())
Ok(serde_json::from_str::<User>(&result_text).unwrap())
}
Err(e) => Err(e),
}
@ -91,7 +88,7 @@ impl User {
pub async fn modify(
&mut self,
modify_schema: UserModifySchema,
) -> Result<UserObject, InstanceServerError> {
) -> Result<User, InstanceServerError> {
if modify_schema.new_password.is_some()
|| modify_schema.email.is_some()
|| modify_schema.code.is_some()
@ -118,7 +115,7 @@ impl User {
Ok(response) => response,
Err(e) => return Err(e),
};
let user_updated: UserObject = from_str(&result.text().await.unwrap()).unwrap();
let user_updated: User = from_str(&result.text().await.unwrap()).unwrap();
let _ = std::mem::replace(
&mut self.object.as_mut().unwrap(),
&mut user_updated.clone(),
@ -171,8 +168,8 @@ impl Instance {
&mut self,
token: String,
id: Option<&String>,
) -> Result<UserObject, InstanceServerError> {
User::get(
) -> Result<User, InstanceServerError> {
UserMeta::get(
&token,
&self.urls.get_api().to_string(),
id,

View File

@ -1,12 +1,11 @@
use std::sync::Arc;
use crate::api::types::*;
use crate::api::WebSocketEvent;
use crate::errors::ObserverError;
use crate::gateway::events::Events;
use crate::types;
use futures_util::stream::SplitSink;
use futures_util::SinkExt;
use futures_util::StreamExt;
use futures_util::stream::SplitSink;
use native_tls::TlsConnector;
use std::sync::Arc;
use tokio::net::TcpStream;
use tokio::sync::mpsc;
use tokio::sync::mpsc::error::TryRecvError;
@ -16,7 +15,7 @@ use tokio::task;
use tokio::time;
use tokio::time::Instant;
use tokio_tungstenite::MaybeTlsStream;
use tokio_tungstenite::{WebSocketStream, Connector, connect_async_tls_with_config};
use tokio_tungstenite::{connect_async_tls_with_config, Connector, WebSocketStream};
#[derive(Debug)]
/**
@ -28,14 +27,25 @@ Using this handle you can also send Gateway Events directly.
pub struct GatewayHandle {
pub url: String,
pub events: Arc<Mutex<Events>>,
pub websocket_tx: Arc<Mutex<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, tokio_tungstenite::tungstenite::Message>>>,
pub websocket_tx: Arc<
Mutex<
SplitSink<
WebSocketStream<MaybeTlsStream<TcpStream>>,
tokio_tungstenite::tungstenite::Message,
>,
>,
>,
}
impl GatewayHandle {
/// Sends json to the gateway with an opcode
async fn send_json_event(&self, op: u8, to_send: serde_json::Value) {
let gateway_payload = GatewaySendPayload { op, d: Some(to_send), s: None };
let gateway_payload = types::GatewaySendPayload {
op,
d: Some(to_send),
s: None
};
let payload_json = serde_json::to_string(&gateway_payload).unwrap();
@ -45,8 +55,7 @@ impl GatewayHandle {
}
/// Sends an identify event to the gateway
pub async fn send_identify(&self, to_send: GatewayIdentifyPayload) {
pub async fn send_identify(&self, to_send: types::GatewayIdentifyPayload) {
let to_send_value = serde_json::to_value(&to_send).unwrap();
println!("GW: Sending Identify..");
@ -55,8 +64,7 @@ impl GatewayHandle {
}
/// Sends a resume event to the gateway
pub async fn send_resume(&self, to_send: GatewayResume) {
pub async fn send_resume(&self, to_send: types::GatewayResume) {
let to_send_value = serde_json::to_value(&to_send).unwrap();
println!("GW: Sending Resume..");
@ -65,8 +73,7 @@ impl GatewayHandle {
}
/// Sends an update presence event to the gateway
pub async fn send_update_presence(&self, to_send: PresenceUpdate) {
pub async fn send_update_presence(&self, to_send: types::PresenceUpdate) {
let to_send_value = serde_json::to_value(&to_send).unwrap();
println!("GW: Sending Presence Update..");
@ -75,8 +82,7 @@ impl GatewayHandle {
}
/// Sends a request guild members to the server
pub async fn send_request_guild_members(&self, to_send: GatewayRequestGuildMembers) {
pub async fn send_request_guild_members(&self, to_send: types::GatewayRequestGuildMembers) {
let to_send_value = serde_json::to_value(&to_send).unwrap();
println!("GW: Sending Request Guild Members..");
@ -85,7 +91,7 @@ impl GatewayHandle {
}
/// Sends an update voice state to the server
pub async fn send_update_voice_state(&self, to_send: UpdateVoiceState) {
pub async fn send_update_voice_state(&self, to_send: types::UpdateVoiceState) {
let to_send_value = serde_json::to_value(&to_send).unwrap();
@ -95,7 +101,7 @@ impl GatewayHandle {
}
/// Sends a call sync to the server
pub async fn send_call_sync(&self, to_send: CallSync) {
pub async fn send_call_sync(&self, to_send: types::CallSync) {
let to_send_value = serde_json::to_value(&to_send).unwrap();
@ -105,7 +111,7 @@ impl GatewayHandle {
}
/// Sends a Lazy Request
pub async fn send_lazy_request(&self, to_send: LazyRequest) {
pub async fn send_lazy_request(&self, to_send: types::LazyRequest) {
let to_send_value = serde_json::to_value(&to_send).unwrap();
@ -118,14 +124,20 @@ impl GatewayHandle {
pub struct Gateway {
pub events: Arc<Mutex<Events>>,
heartbeat_handler: Option<HeartbeatHandler>,
pub websocket_tx: Arc<Mutex<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, tokio_tungstenite::tungstenite::Message>>>
pub websocket_tx: Arc<
Mutex<
SplitSink<
WebSocketStream<MaybeTlsStream<TcpStream>>,
tokio_tungstenite::tungstenite::Message,
>,
>,
>,
}
impl Gateway {
pub async fn new(
websocket_url: String,
) -> Result<GatewayHandle, tokio_tungstenite::tungstenite::Error> {
let (ws_stream, _) = match connect_async_tls_with_config(
&websocket_url,
None,
@ -144,23 +156,31 @@ impl Gateway {
let shared_tx = Arc::new(Mutex::new(ws_tx));
let mut gateway = Gateway { events: Arc::new(Mutex::new(Events::default())), heartbeat_handler: None, websocket_tx: shared_tx.clone() };
let mut gateway = Gateway {
events: Arc::new(Mutex::new(Events::default())),
heartbeat_handler: None,
websocket_tx: shared_tx.clone(),
};
let shared_events = gateway.events.clone();
// Wait for the first hello and then spawn both tasks so we avoid nested tasks
// This automatically spawns the heartbeat task, but from the main thread
let msg = ws_rx.next().await.unwrap().unwrap();
let gateway_payload: GatewayReceivePayload = serde_json::from_str(msg.to_text().unwrap()).unwrap();
let gateway_payload: types::GatewayReceivePayload = serde_json::from_str(msg.to_text().unwrap()).unwrap();
if gateway_payload.op != 10 {
println!("Recieved non hello on gateway init, what is happening?");
return Err(tokio_tungstenite::tungstenite::Error::Protocol(tokio_tungstenite::tungstenite::error::ProtocolError::InvalidOpcode(gateway_payload.op)))
return Err(tokio_tungstenite::tungstenite::Error::Protocol(
tokio_tungstenite::tungstenite::error::ProtocolError::InvalidOpcode(
gateway_payload.op,
),
));
}
println!("GW: Received Hello");
let gateway_hello: HelloData = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let gateway_hello: types::HelloData = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
gateway.heartbeat_handler = Some(HeartbeatHandler::new(gateway_hello.heartbeat_interval, shared_tx.clone()));
// Now we can continously check for messages in a different task, since we aren't going to receive another hello
@ -183,12 +203,11 @@ impl Gateway {
/// This handles a message as a websocket event and updates its events along with the events' observers
pub async fn handle_event(&mut self, msg: tokio_tungstenite::tungstenite::Message) {
if msg.to_string() == String::new() {
return;
}
let gateway_payload: GatewayReceivePayload = serde_json::from_str(msg.to_text().unwrap()).unwrap();
let gateway_payload: types::GatewayReceivePayload = serde_json::from_str(msg.to_text().unwrap()).unwrap();
// See https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes
match gateway_payload.op {
@ -205,11 +224,11 @@ impl Gateway {
// "Some" of these are uncodumented
match gateway_payload_t.as_str() {
"READY" => {
let new_data: GatewayReady = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GatewayReady = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.session.ready.update_data(new_data).await;
},
"READY_SUPPLEMENTAL" => {
let new_data: GatewayReadySupplemental = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GatewayReadySupplemental = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.session.ready_supplimental.update_data(new_data).await;
}
"RESUMED" => {}
@ -219,236 +238,236 @@ impl Gateway {
"AUTO_MODERATION_RULE_DELETE" => {}
"AUTO_MODERATION_ACTION_EXECUTION" => {}
"CHANNEL_CREATE" => {
let new_data: ChannelCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ChannelCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.channel.create.update_data(new_data).await;
}
"CHANNEL_UPDATE" => {
let new_data: ChannelUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ChannelUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.channel.update.update_data(new_data).await;
}
"CHANNEL_UNREAD_UPDATE" => {
let new_data: ChannelUnreadUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ChannelUnreadUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.channel.unread_update.update_data(new_data).await;
}
"CHANNEL_DELETE" => {
let new_data: ChannelDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ChannelDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.channel.delete.update_data(new_data).await;
}
"CHANNEL_PINS_UPDATE" => {
let new_data: ChannelPinsUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ChannelPinsUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.channel.pins_update.update_data(new_data).await;
}
"CALL_CREATE" => {
let new_data: CallCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::CallCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.call.create.update_data(new_data).await;
},
"CALL_UPDATE" => {
let new_data: CallUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::CallUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.call.update.update_data(new_data).await;
}
"CALL_DELETE" => {
let new_data: CallDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::CallDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.call.delete.update_data(new_data).await;
}
"THREAD_CREATE" => {
let new_data: ThreadCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ThreadCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.thread.create.update_data(new_data).await;
}
"THREAD_UPDATE" => {
let new_data: ThreadUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ThreadUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.thread.update.update_data(new_data).await;
}
"THREAD_DELETE" => {
let new_data: ThreadDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ThreadDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.thread.delete.update_data(new_data).await;
}
"THREAD_LIST_SYNC" => {
let new_data: ThreadListSync = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ThreadListSync = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.thread.list_sync.update_data(new_data).await;
}
"THREAD_MEMBER_UPDATE" => {
let new_data: ThreadMemberUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ThreadMemberUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.thread.member_update.update_data(new_data).await;
}
"THREAD_MEMBERS_UPDATE" => {
let new_data: ThreadMembersUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::ThreadMembersUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.thread.members_update.update_data(new_data).await;
}
"GUILD_CREATE" => {
let new_data: GuildCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.create.update_data(new_data).await;
}
"GUILD_UPDATE" => {
let new_data: GuildUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.update.update_data(new_data).await;
}
"GUILD_DELETE" => {
let new_data: GuildDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.delete.update_data(new_data).await;
}
"GUILD_AUDIT_LOG_ENTRY_CREATE" => {
let new_data: GuildAuditLogEntryCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildAuditLogEntryCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.audit_log_entry_create.update_data(new_data).await;
}
"GUILD_BAN_ADD" => {
let new_data: GuildBanAdd = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildBanAdd = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.ban_add.update_data(new_data).await;
}
"GUILD_BAN_REMOVE" => {
let new_data: GuildBanRemove = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildBanRemove = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.ban_remove.update_data(new_data).await;
}
"GUILD_EMOJIS_UPDATE" => {
let new_data: GuildEmojisUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildEmojisUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.emojis_update.update_data(new_data).await;
}
"GUILD_STICKERS_UPDATE" => {
let new_data: GuildStickersUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildStickersUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.stickers_update.update_data(new_data).await;
}
"GUILD_INTEGRATIONS_UPDATE" => {
let new_data: GuildIntegrationsUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildIntegrationsUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.integrations_update.update_data(new_data).await;
}
"GUILD_MEMBER_ADD" => {
let new_data: GuildMemberAdd = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildMemberAdd = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.member_add.update_data(new_data).await;
}
"GUILD_MEMBER_REMOVE" => {
let new_data: GuildMemberRemove = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildMemberRemove = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.member_remove.update_data(new_data).await;
}
"GUILD_MEMBER_UPDATE" => {
let new_data: GuildMemberUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildMemberUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.member_update.update_data(new_data).await;
}
"GUILD_MEMBERS_CHUNK" => {
let new_data: GuildMembersChunk = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildMembersChunk = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.members_chunk.update_data(new_data).await;
}
"GUILD_ROLE_CREATE" => {
let new_data: GuildRoleCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildRoleCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.role_create.update_data(new_data).await;
}
"GUILD_ROLE_UPDATE" => {
let new_data: GuildRoleUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildRoleUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.role_update.update_data(new_data).await;
}
"GUILD_ROLE_DELETE" => {
let new_data: GuildRoleDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildRoleDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.role_delete.update_data(new_data).await;
}
"GUILD_SCHEDULED_EVENT_CREATE" => {
let new_data: GuildScheduledEventCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildScheduledEventCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.role_scheduled_event_create.update_data(new_data).await;
}
"GUILD_SCHEDULED_EVENT_UPDATE" => {
let new_data: GuildScheduledEventUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildScheduledEventUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.role_scheduled_event_update.update_data(new_data).await;
}
"GUILD_SCHEDULED_EVENT_DELETE" => {
let new_data: GuildScheduledEventDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildScheduledEventDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.role_scheduled_event_delete.update_data(new_data).await;
}
"GUILD_SCHEDULED_EVENT_USER_ADD" => {
let new_data: GuildScheduledEventUserAdd = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildScheduledEventUserAdd = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.role_scheduled_event_user_add.update_data(new_data).await;
}
"GUILD_SCHEDULED_EVENT_USER_REMOVE" => {
let new_data: GuildScheduledEventUserRemove = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::GuildScheduledEventUserRemove = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.role_scheduled_event_user_remove.update_data(new_data).await;
}
"PASSIVE_UPDATE_V1" => {
let new_data: PassiveUpdateV1 = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::PassiveUpdateV1 = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.guild.passive_update_v1.update_data(new_data).await;
}
"INTEGRATION_CREATE" => {
let new_data: IntegrationCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::IntegrationCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.integration.create.update_data(new_data).await;
}
"INTEGRATION_UPDATE" => {
let new_data: IntegrationUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::IntegrationUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.integration.update.update_data(new_data).await;
}
"INTEGRATION_DELETE" => {
let new_data: IntegrationDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::IntegrationDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.integration.delete.update_data(new_data).await;
}
"INTERACTION_CREATE" => {}
"INVITE_CREATE" => {
let new_data: InviteCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::InviteCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.invite.create.update_data(new_data).await;
}
"INVITE_DELETE" => {
let new_data: InviteDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::InviteDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.invite.delete.update_data(new_data).await;
}
"MESSAGE_CREATE" => {
let new_data: MessageCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::MessageCreate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.message.create.update_data(new_data).await;
}
"MESSAGE_UPDATE" => {
let new_data: MessageUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::MessageUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.message.update.update_data(new_data).await;
}
"MESSAGE_DELETE" => {
let new_data: MessageDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::MessageDelete = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.message.delete.update_data(new_data).await;
}
"MESSAGE_DELETE_BULK" => {
let new_data: MessageDeleteBulk = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::MessageDeleteBulk = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.message.delete_bulk.update_data(new_data).await;
}
"MESSAGE_REACTION_ADD" => {
let new_data: MessageReactionAdd = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::MessageReactionAdd = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.message.reaction_add.update_data(new_data).await;
}
"MESSAGE_REACTION_REMOVE" => {
let new_data: MessageReactionRemove = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::MessageReactionRemove = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.message.reaction_remove.update_data(new_data).await;
}
"MESSAGE_REACTION_REMOVE_ALL" => {
let new_data: MessageReactionRemoveAll = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::MessageReactionRemoveAll = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.message.reaction_remove_all.update_data(new_data).await;
}
"MESSAGE_REACTION_REMOVE_EMOJI" => {
let new_data: MessageReactionRemoveEmoji= serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::MessageReactionRemoveEmoji= serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.message.reaction_remove_emoji.update_data(new_data).await;
},
"MESSAGE_ACK" => {
let new_data: MessageACK = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::MessageACK = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.message.ack.update_data(new_data).await;
}
"PRESENCE_UPDATE" => {
let new_data: PresenceUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::PresenceUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.user.presence_update.update_data(new_data).await;
}
"STAGE_INSTANCE_CREATE" => {}
"STAGE_INSTANCE_UPDATE" => {}
"STAGE_INSTANCE_DELETE" => {}
"SESSIONS_REPLACE" => {
let sessions: Vec<Session> = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data = SessionsReplace {sessions};
let sessions: Vec<types::Session> = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data = types::SessionsReplace {sessions};
self.events.lock().await.session.replace.update_data(new_data).await;
}
"TYPING_START" => {
let new_data: TypingStartEvent = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::TypingStartEvent = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.user.typing_start_event.update_data(new_data).await;
}
"USER_UPDATE" => {
let new_data: UserUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::UserUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.user.update.update_data(new_data).await;
}
"VOICE_STATE_UPDATE" => {
let new_data: VoiceStateUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::VoiceStateUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.voice.state_update.update_data(new_data).await;
}
"VOICE_SERVER_UPDATE" => {
let new_data: VoiceServerUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::VoiceServerUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.voice.server_update.update_data(new_data).await;
}
"WEBHOOKS_UPDATE" => {
let new_data: WebhooksUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
let new_data: types::WebhooksUpdate = serde_json::from_str(gateway_payload.d.unwrap().get()).unwrap();
self.events.lock().await.webhooks.update.update_data(new_data).await;
}
_ => {
@ -461,9 +480,13 @@ impl Gateway {
// We received a heartbeat from the server
1 => {}
// Reconnect
7 => {todo!()}
7 => {
todo!()
}
// Invalid Session
9 => {todo!()}
9 => {
todo!()
}
// Hello
// Starts our heartbeat
// We should have already handled this in gateway init
@ -481,10 +504,18 @@ impl Gateway {
// If we have an active heartbeat thread and we received a seq number we should let it know
if gateway_payload.s.is_some() {
if self.heartbeat_handler.is_some() {
let heartbeat_communication = HeartbeatThreadCommunication {
op: gateway_payload.op,
d: gateway_payload.s.unwrap(),
};
let heartbeat_communication = HeartbeatThreadCommunication { op: gateway_payload.op, d: gateway_payload.s.unwrap() };
self.heartbeat_handler.as_mut().unwrap().tx.send(heartbeat_communication).await.unwrap();
self.heartbeat_handler
.as_mut()
.unwrap()
.tx
.send(heartbeat_communication)
.await
.unwrap();
}
}
}
@ -500,7 +531,17 @@ struct HeartbeatHandler {
}
impl HeartbeatHandler {
pub fn new(heartbeat_interval: u128, websocket_tx: Arc<Mutex<SplitSink<WebSocketStream<MaybeTlsStream<TcpStream>>, tokio_tungstenite::tungstenite::Message>>>) -> HeartbeatHandler {
pub fn new(
heartbeat_interval: u128,
websocket_tx: Arc<
Mutex<
SplitSink<
WebSocketStream<MaybeTlsStream<TcpStream>>,
tokio_tungstenite::tungstenite::Message,
>,
>,
>,
) -> HeartbeatHandler {
let (tx, mut rx) = mpsc::channel(32);
task::spawn(async move {
@ -508,37 +549,36 @@ impl HeartbeatHandler {
let mut last_seq_number: Option<u64> = None;
loop {
// If we received a seq number update, use that as the last seq number
let hb_communication: Result<HeartbeatThreadCommunication, TryRecvError> = rx.try_recv();
let hb_communication: Result<HeartbeatThreadCommunication, TryRecvError> =
rx.try_recv();
if hb_communication.is_ok() {
last_seq_number = Some(hb_communication.unwrap().d);
}
if last_heartbeat.elapsed().as_millis() > heartbeat_interval {
println!("GW: Sending Heartbeat..");
let heartbeat = GatewayHeartbeat {
let heartbeat = types::GatewayHeartbeat {
op: 1,
d: last_seq_number
d: last_seq_number,
};
let heartbeat_json = serde_json::to_string(&heartbeat).unwrap();
let msg = tokio_tungstenite::tungstenite::Message::text(heartbeat_json);
websocket_tx.lock().await
.send(msg)
.await
.unwrap();
websocket_tx.lock().await.send(msg).await.unwrap();
last_heartbeat = time::Instant::now();
}
}
});
Self { heartbeat_interval, tx }
Self {
heartbeat_interval,
tx,
}
}
}
@ -551,7 +591,7 @@ struct HeartbeatThreadCommunication {
/// An opcode for the communication we received
op: u8,
/// The sequence number we got from discord
d: u64
d: u64,
}
/**
@ -559,7 +599,7 @@ Trait which defines the behaviour of an Observer. An Observer is an object which
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.
*/
pub trait Observer<T: WebSocketEvent>: std::fmt::Debug {
pub trait Observer<T: types::WebSocketEvent>: std::fmt::Debug {
fn update(&self, data: &T);
}
@ -568,13 +608,13 @@ change in the WebSocketEvent. GatewayEvents are observable.
*/
#[derive(Default, Debug)]
pub struct GatewayEvent<T: WebSocketEvent> {
pub struct GatewayEvent<T: types::WebSocketEvent> {
observers: Vec<Arc<Mutex<dyn Observer<T> + Sync + Send>>>,
pub event_data: T,
pub is_observed: bool,
}
impl<T: WebSocketEvent> GatewayEvent<T> {
impl<T: types::WebSocketEvent> GatewayEvent<T> {
fn new(event_data: T) -> Self {
Self {
is_observed: false,
@ -597,7 +637,10 @@ impl<T: WebSocketEvent> GatewayEvent<T> {
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>>) -> Option<ObserverError> {
pub fn subscribe(
&mut self,
observable: Arc<Mutex<dyn Observer<T> + Sync + Send>>,
) -> Option<ObserverError> {
if self.is_observed {
return Some(ObserverError::AlreadySubscribedError);
}
@ -614,7 +657,8 @@ impl<T: WebSocketEvent> GatewayEvent<T> {
// 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
// anddd there is no way to do that without using format
self.observers.retain(|obs| !(format!("{:?}", obs) == format!("{:?}", &observable)));
self.observers
.retain(|obs| !(format!("{:?}", obs) == format!("{:?}", &observable)));
self.is_observed = !self.observers.is_empty();
}
@ -651,136 +695,135 @@ mod events {
pub call: Call,
pub voice: Voice,
pub webhooks: Webhooks,
pub gateway_identify_payload: GatewayEvent<GatewayIdentifyPayload>,
pub gateway_resume: GatewayEvent<GatewayResume>,
pub gateway_identify_payload: GatewayEvent<types::GatewayIdentifyPayload>,
pub gateway_resume: GatewayEvent<types::GatewayResume>,
}
#[derive(Default, Debug)]
pub struct Session {
pub ready: GatewayEvent<GatewayReady>,
pub ready_supplimental: GatewayEvent<GatewayReadySupplemental>,
pub replace: GatewayEvent<SessionsReplace>
pub ready: GatewayEvent<types::GatewayReady>,
pub ready_supplimental: GatewayEvent<types::GatewayReadySupplemental>,
pub replace: GatewayEvent<types::SessionsReplace>
}
#[derive(Default, Debug)]
pub struct Message {
pub create: GatewayEvent<MessageCreate>,
pub update: GatewayEvent<MessageUpdate>,
pub delete: GatewayEvent<MessageDelete>,
pub delete_bulk: GatewayEvent<MessageDeleteBulk>,
pub reaction_add: GatewayEvent<MessageReactionAdd>,
pub reaction_remove: GatewayEvent<MessageReactionRemove>,
pub reaction_remove_all: GatewayEvent<MessageReactionRemoveAll>,
pub reaction_remove_emoji: GatewayEvent<MessageReactionRemoveEmoji>,
pub ack: GatewayEvent<MessageACK>
pub create: GatewayEvent<types::MessageCreate>,
pub update: GatewayEvent<types::MessageUpdate>,
pub delete: GatewayEvent<types::MessageDelete>,
pub delete_bulk: GatewayEvent<types::MessageDeleteBulk>,
pub reaction_add: GatewayEvent<types::MessageReactionAdd>,
pub reaction_remove: GatewayEvent<types::MessageReactionRemove>,
pub reaction_remove_all: GatewayEvent<types::MessageReactionRemoveAll>,
pub reaction_remove_emoji: GatewayEvent<types::MessageReactionRemoveEmoji>,
pub ack: GatewayEvent<types::MessageACK>
}
#[derive(Default, Debug)]
pub struct User {
pub update: GatewayEvent<UserUpdate>,
pub presence_update: GatewayEvent<PresenceUpdate>,
pub typing_start_event: GatewayEvent<TypingStartEvent>,
pub update: GatewayEvent<types::UserUpdate>,
pub presence_update: GatewayEvent<types::PresenceUpdate>,
pub typing_start_event: GatewayEvent<types::TypingStartEvent>,
}
#[derive(Default, Debug)]
pub struct Channel {
pub create: GatewayEvent<ChannelCreate>,
pub update: GatewayEvent<ChannelUpdate>,
pub unread_update: GatewayEvent<ChannelUnreadUpdate>,
pub delete: GatewayEvent<ChannelDelete>,
pub pins_update: GatewayEvent<ChannelPinsUpdate>
pub create: GatewayEvent<types::ChannelCreate>,
pub update: GatewayEvent<types::ChannelUpdate>,
pub unread_update: GatewayEvent<types::ChannelUnreadUpdate>,
pub delete: GatewayEvent<types::ChannelDelete>,
pub pins_update: GatewayEvent<types::ChannelPinsUpdate>,
}
#[derive(Default, Debug)]
pub struct Thread {
pub create: GatewayEvent<ThreadCreate>,
pub update: GatewayEvent<ThreadUpdate>,
pub delete: GatewayEvent<ThreadDelete>,
pub list_sync: GatewayEvent<ThreadListSync>,
pub member_update: GatewayEvent<ThreadMemberUpdate>,
pub members_update: GatewayEvent<ThreadMembersUpdate>,
pub create: GatewayEvent<types::ThreadCreate>,
pub update: GatewayEvent<types::ThreadUpdate>,
pub delete: GatewayEvent<types::ThreadDelete>,
pub list_sync: GatewayEvent<types::ThreadListSync>,
pub member_update: GatewayEvent<types::ThreadMemberUpdate>,
pub members_update: GatewayEvent<types::ThreadMembersUpdate>,
}
#[derive(Default, Debug)]
pub struct Guild {
pub create: GatewayEvent<GuildCreate>,
pub update: GatewayEvent<GuildUpdate>,
pub delete: GatewayEvent<GuildDelete>,
pub audit_log_entry_create: GatewayEvent<GuildAuditLogEntryCreate>,
pub ban_add: GatewayEvent<GuildBanAdd>,
pub ban_remove: GatewayEvent<GuildBanRemove>,
pub emojis_update: GatewayEvent<GuildEmojisUpdate>,
pub stickers_update: GatewayEvent<GuildStickersUpdate>,
pub integrations_update: GatewayEvent<GuildIntegrationsUpdate>,
pub member_add: GatewayEvent<GuildMemberAdd>,
pub member_remove: GatewayEvent<GuildMemberRemove>,
pub member_update: GatewayEvent<GuildMemberUpdate>,
pub members_chunk: GatewayEvent<GuildMembersChunk>,
pub role_create: GatewayEvent<GuildRoleCreate>,
pub role_update: GatewayEvent<GuildRoleUpdate>,
pub role_delete: GatewayEvent<GuildRoleDelete>,
pub role_scheduled_event_create: GatewayEvent<GuildScheduledEventCreate>,
pub role_scheduled_event_update: GatewayEvent<GuildScheduledEventUpdate>,
pub role_scheduled_event_delete: GatewayEvent<GuildScheduledEventDelete>,
pub role_scheduled_event_user_add: GatewayEvent<GuildScheduledEventUserAdd>,
pub role_scheduled_event_user_remove: GatewayEvent<GuildScheduledEventUserRemove>,
pub passive_update_v1: GatewayEvent<PassiveUpdateV1>,
pub create: GatewayEvent<types::GuildCreate>,
pub update: GatewayEvent<types::GuildUpdate>,
pub delete: GatewayEvent<types::GuildDelete>,
pub audit_log_entry_create: GatewayEvent<types::GuildAuditLogEntryCreate>,
pub ban_add: GatewayEvent<types::GuildBanAdd>,
pub ban_remove: GatewayEvent<types::GuildBanRemove>,
pub emojis_update: GatewayEvent<types::GuildEmojisUpdate>,
pub stickers_update: GatewayEvent<types::GuildStickersUpdate>,
pub integrations_update: GatewayEvent<types::GuildIntegrationsUpdate>,
pub member_add: GatewayEvent<types::GuildMemberAdd>,
pub member_remove: GatewayEvent<types::GuildMemberRemove>,
pub member_update: GatewayEvent<types::GuildMemberUpdate>,
pub members_chunk: GatewayEvent<types::GuildMembersChunk>,
pub role_create: GatewayEvent<types::GuildRoleCreate>,
pub role_update: GatewayEvent<types::GuildRoleUpdate>,
pub role_delete: GatewayEvent<types::GuildRoleDelete>,
pub role_scheduled_event_create: GatewayEvent<types::GuildScheduledEventCreate>,
pub role_scheduled_event_update: GatewayEvent<types::GuildScheduledEventUpdate>,
pub role_scheduled_event_delete: GatewayEvent<types::GuildScheduledEventDelete>,
pub role_scheduled_event_user_add: GatewayEvent<types::GuildScheduledEventUserAdd>,
pub role_scheduled_event_user_remove: GatewayEvent<types::GuildScheduledEventUserRemove>,
pub passive_update_v1: GatewayEvent<types::PassiveUpdateV1>,
}
#[derive(Default, Debug)]
pub struct Invite {
pub create: GatewayEvent<InviteCreate>,
pub delete: GatewayEvent<InviteDelete>
pub create: GatewayEvent<types::InviteCreate>,
pub delete: GatewayEvent<types::InviteDelete>
}
#[derive(Default, Debug)]
pub struct Integration {
pub create: GatewayEvent<IntegrationCreate>,
pub update: GatewayEvent<IntegrationUpdate>,
pub delete: GatewayEvent<IntegrationDelete>
pub create: GatewayEvent<types::IntegrationCreate>,
pub update: GatewayEvent<types::IntegrationUpdate>,
pub delete: GatewayEvent<types::IntegrationDelete>
}
#[derive(Default, Debug)]
pub struct Call {
pub create: GatewayEvent<CallCreate>,
pub update: GatewayEvent<CallUpdate>,
pub delete: GatewayEvent<CallDelete>
pub create: GatewayEvent<types::CallCreate>,
pub update: GatewayEvent<types::CallUpdate>,
pub delete: GatewayEvent<types::CallDelete>
}
#[derive(Default, Debug)]
pub struct Voice {
pub state_update: GatewayEvent<VoiceStateUpdate>,
pub server_update: GatewayEvent<VoiceServerUpdate>
pub state_update: GatewayEvent<types::VoiceStateUpdate>,
pub server_update: GatewayEvent<types::VoiceServerUpdate>
}
#[derive(Default, Debug)]
pub struct Webhooks {
pub update: GatewayEvent<WebhooksUpdate>,
pub update: GatewayEvent<types::WebhooksUpdate>,
}
}
#[cfg(test)]
mod example {
use super::*;
use crate::api::types::GatewayResume;
#[derive(Debug)]
struct Consumer;
impl Observer<GatewayResume> for Consumer {
fn update(&self, data: &GatewayResume) {
impl Observer<types::GatewayResume> for Consumer {
fn update(&self, data: &types::GatewayResume) {
println!("{}", data.token)
}
}
#[tokio::test]
async fn test_observer_behaviour() {
let mut event = GatewayEvent::new(GatewayResume {
let mut event = GatewayEvent::new(types::GatewayResume {
token: "start".to_string(),
session_id: "start".to_string(),
seq: "start".to_string(),
});
let new_data = GatewayResume {
let new_data = types::GatewayResume {
token: "token_3276ha37am3".to_string(),
session_id: "89346671230".to_string(),
seq: "3".to_string(),
@ -809,7 +852,6 @@ mod example {
None => assert!(true),
Some(_) => assert!(false),
}
}
#[tokio::test]

View File

@ -1,9 +1,13 @@
use serde::{Deserialize, Serialize};
use crate::api::limits::Limits;
use crate::api::types::InstancePolicies;
use crate::errors::{FieldFormatError, InstanceServerError};
use crate::types::{GeneralConfiguration, User, UserSettings};
use crate::URLBundle;
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
#[derive(Debug, Clone)]
/**
@ -11,7 +15,7 @@ The [`Instance`] what you will be using to perform all sorts of actions on the S
*/
pub struct Instance {
pub urls: URLBundle,
pub instance_info: InstancePolicies,
pub instance_info: GeneralConfiguration,
pub limits: Limits,
}
@ -25,7 +29,7 @@ impl Instance {
pub async fn new(urls: URLBundle) -> Result<Instance, InstanceServerError> {
let mut instance = Instance {
urls: urls.clone(),
instance_info: InstancePolicies::new(
instance_info: GeneralConfiguration::new(
// This is okay, because the instance_info will be overwritten by the instance_policies_schema() function.
"".to_string(),
None,
@ -38,7 +42,7 @@ impl Instance {
),
limits: Limits::check_limits(urls.api).await,
};
instance.instance_info = match instance.instance_policies_schema().await {
instance.instance_info = match instance.general_configuration_schema().await {
Ok(schema) => schema,
Err(e) => {
return Err(InstanceServerError::CantGetInfoError {
@ -50,7 +54,7 @@ impl Instance {
}
}
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Token {
pub token: String,
}
@ -79,3 +83,38 @@ impl Username {
Ok(Username { username })
}
}
#[derive(Debug)]
pub struct UserMeta {
pub belongs_to: Rc<RefCell<Instance>>,
pub token: String,
pub limits: Limits,
pub settings: UserSettings,
pub object: Option<User>,
}
impl UserMeta {
pub fn token(&self) -> String {
self.token.clone()
}
pub fn set_token(&mut self, token: String) {
self.token = token;
}
pub fn new(
belongs_to: Rc<RefCell<Instance>>,
token: String,
limits: Limits,
settings: UserSettings,
object: Option<User>,
) -> UserMeta {
UserMeta {
belongs_to,
token,
limits,
settings,
object,
}
}
}

View File

@ -3,6 +3,7 @@ pub mod errors;
pub mod gateway;
pub mod instance;
pub mod limit;
pub mod types;
pub mod voice;
use url::{ParseError, Url};

187
src/types/config/mod.rs Normal file
View File

@ -0,0 +1,187 @@
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
pub use crate::{
types::config::types::{
api_configuration::ApiConfiguration, cdn_configuration::CdnConfiguration,
defaults_configuration::DefaultsConfiguration, email_configuration::EmailConfiguration,
endpoint_configuration::EndpointConfiguration,
external_tokens_configuration::ExternalTokensConfiguration,
general_configuration::GeneralConfiguration, gif_configuration::GifConfiguration,
guild_configuration::GuildConfiguration, kafka_configuration::KafkaConfiguration,
limit_configuration::LimitsConfiguration, login_configuration::LoginConfiguration,
metrics_configuration::MetricsConfiguration,
password_reset_configuration::PasswordResetConfiguration,
rabbit_mq_configuration::RabbitMQConfiguration, region_configuration::RegionConfiguration,
register_configuration::RegisterConfiguration,
security_configuration::SecurityConfiguration, sentry_configuration::SentryConfiguration,
template_configuration::TemplateConfiguration,
},
types::entities::ConfigEntity,
};
pub mod types;
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConfigValue {
pub gateway: EndpointConfiguration,
pub cdn: CdnConfiguration,
pub api: ApiConfiguration,
pub general: GeneralConfiguration,
pub limits: LimitsConfiguration,
pub security: SecurityConfiguration,
pub login: LoginConfiguration,
pub register: RegisterConfiguration,
pub regions: RegionConfiguration,
pub guild: GuildConfiguration,
pub gif: GifConfiguration,
pub rabbitmq: RabbitMQConfiguration,
pub kafka: KafkaConfiguration,
pub templates: TemplateConfiguration,
pub metrics: MetricsConfiguration,
pub sentry: SentryConfiguration,
pub defaults: DefaultsConfiguration,
pub external: ExternalTokensConfiguration,
pub email: EmailConfiguration,
pub password_reset: PasswordResetConfiguration,
}
impl ConfigValue {
pub fn to_pairs(&self) -> Vec<ConfigEntity> {
let v = serde_json::json!(self);
generate_pairs(&v, "")
}
pub fn from_pairs(pairs: Vec<ConfigEntity>) -> Self {
pairs_to_config(pairs)
}
}
fn generate_pairs(obj: &Value, key: &str) -> Vec<ConfigEntity> {
let mut pairs = Vec::new();
match obj {
Value::Object(map) => {
for (k, v) in map {
let new_key = if key.is_empty() {
k.to_string()
} else {
format!("{}_{}", key, k)
};
pairs.extend(generate_pairs(v, &new_key));
}
}
Value::Array(arr) => {
for (i, v) in arr.iter().enumerate() {
let new_key = format!("{}_{}", key, i);
pairs.extend(generate_pairs(v, &new_key));
}
}
_ => pairs.push(ConfigEntity {
key: key.to_string(),
value: Some(obj.clone()),
}),
}
pairs
}
fn pairs_to_config(pairs: Vec<ConfigEntity>) -> ConfigValue {
let mut value = Value::Object(Map::new());
for p in pairs {
let keys: Vec<&str> = p.key.split('_').collect();
let mut path = vec![];
for (i, &key) in keys.iter().enumerate() {
path.push(key);
if i == keys.len() - 1 {
insert_into(&mut value, &path, p.value.clone().unwrap_or(Value::Null));
} else if keys[i + 1].parse::<usize>().is_ok() {
if !path_exists(&value, &path) {
insert_into(&mut value, &path, Value::Array(Vec::new()));
}
} else if !path_exists(&value, &path) {
insert_into(&mut value, &path, Value::Object(Map::new()));
}
}
}
serde_json::from_value(value).unwrap()
}
fn path_exists(value: &Value, path: &[&str]) -> bool {
let mut current = value;
for &key in path {
match current {
Value::Object(map) => {
if let Some(v) = map.get(key) {
current = v;
} else {
return false;
}
}
Value::Array(arr) => {
if let Ok(index) = key.parse::<usize>() {
if let Some(v) = arr.get(index) {
current = v;
} else {
return false;
}
} else {
return false;
}
}
_ => return false,
}
}
true
}
fn insert_into(value: &mut Value, path: &[&str], new_value: Value) {
let last_key = path.last().unwrap();
let parent_path = &path[0..path.len() - 1];
let mut current = value;
for &key in parent_path {
current = match current {
Value::Object(map) => map.get_mut(key).unwrap(),
Value::Array(arr) => arr.get_mut(key.parse::<usize>().unwrap()).unwrap(),
_ => unreachable!(),
};
}
match current {
Value::Object(map) => {
map.insert((*last_key).to_string(), new_value);
}
Value::Array(arr) => {
let index = last_key.parse::<usize>().unwrap();
if index >= arr.len() {
arr.resize(index + 1, Value::Null);
}
arr[index] = new_value;
}
_ => unreachable!(),
};
}
#[cfg(test)]
mod test {
use crate::types::config::{generate_pairs, pairs_to_config, ConfigValue};
#[test]
fn test_pairs() {
let c = ConfigValue::default();
let v = serde_json::json!(&c);
let pairs = generate_pairs(&v, "");
let cfg = pairs_to_config(pairs);
assert_eq!(cfg, c)
}
}

View File

@ -0,0 +1,24 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ApiConfiguration {
pub default_version: String,
pub active_versions: Vec<String>,
pub endpoint_public: Option<String>,
}
impl Default for ApiConfiguration {
fn default() -> Self {
Self {
default_version: String::from("9"),
active_versions: vec![
String::from("6"),
String::from("7"),
String::from("8"),
String::from("9"),
],
endpoint_public: None,
}
}
}

View File

@ -0,0 +1,25 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CdnConfiguration {
pub resize_height_max: u64,
pub resize_width_max: u64,
pub imagor_server_url: Option<String>,
pub endpoint_public: Option<String>,
pub endpoint_private: Option<String>,
}
impl Default for CdnConfiguration {
fn default() -> Self {
Self {
resize_height_max: 1000,
resize_width_max: 1000,
imagor_server_url: None,
endpoint_private: None,
endpoint_public: None,
}
}
}

View File

@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::defaults::{guild::GuildDefaults, user::UserDefaults};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DefaultsConfiguration {
pub guild: GuildDefaults,
pub user: UserDefaults,
}

View File

@ -0,0 +1,25 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::email::{
mailgun::MailGunConfiguration, mailjet::MailJetConfiguration, sendgrid::SendGridConfiguration,
smtp::SMTPConfiguration,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
pub enum EmailProvider {
Smtp,
MailGun,
MailJet,
SendGrid,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[derive(Default)]
pub struct EmailConfiguration {
pub provider: Option<EmailProvider>,
pub smtp: SMTPConfiguration,
pub mailgun: MailGunConfiguration,
pub mailjet: MailJetConfiguration,
pub sendgrid: SendGridConfiguration,
}

View File

@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EndpointConfiguration {
pub endpoint_client: Option<String>,
pub endpoint_private: Option<String>,
pub endpoint_public: Option<String>,
}

View File

@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExternalTokensConfiguration {
pub twitter: Option<String>,
}

View File

@ -0,0 +1,57 @@
use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeneralConfiguration {
pub instance_name: String,
pub instance_description: Option<String>,
pub front_page: Option<String>,
pub tos_page: Option<String>,
pub correspondence_email: Option<String>,
pub correspondence_user_id: Option<String>,
pub image: Option<String>,
pub instance_id: Option<Snowflake>,
}
impl Default for GeneralConfiguration {
fn default() -> Self {
Self {
instance_name: String::from("Spacebar Instance"),
instance_description: Some(String::from(
"This is a Spacebar instance made in the pre-release days",
)),
front_page: None,
tos_page: None,
correspondence_email: None,
correspondence_user_id: None,
image: None,
instance_id: Some(Snowflake::generate()),
}
}
}
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,
}
}
}

View File

@ -0,0 +1,26 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum GifProvider {
#[default]
Tenor,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GifConfiguration {
pub enabled: bool,
pub provider: GifProvider,
pub api_key: Option<String>,
}
impl Default for GifConfiguration {
fn default() -> Self {
Self {
enabled: true,
provider: GifProvider::Tenor,
api_key: Some(String::from("LIVDSRZULELA")),
}
}
}

View File

@ -0,0 +1,126 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::guild::{
autojoin::AutoJoinConfiguration, discovery::DiscoverConfiguration,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum GuildFeatures {
ActivitiesAlpha,
ActivitiesEmployee,
ActivitiesInternalDev,
AnimatedBanner,
AnimatedIcon,
ApplicationCommandPermissionsV2,
AutoModeration,
AutoModTriggerKeywordFilter,
AutoModTriggerMLSpamFilter,
AutoModTriggerSpamLinkFilter,
AutoModTriggerUserProfile,
Banner,
BFG,
BoostingTiersExperimentMediumGuild,
BoostingTiersExperimentSmallGuild,
BotDeveloperEarlyAccess,
BurstReactions,
CommunityCanary,
CommunityExpLargeGated,
CommunityExpLargeUngated,
CommunityExpMedium,
ChannelEmojisGenerated,
ChannelHighlights,
ChannelHighlightsDisabled,
ClydeEnabled,
ClydeExperimentEnabled,
ClydeDisabled,
Community,
CreatorAcceptedNewTerms,
CreatorMonetizable,
CreatorMonetizableDisabled,
CreatorMonetizablePendingNewOwnerOnboarding,
CreatorMonetizableProvisional,
CreatorMonetizableRestricted,
CreatorMonetizableWhiteglove,
CreatorMonetizableApplicationAllowlist,
CreateStorePage,
DeveloperSupportServer,
DiscoverableDisabled,
Discoverable,
EnabledDiscoverableBefore,
ExposedToActivitiesWTPExperiment,
GuestsEnabled,
GuildAutomodDefaultList,
GuildCommunicationDisabledGuilds,
GuildHomeDeprecationOverride,
GuildHomeOverride,
GuildHomeTest,
GuildMemberVerificationExperiment,
GuildOnboarding,
GuildOnboardingAdminOnly,
GuildOnboardingEverEnabled,
GuildOnboardingHasPrompts,
GuildRoleSubscription,
GuildRoleSubscriptionPurchaseFeedbackLoop,
GuildRoleSubscriptionTrials,
GuildServerGuide,
GuildWebPageVanityURL,
HadEarlyActivitiesAccess,
HasDirectoryEntry,
HideFromExperimentUI,
Hub,
IncreasedThreadLimit,
InternalEmployeeOnly,
InviteSplash,
InvitesDisabled,
LinkedToHub,
MarketplacesConnectionRoles,
MemberProfiles,
MemberVerificationGateEnabled,
MemberVerificationManualApproval,
MobileWebRoleSubscriptionPurchasePage,
MonetizationEnabled,
MoreEmoji,
MoreStickers,
News,
NewThreadPermissions,
Partnered,
PremiumTier3Override,
PreviewEnabled,
RaidAlertsDisabled,
RelayEnabled,
RestrictSpamRiskGuild,
RoleIcons,
RoleSubscriptionsAvailableForPurchase,
RoleSubscriptionsEnabled,
RoleSubscriptionsEnabledForPurchase,
Shard,
SharedCanvasFriendsAndFamilyTest,
Soundboard,
SummariesEnabled,
SummariesEnabledGA,
SummariesDisabledByUser,
SummariesEnabledByUser,
TextInStageEnabled,
TextInVoiceEnabled,
ThreadsEnabledTesting,
ThreadsEnabled,
ThreadDefaultAutoArchiveDuration,
ThreadsOnlyChannel,
TicketedEventsEnabled,
TicketingEnabled,
VanityUrls,
Verified,
VIPRegions,
VoiceChannelEffects,
WelcomeScreenEnabled,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GuildConfiguration {
pub discovery: DiscoverConfiguration,
pub auto_join: AutoJoinConfiguration,
#[serde(default)]
pub default_features: Vec<GuildFeatures>,
}

View File

@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::kafka::KafkaBroker;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct KafkaConfiguration {
#[serde(default)]
pub brokers: Option<Vec<KafkaBroker>>,
}

View File

@ -0,0 +1,17 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::limits::{
channel::ChannelLimits, global::GlobalRateLimits, guild::GuildLimits, message::MessageLimits,
rates::RateLimits, user::UserLimits,
};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LimitsConfiguration {
pub user: UserLimits,
pub guild: GuildLimits,
pub message: MessageLimits,
pub channel: ChannelLimits,
pub rate: RateLimits,
pub absolute_rate: GlobalRateLimits,
}

View File

@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LoginConfiguration {
pub require_captcha: bool,
pub require_verification: bool,
}

View File

@ -0,0 +1,13 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricsConfiguration {
pub timeout: u64,
}
impl Default for MetricsConfiguration {
fn default() -> Self {
Self { timeout: 30000 }
}
}

View File

@ -0,0 +1,21 @@
pub mod api_configuration;
pub mod cdn_configuration;
pub mod defaults_configuration;
pub mod email_configuration;
pub mod endpoint_configuration;
pub mod external_tokens_configuration;
pub mod general_configuration;
pub mod gif_configuration;
pub mod guild_configuration;
pub mod kafka_configuration;
pub mod limit_configuration;
pub mod login_configuration;
pub mod metrics_configuration;
pub mod password_reset_configuration;
pub mod rabbit_mq_configuration;
pub mod region_configuration;
pub mod register_configuration;
pub mod security_configuration;
pub mod sentry_configuration;
pub mod subconfigs;
pub mod template_configuration;

View File

@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PasswordResetConfiguration {
pub require_captcha: bool,
}

View File

@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RabbitMQConfiguration {
pub host: Option<String>,
}

View File

@ -0,0 +1,29 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::region::Region;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RegionConfiguration {
pub default: String,
pub use_default_as_optimal: bool,
pub available: Vec<Region>,
}
impl Default for RegionConfiguration {
fn default() -> Self {
Self {
default: String::from("spacebar"),
use_default_as_optimal: true,
available: vec![Region {
id: String::from("spacebar"),
name: String::from("spacebar"),
endpoint: String::from("127.0.0.1:3004"),
location: None,
vip: false,
custom: false,
deprecated: false,
}],
}
}
}

View File

@ -0,0 +1,41 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::register::{
DateOfBirthConfiguration, PasswordConfiguration, RegistrationEmailConfiguration,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RegisterConfiguration {
pub email: RegistrationEmailConfiguration,
pub date_of_birth: DateOfBirthConfiguration,
pub password: PasswordConfiguration,
pub disabled: bool,
pub require_captcha: bool,
pub require_invite: bool,
pub guests_require_invite: bool,
pub allow_new_registration: bool,
pub allow_multiple_accounts: bool,
pub block_proxies: bool,
pub incrementing_discriminators: bool,
pub default_rights: String,
}
impl Default for RegisterConfiguration {
fn default() -> Self {
Self {
email: RegistrationEmailConfiguration::default(),
date_of_birth: DateOfBirthConfiguration::default(),
password: PasswordConfiguration::default(),
disabled: false,
require_captcha: true,
require_invite: false,
guests_require_invite: true,
allow_new_registration: true,
allow_multiple_accounts: true,
block_proxies: true,
incrementing_discriminators: false,
default_rights: String::from("875069521787904"),
}
}
}

View File

@ -0,0 +1,44 @@
use base64::Engine;
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::security::{
CaptchaConfiguration, TwoFactorConfiguration,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SecurityConfiguration {
pub captcha: CaptchaConfiguration,
pub two_factor: TwoFactorConfiguration,
pub auto_update: bool,
pub request_signature: String,
pub jwt_secret: String,
pub forwarded_for: Option<String>,
pub ipdata_api_key: Option<String>,
pub mfa_backup_code_count: u8,
pub stats_world_readable: bool,
pub default_registration_token_expiration: u64,
}
impl Default for SecurityConfiguration {
fn default() -> Self {
let mut req_sig: [u8; 32] = [0; 32];
let _ = openssl::rand::rand_bytes(&mut req_sig);
let mut jwt_secret: [u8; 256] = [0; 256];
let _ = openssl::rand::rand_bytes(&mut jwt_secret);
Self {
captcha: Default::default(),
two_factor: Default::default(),
auto_update: true,
request_signature: base64::engine::general_purpose::STANDARD.encode(req_sig),
jwt_secret: base64::engine::general_purpose::STANDARD.encode(jwt_secret),
forwarded_for: None,
ipdata_api_key: Some(String::from(
"eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9",
)),
mfa_backup_code_count: 10,
stats_world_readable: true,
default_registration_token_expiration: 1000 * 60 * 60 * 24 * 7,
}
}
}

View File

@ -0,0 +1,28 @@
use std::ffi::OsString;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SentryConfiguration {
pub enabled: bool,
pub endpoint: String,
pub trace_sample_rate: f64,
pub environment: String,
}
impl Default for SentryConfiguration {
fn default() -> Self {
Self {
enabled: false,
endpoint: String::from(
"https://241c6fb08adb469da1bb82522b25c99f@sentry.quartzinc.space/3",
),
trace_sample_rate: 1.0,
environment: hostname::get()
.unwrap_or_else(|_| OsString::new())
.to_string_lossy()
.to_string(),
}
}
}

View File

@ -0,0 +1,17 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientReleaseConfiguration {
pub use_local_release: bool,
pub upstream_version: String,
}
impl Default for ClientReleaseConfiguration {
fn default() -> Self {
Self {
use_local_release: true,
upstream_version: String::from("0.0.264"),
}
}
}

View File

@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GuildDefaults {
pub max_presences: u64,
pub max_video_channel_users: u16,
pub afk_timeout: u16,
pub default_message_notifications: u8,
pub explicit_content_filter: u8,
}
impl Default for GuildDefaults {
fn default() -> Self {
Self {
max_presences: 250_000,
max_video_channel_users: 200,
afk_timeout: 300,
default_message_notifications: 1,
explicit_content_filter: 0,
}
}
}

View File

@ -0,0 +1,2 @@
pub mod guild;
pub mod user;

View File

@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserDefaults {
pub premium: bool,
pub premium_type: u8,
pub verified: bool,
}
impl Default for UserDefaults {
fn default() -> Self {
Self {
premium: true,
premium_type: 2,
verified: true,
}
}
}

View File

@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MailGunConfiguration {
pub api_key: Option<String>,
pub domain: Option<String>,
}

View File

@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MailJetConfiguration {
pub api_key: Option<String>,
pub api_secret: Option<String>,
}

View File

@ -0,0 +1,4 @@
pub mod mailgun;
pub mod mailjet;
pub mod sendgrid;
pub mod smtp;

View File

@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SendGridConfiguration {
pub api_key: Option<String>,
}

View File

@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct SMTPConfiguration {
pub host: Option<String>,
pub port: Option<u16>,
pub secure: bool,
pub username: Option<String>,
pub password: Option<String>,
}

View File

@ -0,0 +1,22 @@
use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AutoJoinConfiguration {
pub enabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub guilds: Option<Vec<Snowflake>>,
pub can_leave: bool,
}
impl Default for AutoJoinConfiguration {
fn default() -> Self {
Self {
enabled: true,
guilds: None,
can_leave: true,
}
}
}

View File

@ -0,0 +1,21 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DiscoverConfiguration {
pub show_all_guilds: bool,
pub use_recommendation: bool,
pub offset: u16,
pub limit: u16,
}
impl Default for DiscoverConfiguration {
fn default() -> Self {
Self {
show_all_guilds: false,
use_recommendation: false,
offset: 0,
limit: 24,
}
}
}

View File

@ -0,0 +1,2 @@
pub mod autojoin;
pub mod discovery;

View File

@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct KafkaBroker {
pub ip: String,
pub port: u16,
}

View File

@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChannelLimits {
pub max_pins: u16,
pub max_topic: u16,
pub max_webhooks: u16,
}
impl Default for ChannelLimits {
fn default() -> Self {
Self {
max_pins: 500,
max_topic: 1024,
max_webhooks: 100,
}
}
}

View File

@ -0,0 +1,41 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GlobalRateLimit {
pub limit: u16,
pub window: u64,
pub enabled: bool,
}
impl Default for GlobalRateLimit {
fn default() -> Self {
Self {
limit: 100,
window: 60 * 60 * 1000,
enabled: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GlobalRateLimits {
pub register: GlobalRateLimit,
pub send_message: GlobalRateLimit,
}
impl Default for GlobalRateLimits {
fn default() -> Self {
Self {
register: GlobalRateLimit {
limit: 25,
..Default::default()
},
send_message: GlobalRateLimit {
limit: 200,
window: 60 * 1000,
..Default::default()
},
}
}
}

View File

@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GuildLimits {
pub max_roles: u16,
pub max_emojis: u16,
pub max_members: u64,
pub max_channels: u32,
pub max_channels_in_category: u32,
}
impl Default for GuildLimits {
fn default() -> Self {
Self {
max_roles: 1000,
max_emojis: 20_000,
max_members: 25_000_000,
max_channels: 65_535,
max_channels_in_category: 65_535,
}
}
}

View File

@ -0,0 +1,26 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MessageLimits {
pub max_characters: u32,
#[serde(default)]
pub max_tts_characters: u32,
pub max_reactions: u32,
pub max_attachment_size: u64,
pub max_bulk_delete: u32,
pub max_embed_download_size: u64,
}
impl Default for MessageLimits {
fn default() -> Self {
Self {
max_characters: 1048576,
max_tts_characters: 160,
max_reactions: 2048,
max_attachment_size: 1024 * 1024 * 1024,
max_bulk_delete: 1000,
max_embed_download_size: 1024 * 1024 * 5,
}
}
}

View File

@ -0,0 +1,7 @@
pub mod channel;
pub mod global;
pub mod guild;
pub mod message;
pub mod ratelimits;
pub mod rates;
pub mod user;

View File

@ -0,0 +1,28 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::limits::ratelimits::RateLimitOptions;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuthRateLimit {
pub login: RateLimitOptions,
pub register: RateLimitOptions,
}
impl Default for AuthRateLimit {
fn default() -> Self {
Self {
login: RateLimitOptions {
bot: None,
count: 5,
window: 60,
only_ip: false,
},
register: RateLimitOptions {
bot: None,
count: 2,
window: 60 * 60 * 12,
only_ip: false,
},
}
}
}

View File

@ -0,0 +1,14 @@
use serde::{Deserialize, Serialize};
pub mod auth;
pub mod route;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RateLimitOptions {
pub bot: Option<u64>,
pub count: u64,
pub window: u64,
#[serde(default)]
pub only_ip: bool,
}

View File

@ -0,0 +1,39 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::limits::ratelimits::{
auth::AuthRateLimit, RateLimitOptions,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RouteRateLimit {
pub guild: RateLimitOptions,
pub webhook: RateLimitOptions,
pub channel: RateLimitOptions,
pub auth: AuthRateLimit,
}
impl Default for RouteRateLimit {
fn default() -> Self {
Self {
guild: RateLimitOptions {
bot: None,
count: 5,
window: 5,
only_ip: false,
},
webhook: RateLimitOptions {
bot: None,
count: 10,
window: 5,
only_ip: false,
},
channel: RateLimitOptions {
bot: None,
count: 10,
window: 5,
only_ip: false,
},
auth: AuthRateLimit::default(),
}
}
}

View File

@ -0,0 +1,41 @@
use serde::{Deserialize, Serialize};
use crate::types::config::types::subconfigs::limits::ratelimits::{
route::RouteRateLimit, RateLimitOptions,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RateLimits {
pub enabled: bool,
pub ip: RateLimitOptions,
pub global: RateLimitOptions,
pub error: RateLimitOptions,
pub routes: RouteRateLimit,
}
impl Default for RateLimits {
fn default() -> Self {
Self {
enabled: false,
ip: RateLimitOptions {
bot: None,
count: 500,
window: 5,
only_ip: false,
},
global: RateLimitOptions {
bot: None,
count: 250,
window: 5,
only_ip: false,
},
error: RateLimitOptions {
bot: None,
count: 10,
window: 5,
only_ip: false,
},
routes: RouteRateLimit::default(),
}
}
}

View File

@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserLimits {
pub max_guilds: u64,
pub max_username: u16,
pub max_friends: u64,
}
impl Default for UserLimits {
fn default() -> Self {
Self {
max_guilds: 1048576,
max_username: 32,
max_friends: 5000,
}
}
}

View File

@ -0,0 +1,9 @@
pub mod client;
pub mod defaults;
pub mod email;
pub mod guild;
pub mod kafka;
pub mod limits;
pub mod region;
pub mod register;
pub mod security;

View File

@ -0,0 +1,21 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LatLong {
pub latitude: f64,
pub longitude: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Region {
pub id: String,
pub name: String,
pub endpoint: String,
pub location: Option<LatLong>,
pub vip: bool,
pub custom: bool,
#[serde(default)]
pub deprecated: bool,
}

View File

@ -0,0 +1,16 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DateOfBirthConfiguration {
pub required: bool,
pub minimum: u8,
}
impl Default for DateOfBirthConfiguration {
fn default() -> Self {
Self {
required: true,
minimum: 13,
}
}
}

View File

@ -0,0 +1,21 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RegistrationEmailConfiguration {
pub required: bool,
pub allowlist: bool,
#[serde(default)]
pub blacklist: bool,
#[serde(default)]
pub domains: Vec<String>,
}
impl Default for RegistrationEmailConfiguration {
fn default() -> Self {
Self {
required: false,
allowlist: false,
blacklist: true,
domains: Vec::new(),
}
}
}

View File

@ -0,0 +1,7 @@
mod date_of_birth;
mod email;
mod password;
pub use date_of_birth::DateOfBirthConfiguration;
pub use email::RegistrationEmailConfiguration;
pub use password::PasswordConfiguration;

View File

@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PasswordConfiguration {
pub required: bool,
pub min_length: u8,
pub min_numbers: u8,
pub min_upper_case: u8,
pub min_symbols: u8,
}
impl Default for PasswordConfiguration {
fn default() -> Self {
Self {
required: false,
min_length: 8,
min_numbers: 2,
min_upper_case: 2,
min_symbols: 0,
}
}
}

View File

@ -0,0 +1,27 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CaptchaService {
Recaptcha,
HCaptcha,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CaptchaConfiguration {
pub enabled: bool,
pub service: CaptchaService,
pub sitekey: Option<String>,
pub secret: Option<String>,
}
impl Default for CaptchaConfiguration {
fn default() -> Self {
Self {
enabled: false,
service: CaptchaService::HCaptcha,
sitekey: None,
secret: None,
}
}
}

View File

@ -0,0 +1,5 @@
mod captcha;
mod twofactor;
pub use captcha::{CaptchaConfiguration, CaptchaService};
pub use twofactor::TwoFactorConfiguration;

View File

@ -0,0 +1,15 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TwoFactorConfiguration {
pub generate_backup_codes: bool,
}
impl Default for TwoFactorConfiguration {
fn default() -> Self {
Self {
generate_backup_codes: true,
}
}
}

View File

@ -0,0 +1,21 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TemplateConfiguration {
pub enabled: bool,
pub allow_template_creation: bool,
pub allow_discord_templates: bool,
pub allow_raws: bool,
}
impl Default for TemplateConfiguration {
fn default() -> Self {
Self {
enabled: true,
allow_template_creation: true,
allow_discord_templates: true,
allow_raws: true,
}
}
}

View File

@ -0,0 +1,138 @@
use crate::types::utils::Snowflake;
use bitflags::{bitflags, Flags};
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[cfg(feature = "sqlx")]
use sqlx::FromRow;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(FromRow))]
pub struct Application {
pub id: Snowflake,
pub name: String,
pub icon: Option<String>,
pub description: Option<String>,
pub summary: Option<String>,
#[cfg(feature = "sqlx")]
pub r#type: Option<sqlx::types::Json<Value>>,
#[cfg(not(feature = "sqlx"))]
pub r#type: Option<Value>,
pub hook: bool,
pub bot_public: bool,
pub bot_require_code_grant: bool,
pub verify_key: String,
pub owner_id: Snowflake,
pub flags: u64,
#[cfg(feature = "sqlx")]
pub redirect_uris: Option<sqlx::types::Json<Vec<String>>>,
#[cfg(not(feature = "sqlx"))]
pub redirect_uris: Option<Vec<String>>,
pub rpc_application_state: i64,
pub store_application_state: i64,
pub verification_state: i64,
pub interactions_endpoint_url: Option<String>,
pub integration_public: bool,
pub integration_require_code_grant: bool,
pub discoverability_state: i64,
pub discovery_eligibility_flags: i64,
pub bot_user_id: Snowflake,
#[cfg(feature = "sqlx")]
pub tags: Option<sqlx::types::Json<Vec<String>>>,
#[cfg(not(feature = "sqlx"))]
pub tags: Option<Vec<String>>,
pub cover_image: Option<String>,
#[cfg(feature = "sqlx")]
pub install_params: Option<sqlx::types::Json<InstallParams>>,
#[cfg(not(feature = "sqlx"))]
pub install_params: Option<InstallParams>,
pub terms_of_service_url: Option<String>,
pub privacy_policy_url: Option<String>,
pub team_id: Option<Snowflake>,
}
impl Application {
pub fn flags(&self) -> ApplicationFlags {
ApplicationFlags::from_bits(self.flags.to_owned()).unwrap()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct InstallParams {
pub scopes: Vec<String>,
pub permissions: String,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct ApplicationFlags: u64 {
const APPLICATION_AUTO_MODERATION_RULE_CREATE_BADGE = 1 << 6;
const GATEWAY_PRESENCE = 1 << 12;
const GATEWAY_PRESENCE_LIMITED = 1 << 13;
const GATEWAY_GUILD_MEMBERS = 1 << 14;
const GATEWAY_GUILD_MEMBERS_LIMITED = 1 << 15;
const VERIFICATION_PENDING_GUILD_LIMIT = 1 << 16;
const EMBEDDED = 1 << 17;
const GATEWAY_MESSAGE_CONTENT = 1 << 18;
const GATEWAY_MESSAGE_CONTENT_LIMITED = 1 << 19;
const APPLICATION_COMMAND_BADGE = 1 << 23;
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationCommand {
pub id: Snowflake,
pub application_id: Snowflake,
pub name: String,
pub description: String,
pub options: Vec<ApplicationCommandOption>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationCommandOption {
pub r#type: ApplicationCommandOptionType,
pub name: String,
pub description: String,
pub required: bool,
pub choices: Vec<ApplicationCommandOptionChoice>,
pub options: Vec<ApplicationCommandOption>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationCommandOptionChoice {
pub name: String,
pub value: Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum ApplicationCommandOptionType {
#[serde(rename = "SUB_COMMAND")]
SubCommand = 1,
#[serde(rename = "SUB_COMMAND_GROUP")]
SubCommandGroup = 2,
#[serde(rename = "STRING")]
String = 3,
#[serde(rename = "INTEGER")]
Integer = 4,
#[serde(rename = "BOOLEAN")]
Boolean = 5,
#[serde(rename = "USER")]
User = 6,
#[serde(rename = "CHANNEL")]
Channel = 7,
#[serde(rename = "ROLE")]
Role = 8,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationCommandInteractionData {
pub id: Snowflake,
pub name: String,
pub options: Vec<ApplicationCommandInteractionDataOption>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApplicationCommandInteractionDataOption {
pub name: String,
pub value: Value,
pub options: Vec<ApplicationCommandInteractionDataOption>,
}

View File

@ -0,0 +1,113 @@
use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Attachment {
pub id: Snowflake,
pub filename: String,
pub description: Option<String>,
pub content_type: Option<String>,
pub size: u64,
pub url: String,
pub proxy_url: String,
pub height: Option<u64>,
pub width: Option<u64>,
pub message_id: Snowflake,
pub ephemeral: Option<bool>,
pub duration_secs: Option<f32>,
pub waveform: Option<String>,
#[serde(skip_serializing)]
pub content: Vec<u8>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PartialDiscordFileAttachment {
pub id: Option<i16>,
pub filename: String,
pub description: Option<String>,
pub content_type: Option<String>,
pub size: Option<i64>,
pub url: Option<String>,
pub proxy_url: Option<String>,
pub height: Option<i32>,
pub width: Option<i32>,
pub ephemeral: Option<bool>,
pub duration_secs: Option<f32>,
pub waveform: Option<String>,
#[serde(skip_serializing)]
pub content: Vec<u8>,
}
impl PartialDiscordFileAttachment {
/**
Moves `self.content` out of `self` and returns it.
# Returns
Vec<u8>
*/
pub fn move_content(self) -> (Vec<u8>, PartialDiscordFileAttachment) {
let content = self.content;
let updated_struct = PartialDiscordFileAttachment {
id: self.id,
filename: self.filename,
description: self.description,
content_type: self.content_type,
size: self.size,
url: self.url,
proxy_url: self.proxy_url,
height: self.height,
width: self.width,
ephemeral: self.ephemeral,
duration_secs: self.duration_secs,
waveform: self.waveform,
content: Vec::new(),
};
(content, updated_struct)
}
pub fn move_filename(self) -> (String, PartialDiscordFileAttachment) {
let filename = self.filename;
let updated_struct = PartialDiscordFileAttachment {
id: self.id,
filename: String::new(),
description: self.description,
content_type: self.content_type,
size: self.size,
url: self.url,
proxy_url: self.proxy_url,
height: self.height,
width: self.width,
ephemeral: self.ephemeral,
duration_secs: self.duration_secs,
waveform: self.waveform,
content: self.content,
};
(filename, updated_struct)
}
pub fn move_content_type(self) -> (Option<String>, PartialDiscordFileAttachment) {
let content_type = self.content_type;
let updated_struct = PartialDiscordFileAttachment {
id: self.id,
filename: self.filename,
description: self.description,
content_type: None,
size: self.size,
url: self.url,
proxy_url: self.proxy_url,
height: self.height,
width: self.width,
ephemeral: self.ephemeral,
duration_secs: self.duration_secs,
waveform: self.waveform,
content: self.content,
};
(content_type, updated_struct)
}
pub fn set_id(&mut self, id: i16) {
self.id = Some(id);
}
}

View File

@ -0,0 +1,25 @@
use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake;
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object
pub struct AuditLogEntry {
pub target_id: Option<String>,
pub changes: Option<Vec<AuditLogChange>>,
pub user_id: Option<String>,
pub id: Snowflake,
// to:do implement an enum for these types
pub action_type: u8,
// to:do add better options type
pub options: Option<serde_json::Value>,
pub reason: Option<String>
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/audit-log#audit-log-change-object
pub struct AuditLogChange {
pub new_value: Option<serde_json::Value>,
pub old_value: Option<serde_json::Value>,
pub key: String
}

View File

@ -0,0 +1,129 @@
use serde::{Deserialize, Serialize};
use serde_aux::prelude::{deserialize_number_from_string, deserialize_option_number_from_string, deserialize_string_from_number};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::types::{
entities::{GuildMember, User},
utils::Snowflake,
};
#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Channel {
pub id: Snowflake,
#[serde(rename = "type")]
pub channel_type: ChannelType,
pub guild_id: Option<Snowflake>,
pub position: Option<i32>,
pub permission_overwrites: Option<Vec<PermissionOverwrite>>,
pub name: Option<String>,
pub topic: Option<String>,
pub nsfw: Option<bool>,
pub last_message_id: Option<String>,
pub bitrate: Option<i32>,
pub user_limit: Option<i32>,
pub rate_limit_per_user: Option<i32>,
pub recipients: Option<Vec<User>>,
pub icon: Option<String>,
pub owner_id: Option<String>,
pub application_id: Option<String>,
pub parent_id: Option<String>,
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>,
pub thread_metadata: Option<ThreadMetadata>,
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>,
pub available_tags: Option<Vec<Tag>>,
pub applied_tags: Option<Vec<String>>,
pub default_reaction_emoji: Option<DefaultReaction>,
pub default_thread_rate_limit_per_user: Option<i32>,
pub default_sort_order: Option<i32>,
pub default_forum_layout: Option<i32>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct Tag {
#[serde(default)]
#[serde(deserialize_with = "deserialize_number_from_string")]
pub id: u64,
pub name: String,
pub moderated: bool,
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub emoji_id: Option<u64>,
pub emoji_name: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct PermissionOverwrite {
pub id: String,
#[serde(rename = "type")]
#[serde(deserialize_with = "deserialize_string_from_number")]
pub overwrite_type: String,
#[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")]
pub allow: String,
#[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")]
pub deny: String,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct ThreadMetadata {
pub archived: bool,
pub auto_archive_duration: i32,
pub archive_timestamp: String,
pub locked: bool,
pub invitable: Option<bool>,
pub create_timestamp: Option<String>,
}
#[derive(Default, Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct ThreadMember {
pub id: Option<u64>,
pub user_id: Option<u64>,
pub join_timestamp: Option<String>,
pub flags: Option<u64>,
pub member: Option<GuildMember>,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct DefaultReaction {
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub emoji_id: Option<u64>,
pub emoji_name: Option<String>,
}
#[derive(Default, Clone, Copy, Debug, Serialize_repr, Deserialize_repr, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[repr(i32)]
pub enum ChannelType {
#[default]
GuildText = 0,
Dm = 1,
GuildVoice = 2,
GroupDm = 3,
GuildCategory = 4,
GuildNews = 5,
GuildStore = 6,
Encrypted = 7,
EncryptedThreads = 8,
Transactional = 9,
GuildNewsThread = 10,
GuildPublicThread = 11,
GuildPrivateThread = 12,
GuildStageVoice = 13,
Directory = 14,
GuildForum = 15,
TicketTracker = 33,
Kanban = 34,
VoicelessWhiteboard = 35,
CustomStart = 64,
Unhandled = 255,
}

View File

@ -0,0 +1,34 @@
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[cfg(feature = "sqlx")]
use sqlx::FromRow;
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(FromRow))]
pub struct ConfigEntity {
pub key: String,
pub value: Option<Value>,
}
impl ConfigEntity {
pub fn as_string(&self) -> Option<String> {
let Some(v) = self.value.as_ref() else {
return None;
};
Some(v.as_str().expect("value is not a string").to_string())
}
pub fn as_bool(&self) -> Option<bool> {
let Some(v) = self.value.as_ref() else {
return None;
};
Some(v.as_bool().expect("value is not a boolean"))
}
pub fn as_int(&self) -> Option<i64> {
let Some(v) = self.value.as_ref() else {
return None;
};
Some(v.as_i64().expect("value is not a number"))
}
}

View File

@ -0,0 +1,18 @@
use serde::{Deserialize, Serialize};
use serde_aux::prelude::deserialize_option_number_from_string;
use crate::types::entities::User;
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
pub struct Emoji {
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub id: Option<u64>,
pub name: Option<String>,
pub roles: Option<Vec<u64>>,
pub user: Option<User>,
pub require_colons: Option<bool>,
pub managed: Option<bool>,
pub animated: Option<bool>,
pub available: Option<bool>,
}

174
src/types/entities/guild.rs Normal file
View File

@ -0,0 +1,174 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_repr::{Serialize_repr, Deserialize_repr};
use crate::types::{
entities::{Channel, Emoji, GuildTemplate, RoleObject, Sticker, User, VoiceState, Webhook},
interfaces::WelcomeScreenObject,
utils::Snowflake,
};
/// See https://discord.com/developers/docs/resources/guild
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Guild {
pub id: Snowflake,
pub name: String,
pub icon: Option<String>,
pub icon_hash: Option<String>,
pub splash: Option<String>,
pub discovery_splash: Option<String>,
pub owner: Option<User>,
pub owner_id: Option<Snowflake>,
pub permissions: Option<String>,
pub afk_channel_id: Option<Snowflake>,
pub afk_timeout: Option<u8>,
pub widget_enabled: Option<bool>,
pub widget_channel_id: Option<Snowflake>,
pub widget_channel: Option<Channel>,
pub verification_level: Option<u8>,
pub default_message_notifications: Option<u8>,
pub explicit_content_filter: Option<u8>,
pub roles: Vec<RoleObject>,
pub emojis: Vec<Emoji>,
pub features: Vec<String>,
pub application_id: Option<String>,
pub system_channel_id: Option<Snowflake>,
pub system_channel_flags: Option<u8>,
pub rules_channel_id: Option<String>,
pub rules_channel: Option<String>,
pub max_presences: Option<u64>,
pub max_members: Option<u64>,
pub vanity_url_code: Option<String>,
pub description: Option<String>,
pub banner: Option<String>,
pub premium_tier: Option<u8>,
pub premium_subscription_count: Option<u64>,
pub preferred_locale: Option<String>,
pub public_updates_channel_id: Option<Snowflake>,
pub public_updates_channel: Option<Channel>,
pub max_video_channel_users: Option<u8>,
pub max_stage_video_channel_users: Option<u8>,
pub approximate_member_count: Option<u64>,
pub approximate_presence_count: Option<u64>,
pub member_count: Option<u64>,
pub presence_count: Option<u64>,
pub welcome_screen: Option<WelcomeScreenObject>,
pub nsfw_level: u8,
pub nsfw: bool,
pub stickers: Option<Vec<Sticker>>,
pub premium_progress_bar_enabled: Option<bool>,
pub joined_at: String,
pub afk_channel: Option<Channel>,
pub bans: Option<Vec<GuildBan>>,
pub primary_category_id: Option<Snowflake>,
pub large: Option<bool>,
pub channels: Option<Vec<Channel>>,
pub template_id: Option<Snowflake>,
pub template: Option<GuildTemplate>,
pub invites: Option<Vec<GuildInvite>>,
pub voice_states: Option<Vec<VoiceState>>,
pub webhooks: Option<Vec<Webhook>>,
pub mfa_level: Option<u8>,
pub region: Option<String>,
pub unavailable: bool,
pub parent: Option<String>,
}
/// See https://docs.spacebar.chat/routes/#get-/guilds/-guild_id-/bans/-user-
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct GuildBan {
pub id: Snowflake,
pub user_id: Snowflake,
pub guild_id: Snowflake,
pub executor_id: Snowflake,
pub reason: Option<String>,
}
/// See https://docs.spacebar.chat/routes/#cmp--schemas-invite
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct GuildInvite {
pub code: String,
pub temporary: Option<bool>,
pub uses: Option<i32>,
pub max_uses: Option<i32>,
pub max_age: Option<i32>,
pub created_at: DateTime<Utc>,
pub expires_at: Option<DateTime<Utc>>,
pub guild_id: String,
pub guild: Option<Guild>,
pub channel_id: String,
pub channel: Option<Channel>,
pub inviter_id: Option<String>,
pub inviter: Option<User>,
pub target_user_id: Option<String>,
pub target_user: Option<String>,
pub target_user_type: Option<i32>,
pub vanity_url: Option<bool>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct UnavailableGuild {
id: String,
unavailable: bool,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct GuildCreateResponse {
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object
pub struct GuildScheduledEvent {
pub id: String,
pub guild_id: String,
pub channel_id: Option<String>,
pub creator_id: Option<String>,
pub name: String,
pub description: String,
pub scheduled_start_time: DateTime<Utc>,
pub scheduled_end_time: Option<DateTime<Utc>>,
pub privacy_level: GuildScheduledEventPrivacyLevel,
pub status: GuildScheduledEventStatus,
pub entity_type: GuildScheduledEventEntityType,
pub entity_id: Option<String>,
pub entity_metadata: Option<GuildScheduledEventEntityMetadata>,
pub creator: Option<User>,
pub user_count: Option<u64>,
pub image: Option<String>
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
#[repr(u8)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level
pub enum GuildScheduledEventPrivacyLevel {
#[default]
GuildOnly = 2,
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
#[repr(u8)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status
pub enum GuildScheduledEventStatus {
#[default]
Scheduled = 1,
Active = 2,
Completed = 3,
Canceled = 4
}
#[derive(Serialize_repr, Deserialize_repr, Debug, Default, Clone)]
#[repr(u8)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types
pub enum GuildScheduledEventEntityType {
#[default]
StageInstance = 1,
Voice = 2,
External = 3,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata
pub struct GuildScheduledEventEntityMetadata {
pub location: Option<String>
}

View File

@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};
use crate::types::entities::User;
#[derive(Debug, Deserialize, Default, Serialize, Clone, PartialEq, Eq)]
pub struct GuildMember {
pub user: Option<User>,
pub nick: Option<String>,
pub avatar: Option<String>,
pub roles: Vec<String>,
pub joined_at: String,
pub premium_since: Option<String>,
pub deaf: bool,
pub mute: bool,
pub flags: Option<i32>,
pub pending: Option<bool>,
pub permissions: Option<String>,
pub communication_disabled_until: Option<String>,
}

View File

@ -0,0 +1,36 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::{
entities::{Application, User},
utils::Snowflake,
};
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
/// See https://discord.com/developers/docs/resources/guild#integration-object-integration-structure
pub struct Integration {
pub id: Snowflake,
pub name: String,
#[serde(rename = "type")]
pub integration_type: String,
pub enabled: bool,
pub syncing: Option<bool>,
pub role_id: Option<String>,
pub enabled_emoticons: Option<bool>,
pub expire_behaviour: Option<u8>,
pub expire_grace_period: Option<u16>,
pub user: Option<User>,
pub account: IntegrationAccount,
pub synced_at: Option<DateTime<Utc>>,
pub subscriber_count: Option<f64>,
pub revoked: Option<bool>,
pub application: Option<Application>,
pub scopes: Option<Vec<String>>,
}
#[derive(Default, Debug, Deserialize, Serialize, Clone)]
/// See https://discord.com/developers/docs/resources/guild#integration-account-object-integration-account-structure
pub struct IntegrationAccount {
pub id: String,
pub name: String,
}

View File

@ -0,0 +1,185 @@
use serde::{Deserialize, Serialize};
use crate::types::{
entities::{
Application, Attachment, Channel, Emoji, GuildMember, RoleSubscriptionData, Sticker,
StickerItem, User,
},
utils::Snowflake,
};
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct Message {
id: Snowflake,
pub channel_id: Snowflake,
author: User,
content: String,
timestamp: String,
edited_timestamp: Option<String>,
tts: bool,
mention_everyone: bool,
mentions: Option<Vec<User>>,
mention_roles: Vec<String>,
mention_channels: Option<Vec<ChannelMention>>,
pub attachments: Vec<Attachment>,
embeds: Vec<Embed>,
reactions: Option<Vec<Reaction>>,
nonce: Option<serde_json::Value>,
pinned: bool,
webhook_id: Option<String>,
#[serde(rename = "type")]
message_type: i32,
activity: Option<MessageActivity>,
application: Option<Application>,
application_id: Option<String>,
message_reference: Option<MessageReference>,
flags: Option<i32>,
referenced_message: Option<Box<Message>>,
interaction: Option<MessageInteraction>,
thread: Option<Channel>,
components: Option<Vec<Component>>,
sticker_items: Option<Vec<StickerItem>>,
stickers: Option<Vec<Sticker>>,
position: Option<i32>,
role_subscription_data: Option<RoleSubscriptionData>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MessageReference {
pub message_id: Snowflake,
pub channel_id: Snowflake,
pub guild_id: Option<Snowflake>,
pub fail_if_not_exists: Option<bool>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct MessageInteraction {
pub id: Snowflake,
#[serde(rename = "type")]
pub interaction_type: u8,
pub name: String,
pub user: User,
pub member: Option<GuildMember>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AllowedMention {
parse: Vec<AllowedMentionType>,
roles: Vec<Snowflake>,
users: Vec<Snowflake>,
replied_user: bool,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AllowedMentionType {
Roles,
Users,
Everyone,
}
#[derive(Debug, Serialize, Deserialize)]
struct ChannelMention {
id: Snowflake,
guild_id: Snowflake,
#[serde(rename = "type")]
channel_type: i32,
name: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Embed {
title: Option<String>,
#[serde(rename = "type")]
embed_type: Option<String>,
description: Option<String>,
url: Option<String>,
timestamp: Option<String>,
color: Option<i32>,
footer: Option<EmbedFooter>,
image: Option<EmbedImage>,
thumbnail: Option<EmbedThumbnail>,
video: Option<EmbedVideo>,
provider: Option<EmbedProvider>,
author: Option<EmbedAuthor>,
fields: Option<Vec<EmbedField>>,
}
#[derive(Debug, Serialize, Deserialize)]
struct EmbedFooter {
text: String,
icon_url: Option<String>,
proxy_icon_url: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
struct EmbedImage {
url: String,
proxy_url: String,
height: Option<i32>,
width: Option<i32>,
}
#[derive(Debug, Serialize, Deserialize)]
struct EmbedThumbnail {
url: String,
proxy_url: Option<String>,
height: Option<i32>,
width: Option<i32>,
}
#[derive(Debug, Serialize, Deserialize)]
struct EmbedVideo {
url: Option<String>,
proxy_url: Option<String>,
height: Option<i32>,
width: Option<i32>,
}
#[derive(Debug, Serialize, Deserialize)]
struct EmbedProvider {
name: Option<String>,
url: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
struct EmbedAuthor {
name: String,
url: Option<String>,
icon_url: Option<String>,
proxy_icon_url: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
struct EmbedField {
name: String,
value: String,
inline: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Reaction {
pub count: i32,
pub me: bool,
pub emoji: Emoji,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Component {
ActionRow = 1,
Button = 2,
StringSelect = 3,
TextInput = 4,
UserSelect = 5,
RoleSelect = 6,
MentionableSelect = 7,
ChannelSelect = 8,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MessageActivity {
#[serde(rename = "type")]
pub activity_type: i64,
pub party_id: Option<String>,
}

39
src/types/entities/mod.rs Normal file
View File

@ -0,0 +1,39 @@
mod application;
mod attachment;
mod channel;
mod config;
mod emoji;
mod guild;
mod guild_member;
mod integration;
mod message;
mod role;
mod security_key;
mod sticker;
mod team;
mod template;
mod user;
mod user_settings;
mod voice_state;
mod webhook;
mod audit_log;
pub use application::*;
pub use attachment::*;
pub use channel::*;
pub use config::*;
pub use emoji::*;
pub use guild::*;
pub use guild_member::*;
pub use integration::*;
pub use message::*;
pub use role::*;
pub use security_key::*;
pub use sticker::*;
pub use team::*;
pub use template::*;
pub use user::*;
pub use user_settings::*;
pub use voice_state::*;
pub use webhook::*;
pub use audit_log::*;

View File

@ -0,0 +1,48 @@
use serde::{Deserialize, Serialize};
use serde_aux::prelude::{deserialize_string_from_number, deserialize_option_number_from_string};
use crate::types::utils::Snowflake;
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
/// See https://discord.com/developers/docs/topics/permissions#role-object
pub struct RoleObject {
pub id: Snowflake,
pub name: String,
pub color: f64,
pub hoist: bool,
pub icon: Option<String>,
pub unicode_emoji: Option<String>,
pub position: u16,
#[serde(default)]
#[serde(deserialize_with = "deserialize_string_from_number")]
pub permissions: String,
pub managed: bool,
pub mentionable: bool,
pub tags: Option<RoleTags>
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RoleSubscriptionData {
pub role_subscription_listing_id: Snowflake,
pub tier_name: String,
pub total_months_subscribed: u32,
pub is_renewal: bool,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)]
/// See https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure
pub struct RoleTags {
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub bot_id: Option<usize>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub integration_id: Option<usize>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub subscription_listing_id: Option<usize>,
// These use the bad bool format, "Tags with type null represent booleans. They will be present and set to null if they are "true", and will be not present if they are "false"."
// premium_subscriber: bool,
// available_for_purchase: bool,
// guild_connections: bool,
}

View File

@ -0,0 +1,26 @@
use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SecurityKey {
pub id: String,
pub user_id: String,
pub key_id: String,
pub public_key: String,
pub counter: u64,
pub name: String,
}
impl Default for SecurityKey {
fn default() -> Self {
Self {
id: Snowflake::generate().to_string(),
user_id: String::new(),
key_id: String::new(),
public_key: String::new(),
counter: 0,
name: String::new(),
}
}
}

View File

@ -0,0 +1,31 @@
use serde::{Deserialize, Serialize};
use serde_aux::prelude::{deserialize_option_number_from_string};
use crate::types::{entities::User, utils::Snowflake};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Sticker {
#[serde(default)]
pub id: Snowflake,
pub pack_id: Option<Snowflake>,
pub name: String,
pub description: Option<String>,
pub tags: String,
pub asset: Option<String>,
#[serde(rename = "type")]
pub sticker_type: u8,
pub format_type: u8,
pub available: Option<bool>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
pub guild_id: Option<u64>,
pub user: Option<User>,
pub sort_value: Option<u8>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct StickerItem {
pub id: Snowflake,
pub name: String,
pub format_type: u8,
}

View File

@ -0,0 +1,20 @@
use serde::{Deserialize, Serialize};
use crate::types::entities::User;
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Team {
pub icon: Option<String>,
pub id: u64,
pub members: Vec<TeamMember>,
pub name: String,
pub owner_user_id: u64,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct TeamMember {
pub membership_state: u8,
pub permissions: Vec<String>,
pub team_id: u64,
pub user: User,
}

View File

@ -0,0 +1,24 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::{
entities::{Guild, User},
utils::Snowflake,
};
/// See https://docs.spacebar.chat/routes/#cmp--schemas-template
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct GuildTemplate {
pub code: String,
pub name: String,
pub description: Option<String>,
pub usage_count: Option<u64>,
pub creator_id: Snowflake,
pub creator: User,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub source_guild_id: String,
pub source_guild: Vec<Guild>, // Unsure how a {recursive: Guild} looks like, might be a Vec?
pub serialized_source_guild: Vec<Guild>,
id: Snowflake,
}

123
src/types/entities/user.rs Normal file
View File

@ -0,0 +1,123 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_aux::prelude::deserialize_option_number_from_string;
use serde_json::{Map, Value};
#[cfg(feature = "sqlx")]
use sqlx::{FromRow, Type};
use crate::types::{
errors::Error,
utils::Snowflake, //util::{email::adjust_email, entities::user_setting::UserSettings},
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[cfg_attr(feature = "sqlx", derive(Type))]
pub struct UserData {
pub valid_tokens_since: DateTime<Utc>,
pub hash: Option<String>,
}
impl User {
pub fn to_public_user(self) -> PublicUser {
PublicUser::from(self)
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct User {
pub id: Snowflake,
username: String,
discriminator: String,
avatar: Option<String>,
bot: bool,
system: Option<bool>,
mfa_enabled: Option<bool>,
accent_color: Option<u8>,
locale: Option<String>,
verified: Option<bool>,
email: Option<String>,
/// This field comes as either a string or a number as a string
/// So we need to account for that
#[serde(default)]
#[serde(deserialize_with = "deserialize_option_number_from_string")]
flags: Option<i32>,
premium_since: Option<DateTime<Utc>>,
premium_type: u8,
pronouns: Option<String>,
public_flags: Option<u16>,
banner: Option<String>,
bio: String,
theme_colors: Option<Vec<u8>>,
phone: Option<String>,
nsfw_allowed: bool,
premium: bool,
purchased_flags: i32,
premium_usage_flags: i32,
disabled: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PublicUser {
pub id: Snowflake,
pub username: String,
pub discriminator: String,
pub avatar: Option<String>,
pub accent_color: Option<u8>,
pub banner: Option<String>,
pub theme_colors: Option<Vec<u8>>,
pub pronouns: Option<String>,
pub bot: bool,
pub bio: String,
pub premium_type: u8,
pub premium_since: Option<DateTime<Utc>>,
pub public_flags: Option<u16>,
}
impl From<User> for PublicUser {
fn from(value: User) -> Self {
Self {
id: value.id,
username: value.username,
discriminator: value.discriminator,
avatar: value.avatar,
accent_color: value.accent_color,
banner: value.banner,
theme_colors: value.theme_colors,
pronouns: value.pronouns,
bot: value.bot,
bio: value.bio,
premium_type: value.premium_type,
premium_since: value.premium_since,
public_flags: value.public_flags,
}
}
}
const CUSTOM_USER_FLAG_OFFSET: u64 = 1 << 32;
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "sqlx", derive(Type))]
pub struct UserFlags: u64 {
const DISCORD_EMPLOYEE = 1 << 0;
const PARTNERED_SERVER_OWNER = 1 << 1;
const HYPESQUAD_EVENTS = 1 << 2;
const BUGHUNTER_LEVEL_1 =1 << 3;
const MFA_SMS = 1 << 4;
const PREMIUM_PROMO_DISMISSED = 1 << 5;
const HOUSE_BRAVERY = 1 << 6;
const HOUSE_BRILLIANCE = 1 << 7;
const HOUSE_BALANCE = 1 << 8;
const EARLY_SUPPORTER = 1 << 9;
const TEAM_USER = 1 << 10;
const TRUST_AND_SAFETY = 1 << 11;
const SYSTEM = 1 << 12;
const HAS_UNREAD_URGENT_MESSAGES = 1 << 13;
const BUGHUNTER_LEVEL_2 = 1 << 14;
const UNDERAGE_DELETED = 1 << 15;
const VERIFIED_BOT = 1 << 16;
const EARLY_VERIFIED_BOT_DEVELOPER = 1 << 17;
const CERTIFIED_MODERATOR = 1 << 18;
const BOT_HTTP_INTERACTIONS = 1 << 19;
}
}

View File

@ -0,0 +1,130 @@
use chrono::{serde::ts_milliseconds_option, Utc};
use serde::{Deserialize, Serialize};
use crate::types::utils::Snowflake;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum UserStatus {
#[default]
Online,
Offline,
Dnd,
Idle,
Invisible,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum UserTheme {
#[default]
Dark,
Light,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UserSettings {
pub afk_timeout: u16,
pub allow_accessibility_detection: bool,
pub animate_emoji: bool,
pub animate_stickers: u8,
pub contact_sync_enabled: bool,
pub convert_emoticons: bool,
pub custom_status: Option<CustomStatus>,
pub default_guilds_restricted: bool,
pub detect_platform_accounts: bool,
pub developer_mode: bool,
pub disable_games_tab: bool,
pub enable_tts_command: bool,
pub explicit_content_filter: u8,
pub friend_source_flags: FriendSourceFlags,
pub gateway_connected: bool,
pub gif_auto_play: bool,
pub guild_folders: Vec<GuildFolder>,
pub guild_positions: Vec<String>,
pub inline_attachment_media: bool,
pub inline_embed_media: bool,
pub locale: String,
pub message_display_compact: bool,
pub native_phone_integration_enabled: bool,
pub render_embeds: bool,
pub render_reactions: bool,
pub restricted_guilds: Vec<String>,
pub show_current_game: bool,
pub status: UserStatus,
pub stream_notifications_enabled: bool,
pub theme: UserTheme,
pub timezone_offset: i16,
}
impl Default for UserSettings {
fn default() -> Self {
Self {
afk_timeout: 3600,
allow_accessibility_detection: true,
animate_emoji: true,
animate_stickers: 0,
contact_sync_enabled: false,
convert_emoticons: false,
custom_status: None,
default_guilds_restricted: false,
detect_platform_accounts: false,
developer_mode: true,
disable_games_tab: true,
enable_tts_command: false,
explicit_content_filter: 0,
friend_source_flags: FriendSourceFlags::default(),
gateway_connected: false,
gif_auto_play: false,
guild_folders: Vec::new(),
guild_positions: Vec::new(),
inline_attachment_media: true,
inline_embed_media: true,
locale: "en-US".to_string(),
message_display_compact: false,
native_phone_integration_enabled: true,
render_embeds: true,
render_reactions: true,
restricted_guilds: Vec::new(),
show_current_game: true,
status: UserStatus::Online,
stream_notifications_enabled: false,
theme: UserTheme::Dark,
timezone_offset: 0,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CustomStatus {
pub emoji_id: Option<String>,
pub emoji_name: Option<String>,
#[serde(with = "ts_milliseconds_option")]
pub expires_at: Option<chrono::DateTime<Utc>>,
pub text: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FriendSourceFlags {
pub all: bool,
}
impl Default for FriendSourceFlags {
fn default() -> Self {
Self { all: true }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GuildFolder {
pub color: u32,
pub guild_ids: Vec<String>,
pub id: u16,
pub name: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct LoginResult {
pub token: String,
pub settings: UserSettings,
}

View File

@ -0,0 +1,30 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::{
entities::{Channel, Guild, GuildMember, User},
utils::Snowflake,
};
/// See https://docs.spacebar.chat/routes/#cmp--schemas-voicestate
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct VoiceState {
pub guild_id: Option<Snowflake>,
pub guild: Option<Guild>,
pub channel_id: Snowflake,
pub channel: Option<Channel>,
pub user_id: Snowflake,
pub user: Option<User>,
pub member: Option<GuildMember>,
pub session_id: Snowflake,
pub token: Option<String>,
pub deaf: bool,
pub mute: bool,
pub self_deaf: bool,
pub self_mute: bool,
pub self_stream: Option<bool>,
pub self_video: bool,
pub suppress: bool,
pub request_to_speak_timestamp: Option<DateTime<Utc>>,
pub id: Option<Snowflake>,
}

View File

@ -0,0 +1,32 @@
use serde::{Deserialize, Serialize};
use crate::types::{
entities::{Application, Channel, Guild, User},
utils::Snowflake,
};
/// See https://docs.spacebar.chat/routes/#cmp--schemas-webhook
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Webhook {
#[serde(rename = "type")]
pub webhook_type: i32,
pub name: String,
pub avatar: String,
pub token: String,
pub guild_id: Snowflake,
#[serde(skip_serializing_if = "Option::is_none")]
pub guild: Option<Guild>,
pub channel_id: Snowflake,
#[serde(skip_serializing_if = "Option::is_none")]
pub channel: Option<Channel>,
pub application_id: Snowflake,
#[serde(skip_serializing_if = "Option::is_none")]
pub application: Option<Application>,
pub user_id: Snowflake,
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<User>,
pub source_guild_id: Snowflake,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_guild: Option<Guild>,
pub id: Snowflake,
}

54
src/types/errors.rs Normal file
View File

@ -0,0 +1,54 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[cfg(feature = "sqlx")]
#[error("SQLX error: {0}")]
SQLX(#[from] sqlx::Error),
#[error("serde: {0}")]
Serde(#[from] serde_json::Error),
#[error(transparent)]
IO(#[from] std::io::Error),
#[error(transparent)]
FieldFormat(#[from] FieldFormatError),
}
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum FieldFormatError {
#[error("Password must be between 1 and 72 characters.")]
PasswordError,
#[error("Username must be between 2 and 32 characters.")]
UsernameError,
#[error("Consent must be 'true' to register.")]
ConsentError,
#[error("The provided email address is in an invalid format.")]
EmailError,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ErrorResponse {
pub code: i32,
pub message: String,
pub errors: IntermittentError,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct IntermittentError {
#[serde(flatten)]
pub errors: std::collections::HashMap<String, ErrorField>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct ErrorField {
#[serde(default)]
pub _errors: Vec<APIError>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct APIError {
pub message: String,
pub code: String,
}

51
src/types/events/call.rs Normal file
View File

@ -0,0 +1,51 @@
use serde::{Deserialize, Serialize};
use crate::types::{VoiceState, WebSocketEvent};
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// Is sent to a client by the server to signify a new being created
/// {"t":"CALL_CREATE","s":2,"op":0,"d":{"voice_states":[],"ringing":[],"region":"milan","message_id":"1107187514906775613","embedded_activities":[],"channel_id":"837609115475771392"}}
pub struct CallCreate {
pub voice_states: Vec<VoiceState>,
/// Seems like a vec of channel ids
pub ringing: Vec<String>,
pub region: String, // milan
pub message_id: String,
/// What is this?
pub embedded_activities: Vec<serde_json::Value>,
pub channel_id: String,
}
impl WebSocketEvent for CallCreate {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// Updates the status of calls
/// {"t":"CALL_UPDATE","s":5,"op":0,"d":{"ringing":["837606544539254834"],"region":"milan","message_id":"1107191540234846308","guild_id":null,"channel_id":"837609115475771392"}}
pub struct CallUpdate {
/// Seems like a vec of channel ids
pub ringing: Vec<String>,
pub region: String, // milan
pub message_id: String,
pub guild_id: Option<String>,
pub channel_id: String,
}
impl WebSocketEvent for CallUpdate {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// Deletes a ringing call
/// {"t":"CALL_DELETE","s":8,"op":0,"d":{"channel_id":"837609115475771392"}}
pub struct CallDelete {
pub channel_id: String,
}
impl WebSocketEvent for CallDelete {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
/// See https://unofficial-discord-docs.vercel.app/gateway/op13
/// {"op":13,"d":{"channel_id":"837609115475771392"}}
pub struct CallSync {
pub channel_id: String,
}
impl WebSocketEvent for CallSync {}

View File

@ -0,0 +1,61 @@
use crate::types::entities::Channel;
use crate::types::events::WebSocketEvent;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#channel-pins-update
pub struct ChannelPinsUpdate {
pub guild_id: Option<String>,
pub channel_id: String,
pub last_pin_timestamp: Option<DateTime<Utc>>,
}
impl WebSocketEvent for ChannelPinsUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#channel-create
pub struct ChannelCreate {
#[serde(flatten)]
pub channel: Channel,
}
impl WebSocketEvent for ChannelCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#channel-update
pub struct ChannelUpdate {
#[serde(flatten)]
pub channel: Channel,
}
impl WebSocketEvent for ChannelUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// Officially undocumented.
/// Sends updates to client about a new message with its id
/// {"channel_unread_updates": [{"id": "816412869766938648", "last_message_id": "1085892012085104680"}}
pub struct ChannelUnreadUpdate {
pub channel_unread_updates: Vec<ChannelUnreadUpdateObject>,
pub guild_id: String,
}
#[derive(Debug, Default, Deserialize, Serialize)]
/// Contains very few fields from [Channel]
/// See also [ChannelUnreadUpdates]
pub struct ChannelUnreadUpdateObject {
pub id: String,
pub last_message_id: String,
pub last_pin_timestamp: Option<String>
}
impl WebSocketEvent for ChannelUnreadUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#channel-delete
pub struct ChannelDelete {
#[serde(flatten)]
pub channel: Channel,
}
impl WebSocketEvent for ChannelDelete {}

225
src/types/events/guild.rs Normal file
View File

@ -0,0 +1,225 @@
use crate::types::{AuditLogEntry, Emoji, Sticker, GuildMember, RoleObject, GuildScheduledEvent};
use crate::types::entities::{Guild, UnavailableGuild, User};
use crate::types::events::WebSocketEvent;
use chrono::{Utc, DateTime};
use serde::{Deserialize, Serialize};
use super::PresenceUpdate;
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-create
/// This one is particularly painful, it can be a Guild object with extra field or an unavailbile guild object
pub struct GuildCreate {
#[serde(flatten)]
pub d: GuildCreateDataOption,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum GuildCreateDataOption {
UnavailableGuild(UnavailableGuild),
Guild(Guild),
}
impl Default for GuildCreateDataOption {
fn default() -> Self {
GuildCreateDataOption::UnavailableGuild(UnavailableGuild::default())
}
}
impl WebSocketEvent for GuildCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-ban-add-guild-ban-add-event-fields
pub struct GuildBanAdd {
pub guild_id: String,
pub user: User,
}
impl WebSocketEvent for GuildBanAdd {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-ban-remove
pub struct GuildBanRemove {
pub guild_id: String,
pub user: User,
}
impl WebSocketEvent for GuildBanRemove {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-update
pub struct GuildUpdate {
#[serde(flatten)]
pub guild: Guild
}
impl WebSocketEvent for GuildUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-delete
pub struct GuildDelete {
#[serde(flatten)]
pub guild: UnavailableGuild
}
impl WebSocketEvent for GuildDelete {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-audit-log-entry-create
pub struct GuildAuditLogEntryCreate {
#[serde(flatten)]
pub entry: AuditLogEntry
}
impl WebSocketEvent for GuildAuditLogEntryCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-emojis-update
pub struct GuildEmojisUpdate {
pub guild_id: String,
pub emojis: Vec<Emoji>
}
impl WebSocketEvent for GuildEmojisUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-stickers-update
pub struct GuildStickersUpdate {
pub guild_id: String,
pub stickers: Vec<Sticker>
}
impl WebSocketEvent for GuildStickersUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-integrations-update
pub struct GuildIntegrationsUpdate {
pub guild_id: String,
}
impl WebSocketEvent for GuildIntegrationsUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-member-add
pub struct GuildMemberAdd {
#[serde(flatten)]
pub member: GuildMember,
pub guild_id: String,
}
impl WebSocketEvent for GuildMemberAdd {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-member-remove
pub struct GuildMemberRemove {
pub guild_id: String,
pub user: User,
}
impl WebSocketEvent for GuildMemberRemove {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-member-update
pub struct GuildMemberUpdate {
pub guild_id: String,
pub roles: Vec<String>,
pub user: User,
pub nick: Option<String>,
pub avatar: Option<String>,
pub joined_at: Option<DateTime<Utc>>,
pub premium_since: Option<DateTime<Utc>>,
pub deaf: Option<bool>,
pub mute: Option<bool>,
pub pending: Option<bool>,
pub communication_disabled_until: Option<DateTime<Utc>>,
}
impl WebSocketEvent for GuildMemberUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-members-chunk
pub struct GuildMembersChunk {
pub guild_id: String,
pub members: Vec<GuildMember>,
pub chunk_index: u16,
pub chunk_count: u16,
pub not_found: Option<Vec<String>>,
pub presences: Option<PresenceUpdate>,
pub nonce: Option<String>
}
impl WebSocketEvent for GuildMembersChunk {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-role-create
pub struct GuildRoleCreate {
pub guild_id: String,
pub role: RoleObject,
}
impl WebSocketEvent for GuildRoleCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-role-update
pub struct GuildRoleUpdate {
pub guild_id: String,
pub role: RoleObject,
}
impl WebSocketEvent for GuildRoleUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-role-delete
pub struct GuildRoleDelete {
pub guild_id: String,
pub role_id: String,
}
impl WebSocketEvent for GuildRoleDelete {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-create
pub struct GuildScheduledEventCreate {
#[serde(flatten)]
pub event: GuildScheduledEvent,
}
impl WebSocketEvent for GuildScheduledEventCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-update
pub struct GuildScheduledEventUpdate {
#[serde(flatten)]
pub event: GuildScheduledEvent,
}
impl WebSocketEvent for GuildScheduledEventUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-delete
pub struct GuildScheduledEventDelete {
#[serde(flatten)]
pub event: GuildScheduledEvent,
}
impl WebSocketEvent for GuildScheduledEventDelete {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-add
pub struct GuildScheduledEventUserAdd {
pub guild_scheduled_event_id: String,
pub user_id: String,
pub guild_id: String,
}
impl WebSocketEvent for GuildScheduledEventUserAdd {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#guild-scheduled-event-user-remove
pub struct GuildScheduledEventUserRemove {
pub guild_scheduled_event_id: String,
pub user_id: String,
pub guild_id: String,
}
impl WebSocketEvent for GuildScheduledEventUserRemove {}

View File

@ -0,0 +1,17 @@
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct GatewayHeartbeat {
pub op: u8,
pub d: Option<u64>,
}
impl WebSocketEvent for GatewayHeartbeat {}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct GatewayHeartbeatAck {
pub op: i32,
}
impl WebSocketEvent for GatewayHeartbeatAck {}

17
src/types/events/hello.rs Normal file
View File

@ -0,0 +1,17 @@
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct GatewayHello {
pub op: i32,
pub d: HelloData,
}
impl WebSocketEvent for GatewayHello {}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct HelloData {
pub heartbeat_interval: u128,
}
impl WebSocketEvent for HelloData {}

View File

@ -0,0 +1,48 @@
use crate::types::events::{PresenceUpdate, WebSocketEvent};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct GatewayIdentifyPayload {
pub token: String,
pub properties: GatewayIdentifyConnectionProps,
#[serde(skip_serializing_if = "Option::is_none")]
pub compress: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub large_threshold: Option<i16>, //default: 50
#[serde(skip_serializing_if = "Option::is_none")]
pub shard: Option<Vec<(i32, i32)>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub presence: Option<PresenceUpdate>,
// What is the difference between these two?
// Intents is documented, capabilities is used in users
// I wonder if these are interchangable..
#[serde(skip_serializing_if = "Option::is_none")]
pub intents: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub capabilities: Option<i32>,
}
impl GatewayIdentifyPayload {
/// Creates an identify payload with the same default capabilities as the official client
pub fn default_w_client_capabilities() -> Self {
let mut def = Self::default();
def.capabilities = Some(8189); // Default capabilities for a client
def
}
/// Creates an identify payload with all possible capabilities
pub fn default_w_all_capabilities() -> Self {
let mut def = Self::default();
def.capabilities = Some(i32::MAX); // Since discord uses bitwise for capabilities, this has almost every bit as 1, so all capabilities
def
}
}
impl WebSocketEvent for GatewayIdentifyPayload {}
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct GatewayIdentifyConnectionProps {
pub os: String,
pub browser: String,
pub device: String,
}

View File

@ -0,0 +1,33 @@
use serde::{Deserialize, Serialize};
use crate::types::{Integration, WebSocketEvent};
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#integration-create
pub struct IntegrationCreate {
#[serde(flatten)]
pub integration: Integration,
pub guild_id: String,
}
impl WebSocketEvent for IntegrationCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#integration-update
pub struct IntegrationUpdate {
#[serde(flatten)]
pub integration: Integration,
pub guild_id: String,
}
impl WebSocketEvent for IntegrationUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#integration-delete
pub struct IntegrationDelete {
pub id: String,
pub guild_id: String,
pub application_id: Option<String>,
}
impl WebSocketEvent for IntegrationDelete {}

View File

@ -0,0 +1,22 @@
use serde::{Deserialize, Serialize};
use crate::types::{GuildInvite, WebSocketEvent};
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#invite-create
pub struct InviteCreate {
#[serde(flatten)]
pub invite: GuildInvite
}
impl WebSocketEvent for InviteCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#invite-delete
pub struct InviteDelete {
pub channel_id: String,
pub guild_id: Option<String>,
pub code: String,
}
impl WebSocketEvent for InviteDelete {}

View File

@ -0,0 +1,27 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
///
/// Sent to the server to signify lazy loading of a guild;
/// Sent by the official client when switching to a guild or channel;
/// After this, you should recieve message updates
///
/// See https://luna.gitlab.io/discord-unofficial-docs/lazy_guilds.html#op-14-lazy-request
///
/// {"op":14,"d":{"guild_id":"848582562217590824","typing":true,"activities":true,"threads":true}}
pub struct LazyRequest {
pub guild_id: String,
pub typing: bool,
pub activities: bool,
pub threads: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub members: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub channels: Option<HashMap<String, Vec<Vec<u64>>>>
}
impl WebSocketEvent for LazyRequest {}

153
src/types/events/message.rs Normal file
View File

@ -0,0 +1,153 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::types::{
entities::{Emoji, GuildMember, Message, User},
utils::Snowflake,
};
use super::WebSocketEvent;
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct TypingStartEvent {
pub channel_id: String,
pub guild_id: Option<String>,
pub user_id: String,
pub timestamp: i64,
pub member: Option<GuildMember>,
}
impl WebSocketEvent for TypingStartEvent {}
#[derive(Debug, Serialize, Deserialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#message-create
pub struct MessageCreate {
#[serde(flatten)]
message: Message,
guild_id: Option<String>,
member: Option<GuildMember>,
mentions: Option<Vec<MessageCreateUser>>,
}
#[derive(Debug, Serialize, Deserialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#message-create-message-create-extra-fields
pub struct MessageCreateUser {
pub id: String,
username: String,
discriminator: String,
avatar: Option<String>,
bot: Option<bool>,
system: Option<bool>,
mfa_enabled: Option<bool>,
accent_color: Option<String>,
locale: Option<String>,
verified: Option<bool>,
email: Option<String>,
premium_since: Option<String>,
premium_type: Option<i8>,
pronouns: Option<String>,
public_flags: Option<i32>,
banner: Option<String>,
bio: Option<String>,
theme_colors: Option<Vec<i32>>,
phone: Option<String>,
nsfw_allowed: Option<bool>,
premium: Option<bool>,
purchased_flags: Option<i32>,
premium_usage_flags: Option<i32>,
disabled: Option<bool>,
member: GuildMember
}
impl WebSocketEvent for MessageCreate {}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessageUpdate {
#[serde(flatten)]
message: Message,
guild_id: Option<String>,
member: Option<GuildMember>,
mentions: Option<Vec<(User, GuildMember)>>, // Not sure if this is correct: https://discord.com/developers/docs/topics/gateway-events#message-create
}
impl WebSocketEvent for MessageUpdate {}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessageDelete {
id: String,
channel_id: String,
guild_id: Option<String>,
}
impl WebSocketEvent for MessageDelete {}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessageDeleteBulk {
ids: Vec<String>,
channel_id: String,
guild_id: Option<String>,
}
impl WebSocketEvent for MessageDeleteBulk {}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessageReactionAdd {
user_id: String,
channel_id: String,
message_id: String,
guild_id: Option<String>,
member: Option<GuildMember>,
emoji: Emoji,
}
impl WebSocketEvent for MessageReactionAdd {}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessageReactionRemove {
user_id: String,
channel_id: String,
message_id: String,
guild_id: Option<String>,
emoji: Emoji,
}
impl WebSocketEvent for MessageReactionRemove {}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessageReactionRemoveAll {
channel_id: String,
message_id: String,
guild_id: Option<String>,
}
impl WebSocketEvent for MessageReactionRemoveAll {}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessageReactionRemoveEmoji {
channel_id: String,
message_id: String,
guild_id: Option<String>,
emoji: Emoji,
}
impl WebSocketEvent for MessageReactionRemoveEmoji {}
#[derive(Debug, Deserialize, Serialize, Default)]
/// Officially Undocumented
///
/// Not documented anywhere unofficially
///
/// Apparently "Message ACK refers to marking a message as read for Discord's API." (https://github.com/Rapptz/discord.py/issues/1851)
/// I suspect this is sent and recieved from the gateway to let clients on other devices know the user has read a message
///
/// {"t":"MESSAGE_ACK","s":3,"op":0,"d":{"version":52,"message_id":"1107236673638633472","last_viewed":null,"flags":null,"channel_id":"967363950217936897"}}
pub struct MessageACK {
/// ?
pub version: u16,
pub message_id: String,
pub last_viewed: Option<DateTime<Utc>>,
/// What flags?
pub flags: Option<serde_json::Value>,
pub channel_id: String,
}
impl WebSocketEvent for MessageACK {}

76
src/types/events/mod.rs Normal file
View File

@ -0,0 +1,76 @@
use serde::{Deserialize, Serialize};
mod channel;
mod guild;
mod heartbeat;
mod hello;
mod identify;
mod message;
mod presence;
mod ready;
mod request_members;
mod resume;
mod thread;
mod user;
mod voice;
mod session;
mod webhooks;
mod passive_update;
mod integration;
mod invite;
mod call;
mod lazy_request;
pub use channel::*;
pub use guild::*;
pub use heartbeat::*;
pub use hello::*;
pub use identify::*;
pub use message::*;
pub use presence::*;
pub use ready::*;
pub use request_members::*;
pub use resume::*;
pub use thread::*;
pub use user::*;
pub use voice::*;
pub use session::*;
pub use webhooks::*;
pub use passive_update::*;
pub use integration::*;
pub use invite::*;
pub use call::*;
pub use lazy_request::*;
pub trait WebSocketEvent {}
#[derive(Debug, Default, Serialize, Clone)]
/// The payload used for sending events to the gateway
///
/// Similar to [GatewayReceivePayload], except we send a [Value] for d whilst we receive a [serde_json::value::RawValue]
/// Also, we never need to send the event name
pub struct GatewaySendPayload {
pub op: u8,
#[serde(skip_serializing_if = "Option::is_none")]
pub d: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s: Option<u64>,
}
impl WebSocketEvent for GatewaySendPayload {}
#[derive(Debug, Default, Deserialize, Clone)]
/// The payload used for receiving events from the gateway
///
/// Similar to [GatewaySendPayload], except we send a [Value] for d whilst we receive a [serde_json::value::RawValue]
/// Also, we never need to sent the event name
pub struct GatewayReceivePayload<'a> {
pub op: u8,
#[serde(borrow)]
pub d: Option<&'a serde_json::value::RawValue>,
pub s: Option<u64>,
pub t: Option<String>,
}
impl<'a> WebSocketEvent for GatewayReceivePayload<'a> {}

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