revolt_models/v0/
users.rs

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