From de5fe927a35e145bd60afa702e5fa5d614b7a0c9 Mon Sep 17 00:00:00 2001 From: March 7th <71698422+aiko-chan-ai@users.noreply.github.com> Date: Tue, 9 Aug 2022 10:42:30 +0700 Subject: [PATCH] fix(User): Profile data (API) change - Add GuildMember bio, banner, ... --- src/structures/GuildMember.js | 44 +++++++++++++++++++++++++++++++++++ src/structures/User.js | 43 +++++++++++++++++++++++++++++----- src/util/Constants.js | 4 ++++ typings/index.d.ts | 2 +- 4 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index 57cd041..0db6cc9 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -97,6 +97,33 @@ class GuildMember extends Base { } } + _ProfilePatch(data) { + if ('accent_color' in data) { + /** + * The member's accent color + * The user must be force fetched for this property to be present or be updated + * @type {?number} + */ + this.accentColor = data.accent_color; + } + if ('banner' in data) { + /** + * The member's banner hash + * The user must be force fetched for this property to be present or be updated + * @type {?string} + */ + this.banner = data.banner; + } + if ('bio' in data) { + /** + * The member's biography (About me) + * The user must be force fetched for this property to be present or be updated + * @type {?string} + */ + this.bio = data.bio; + } + } + _clone() { const clone = super._clone(); clone._roles = this._roles.slice(); @@ -170,6 +197,21 @@ class GuildMember extends Base { return this.client.rest.cdn.GuildMemberAvatar(this.guild.id, this.id, this.avatar, format, size, dynamic); } + /** + * A link to the user's banner. + * This method will throw an error if called before the user is force fetched Profile. + * See {@link GuildMember#banner} for more info + * @param {ImageURLOptions} [options={}] Options for the Image URL + * @returns {?string} + */ + bannerURL({ format, size, dynamic } = {}) { + if (typeof this.banner === 'undefined') { + throw new Error('USER_BANNER_NOT_FETCHED'); + } + if (!this.banner) return null; + return this.client.rest.cdn.GuildMemberBanner(this.guild.id, this.id, this.banner, format, size, dynamic); + } + /** * A link to the member's guild avatar if they have one. * Otherwise, a link to their {@link User#displayAvatarURL} will be returned. @@ -489,6 +531,8 @@ class GuildMember extends Base { this.joinedTimestamp === member.joinedTimestamp && this.nickname === member.nickname && this.avatar === member.avatar && + this.accentColor === member.accentColor && + this.bio === member.bio && this.pending === member.pending && this.communicationDisabledUntilTimestamp === member.communicationDisabledUntilTimestamp && (this._roles === member._roles || diff --git a/src/structures/User.js b/src/structures/User.js index aa74261..9786d5a 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -6,7 +6,7 @@ const ClientApplication = require('./ClientApplication'); const VoiceState = require('./VoiceState'); const TextBasedChannel = require('./interfaces/TextBasedChannel'); const { Error } = require('../errors'); -const { RelationshipTypes } = require('../util/Constants'); +const { RelationshipTypes, NitroType } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); const UserFlags = require('../util/UserFlags'); @@ -41,11 +41,13 @@ class User extends Base { /** * Accounts connected to this user + * The user must be force fetched for this property to be present or be updated * @type {?ConnectionAccount[]} */ this.connectedAccounts = []; /** * Time that User has nitro (Unix Timestamp) + * The user must be force fetched for this property to be present or be updated * @type {?number} * @readonly */ @@ -58,12 +60,14 @@ class User extends Base { this.premiumGuildSince = null; /** * About me (User) + * The user must be force fetched for this property to be present or be updated * @type {?string} * @readonly */ this.bio = null; /** * This user is on the same servers as Client User + * The user must be force fetched for this property to be present or be updated * @type {Collection} * @readonly */ @@ -216,8 +220,23 @@ class User extends Base { this.premiumGuildSince = date.getTime(); } - if ('bio' in data.user) { - this.bio = data.user.bio; + if ('bio' in data.user_profile || 'bio' in data.user) { + this.bio = data.user_profile || data.user.bio; + } + + if ('premium_type' in data) { + const nitro = NitroType[data.premium_type]; + /** + * Nitro type of the user. + * @type {NitroType} + */ + this.nitroType = nitro ?? `UNKNOWN_${data.premium_type}`; + } + + if ('guild_member_profile' in data && 'guild_member' in data) { + const guild = this.client.guilds.cache.get(data.guild_member_profile.guild_id); + const member = guild?.members._add(data.guild_member); + member._ProfilePatch(data.guild_member_profile); } this.mutualGuilds = new Collection(data.mutual_guilds.map(obj => [obj.id, obj])); @@ -226,11 +245,22 @@ class User extends Base { /** * Get profile from Discord, if client is in a server with the target. * @type {User} + * @param {Snowflake | null} guildId The guild id to get the profile from * @returns {Promise} */ - async getProfile() { + async getProfile(guildId) { if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); - const data = await this.client.api.users(this.id).profile.get(); + const query = guildId + ? { + with_mutual_guilds: true, + guild_id: guildId, + } + : { + with_mutual_guilds: true, + }; + const data = await this.client.api.users(this.id).profile.get({ + query, + }); this._ProfilePatch(data); return this; } @@ -436,7 +466,8 @@ 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.bio === user.bio ); } diff --git a/src/util/Constants.js b/src/util/Constants.js index 792468f..e16d74f 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -169,6 +169,10 @@ exports.Endpoints = { if (dynamic && hash.startsWith('a_')) format = 'gif'; return makeImageUrl(`${root}/guilds/${guildId}/users/${memberId}/avatars/${hash}`, { format, size }); }, + GuildMemberBanner: (guildId, memberId, hash, format = 'webp', size, dynamic = false) => { + if (dynamic && hash.startsWith('a_')) format = 'gif'; + return makeImageUrl(`${root}/guilds/${guildId}/users/${memberId}/banners/${hash}`, { format, size }); + }, Banner: (id, hash, format, size, dynamic = false) => { if (dynamic && hash.startsWith('a_')) format = 'gif'; return makeImageUrl(`${root}/banners/${id}/${hash}`, { format, size }); diff --git a/typings/index.d.ts b/typings/index.d.ts index 07e462c..bf60474 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2938,7 +2938,7 @@ export class User extends PartialTextBasedChannel(Base) { public unFriend(): Promise; public unBlock(): Promise; public setNote(note?: any): Promise; - public getProfile(): Promise; + public getProfile(guildId?: Snowflake): Promise; public toString(): UserMention; public ring(): Promise; }