Skip to content

Commit 1ab60f9

Browse files
SyjaloJiraliteImRodryvaporoxx
authored
feat: support new username system (#9512)
* feat: support new username system * docs(GuildMember#displayName): update * fix(User#hasNewUsername): use User#discriminator * feat(User#tag): add new username * docs(User#tag): update * docs(User#hasNewUsername): update * docs: update * feat(User#defaultAvatarURL): update * feat: remove some newly added properties * docs(User#discriminator): update * docs(User#globalName): bad Copilot * feat(User#displayName): return display name * types(User#displayName): not a partial structure * feat: add `calculateUserDefaultAvatarId` function * docs(CND#defaultAvatar): update * docs(CDN#defaultAvatar): update * feat: change default avatar id to type * feat: deprecate `User#tag` * docs(CDN#defaultAvatar): update Co-authored-by: Jiralite <[email protected]> * docs(User): update Co-authored-by: Rodrigo Leitão <[email protected]> * feat: update * typing: update Co-authored-by: Jan <[email protected]> * feat: change the param type to `Snowflake` --------- Co-authored-by: Jiralite <[email protected]> Co-authored-by: Rodrigo Leitão <[email protected]> Co-authored-by: Jan <[email protected]>
1 parent df8b6e9 commit 1ab60f9

File tree

6 files changed

+64
-12
lines changed

6 files changed

+64
-12
lines changed

packages/discord.js/src/structures/GuildMember.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,12 @@ class GuildMember extends Base {
238238
}
239239

240240
/**
241-
* The nickname of this member, or their username if they don't have one
241+
* The nickname of this member, or their user display name if they don't have one
242242
* @type {?string}
243243
* @readonly
244244
*/
245245
get displayName() {
246-
return this.nickname ?? this.user.username;
246+
return this.nickname ?? this.user.displayName;
247247
}
248248

249249
/**

packages/discord.js/src/structures/User.js

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
'use strict';
22

3+
const process = require('node:process');
34
const { userMention } = require('@discordjs/builders');
5+
const { calculateUserDefaultAvatarIndex } = require('@discordjs/rest');
46
const { DiscordSnowflake } = require('@sapphire/snowflake');
57
const Base = require('./Base');
68
const TextBasedChannel = require('./interfaces/TextBasedChannel');
79
const UserFlagsBitField = require('../util/UserFlagsBitField');
810

11+
let tagDeprecationEmitted = false;
12+
913
/**
1014
* Represents a user on Discord.
1115
* @implements {TextBasedChannel}
@@ -41,6 +45,16 @@ class User extends Base {
4145
this.username ??= null;
4246
}
4347

48+
if ('global_name' in data) {
49+
/**
50+
* The global name of this user
51+
* @type {?string}
52+
*/
53+
this.globalName = data.global_name;
54+
} else {
55+
this.globalName ??= null;
56+
}
57+
4458
if ('bot' in data) {
4559
/**
4660
* Whether or not the user is a bot
@@ -53,7 +67,8 @@ class User extends Base {
5367

5468
if ('discriminator' in data) {
5569
/**
56-
* A discriminator based on username for the user
70+
* The discriminator of this user
71+
* <info>`'0'`, or a 4-digit stringified number if they're using the legacy username system</info>
5772
* @type {?string}
5873
*/
5974
this.discriminator = data.discriminator;
@@ -154,7 +169,8 @@ class User extends Base {
154169
* @readonly
155170
*/
156171
get defaultAvatarURL() {
157-
return this.client.rest.cdn.defaultAvatar(this.discriminator % 5);
172+
const index = this.discriminator === '0' ? calculateUserDefaultAvatarIndex(this.id) : this.discriminator % 5;
173+
return this.client.rest.cdn.defaultAvatar(index);
158174
}
159175

160176
/**
@@ -188,12 +204,33 @@ class User extends Base {
188204
}
189205

190206
/**
191-
* The Discord "tag" (e.g. `hydrabolt#0001`) for this user
207+
* The tag of this user
208+
* <info>This user's username, or their legacy tag (e.g. `hydrabolt#0001`)
209+
* if they're using the legacy username system</info>
192210
* @type {?string}
193211
* @readonly
212+
* @deprecated Use {@link User#username} instead.
194213
*/
195214
get tag() {
196-
return typeof this.username === 'string' ? `${this.username}#${this.discriminator}` : null;
215+
if (!tagDeprecationEmitted) {
216+
process.emitWarning('User#tag is deprecated. Use User#username instead.', 'DeprecationWarning');
217+
tagDeprecationEmitted = true;
218+
}
219+
220+
return typeof this.username === 'string'
221+
? this.discriminator === '0'
222+
? this.username
223+
: `${this.username}#${this.discriminator}`
224+
: null;
225+
}
226+
227+
/**
228+
* The global name of this user, or their username if they don't have one
229+
* @type {?string}
230+
* @readonly
231+
*/
232+
get displayName() {
233+
return this.globalName ?? this.username;
197234
}
198235

199236
/**

packages/discord.js/typings/index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3060,13 +3060,16 @@ export class User extends PartialTextBasedChannel(Base) {
30603060
public get createdAt(): Date;
30613061
public get createdTimestamp(): number;
30623062
public discriminator: string;
3063+
public get displayName(): string;
30633064
public get defaultAvatarURL(): string;
30643065
public get dmChannel(): DMChannel | null;
30653066
public flags: Readonly<UserFlagsBitField> | null;
3067+
public globalName: string | null;
30663068
public get hexAccentColor(): HexColorString | null | undefined;
30673069
public id: Snowflake;
30683070
public get partial(): false;
30693071
public system: boolean;
3072+
/** @deprecated Use {@link User#username} instead. */
30703073
public get tag(): string;
30713074
public username: string;
30723075
public avatarURL(options?: ImageURLOptions): string | null;

packages/rest/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export * from './lib/errors/RateLimitError.js';
55
export * from './lib/RequestManager.js';
66
export * from './lib/REST.js';
77
export * from './lib/utils/constants.js';
8-
export { makeURLSearchParams, parseResponse } from './lib/utils/utils.js';
8+
export { calculateUserDefaultAvatarIndex, makeURLSearchParams, parseResponse } from './lib/utils/utils.js';
99

1010
/**
1111
* The {@link https://github.com/discordjs/discord.js/blob/main/packages/rest/#readme | @discordjs/rest} version

packages/rest/src/lib/CDN.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,15 @@ export class CDN {
119119
}
120120

121121
/**
122-
* Generates the default avatar URL for a discriminator.
122+
* Generates a default avatar URL
123123
*
124-
* @param discriminator - The discriminator modulo 5
124+
* @param index - The default avatar index
125+
* @remarks
126+
* To calculate the index for a user do `(userId >> 22) % 6`,
127+
* or `discriminator % 5` if they're using the legacy username system.
125128
*/
126-
public defaultAvatar(discriminator: number): string {
127-
return this.makeURL(`/embed/avatars/${discriminator}`, { extension: 'png' });
129+
public defaultAvatar(index: number): string {
130+
return this.makeURL(`/embed/avatars/${index}`, { extension: 'png' });
128131
}
129132

130133
/**

packages/rest/src/lib/utils/utils.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { URLSearchParams } from 'node:url';
2-
import type { RESTPatchAPIChannelJSONBody } from 'discord-api-types/v10';
2+
import type { RESTPatchAPIChannelJSONBody, Snowflake } from 'discord-api-types/v10';
33
import type { RateLimitData, ResponseLike } from '../REST.js';
44
import { type RequestManager, RequestMethod } from '../RequestManager.js';
55
import { RateLimitError } from '../errors/RateLimitError.js';
@@ -112,3 +112,12 @@ export async function onRateLimit(manager: RequestManager, rateLimitData: RateLi
112112
throw new RateLimitError(rateLimitData);
113113
}
114114
}
115+
116+
/**
117+
* Calculates the default avatar index for a given user id.
118+
*
119+
* @param userId - The user id to calculate the default avatar index for
120+
*/
121+
export function calculateUserDefaultAvatarIndex(userId: Snowflake) {
122+
return Number(BigInt(userId) >> 22n) % 6;
123+
}

0 commit comments

Comments
 (0)