diff --git a/packages/discord.js/src/structures/User.js b/packages/discord.js/src/structures/User.js index 748224ac7ddf..2671175a4ab1 100644 --- a/packages/discord.js/src/structures/User.js +++ b/packages/discord.js/src/structures/User.js @@ -127,11 +127,31 @@ class User extends Base { /** * The user avatar decoration's hash * @type {?string} + * @deprecated Use `avatarDecorationData` instead */ this.avatarDecoration = data.avatar_decoration; } else { this.avatarDecoration ??= null; } + + /** + * @typedef {Object} AvatarDecorationData + * @property {string} asset The avatar decoration hash + * @property {Snowflake} skuId The id of the avatar decoration's SKU + */ + + if (data.avatar_decoration_data) { + /** + * The user avatar decoration's data + * @type {?AvatarDecorationData} + */ + this.avatarDecorationData = { + asset: data.avatar_decoration_data.asset, + skuId: data.avatar_decoration_data.sku_id, + }; + } else { + this.avatarDecorationData = null; + } } /** @@ -176,6 +196,10 @@ class User extends Base { * @returns {?string} */ avatarDecorationURL(options = {}) { + if (this.avatarDecorationData) { + return this.client.rest.cdn.avatarDecoration(this.avatarDecorationData.asset); + } + return this.avatarDecoration && this.client.rest.cdn.avatarDecoration(this.id, this.avatarDecoration, options); } @@ -286,7 +310,10 @@ class User extends Base { this.avatar === user.avatar && this.flags?.bitfield === user.flags?.bitfield && this.banner === user.banner && - this.accentColor === user.accentColor + this.accentColor === user.accentColor && + this.avatarDecoration === user.avatarDecoration && + this.avatarDecorationData?.asset === user.avatarDecorationData?.asset && + this.avatarDecorationData?.skuId === user.avatarDecorationData?.skuId ); } @@ -306,7 +333,12 @@ class User extends Base { this.avatar === user.avatar && this.flags?.bitfield === user.public_flags && ('banner' in user ? this.banner === user.banner : true) && - ('accent_color' in user ? this.accentColor === user.accent_color : true) + ('accent_color' in user ? this.accentColor === user.accent_color : true) && + ('avatar_decoration' in user ? this.avatarDecoration === user.avatar_decoration : true) && + ('avatar_decoration_data' in user + ? this.avatarDecorationData?.asset === user.avatar_decoration_data?.asset && + this.avatarDecorationData?.skuId === user.avatar_decoration_data?.sku_id + : true) ); } diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index a4a8896d0c15..97bcc1976e58 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -3311,6 +3311,11 @@ export class Typing extends Base { }; } +export interface AvatarDecorationData { + asset: string; + skuId: Snowflake; +} + // tslint:disable-next-line no-empty-interface export interface User extends PartialTextBasedChannelFields {} export class User extends Base { @@ -3319,7 +3324,9 @@ export class User extends Base { public accentColor: number | null | undefined; public avatar: string | null; + /** @deprecated Use {@link User.avatarDecorationData} instead */ public avatarDecoration: string | null; + public avatarDecorationData: AvatarDecorationData | null; public banner: string | null | undefined; public bot: boolean; public get createdAt(): Date; diff --git a/packages/rest/__tests__/CDN.test.ts b/packages/rest/__tests__/CDN.test.ts index 8efe234e587d..ed2600f4f614 100644 --- a/packages/rest/__tests__/CDN.test.ts +++ b/packages/rest/__tests__/CDN.test.ts @@ -29,6 +29,14 @@ test('avatar dynamic-not-animated', () => { expect(cdn.avatar(id, hash)).toEqual(`${base}/avatars/${id}/${hash}.webp`); }); +test('avatar decoration default', () => { + expect(cdn.avatarDecoration(id, hash)).toEqual(`${base}/avatar-decorations/${id}/${hash}.webp`); +}); + +test('avatar decoration preset', () => { + expect(cdn.avatarDecoration(hash)).toEqual(`${base}/avatar-decoration-presets/${hash}.png`); +}); + test('banner default', () => { expect(cdn.banner(id, hash)).toEqual(`${base}/banners/${id}/${hash}.webp`); }); diff --git a/packages/rest/src/lib/CDN.ts b/packages/rest/src/lib/CDN.ts index 6ef6f67e2b27..7d2ac6540c1e 100644 --- a/packages/rest/src/lib/CDN.ts +++ b/packages/rest/src/lib/CDN.ts @@ -97,9 +97,17 @@ export class CDN { return this.dynamicMakeURL(`/avatars/${id}/${avatarHash}`, avatarHash, options); } + /** + * Generates a user avatar decoration preset URL. + * + * @param asset - The avatar decoration hash + */ + public avatarDecoration(asset: string): string; + /** * Generates a user avatar decoration URL. * + * @deprecated This overload is deprecated. Pass a hash instead. * @param userId - The id of the user * @param userAvatarDecoration - The hash provided by Discord for this avatar decoration * @param options - Optional options for the avatar decoration @@ -107,9 +115,20 @@ export class CDN { public avatarDecoration( userId: string, userAvatarDecoration: string, + // eslint-disable-next-line @typescript-eslint/unified-signatures + options?: Readonly, + ): string; + + public avatarDecoration( + userIdOrAsset: string, + userAvatarDecoration?: string, options?: Readonly, ): string { - return this.makeURL(`/avatar-decorations/${userId}/${userAvatarDecoration}`, options); + if (userAvatarDecoration) { + return this.makeURL(`/avatar-decorations/${userIdOrAsset}/${userAvatarDecoration}`, options); + } + + return this.makeURL(`/avatar-decoration-presets/${userIdOrAsset}`, { extension: 'png' }); } /**