@@ -289,6 +289,15 @@ pub struct User {
289289 /// [Discord docs](https://discord.com/developers/docs/topics/gateway-events#message-create-message-create-extra-fields).
290290 // Box required to avoid infinitely recursive types
291291 pub member : Option < Box < PartialMember > > ,
292+ /// The primary guild and tag the user has active.
293+ ///
294+ /// Note: just because this guild is populated does not mean the tag is visible.
295+ pub primary_guild : Option < PrimaryGuild > ,
296+ /// Information about this user's avatar decoration.
297+ pub avatar_decoration_data : Option < AvatarDecorationData > ,
298+ /// The collectibles the user currently has active, excluding avatar decorations and profile
299+ /// effects.
300+ pub collectibles : Option < Collectibles > ,
292301}
293302
294303enum_number ! {
@@ -355,6 +364,99 @@ bitflags! {
355364 }
356365}
357366
367+ /// User's Primary Guild object
368+ ///
369+ /// [Discord docs](https://discord.com/developers/docs/resources/user#user-object-user-primary-guild)
370+ #[ cfg_attr( feature = "typesize" , derive( typesize:: derive:: TypeSize ) ) ]
371+ #[ derive( Clone , Debug , Default , Deserialize , Serialize ) ]
372+ #[ non_exhaustive]
373+ pub struct PrimaryGuild {
374+ /// The id of the user's primary guild.
375+ pub identity_guild_id : Option < GuildId > ,
376+ /// Whether the user is displaying the primary guild's server tag. This can be null if the
377+ /// system clears the identity, e.g. because the server no longer supports tags.
378+ pub identity_enabled : Option < bool > ,
379+ /// The text of the [`User`]'s server tag.
380+ pub tag : Option < String > ,
381+ /// The hash of the server badge.
382+ pub badge : Option < ImageHash > ,
383+ }
384+
385+ #[ cfg( feature = "model" ) ]
386+ impl PrimaryGuild {
387+ #[ must_use]
388+ /// Returns the formatted URL of the badge's icon, if one exists.
389+ pub fn badge_url ( & self ) -> Option < String > {
390+ primary_guild_badge_url ( self . identity_guild_id , self . badge . as_ref ( ) )
391+ }
392+ }
393+
394+ #[ cfg_attr( feature = "typesize" , derive( typesize:: derive:: TypeSize ) ) ]
395+ #[ derive( Clone , Copy , Debug , Deserialize , Serialize ) ]
396+ #[ non_exhaustive]
397+ /// The data for a [`User`]'s avatar decoration.
398+ ///
399+ /// [Discord docs](https://discord.com/developers/docs/resources/user#avatar-decoration-data-object).
400+ pub struct AvatarDecorationData {
401+ /// The avatar decoration hash
402+ pub asset : ImageHash ,
403+ /// id of the avatar decoration's SKU
404+ pub sku_id : SkuId ,
405+ }
406+
407+ #[ cfg( feature = "model" ) ]
408+ impl AvatarDecorationData {
409+ #[ must_use]
410+ /// Returns the formatted URL of the decoration.
411+ pub fn decoration_url ( & self ) -> String {
412+ avatar_decoration_url ( & self . asset )
413+ }
414+ }
415+
416+ /// The collectibles the user has, excluding Avatar Decorations and Profile Effects.
417+ ///
418+ /// [Discord docs](https://discord.com/developers/docs/resources/user#collectibles).
419+ #[ cfg_attr( feature = "typesize" , derive( typesize:: derive:: TypeSize ) ) ]
420+ #[ derive( Clone , Debug , Deserialize , Serialize ) ]
421+ #[ non_exhaustive]
422+ pub struct Collectibles {
423+ /// The [`User`]'s nameplate, if they have one.
424+ pub nameplate : Option < Nameplate > ,
425+ }
426+
427+ /// A nameplate, shown on the member list on official clients.
428+ ///
429+ /// [Discord docs](https://discord.com/developers/docs/resources/user#nameplate-nameplate-structure).
430+ #[ cfg_attr( feature = "typesize" , derive( typesize:: derive:: TypeSize ) ) ]
431+ #[ derive( Clone , Debug , Deserialize , Serialize ) ]
432+ #[ non_exhaustive]
433+ pub struct Nameplate {
434+ /// Id of the nameplate SKU
435+ pub sku_id : SkuId ,
436+ /// Path to the nameplate asset.
437+ pub asset : String ,
438+ /// The label of this nameplate.
439+ pub label : String ,
440+ /// Background color of the nameplate, one of: `crimson`, `berry`, `sky`, `teal`, `forest`,
441+ /// `bubble_gum`, `violet`, `cobalt`, `clover`, `lemon`, `white`
442+ pub palette : String ,
443+ }
444+
445+ #[ cfg( all( feature = "unstable_discord_api" , feature = "model" ) ) ]
446+ impl Nameplate {
447+ /// Gets the static version of the nameplate's url.
448+ #[ must_use]
449+ pub fn static_url ( & self ) -> String {
450+ static_nameplate_url ( & self . asset )
451+ }
452+
453+ /// Gets the animated version of the nameplate's url.
454+ #[ must_use]
455+ pub fn url ( & self ) -> String {
456+ nameplate_url ( & self . asset )
457+ }
458+ }
459+
358460use std:: hash:: { Hash , Hasher } ;
359461
360462impl PartialEq for User {
@@ -824,6 +926,31 @@ fn tag(name: &str, discriminator: Option<NonZeroU16>) -> String {
824926 tag
825927}
826928
929+ #[ cfg( feature = "model" ) ]
930+ fn primary_guild_badge_url ( guild_id : Option < GuildId > , hash : Option < & ImageHash > ) -> Option < String > {
931+ if let Some ( guild_id) = guild_id {
932+ return hash. map ( |hash| cdn ! ( "/guild-tag-badges/{}/{}.png?size=1024" , guild_id, hash) ) ;
933+ }
934+
935+ None
936+ }
937+
938+ #[ cfg( feature = "model" ) ]
939+ fn avatar_decoration_url ( hash : & ImageHash ) -> String {
940+ cdn ! ( "/avatar-decoration-presets/{}.png?size=1024" , hash)
941+ }
942+
943+ #[ cfg( all( feature = "unstable_discord_api" , feature = "model" ) ) ]
944+ fn nameplate_url ( path : & str ) -> String {
945+ cdn ! ( "https://cdn.discordapp.com/assets/collectibles/{}/asset.webm" , path)
946+ }
947+
948+ #[ cfg( all( feature = "unstable_discord_api" , feature = "model" ) ) ]
949+ #[ cfg( feature = "model" ) ]
950+ fn static_nameplate_url ( path : & str ) -> String {
951+ cdn ! ( "https://cdn.discordapp.com/assets/collectibles/{}/static.png" , path)
952+ }
953+
827954#[ cfg( test) ]
828955mod test {
829956 use std:: num:: NonZeroU16 ;
0 commit comments