revolt_models/v0/
users.rs

1use once_cell::sync::Lazy;
2use regex::Regex;
3
4use super::File;
5
6#[cfg(feature = "validator")]
7use validator::Validate;
8
9/// Regex for valid usernames
10///
11/// Block zero width space
12/// Block lookalike characters
13pub static RE_USERNAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(\p{L}|[\d_.-])+$").unwrap());
14
15/// Regex for valid display names
16///
17/// Block zero width space
18/// Block newline and carriage return
19pub static RE_DISPLAY_NAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[^\u200B\n\r]+$").unwrap());
20
21auto_derived_partial!(
22    /// User
23    pub struct User {
24        /// Unique Id
25        #[cfg_attr(feature = "serde", serde(rename = "_id"))]
26        pub id: String,
27        /// Username
28        pub username: String,
29        /// Discriminator
30        pub discriminator: String,
31        /// Display name
32        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
33        pub display_name: Option<String>,
34        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
35        /// Avatar attachment
36        pub avatar: Option<File>,
37        /// Relationships with other users
38        #[cfg_attr(
39            feature = "serde",
40            serde(skip_serializing_if = "Vec::is_empty", default)
41        )]
42        pub relations: Vec<Relationship>,
43
44        /// Bitfield of user badges
45        ///
46        /// https://docs.rs/revolt-models/latest/revolt_models/v0/enum.UserBadges.html
47        #[cfg_attr(
48            feature = "serde",
49            serde(skip_serializing_if = "crate::if_zero_u32", default)
50        )]
51        pub badges: u32,
52        /// User's current status
53        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
54        pub status: Option<UserStatus>,
55
56        /// Enum of user flags
57        ///
58        /// https://docs.rs/revolt-models/latest/revolt_models/v0/enum.UserFlags.html
59        #[cfg_attr(
60            feature = "serde",
61            serde(skip_serializing_if = "crate::if_zero_u32", default)
62        )]
63        pub flags: u32,
64        /// Whether this user is privileged
65        #[cfg_attr(
66            feature = "serde",
67            serde(skip_serializing_if = "crate::if_false", default)
68        )]
69        pub privileged: bool,
70        /// Bot information
71        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
72        pub bot: Option<BotInformation>,
73
74        /// Current session user's relationship with this user
75        pub relationship: RelationshipStatus,
76        /// Whether this user is currently online
77        pub online: bool,
78    },
79    "PartialUser"
80);
81
82auto_derived!(
83    /// Optional fields on user object
84    pub enum FieldsUser {
85        Avatar,
86        StatusText,
87        StatusPresence,
88        ProfileContent,
89        ProfileBackground,
90        DisplayName,
91
92        /// Internal field, ignore this.
93        Internal,
94    }
95
96    /// User's relationship with another user (or themselves)
97    #[derive(Default)]
98    pub enum RelationshipStatus {
99        /// No relationship with other user
100        #[default]
101        None,
102        /// Other user is us
103        User,
104        /// Friends with the other user
105        Friend,
106        /// Pending friend request to user
107        Outgoing,
108        /// Incoming friend request from user
109        Incoming,
110        /// Blocked this user
111        Blocked,
112        /// Blocked by this user
113        BlockedOther,
114    }
115
116    /// Relationship entry indicating current status with other user
117    pub struct Relationship {
118        /// Other user's Id
119        #[cfg_attr(feature = "serde", serde(rename = "_id"))]
120        pub user_id: String,
121        /// Relationship status with them
122        pub status: RelationshipStatus,
123    }
124
125    /// Presence status
126    pub enum Presence {
127        /// User is online
128        Online,
129        /// User is not currently available
130        Idle,
131        /// User is focusing / will only receive mentions
132        Focus,
133        /// User is busy / will not receive any notifications
134        Busy,
135        /// User appears to be offline
136        Invisible,
137    }
138
139    /// User's active status
140    #[derive(Default)]
141    #[cfg_attr(feature = "validator", derive(Validate))]
142    pub struct UserStatus {
143        /// Custom status text
144        #[validate(length(min = 0, max = 128))]
145        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
146        pub text: Option<String>,
147        /// Current presence option
148        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
149        pub presence: Option<Presence>,
150    }
151
152    /// User's profile
153    #[derive(Default)]
154    #[cfg_attr(feature = "validator", derive(Validate))]
155    pub struct UserProfile {
156        /// Text content on user's profile
157        #[validate(length(min = 0, max = 2000))]
158        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
159        pub content: Option<String>,
160        /// Background visible on user's profile
161        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
162        pub background: Option<File>,
163    }
164
165    /// User badge bitfield
166    #[repr(u32)]
167    pub enum UserBadges {
168        /// Revolt Developer
169        Developer = 1,
170        /// Helped translate Revolt
171        Translator = 2,
172        /// Monetarily supported Revolt
173        Supporter = 4,
174        /// Responsibly disclosed a security issue
175        ResponsibleDisclosure = 8,
176        /// Revolt Founder
177        Founder = 16,
178        /// Platform moderator
179        PlatformModeration = 32,
180        /// Active monetary supporter
181        ActiveSupporter = 64,
182        /// 🦊🦝
183        Paw = 128,
184        /// Joined as one of the first 1000 users in 2021
185        EarlyAdopter = 256,
186        /// Amogus
187        ReservedRelevantJokeBadge1 = 512,
188        /// Low resolution troll face
189        ReservedRelevantJokeBadge2 = 1024,
190    }
191
192    /// User flag enum
193    #[repr(u32)]
194    pub enum UserFlags {
195        /// User has been suspended from the platform
196        SuspendedUntil = 1,
197        /// User has deleted their account
198        Deleted = 2,
199        /// User was banned off the platform
200        Banned = 4,
201        /// User was marked as spam and removed from platform
202        Spam = 8,
203    }
204
205    /// New user profile data
206    #[cfg_attr(feature = "validator", derive(Validate))]
207    pub struct DataUserProfile {
208        /// Text to set as user profile description
209        #[cfg_attr(feature = "validator", validate(length(min = 0, max = 2000)))]
210        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
211        pub content: Option<String>,
212        /// Attachment Id for background
213        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
214        #[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
215        pub background: Option<String>,
216    }
217
218    /// New user information
219    #[cfg_attr(feature = "validator", derive(Validate))]
220    pub struct DataEditUser {
221        /// New display name
222        #[cfg_attr(
223            feature = "validator",
224            validate(length(min = 2, max = 32), regex = "RE_DISPLAY_NAME")
225        )]
226        pub display_name: Option<String>,
227        /// Attachment Id for avatar
228        #[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
229        pub avatar: Option<String>,
230
231        /// New user status
232        #[cfg_attr(feature = "validator", validate)]
233        pub status: Option<UserStatus>,
234        /// New user profile data
235        ///
236        /// This is applied as a partial.
237        #[cfg_attr(feature = "validator", validate)]
238        pub profile: Option<DataUserProfile>,
239
240        /// Bitfield of user badges
241        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
242        pub badges: Option<i32>,
243        /// Enum of user flags
244        #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
245        pub flags: Option<i32>,
246
247        /// Fields to remove from user object
248        #[cfg_attr(feature = "validator", validate(length(min = 1)))]
249        pub remove: Option<Vec<FieldsUser>>,
250    }
251
252    /// User flag reponse
253    pub struct FlagResponse {
254        /// Flags
255        pub flags: i32,
256    }
257
258    /// Mutual friends and servers response
259    pub struct MutualResponse {
260        /// Array of mutual user IDs that both users are friends with
261        pub users: Vec<String>,
262        /// Array of mutual server IDs that both users are in
263        pub servers: Vec<String>,
264    }
265
266    /// Bot information for if the user is a bot
267    pub struct BotInformation {
268        /// Id of the owner of this bot
269        #[cfg_attr(feature = "serde", serde(rename = "owner"))]
270        pub owner_id: String,
271    }
272
273    /// User lookup information
274    pub struct DataSendFriendRequest {
275        /// Username and discriminator combo separated by #
276        pub username: String,
277    }
278);
279
280pub trait CheckRelationship {
281    fn with(&self, user: &str) -> RelationshipStatus;
282}
283
284impl CheckRelationship for Vec<Relationship> {
285    fn with(&self, user: &str) -> RelationshipStatus {
286        for entry in self {
287            if entry.user_id == user {
288                return entry.status.clone();
289            }
290        }
291
292        RelationshipStatus::None
293    }
294}