diff --git a/index.d.ts b/index.d.ts index 5a93621c3..e73055d8b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -751,6 +751,7 @@ declare namespace Eris { } interface OldMember { avatar: string | null; + avatarDecorationData?: AvatarDecorationData | null; communicationDisabledUntil?: number | null; nick: string | null; pending?: boolean; @@ -1332,6 +1333,10 @@ declare namespace Eris { } // Member/User + interface AvatarDecorationData { + asset: string; + sku_id: string; + } interface MemberOptions { channelID?: string | null; communicationDisabledUntil?: Date | null; @@ -1351,6 +1356,7 @@ declare namespace Eris { interface PartialUser { accentColor?: number | null; avatar: string | null; + avatarDecorationData?: AvatarDecorationData | null; banner?: string | null; discriminator: string; id: string; @@ -3454,6 +3460,8 @@ declare namespace Eris { accentColor?: number | null; activities?: Activity[]; avatar: string | null; + avatarDecorationData?: AvatarDecorationData | null; + avatarDecorationURL: string | null; avatarURL: string; banner?: string | null; bannerURL: string | null; @@ -3994,6 +4002,8 @@ declare namespace Eris { export class User extends Base { accentColor?: number | null; avatar: string | null; + avatarDecorationData?: AvatarDecorationData | null; + avatarDecorationURL: string | null; avatarURL: string; banner?: string | null; bannerURL: string | null; diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 4fc1c3c62..862fed669 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -751,22 +751,24 @@ class Shard extends EventEmitter { if(packet.d.user.username !== undefined) { let user = this.client.users.get(packet.d.user.id); let oldUser = null; - if(user && (user.username !== packet.d.user.username || user.globalName !== packet.d.user.global_name || user.discriminator !== packet.d.user.discriminator || user.avatar !== packet.d.user.avatar)) { + if(user && (user.username !== packet.d.user.username || user.globalName !== packet.d.user.global_name || user.discriminator !== packet.d.user.discriminator || user.avatar !== packet.d.user.avatar || (packet.d.user.avatar_decoration_data && user.avatarDecorationData && user.avatarDecorationData.asset !== packet.d.user.avatar_decoration_data.asset))) { oldUser = { username: user.username, globalName: user.globalName, discriminator: user.discriminator, - avatar: user.avatar + avatar: user.avatar, + avatarDecorationData: user.avatarDecorationData }; } if(!user || oldUser) { user = this.client.users.update(packet.d.user, this.client); /** - * Fired when a user's avatar, discriminator, display name or username changes + * Fired when a user's avatar, avatar decoration, discriminator, display name or username changes * @event Client#userUpdate * @prop {User} user The updated user * @prop {Object?} oldUser The old user data. If the user was uncached, this will be null * @prop {String?} oldUser.avatar The hash of the user's avatar, or null if no avatar + * @prop {Object?} oldUser.avatarDecorationData The data of the user's avatar decoration, including the asset and sku ID, or null if no avatar decoration * @prop {String} oldUser.discriminator The discriminator of the user * @prop {String?} oldUser.globalName The user's display name, if it is set. For bots, this is the application name * @prop {String} oldUser.username The username of the user @@ -1268,12 +1270,13 @@ class Shard extends EventEmitter { if(!(this.client.options.intents & Constants.Intents.guildPresences) && packet.d.user.username !== undefined) { let user = this.client.users.get(packet.d.user.id); let oldUser = null; - if(user && (user.username !== packet.d.user.username || user.globalName !== packet.d.user.global_name || user.discriminator !== packet.d.user.discriminator || user.avatar !== packet.d.user.avatar)) { + if(user && (user.username !== packet.d.user.username || user.globalName !== packet.d.user.global_name || user.discriminator !== packet.d.user.discriminator || user.avatar !== packet.d.user.avatar || (packet.d.user.avatar_decoration_data && user.avatarDecorationData && user.avatarDecorationData.asset !== packet.d.user.avatar_decoration_data.asset))) { oldUser = { username: user.username, globalName: user.globalName, discriminator: user.discriminator, - avatar: user.avatar + avatar: user.avatar, + avatarDecorationData: user.avatarDecorationData }; } if(!user || oldUser) { @@ -2136,7 +2139,8 @@ class Shard extends EventEmitter { oldUser = { username: user.username, discriminator: user.discriminator, - avatar: user.avatar + avatar: user.avatar, + avatarDecorationData: user.avatarDecorationData }; } user = this.client.users.update(packet.d, this.client); diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index 16d9e65f2..45dde8b97 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -136,6 +136,7 @@ module.exports.GUILD_SPLASH = (guildID, guildSplash) module.exports.ROLE_ICON = (roleID, roleIcon) => `/role-icons/${roleID}/${roleIcon}`; module.exports.TEAM_ICON = (teamID, teamIcon) => `/team-icons/${teamID}/${teamIcon}`; module.exports.USER_AVATAR = (userID, userAvatar) => `/avatars/${userID}/${userAvatar}`; +module.exports.USER_AVATAR_DECORATION = (userDecoration) => `/avatar-decoration-presets/${userDecoration}`; // Client Endpoints module.exports.MESSAGE_LINK = (guildID, channelID, messageID) => `/channels/${guildID}/${channelID}/${messageID}`; diff --git a/lib/structures/Member.js b/lib/structures/Member.js index 86d40a690..6fab774d9 100644 --- a/lib/structures/Member.js +++ b/lib/structures/Member.js @@ -11,6 +11,8 @@ const VoiceState = require("./VoiceState"); * @prop {Number?} accentColor The user's banner color, or null if no banner color (REST only) * @prop {Array?} activities The member's current activities * @prop {String?} avatar The hash of the member's guild avatar, or null if no guild avatar + * @prop {Object?} avatarDecorationData The data of the user's avatar decoration, including the asset and sku ID, or null if no avatar decoration + * @prop {String?} avatarDecorationURL The URL of the user's avatar decoration * @prop {String} avatarURL The URL of the user's avatar which can be either a JPG or GIF * @prop {String?} banner The hash of the user's banner, or null if no banner (REST only) * @prop {String?} bannerURL The URL of the user's banner @@ -129,6 +131,14 @@ class Member extends Base { return this.user.accentColor; } + get avatarDecorationData() { + return this.user.avatarDecorationData; + } + + get avatarDecorationURL() { + return this.user.avatarDecorationURL; + } + get avatarURL() { return this.avatar ? this.guild.shard.client._formatImage(Endpoints.GUILD_AVATAR(this.guild.id, this.id, this.avatar)) : this.user.avatarURL; } diff --git a/lib/structures/User.js b/lib/structures/User.js index f5200a72f..73d621a71 100644 --- a/lib/structures/User.js +++ b/lib/structures/User.js @@ -7,6 +7,8 @@ const Endpoints = require("../rest/Endpoints"); * Represents a user * @prop {Number?} accentColor The user's banner color, or null if no banner color (REST only) * @prop {String?} avatar The hash of the user's avatar, or null if no avatar + * @prop {Object?} avatarDecorationData The data of the user's avatar decoration, including the asset and sku ID, or null if no avatar decoration + * @prop {String?} avatarDecorationURL The URL of the user's avatar decoration * @prop {String} avatarURL The URL of the user's avatar which can be either a JPG or GIF * @prop {String?} banner The hash of the user's banner, or null if no banner (REST only) * @prop {String?} bannerURL The URL of the user's banner @@ -57,6 +59,19 @@ class User extends Base { if(data.accent_color !== undefined) { this.accentColor = data.accent_color; } + if(data.avatar_decoration_data !== undefined) { + this.avatarDecorationData = data.avatar_decoration_data; + } + } + + get avatarDecorationURL() { + if(!this.avatarDecorationData) { + return null; + } + if(this._missingClientError) { + throw this._missingClientError; + } + return this._client._formatImage(Endpoints.USER_AVATAR_DECORATION(this.avatarDecorationData.asset)); } get avatarURL() { @@ -180,6 +195,7 @@ class User extends Base { return super.toJSON([ "accentColor", "avatar", + "avatarDecorationData", "banner", "bot", "discriminator",