Integrate polyphony-types into chorus

This commit is contained in:
bitfl0wer 2023-05-25 21:11:08 +02:00
parent d1b72a583f
commit 48113825b8
109 changed files with 4180 additions and 4 deletions

View File

@ -5,9 +5,9 @@ license = "AGPL-3"
edition = "2021"
[dependencies]
tokio = {version = "1.27.0", features = ["rt", "macros", "rt-multi-thread"]}
serde = {version = "1.0.159", features = ["derive"]}
serde_json = "1.0.95"
tokio = {version = "1.28.1", features = ["rt", "macros", "rt-multi-thread", "full"]}
serde = {version = "1.0.162", features = ["derive"]}
serde_json = { version = "1.0.96", features = ["raw_value"] }
reqwest = {version = "0.11.16", features = ["multipart"]}
url = "2.3.1"
chrono = {version = "0.4.24", features = ["serde"]}
@ -17,6 +17,18 @@ native-tls = "0.2.11"
tokio-tungstenite = {version = "0.18.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

@ -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,35 @@
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>,
pub auto_create_bot_users: Option<bool>,
}
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()),
auto_create_bot_users: Some(false),
}
}
}

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,116 @@
use serde::{Deserialize, Serialize};
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<String>,
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 {
pub id: u64,
pub name: String,
pub moderated: bool,
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")]
pub overwrite_type: u8,
pub allow: String,
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 {
pub emoji_id: Option<String>,
pub emoji_name: Option<String>,
}
#[derive(Default, Clone, Copy, Debug, Serialize, Deserialize, 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,15 @@
use serde::{Deserialize, Serialize};
use crate::types::entities::User;
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
pub struct Emoji {
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>,
}

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

@ -0,0 +1,117 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
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,
}

View File

@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};
use crate::types::entities::User;
#[derive(Debug, Deserialize, 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: 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: 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>,
}

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

@ -0,0 +1,37 @@
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;
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::*;

View File

@ -0,0 +1,28 @@
use serde::{Deserialize, Serialize};
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,
pub permissions: String,
pub managed: bool,
pub mentionable: bool,
// to:do add role tags https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure
//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,
}

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,27 @@
use serde::{Deserialize, Serialize};
use crate::types::{entities::User, utils::Snowflake};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Sticker {
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>,
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,
}

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

