From 7db5015c07412e5c4372d43c3cd41736c37388ce Mon Sep 17 00:00:00 2001 From: kozabrada123 Date: Sun, 28 Jul 2024 11:42:26 +0200 Subject: [PATCH] feat: Add UserProfile and other types --- src/types/entities/guild.rs | 13 +- src/types/entities/user.rs | 557 +++++++++++++++++++++++++++++++++++- 2 files changed, 563 insertions(+), 7 deletions(-) diff --git a/src/types/entities/guild.rs b/src/types/entities/guild.rs index cca9b94..4de8569 100644 --- a/src/types/entities/guild.rs +++ b/src/types/entities/guild.rs @@ -444,7 +444,7 @@ pub enum VerificationLevel { #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[repr(u8)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] -/// See +/// See pub enum MFALevel { #[default] None = 0, @@ -467,7 +467,7 @@ pub enum MFALevel { #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[repr(u8)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] -/// See +/// See pub enum NSFWLevel { #[default] Default = 0, @@ -492,12 +492,19 @@ pub enum NSFWLevel { #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[repr(u8)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] -/// See +// Note: Maybe rename this to GuildPremiumTier? +/// **Guild** premium (Boosting) tier +/// +/// See pub enum PremiumTier { #[default] + /// No server boost perks None = 0, + /// Level 1 server boost perks Tier1 = 1, + /// Level 2 server boost perks Tier2 = 2, + /// Level 3 server boost perks Tier3 = 3, } diff --git a/src/types/entities/user.rs b/src/types/entities/user.rs index 674c931..1db4b6f 100644 --- a/src/types/entities/user.rs +++ b/src/types/entities/user.rs @@ -7,6 +7,7 @@ use crate::types::utils::Snowflake; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_aux::prelude::deserialize_option_number_from_string; +use serde_repr::{Deserialize_repr, Serialize_repr}; use std::array::TryFromSliceError; use std::fmt::Debug; @@ -22,7 +23,7 @@ use crate::gateway::GatewayHandle; #[cfg(feature = "client")] use chorus_macros::{Composite, Updateable}; -use super::Emoji; +use super::{Emoji, GuildMember}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] @@ -39,6 +40,8 @@ impl User { #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "client", derive(Updateable, Composite))] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] +/// # Reference +/// See pub struct User { pub id: Snowflake, pub username: String, @@ -57,8 +60,10 @@ pub struct User { #[serde(default)] #[serde(deserialize_with = "deserialize_option_number_from_string")] pub flags: Option, + pub premium: Option, + /// The type of premium (Nitro) a user has + pub premium_type: Option, pub premium_since: Option>, - pub premium_type: Option, pub pronouns: Option, pub public_flags: Option, pub banner: Option, @@ -66,13 +71,15 @@ pub struct User { pub theme_colors: Option, pub phone: Option, pub nsfw_allowed: Option, - pub premium: Option, pub purchased_flags: Option, pub premium_usage_flags: Option, pub disabled: Option, } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Copy)] +/// A user's theme colors, as u32s representing hex color codes +/// +/// found in [UserProfileMetadata] pub struct ThemeColors { #[serde(flatten)] inner: (u32, u32), @@ -139,6 +146,8 @@ impl sqlx::Type for ThemeColors { } #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] +/// # Reference +/// See pub struct PublicUser { pub id: Snowflake, pub username: Option, @@ -150,7 +159,9 @@ pub struct PublicUser { pub pronouns: Option, pub bot: Option, pub bio: Option, - pub premium_type: Option, + /// The type of premium (Nitro) a user has + pub premium_type: Option, + /// The date the user's premium (Nitro) subscribtion started pub premium_since: Option>, pub public_flags: Option, } @@ -181,6 +192,8 @@ const CUSTOM_USER_FLAG_OFFSET: u64 = 1 << 32; bitflags::bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, chorus_macros::SerdeBitFlags)] #[cfg_attr(feature = "sqlx", derive(chorus_macros::SqlxBitFlags))] + /// # Reference + /// See pub struct UserFlags: u64 { const DISCORD_EMPLOYEE = 1 << 0; const PARTNERED_SERVER_OWNER = 1 << 1; @@ -194,6 +207,7 @@ bitflags::bitflags! { const EARLY_SUPPORTER = 1 << 9; const TEAM_USER = 1 << 10; const TRUST_AND_SAFETY = 1 << 11; + /// Note: deprecated by Discord const SYSTEM = 1 << 12; const HAS_UNREAD_URGENT_MESSAGES = 1 << 13; const BUGHUNTER_LEVEL_2 = 1 << 14; @@ -205,14 +219,549 @@ bitflags::bitflags! { } } +#[derive( + Serialize_repr, + Deserialize_repr, + Debug, + Default, + Clone, + Eq, + PartialEq, + Hash, + Copy, + PartialOrd, + Ord, +)] +#[cfg_attr(feature = "sqlx", derive(sqlx::Type))] +#[repr(u8)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +/// **User** premium (Nitro) type +/// +/// See +pub enum PremiumType { + #[default] + /// No Nitro + None = 0, + /// Nitro Classic + Tier1 = 1, + /// Nitro + Tier2 = 2, + /// Nitro Basic + Tier3 = 3, +} + #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +/// # Reference +/// See pub struct UserProfileMetadata { + /// The guild ID this profile applies to, if it is a guild profile. pub guild_id: Option, + /// The user's pronouns, up to 40 characters pub pronouns: String, + /// The user's bio / description, up to 190 characters pub bio: Option, + /// The hash used to retrieve the user's banned from the CDN pub banner: Option, + /// Banner color encoded as an i32 representation of a hex color code pub accent_color: Option, + /// See [ThemeColors] pub theme_colors: Option, pub popout_animation_particle_type: Option, pub emoji: Option, } + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] +/// A user's publically facing profile +/// +/// # Reference +/// See +pub struct UserProfile { + // TODO: add profile application object + pub user: PublicUser, + + #[serde(rename = "user_profile")] + pub profile_metadata: UserProfileMetadata, + + #[serde(default)] + pub badges: Vec, + + pub guild_member: Option, + + #[serde(rename = "guild_member_profile")] + pub guild_member_profile_metadata: Option, + + #[serde(default)] + pub guild_badges: Vec, + + /// The user's legacy username#discriminator, if existing and shown + pub legacy_username: Option, + + #[serde(default)] + pub mutual_guilds: Vec, + + #[serde(default)] + pub mutual_friends: Vec, + + pub mutual_friends_count: Option, + + // TODO: Add connections! + // TODO: And application role connections! + /// The type of premium (Nitro) a user has + pub premium_type: Option, + /// The date the user's premium (Nitro) subscribtion started + pub premium_since: Option>, + /// The date the user's premium guild (Boosting) subscribtion started + pub premium_guild_since: Option>, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +/// Info about a badge on a user's profile ([UserProfile]) +/// +/// # Reference +/// See +/// +/// For a list of know badges, see +pub struct ProfileBadge { + /// The badge's unique id, e.g. "staff", "partner", "premium", ... + pub id: String, + /// Description of what the badge represents, e.g. "Discord Staff" + pub description: String, + /// An icon hash, to get the badge's icon from the CDN + pub icon: String, + /// A link (potentially used for href) for the badge. + /// + /// e.g.: + /// "staff" badge links to "https://discord.com/company" + /// "certified_moderator" links to "https://discord.com/safety" + pub link: Option, +} + +impl PartialEq for ProfileBadge { + fn eq(&self, other: &Self) -> bool { + // Note: does not include description, since it changes for some badges + // + // Think nitro "Subscriber since ...", "Server boosting since ..." + self.id.eq(&other.id) && self.icon.eq(&other.icon) && self.link.eq(&other.link) + } +} + +impl ProfileBadge { + /// Returns a badge representing the "staff" badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_staff() -> Self { + Self { + id: "staff".to_string(), + description: "Discord Staff".to_string(), + icon: "5e74e9b61934fc1f67c65515d1f7e60d".to_string(), + link: Some("https://discord.com/company".to_string()), + } + } + + /// Returns a badge representing the partnered server owner badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_partner() -> Self { + Self { + id: "partner".to_string(), + description: "Partnered Server Owner".to_string(), + icon: "3f9748e53446a137a052f3454e2de41e".to_string(), + link: Some("https://discord.com/partners".to_string()), + } + } + + /// Returns a badge representing the certified moderator badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_certified_moderator() -> Self { + Self { + id: "certified_moderator".to_string(), + description: "Moderator Programs Alumni".to_string(), + icon: "fee1624003e2fee35cb398e125dc479b".to_string(), + link: Some("https://discord.com/safety".to_string()), + } + } + + /// Returns a badge representing the hypesquad events badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_hypesquad() -> Self { + Self { + id: "hypesquad".to_string(), + description: "HypeSquad Events".to_string(), + icon: "bf01d1073931f921909045f3a39fd264".to_string(), + link: Some("https://support.discord.com/hc/en-us/articles/360035962891-Profile-Badges-101#h_01GM67K5EJ16ZHYZQ5MPRW3JT3".to_string()), + } + } + + /// Returns a badge representing the hypesquad bravery badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_hypesquad_bravery() -> Self { + Self { + id: "hypesquad_house_1".to_string(), + description: "HypeSquad Bravery".to_string(), + icon: "8a88d63823d8a71cd5e390baa45efa02".to_string(), + link: Some("https://discord.com/settings/hypesquad-online".to_string()), + } + } + + /// Returns a badge representing the hypesquad brilliance badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_hypesquad_brilliance() -> Self { + Self { + id: "hypesquad_house_2".to_string(), + description: "HypeSquad Brilliance".to_string(), + icon: "011940fd013da3f7fb926e4a1cd2e618".to_string(), + link: Some("https://discord.com/settings/hypesquad-online".to_string()), + } + } + + /// Returns a badge representing the hypesquad balance badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_hypesquad_balance() -> Self { + Self { + id: "hypesquad_house_3".to_string(), + description: "HypeSquad Balance".to_string(), + icon: "3aa41de486fa12454c3761e8e223442e".to_string(), + link: Some("https://discord.com/settings/hypesquad-online".to_string()), + } + } + + /// Returns a badge representing the bug hunter level 1 badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_bug_hunter_1() -> Self { + Self { + id: "bug_hunter_level_1".to_string(), + description: "Discord Bug Hunter".to_string(), + icon: "2717692c7dca7289b35297368a940dd0".to_string(), + link: Some( + "https://support.discord.com/hc/en-us/articles/360046057772-Discord-Bugs" + .to_string(), + ), + } + } + + /// Returns a badge representing the bug hunter level 2 badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_bug_hunter_2() -> Self { + Self { + id: "bug_hunter_level_2".to_string(), + description: "Discord Bug Hunter".to_string(), + icon: "848f79194d4be5ff5f81505cbd0ce1e6".to_string(), + link: Some( + "https://support.discord.com/hc/en-us/articles/360046057772-Discord-Bugs" + .to_string(), + ), + } + } + + /// Returns a badge representing the active developer badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_active_developer() -> Self { + Self { + id: "active_developer".to_string(), + description: "Active Developer".to_string(), + icon: "6bdc42827a38498929a4920da12695d9".to_string(), + link: Some( + "https://support-dev.discord.com/hc/en-us/articles/10113997751447?ref=badge" + .to_string(), + ), + } + } + + /// Returns a badge representing the early verified bot developer badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_early_verified_developer() -> Self { + Self { + id: "verified_developer".to_string(), + description: "Early Verified Bot Developer".to_string(), + icon: "6df5892e0f35b051f8b61eace34f4967".to_string(), + link: None, + } + } + + /// Returns a badge representing the early supporter badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_early_supporter() -> Self { + Self { + id: "early_supporter".to_string(), + description: "Early Supporter".to_string(), + icon: "7060786766c9c840eb3019e725d2b358".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the nitro subscriber badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_nitro() -> Self { + Self { + id: "premium".to_string(), + description: "Subscriber since 1 Jan 2015".to_string(), + icon: "2ba85e8026a8614b640c2837bcdfe21b".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the level 1 server boosting badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_server_boosting_1() -> Self { + Self { + id: "guild_booster_lvl1".to_string(), + description: "Server boosting since 1 Jan 2015".to_string(), + icon: "51040c70d4f20a921ad6674ff86fc95c".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the level 2 server boosting badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_server_boosting_2() -> Self { + Self { + id: "guild_booster_lvl2".to_string(), + description: "Server boosting since 1 Jan 2015".to_string(), + icon: "0e4080d1d333bc7ad29ef6528b6f2fb7".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the level 3 server boosting badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_server_boosting_3() -> Self { + Self { + id: "guild_booster_lvl3".to_string(), + description: "Server boosting since 1 Jan 2015".to_string(), + icon: "72bed924410c304dbe3d00a6e593ff59".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the level 4 server boosting badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_server_boosting_4() -> Self { + Self { + id: "guild_booster_lvl4".to_string(), + description: "Server boosting since 1 Jan 2015".to_string(), + icon: "df199d2050d3ed4ebf84d64ae83989f8".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the level 5 server boosting badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_server_boosting_5() -> Self { + Self { + id: "guild_booster_lvl5".to_string(), + description: "Server boosting since 1 Jan 2015".to_string(), + icon: "996b3e870e8a22ce519b3a50e6bdd52f".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the level 6 server boosting badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_server_boosting_6() -> Self { + Self { + id: "guild_booster_lvl6".to_string(), + description: "Server boosting since 1 Jan 2015".to_string(), + icon: "991c9f39ee33d7537d9f408c3e53141e".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the level 7 server boosting badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_server_boosting_7() -> Self { + Self { + id: "guild_booster_lvl7".to_string(), + description: "Server boosting since 1 Jan 2015".to_string(), + icon: "cb3ae83c15e970e8f3d410bc62cb8b99".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the level 8 server boosting badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_server_boosting_8() -> Self { + Self { + id: "guild_booster_lvl8".to_string(), + description: "Server boosting since 1 Jan 2015".to_string(), + icon: "7142225d31238f6387d9f09efaa02759".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the level 9 server boosting badge on Discord.com + /// + /// Note: The description updates for the start date + /// + /// # Reference + /// See + pub fn discord_server_boosting_9() -> Self { + Self { + id: "guild_booster_lvl9".to_string(), + description: "Server boosting since 1 Jan 2015".to_string(), + icon: "ec92202290b48d0879b7413d2dde3bab".to_string(), + link: Some("https://discord.com/settings/premium".to_string()), + } + } + + /// Returns a badge representing the legacy username badge on Discord.com + /// + /// # Reference + /// See + pub fn discord_legacy_username() -> Self { + Self { + id: "legacy_username".to_string(), + description: "Originally known as USERNAME".to_string(), + icon: "6de6d34650760ba5551a79732e98ed60".to_string(), + link: None, + } + } + + /// Returns a badge representing the legacy username badge on Discord.com, + /// with the provided username (which should already contain the #DISCRIM part) + /// + /// # Reference + /// See + pub fn discord_legacy_username_with_username(username: String) -> Self { + Self { + id: "legacy_username".to_string(), + description: format!("Originally known as {username}"), + icon: "6de6d34650760ba5551a79732e98ed60".to_string(), + link: None, + } + } + + /// Returns a badge representing the legacy username badge on Discord.com, + /// with the provided username and discriminator + /// + /// # Reference + /// See + pub fn discord_legacy_username_with_username_and_discriminator( + username: String, + discriminator: String, + ) -> Self { + Self { + id: "legacy_username".to_string(), + description: format!("Originally known as {username}#{discriminator}"), + icon: "6de6d34650760ba5551a79732e98ed60".to_string(), + link: None, + } + } + + /// Returns a badge representing the bot commands badge on Discord.com + /// + /// Note: This badge is only for bot accounts + /// + /// # Reference + /// See + pub fn discord_bot_commands() -> Self { + Self { + id: "bot_commands".to_string(), + description: "Supports Commands".to_string(), + icon: "6f9e37f9029ff57aef81db857890005e".to_string(), + link: Some( + "https://discord.com/blog/welcome-to-the-new-era-of-discord-apps?ref=badge" + .to_string(), + ), + } + } + + /// Returns a badge representing the bot automod badge on Discord.com + /// + /// Note: This badge is only for bot accounts + /// + /// # Reference + /// See + pub fn discord_bot_automod() -> Self { + Self { + id: "automod".to_string(), + description: "Uses AutoMod".to_string(), + icon: "f2459b691ac7453ed6039bbcfaccbfcd".to_string(), + link: None, + } + } + + /// Returns a badge representing the application guild subscription badge on Discord.com + /// + /// No idea where this badge could show up, but apparently it means a guild has an + /// application's premium + /// + /// # Reference + /// See + pub fn discord_application_guild_subscription() -> Self { + Self { + id: "application_guild_subscription".to_string(), + description: "This server has APPLICATION Premium".to_string(), + icon: "d2010c413a8da2208b7e4f35bd8cd4ac".to_string(), + link: None, + } + } +} + +/// Structure which shows a mutual guild with a user +/// +/// # Reference +/// See +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct MutualGuild { + pub id: Snowflake, + /// The user's nickname in the guild, if any + pub nick: Option, +}