Skip to main content

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