1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
use crate::model::{Emoji, Member};
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
/// User unique ID
pub id: String,
/// Username
pub username: String,
/// Discriminator (deprecated)
pub discriminator: String,
/// User's display name (if any)
pub global_name: Option<String>,
// Avatar hash
pub avatar: Option<String>,
#[serde(default)]
/// Whether the user is a bot
pub bot: bool,
#[serde(default)]
/// Whether the user is a system user (e.g., official Discord accounts)
pub system: bool,
// Whether the user has MFA enabled
#[serde(default)]
pub mfa_enabled: bool,
/// User's banner hash (if any)
pub banner: Option<String>,
/// User's accent color (if any)
pub accent_color: Option<u32>,
/// User's locale (e.g., "en-US")
pub locale: Option<String>,
/// Whether the user has verified their email (only for the current user)
pub verified: Option<bool>,
/// Email (only for the current user, requires "email" scope)
pub email: Option<String>,
/// Phone number (only for the current user, requires "phone" scope)
pub phone: Option<String>,
/// Whether the use has used the desktop client before
#[serde(default)]
pub desktop: bool,
/// Whether the user has used the mobile client before
#[serde(default)]
pub mobile: bool,
/// Flags (bitfield representing user features)
pub flags: Option<u64>,
/// Premium type (0 = none, 1 = Nitro Classic, 2 = Nitro)
pub premium_type: Option<u8>,
/// Public flags (bitfield representing public user features)
pub public_flags: Option<u64>,
/// Avatar decoration data (if any)
pub avatar_decoration: Option<AvatarDecoration>,
/// Data for the user's collectibles (if any)
#[serde(default)]
pub collectibles: Option<Nameplate>,
/// The user's primary guild
#[serde(default)]
pub primary_guild: Option<PrimaryGuild>,
/// Live presence data when available from gateway events.
#[serde(default)]
pub presence: Option<Presence>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Presence {
/// Online status (`online`, `idle`, `dnd`, `offline`, ...)
pub status: String,
/// Current activities as raw payload entries.
#[serde(default)]
pub activities: Vec<serde_json::Value>,
/// Client platform statuses (`desktop`, `mobile`, `web`) when provided.
pub client_status: Option<ClientStatus>,
/// Unix time (ms) when user went idle, if any.
pub since: Option<i64>,
/// AFK flag from gateway presence payload.
pub afk: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientStatus {
pub desktop: Option<String>,
pub mobile: Option<String>,
pub web: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserProfile {
/// The bot's application profile
pub application: Option<ApplicationProfile>,
/// The user's profile user data
pub user: Option<User>,
/// The user's profile metadata
#[serde(default)]
pub user_profile: Option<ProfileMetadata>,
/// The user's profile badges
#[serde(default)]
pub badges: Option<Vec<Badge>>,
/// The guild member in the guild specified
#[serde(default)]
pub guild_member: Option<Member>,
/// The guild member's profile in the guild specified
#[serde(default)]
pub guild_member_profile: Option<ProfileMetadata>,
/// The user's pre-migration username#discriminator, if applicable and shown
pub legacy_username: Option<String>,
/// The mutual guilds of the user with the current user
#[serde(default)]
pub mutual_guilds: Option<Vec<MutualGuild>>,
/// The mutual friends the user has with the current user
#[serde(default)]
pub mutual_friends: Option<Vec<User>>,
/// The number of mutual friends the user has with the current user
pub mutual_friend_count: Option<u64>,
/// The type of premium (Nitro) subscription on a user's account
pub premium_type: Option<u8>,
/// The date the user's premium subscription started
pub premium_since: Option<String>,
/// The date the user's premium guild (boosting) subscription started
pub premium_guild_since: Option<String>,
}
impl User {
/// Returns the user's tag (username#discriminator)
pub fn tag(&self) -> String {
format!("{}#{}", self.username, self.discriminator)
}
/// Returns the URL of the user's avatar (if any)
pub fn avatar_url(&self) -> Option<String> {
self.avatar.as_ref().map(|hash| {
format!(
"https://cdn.discordapp.com/avatars/{}/{}.png",
self.id, hash
)
})
}
/// Returns the URL of the user's banner (if any)
pub fn banner_url(&self) -> Option<String> {
self.banner.as_ref().map(|hash| {
let extension = if hash.starts_with("a_") { "gif" } else { "png" };
format!(
"https://cdn.discordapp.com/banners/{}/{}.{}",
self.id, hash, extension
)
})
}
/// Returns a string representation of the user's mention (e.g., "<@123456789>")
pub fn mention(&self) -> String {
format!("<@{}>", self.id)
}
/// Checks if the user has any form of Nitro subscription
pub fn has_nitro(&self) -> bool {
matches!(self.premium_type, Some(1) | Some(2) | Some(3))
}
/// Returns a human-readable name for the user's Nitro subscription (if any)
pub fn premium_type_name(&self) -> &str {
match self.premium_type {
Some(1) => "Nitro Classic",
Some(2) => "Nitro",
Some(3) => "Nitro Basic",
_ => "None",
}
}
/// Sends a friend request to this user.
pub async fn add_friend(&self, http: &crate::HttpClient) -> crate::Result<()> {
let url = crate::http::api_url(&format!("/users/@me/relationships/{}", self.id));
http.put(&url, json!({ "type": 1 })).await?;
Ok(())
}
/// Blocks this user.
pub async fn block(&self, http: &crate::HttpClient) -> crate::Result<()> {
let url = crate::http::api_url(&format!("/users/@me/relationships/{}", self.id));
http.put(&url, json!({ "type": 2 })).await?;
Ok(())
}
/// Removes any relationship with this user (friend, blocked, pending...).
pub async fn remove_relationship(&self, http: &crate::HttpClient) -> crate::Result<()> {
let url = crate::http::api_url(&format!("/users/@me/relationships/{}", self.id));
http.delete(&url).await?;
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Badge {
/// The reference ID of the badge
pub id: String,
/// A description of the badge
pub description: String,
/// The badge's icon hash
pub icon: Option<String>,
/// A link representing the badge
pub link: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileMetadata {
/// The guild ID this profile applies to, if it is a guild profile
pub guild_id: Option<String>,
/// The user's pronouns (max 40 characters)
pub pronouns: Option<String>,
/// The user's bio
pub bio: Option<String>,
/// The user's banner hash
pub banner: Option<String>,
/// The user's banner accent color
pub accent_color: Option<u32>,
/// The user's profile theme (currently unused)
pub theme_colors: Option<Vec<u32>>,
/// The user's profile popout animation particle type
pub popout_animation_particle_type: Option<u8>,
/// The user's profile emoji
#[serde(default)]
pub emoji: Option<Emoji>,
/// The user's profile effect
#[serde(default)]
pub profile_effect: Option<ProfileEffect>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MutualGuild {
/// The guild ID
pub id: String,
/// The user's nickname in the guild
pub nickname: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileEffect {
/// The profile effect's ID
pub id: String,
/// Unix timestamp of when the current profile effect expires
pub expires_at: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApplicationProfile {
/// The application's ID
pub id: String,
/// The application's flags
pub flags: Option<u64>,
/// Whether the application is verified
#[serde(default)]
pub verified: bool,
/// Whether the application has monetization enabled
#[serde(default)]
pub storefront_available: bool,
/// The ID of the application's primary SKU (if any)
pub primary_sku_id: Option<String>,
/// The default in-app authorization link for the intergation
pub install_params: Option<String>,
/// The ID of the application's most popular application commands (max 5)
pub popular_application_command_ids: Option<Vec<String>>,
/// The application's default custom authorization link (if any)
pub custom_install_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AvatarDecoration {
/// The avatar decoration hash
pub asset: Option<String>,
/// ID of the avatar decoration's SKU (if any)
pub sku_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Nameplate {
/// ID of the nameplate SKU
pub sku_id: Option<String>,
/// Path to the nameplate asset
pub asset: Option<String>,
/// The label of this nameplate (Currently unused)
pub label: Option<String>,
/// Background color of the nameplate (crimson, berry, sky, teal, forest, bubble_gum, violet, cobalt, clover, lemon, white)
pub palette: Option<String>,
/// Unix timestamp of when the current nameplate expires (if any)
pub expires_at: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PrimaryGuild {
/// User's primary guild ID
pub identity_guild_id: Option<String>,
/// Whether the user is displaying the primary guild's server tag. This can be null if the system clears the identity, e.g. the server no longer supports tags. This will be false if the user manually removes their tag.
pub identity_enabled: Option<bool>,
/// The text of the user's server tag. Limited to 4 characters.
pub tag: Option<String>,
/// The sevrer tag badge hash
pub badge: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Avatar {
/// The avatar ID
pub id: Option<String>,
/// The avatar hash
pub storage_hash: Option<String>,
/// The avatar's description (if any)
pub description: Option<String>,
}