@ -0,0 +1,238 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
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)]
#[cfg_attr(feature = "sqlx", derive(FromRow))]
pub struct User {
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 phone: Option<String>,
pub desktop: bool,
pub mobile: bool,
pub premium: bool,
pub premium_type: u8,
pub bot: bool,
pub bio: String,
pub system: bool,
pub nsfw_allowed: bool,
pub mfa_enabled: bool,
pub webauthn_enabled: bool,
#[serde(skip)]
pub totp_secret: Option<String>,
#[serde(skip)]
pub totp_last_ticket: Option<String>,
pub created_at: DateTime<Utc>,
pub premium_since: Option<DateTime<Utc>>,
pub verified: bool,
pub disabled: bool,
pub deleted: bool,
pub email: Option<String>,
pub flags: String,
pub public_flags: u16,
pub purchased_flags: u16,
pub premium_usage_flags: u16,
pub rights: String,
#[cfg(feature = "sqlx")]
pub relationship_ids: sqlx::types::Json<Vec<String>>,
#[cfg(not(feature = "sqlx"))]
pub relationship_ids: Vec<String>,
#[cfg(feature = "sqlx")]
pub connected_account_ids: sqlx::types::Json<Vec<String>>,
#[cfg(not(feature = "sqlx"))]
pub connected_account_ids: Vec<String>,
#[cfg(feature = "sqlx")]
pub data: sqlx::types::Json<UserData>,
#[cfg(not(feature = "sqlx"))]
pub data: UserData,
#[cfg(feature = "sqlx")]
pub fingerprints: sqlx::types::Json<Vec<String>>,
#[cfg(not(feature = "sqlx"))]
pub fingerprints: Vec<String>,
// pub settings: UserSettings,
pub extended_settings: Value,
}
#[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 Default for User {
fn default() -> Self {
Self {
id: Snowflake::generate(),
username: String::new(),
discriminator: String::new(),
avatar: None,
accent_color: None,
banner: None,
theme_colors: None,
pronouns: None,
phone: None,
desktop: false,
mobile: false,
premium: false,
premium_type: 0,
bot: false,
bio: String::new(),
system: false,
nsfw_allowed: false,
mfa_enabled: false,
webauthn_enabled: false,
totp_secret: None,
totp_last_ticket: None,
created_at: Utc::now(),
premium_since: None,
verified: false,
disabled: false,
deleted: false,
email: None,
flags: String::from("0"),
public_flags: 0,
purchased_flags: 0,
premium_usage_flags: 0,
rights: String::new(),
relationship_ids: Default::default(),
connected_account_ids: Default::default(),
data: Default::default(),
fingerprints: Default::default(),
//settings: UserSettings::default(),
extended_settings: Value::Object(Map::new()),
}
}
}
impl User {
pub fn validate(&mut self) -> Result<(), Error> {
/*if let Some(email) = self.email.as_ref() {
self.email = Some(adjust_email(email)?);
}
if self.discriminator.len() < 4 {
self.discriminator = format!("{:0>4}", self.discriminator);
}
Ok(())*/
todo!()
}
pub async fn generate_discriminator(_username: &str) -> Result<String, Error> {
todo!()
}
}
impl User {
pub fn to_public_user(self) -> PublicUser {
PublicUser::from(self)
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct PrivateUser {
pub id: String,
username: String,
discriminator: String,
avatar: Option<String>,
bot: bool,
system: Option<bool>,
mfa_enabled: Option<bool>,
accent_color: Option<String>,
locale: Option<String>,
verified: Option<bool>,
email: Option<String>,
flags: String,
premium_since: Option<String>,
premium_type: i8,
pronouns: Option<String>,
public_flags: Option<i8>,
banner: Option<String>,
bio: String,
theme_colors: Option<Vec<i32>>,
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: 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,133 @@
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 {
#[serde(skip_serializing)]
pub index: String, // Database relevance only
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 {
index: Snowflake::generate().to_string(),
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: 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: 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: 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,
}

View File

@ -0,0 +1,41 @@
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
/// Not directly serialized, as the inner payload is a channel object
pub struct ChannelCreate {
pub channel: Channel,
}
impl WebSocketEvent for ChannelCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#channel-update
/// Not directly serialized, as the inner payload is a channel object
pub struct ChannelUpdate {
pub channel: Channel,
}
impl WebSocketEvent for ChannelUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#channel-delete
/// Not directly serialized, as the inner payload is a channel object
pub struct ChannelDelete {
pub channel: Channel,
}
impl WebSocketEvent for ChannelDelete {}

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

@ -0,0 +1,41 @@
use crate::types::entities::{Guild, UnavailableGuild, User};
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[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 {
pub d: GuildCreateDataOption,
}
#[derive(Debug, Deserialize, Serialize)]
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 {}

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,22 @@
use crate::types::events::{PresenceUpdate, WebSocketEvent};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct GatewayIdentifyPayload {
pub token: String,
pub properties: GatewayIdentifyConnectionProps,
pub compress: Option<bool>,
pub large_threshold: Option<i16>, //default: 50
pub shard: Option<Vec<(i32, i32)>>,
pub presence: Option<PresenceUpdate>,
pub intents: i32,
}
impl WebSocketEvent for GatewayIdentifyPayload {}
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct GatewayIdentifyConnectionProps {
pub os: String,
pub browser: String,
pub device: String,
}

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

@ -0,0 +1,101 @@
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)]
pub struct MessageCreate {
#[serde(flatten)]
message: Message,
guild_id: Option<Snowflake>,
member: Option<GuildMember>,
mentions: Vec<(User, GuildMember)>, // Not sure if this is correct: https://discord.com/developers/docs/topics/gateway-events#message-create
}
impl WebSocketEvent for MessageCreate {}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessageUpdate {
#[serde(flatten)]
message: Message,
guild_id: Option<String>,
member: Option<GuildMember>,
mentions: 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 {}

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

@ -0,0 +1,41 @@
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_status;
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_status::*;
pub trait WebSocketEvent {}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct GatewayPayload {
pub op: u8,
pub d: Option<serde_json::Value>,
pub s: Option<u64>,
pub t: Option<String>,
}
impl WebSocketEvent for GatewayPayload {}

View File

@ -0,0 +1,24 @@
use crate::types::entities::User;
use crate::types::events::WebSocketEvent;
use crate::types::interfaces::Activity;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See https://discord.com/developers/docs/topics/gateway-events#presence-update-presence-update-event-fields
pub struct PresenceUpdate {
pub user: User,
pub guild_id: String,
pub status: String,
pub activities: Vec<Activity>,
pub client_status: ClientStatusObject,
}
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See https://discord.com/developers/docs/topics/gateway-events#client-status-object
pub struct ClientStatusObject {
pub desktop: Option<String>,
pub mobile: Option<String>,
pub web: Option<String>,
}
impl WebSocketEvent for PresenceUpdate {}

15
src/types/events/ready.rs Normal file
View File

@ -0,0 +1,15 @@
use crate::types::entities::{UnavailableGuild, User};
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct GatewayReady {
pub v: u8,
pub user: User,
pub guilds: Vec<UnavailableGuild>,
pub session_id: String,
pub resume_gateway_url: Option<String>,
pub shard: Option<(u64, u64)>,
}
impl WebSocketEvent for GatewayReady {}

View File

@ -0,0 +1,15 @@
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#request-guild-members-request-guild-members-structure
pub struct GatewayRequestGuildMembers {
pub guild_id: String,
pub query: Option<String>,
pub limit: u64,
pub presence: Option<bool>,
pub user_ids: Option<String>,
pub nonce: Option<String>,
}
impl WebSocketEvent for GatewayRequestGuildMembers {}

View File

@ -0,0 +1,11 @@
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct GatewayResume {
pub token: String,
pub session_id: String,
pub seq: String,
}
impl WebSocketEvent for GatewayResume {}

View File

@ -0,0 +1,82 @@
use crate::types::entities::{Channel, GuildMember, ThreadMember};
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#thread-create
/// Not directly serialized, as the inner payload is a channel object
pub struct ThreadCreate {
pub thread: Channel,
}
impl WebSocketEvent for ThreadCreate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#thread-update
/// Not directly serialized, as the inner payload is a channel object
pub struct ThreadUpdate {
pub thread: Channel,
}
impl WebSocketEvent for ThreadUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#thread-delete
/// Not directly serialized, as the inner payload is a channel object
pub struct ThreadDelete {
pub thread: Channel,
}
impl WebSocketEvent for ThreadDelete {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#thread-list-sync
pub struct ThreadListSync {
pub guild_id: String,
pub channel_ids: Option<Vec<String>>,
pub threads: Vec<Channel>,
pub members: Vec<ThreadMember>,
}
impl WebSocketEvent for ThreadListSync {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#thread-member-update
/// The inner payload is a thread member object with an extra field.
/// The extra field is a bit painful, because we can't just serialize a thread member object
pub struct ThreadMemberUpdate {
pub id: Option<u64>,
pub user_id: Option<u64>,
pub join_timestamp: Option<String>,
pub flags: Option<u64>,
pub member: Option<GuildMember>,
pub guild_id: String,
}
impl ThreadMemberUpdate {
/// Convert self to a thread member, losing the added guild_id field
pub fn to_thread_member(self) -> ThreadMember {
ThreadMember {
id: self.id,
user_id: self.user_id,
join_timestamp: self.join_timestamp.clone(),
flags: self.flags,
member: self.member,
}
}
}
impl WebSocketEvent for ThreadMemberUpdate {}
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#thread-members-update
pub struct ThreadMembersUpdate {
pub id: String,
pub guild_id: String,
/// Capped at 50
pub member_count: u8,
pub added_members: Option<Vec<ThreadMember>>,
pub removed_members: Option<Vec<String>>,
}
impl WebSocketEvent for ThreadMembersUpdate {}

12
src/types/events/user.rs Normal file
View File

@ -0,0 +1,12 @@
use crate::types::entities::User;
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)]
/// See https://discord.com/developers/docs/topics/gateway-events#user-update
/// Not directly serialized, as the inner payload is the user object
pub struct UserUpdate {
pub user: User,
}
impl WebSocketEvent for UserUpdate {}

View File

@ -0,0 +1,13 @@
use crate::types::events::WebSocketEvent;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default)]
/// See https://discord.com/developers/docs/topics/gateway-events#update-voice-state-gateway-voice-state-update-structure
pub struct GatewayVoiceStateUpdate {
pub guild_id: String,
pub channel_id: Option<String>,
pub self_mute: bool,
pub self_deaf: bool,
}
impl WebSocketEvent for GatewayVoiceStateUpdate {}

View File

@ -0,0 +1,56 @@
use crate::types::entities::Emoji;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Activity {
name: String,
#[serde(rename = "type")]
activity_type: i32,
url: Option<String>,
created_at: i64,
timestamps: Option<ActivityTimestamps>,
application_id: Option<String>,
details: Option<String>,
state: Option<String>,
emoji: Option<Emoji>,
party: Option<ActivityParty>,
assets: Option<ActivityAssets>,
secrets: Option<ActivitySecrets>,
instance: Option<bool>,
flags: Option<i32>,
buttons: Option<Vec<ActivityButton>>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
struct ActivityTimestamps {
start: Option<i64>,
end: Option<i64>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
struct ActivityParty {
id: Option<String>,
size: Option<Vec<(i32, i32)>>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
struct ActivityAssets {
large_image: Option<String>,
large_text: Option<String>,
small_image: Option<String>,
small_text: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
struct ActivitySecrets {
join: Option<String>,
spectate: Option<String>,
#[serde(rename = "match")]
match_string: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
struct ActivityButton {
label: String,
url: String,
}

View File

@ -0,0 +1,17 @@
use crate::types::utils::Snowflake;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct WelcomeScreenObject {
pub enabled: bool,
pub description: Option<String>,
pub welcome_channels: Vec<WelcomeScreenChannel>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct WelcomeScreenChannel {
pub channel_id: Snowflake,
pub description: String,
pub emoji_id: Option<Snowflake>,
pub emoji_name: Option<String>,
}

View File

@ -0,0 +1,39 @@
use crate::types::entities::{AllowedMention, Embed};
use crate::types::utils::Snowflake;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Interaction {
pub id: Snowflake,
pub r#type: InteractionType,
pub data: Value,
pub guild_id: Snowflake,
pub channel_id: Snowflake,
pub member_id: Snowflake,
pub token: String,
pub version: i32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum InteractionType {
SelfCommand = 0,
Ping = 1,
ApplicationCommand = 2,
}
pub enum InteractionResponseType {
SelfCommandResponse = 0,
Pong = 1,
Acknowledge = 2,
ChannelMessage = 3,
ChannelMessageWithSource = 4,
AcknowledgeWithSource = 5,
}
pub struct InteractionApplicationCommandCallbackData {
pub tts: bool,
pub content: String,
pub embeds: Vec<Embed>,
pub allowed_mentions: AllowedMention,
}

View File

@ -0,0 +1,11 @@
mod activity;
mod connected_account;
mod guild_welcome_screen;
mod interaction;
mod status;
pub use activity::*;
pub use connected_account::*;
pub use guild_welcome_screen::*;
pub use interaction::*;
pub use status::*;

View File

@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Default, Clone)]
/// See https://discord.com/developers/docs/topics/gateway-events#client-status-object
pub struct ClientStatusObject {
pub desktop: Option<String>,
pub mobile: Option<String>,
pub web: Option<String>,
}

14
src/types/mod.rs Normal file
View File

@ -0,0 +1,14 @@
pub use config::*;
pub use errors::*;
pub use events::*;
pub use interfaces::*;
pub use schema::*;
pub use utils::*;
mod config;
mod entities;
mod errors;
mod events;
mod interfaces;
mod schema;
mod utils;

View File

@ -0,0 +1,72 @@
#[cfg(feature = "poem")]
use poem::{http::StatusCode, IntoResponse, Response};
use serde_json::{json, Value};
#[derive(Debug, thiserror::Error)]
pub enum APIError {
#[error(transparent)]
Auth(#[from] AuthError),
}
impl APIError {
pub fn error_payload(&self) -> Value {
match self {
APIError::Auth(auth_err) => auth_err.error_payload(),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum AuthError {
#[error("INVALID_LOGIN")]
InvalidLogin,
#[error("INVALID_CAPTCHA")]
InvalidCaptcha,
}
impl AuthError {
pub fn error_code(&self) -> &str {
match self {
AuthError::InvalidLogin => "INVALID_LOGIN",
AuthError::InvalidCaptcha => "INVALID_CATPCA",
}
}
pub fn error_payload(&self) -> Value {
match self {
AuthError::InvalidLogin => json!({
"login": {
"message": "auth:login.INVALID_LOGIN",
"code": self.error_code()
}
}),
AuthError::InvalidCaptcha => json!([json!({
"captcha_key": "TODO",
"captcha_sitekey": "TODO",
"captcha_service": "TODO"
})]),
}
}
}
#[cfg(feature = "poem")]
impl poem::error::ResponseError for APIError {
fn status(&self) -> StatusCode {
match self {
APIError::Auth(auth_err) => match auth_err {
AuthError::InvalidLogin => StatusCode::UNAUTHORIZED,
AuthError::InvalidCaptcha => StatusCode::BAD_REQUEST,
},
}
}
fn as_response(&self) -> Response
where
Self: std::error::Error + Send + Sync + 'static,
{
Response::builder()
.status(self.status())
.body(self.error_payload().to_string())
.into_response()
}
}

240
src/types/schema/auth.rs Normal file
View File

@ -0,0 +1,240 @@
use crate::errors::FieldFormatError;
use regex::Regex;
use serde::{Deserialize, Serialize};
/**
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: 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>,
) -> Result<RegisterSchema, FieldFormatError> {
let username = AuthUsername::new(username)?.username;
let email = if let Some(email) = email {
Some(AuthEmail::new(email)?.email)
} else {
None
};
let password = if let Some(password) = password {
Some(AuthPassword::new(password)?.password)
} else {
None
};
if !consent {
return Err(FieldFormatError::ConsentError);
}
Ok(RegisterSchema {
username,
password,
consent,
email,
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 {
pub login: String,
pub password: String,
pub undelete: Option<bool>,
pub captcha_key: Option<String>,
pub login_source: Option<String>,
pub 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: String,
password: String,
undelete: Option<bool>,
captcha_key: Option<String>,
login_source: Option<String>,
gift_code_sku_id: Option<String>,
) -> Result<LoginSchema, FieldFormatError> {
let login = AuthUsername::new(login)?.username;
let password = AuthPassword::new(password)?.password;
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>,
}

View File

@ -0,0 +1,27 @@
use serde::{Deserialize, Serialize};
use crate::types::entities::PermissionOverwrite;
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
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<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>,
}

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