From d6eae222ebbd2a9c9b4be79959801becd7b0208b Mon Sep 17 00:00:00 2001 From: aldiwildan77 Date: Thu, 8 Jun 2023 21:42:08 +0700 Subject: [PATCH 01/18] feat: support new username only --- user.go | 5 +++++ user_test.go | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/user.go b/user.go index 6c48bf25b..99b14b4f3 100644 --- a/user.go +++ b/user.go @@ -81,7 +81,12 @@ type User struct { } // String returns a unique identifier of the form username#discriminator +// or username only if the discriminator is "0" func (u *User) String() string { + if u.Discriminator == "0" || u.Discriminator == "" { + return u.Username + } + return u.Username + "#" + u.Discriminator } diff --git a/user_test.go b/user_test.go index a36f673e3..1019d29de 100644 --- a/user_test.go +++ b/user_test.go @@ -5,12 +5,40 @@ import "testing" func TestUser(t *testing.T) { t.Parallel() - user := &User{ - Username: "bob", - Discriminator: "8192", + tests := []struct { + name string + u *User + want string + }{ + { + name: "Given a user with a username and discriminator, When String() is called, Then it should return the username and discriminator", + u: &User{ + Username: "bob", + Discriminator: "8192", + }, + want: "bob#8192", + }, + { + name: "Given a user with a username and no discriminator, When String() is called, Then it should return the username", + u: &User{ + Username: "aldiwildan", + }, + want: "aldiwildan", + }, + { + name: "Given a user with a username and a 0 discriminator, When String() is called, Then it should return the username", + u: &User{ + Username: "aldiwildan", + Discriminator: "0", + }, + want: "aldiwildan", + }, } - - if user.String() != "bob#8192" { - t.Errorf("user.String() == %v", user.String()) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.u.String(); got != tt.want { + t.Errorf("User.String() = %v, want %v", got, tt.want) + } + }) } } From 06d750a9efb3c6050106077dc9f78bb8a8bbecc1 Mon Sep 17 00:00:00 2001 From: aldiwildan77 Date: Thu, 8 Jun 2023 22:06:52 +0700 Subject: [PATCH 02/18] docs: add note for user fallback string --- user.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/user.go b/user.go index 99b14b4f3..c8a24f23b 100644 --- a/user.go +++ b/user.go @@ -87,6 +87,9 @@ func (u *User) String() string { return u.Username } + // The code below is a fallback to the special case for a pre-existing user + // before all users were migrated to the new format + // Notes: https://support-dev.discord.com/hc/en-us/articles/13667755828631 return u.Username + "#" + u.Discriminator } From 71b5101ba2ac90b9649f9b32181a2521b1afd397 Mon Sep 17 00:00:00 2001 From: Muhammad Wildan Aldiansyah Date: Fri, 9 Jun 2023 18:47:21 +0700 Subject: [PATCH 03/18] feat: add globalName --- user.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/user.go b/user.go index c8a24f23b..5bfd37145 100644 --- a/user.go +++ b/user.go @@ -43,6 +43,10 @@ type User struct { // The discriminator of the user (4 numbers after name). Discriminator string `json:"discriminator"` + + // The user's display name, if it is set. + // For bots, this is the application name + GlobalName string `json:"global_name"` // The token of the user. This is only present for // the user represented by the current session. From 9cf07f8fd2e8b128361d8aba0ddb0525d23516d1 Mon Sep 17 00:00:00 2001 From: Muhammad Wildan Aldiansyah Date: Sat, 10 Jun 2023 22:30:57 +0700 Subject: [PATCH 04/18] [github] docs: update the user string() func Co-authored-by: Fedor Lapshin --- user.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/user.go b/user.go index 5bfd37145..946c7cd32 100644 --- a/user.go +++ b/user.go @@ -91,9 +91,8 @@ func (u *User) String() string { return u.Username } - // The code below is a fallback to the special case for a pre-existing user - // before all users were migrated to the new format - // Notes: https://support-dev.discord.com/hc/en-us/articles/13667755828631 + // The code below handles applications and users without a migrated username. + // https://support-dev.discord.com/hc/en-us/articles/13667755828631 return u.Username + "#" + u.Discriminator } From 65bd2cba8a79df1f2ea76a54ade9064afe03cd61 Mon Sep 17 00:00:00 2001 From: Muhammad Wildan Aldiansyah Date: Sat, 10 Jun 2023 22:31:33 +0700 Subject: [PATCH 05/18] [github] docs: update GlobalName Co-authored-by: Fedor Lapshin --- user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user.go b/user.go index 946c7cd32..cfb34871d 100644 --- a/user.go +++ b/user.go @@ -45,7 +45,7 @@ type User struct { Discriminator string `json:"discriminator"` // The user's display name, if it is set. - // For bots, this is the application name + // For bots, this is the application name. GlobalName string `json:"global_name"` // The token of the user. This is only present for From 55092634ea4d766a313fa3d872e16d85b35a1d60 Mon Sep 17 00:00:00 2001 From: Muhammad Wildan Aldiansyah Date: Sun, 11 Jun 2023 07:00:11 +0700 Subject: [PATCH 06/18] [github] fix: simplify unit test Co-authored-by: Fedor Lapshin --- user_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_test.go b/user_test.go index 1019d29de..c5a49b88d 100644 --- a/user_test.go +++ b/user_test.go @@ -11,7 +11,7 @@ func TestUser(t *testing.T) { want string }{ { - name: "Given a user with a username and discriminator, When String() is called, Then it should return the username and discriminator", + name: "Username with a discriminator", u: &User{ Username: "bob", Discriminator: "8192", From 83105a8e4a9ad62c0f4511217a91457a003c311a Mon Sep 17 00:00:00 2001 From: Muhammad Wildan Aldiansyah Date: Sun, 11 Jun 2023 07:00:17 +0700 Subject: [PATCH 07/18] [github] fix: simplify unit test Co-authored-by: Fedor Lapshin --- user_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_test.go b/user_test.go index c5a49b88d..42081c6e7 100644 --- a/user_test.go +++ b/user_test.go @@ -26,7 +26,7 @@ func TestUser(t *testing.T) { want: "aldiwildan", }, { - name: "Given a user with a username and a 0 discriminator, When String() is called, Then it should return the username", + name: "User with discriminator set to 0", u: &User{ Username: "aldiwildan", Discriminator: "0", From 5f3d7915dab3fbd227eafec38883d67e24669a35 Mon Sep 17 00:00:00 2001 From: Muhammad Wildan Aldiansyah Date: Sun, 11 Jun 2023 07:03:14 +0700 Subject: [PATCH 08/18] [github] fix: simplify unit test Co-authored-by: Fedor Lapshin --- user_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_test.go b/user_test.go index 42081c6e7..eb50f559b 100644 --- a/user_test.go +++ b/user_test.go @@ -19,7 +19,7 @@ func TestUser(t *testing.T) { want: "bob#8192", }, { - name: "Given a user with a username and no discriminator, When String() is called, Then it should return the username", + name: "User without a discriminator", u: &User{ Username: "aldiwildan", }, From 23b139bb5233d0d38ca1cf79bee35fa3305c2b7e Mon Sep 17 00:00:00 2001 From: Muhammad Wildan Aldiansyah Date: Sun, 11 Jun 2023 07:05:53 +0700 Subject: [PATCH 09/18] [github] fix: rename variable to more clearly change `tt` to `tc`. the purpose is to make the variable better communicate. Co-authored-by: Fedor Lapshin --- user_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/user_test.go b/user_test.go index eb50f559b..54ef817cf 100644 --- a/user_test.go +++ b/user_test.go @@ -34,10 +34,10 @@ func TestUser(t *testing.T) { want: "aldiwildan", }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := tt.u.String(); got != tt.want { - t.Errorf("User.String() = %v, want %v", got, tt.want) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := tc.u.String(); got != tc.want { + t.Errorf("User.String() = %v, want %v", got, tc.want) } }) } From 019d49e3d3d54f3c9b5e08af66baa8b313dc5957 Mon Sep 17 00:00:00 2001 From: Fedor Lapshin Date: Sat, 17 Jun 2023 03:37:09 +0300 Subject: [PATCH 10/18] fix: inconsistent test names --- user_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_test.go b/user_test.go index 54ef817cf..4b5007fd9 100644 --- a/user_test.go +++ b/user_test.go @@ -11,7 +11,7 @@ func TestUser(t *testing.T) { want string }{ { - name: "Username with a discriminator", + name: "User with a discriminator", u: &User{ Username: "bob", Discriminator: "8192", From 5cde9d1fd19f4a0c0352f0507266ea96960141c6 Mon Sep 17 00:00:00 2001 From: aldiwildan77 Date: Sat, 17 Jun 2023 20:12:05 +0700 Subject: [PATCH 11/18] fix: format user.go --- user.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user.go b/user.go index cfb34871d..ab515c8e2 100644 --- a/user.go +++ b/user.go @@ -43,8 +43,8 @@ type User struct { // The discriminator of the user (4 numbers after name). Discriminator string `json:"discriminator"` - - // The user's display name, if it is set. + + // The user's display name, if it is set. // For bots, this is the application name. GlobalName string `json:"global_name"` From 4ba35618ce8fcbe9572890a88f5d6ae1dc8cf2b3 Mon Sep 17 00:00:00 2001 From: aldiwildan77 Date: Mon, 26 Jun 2023 23:17:01 +0700 Subject: [PATCH 12/18] feat: support new avatar --- endpoints.go | 4 ++++ user.go | 27 ++++++++++++++++++++++++--- user_test.go | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/endpoints.go b/endpoints.go index a2a05fe3e..2b779fcb0 100644 --- a/endpoints.go +++ b/endpoints.go @@ -53,6 +53,10 @@ var ( uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator) return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png" } + EndpointDefaultUserAvatarMigrated = func(userID string) string { + userIDInt, _ := strconv.Atoi(userID) + return EndpointCDN + "embed/avatars/" + strconv.Itoa((userIDInt>>22)%5) + ".png" + } EndpointUserBanner = func(uID, cID string) string { return EndpointCDNBanners + uID + "/" + cID + ".png" } diff --git a/user.go b/user.go index ab515c8e2..894d2bf5b 100644 --- a/user.go +++ b/user.go @@ -87,7 +87,7 @@ type User struct { // String returns a unique identifier of the form username#discriminator // or username only if the discriminator is "0" func (u *User) String() string { - if u.Discriminator == "0" || u.Discriminator == "" { + if u.isMigrated() { return u.Username } @@ -105,9 +105,24 @@ func (u *User) Mention() string { // size: The size of the user's avatar as a power of two // if size is an empty string, no size parameter will // be added to the URL. +// +// Update: https://discord.com/developers/docs/change-log#default-avatars +// Based on the update from new username +// Users on the legacy username will use the discriminator % 5 +// Otherwise, users will use the (user_id >> 22) % 6 func (u *User) AvatarURL(size string) string { - return avatarURL(u.Avatar, EndpointDefaultUserAvatar(u.Discriminator), - EndpointUserAvatar(u.ID, u.Avatar), EndpointUserAvatarAnimated(u.ID, u.Avatar), size) + defaultUserAvatar := EndpointDefaultUserAvatar(u.Discriminator) + if u.isMigrated() { + defaultUserAvatar = EndpointDefaultUserAvatarMigrated(u.ID) + } + + return avatarURL( + u.Avatar, + defaultUserAvatar, + EndpointUserAvatar(u.ID, u.Avatar), + EndpointUserAvatarAnimated(u.ID, u.Avatar), + size, + ) } // BannerURL returns the URL of the users's banner image. @@ -116,3 +131,9 @@ func (u *User) AvatarURL(size string) string { func (u *User) BannerURL(size string) string { return bannerURL(u.Banner, EndpointUserBanner(u.ID, u.Banner), EndpointUserBannerAnimated(u.ID, u.Banner), size) } + +// isMigrated returns true if the user is migrated from the legacy username system +// https://discord.com/developers/docs/change-log#identifying-migrated-users +func (u *User) isMigrated() bool { + return u.Discriminator == "" || u.Discriminator == "0" +} diff --git a/user_test.go b/user_test.go index 4b5007fd9..edbf14d4a 100644 --- a/user_test.go +++ b/user_test.go @@ -2,7 +2,7 @@ package discordgo import "testing" -func TestUser(t *testing.T) { +func TestUser_String(t *testing.T) { t.Parallel() tests := []struct { From df354b148ae1e89d2c3565e6274fb234a2213949 Mon Sep 17 00:00:00 2001 From: Muhammad Wildan Aldiansyah Date: Thu, 29 Jun 2023 07:54:29 +0700 Subject: [PATCH 13/18] Update comment explanation user.go Co-authored-by: Fedor Lapshin --- user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user.go b/user.go index 894d2bf5b..ed244ea5d 100644 --- a/user.go +++ b/user.go @@ -85,7 +85,7 @@ type User struct { } // String returns a unique identifier of the form username#discriminator -// or username only if the discriminator is "0" +// or just username, if the discriminator is empty or "0". func (u *User) String() string { if u.isMigrated() { return u.Username From 35027828e581df19feb9f765446da96ffc9c2acd Mon Sep 17 00:00:00 2001 From: aldiwildan77 Date: Sun, 2 Jul 2023 09:50:32 +0700 Subject: [PATCH 14/18] fix: default avatar index --- endpoints.go | 9 ++------- user.go | 37 ++++++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/endpoints.go b/endpoints.go index 2b779fcb0..f61a51975 100644 --- a/endpoints.go +++ b/endpoints.go @@ -49,13 +49,8 @@ var ( EndpointUser = func(uID string) string { return EndpointUsers + uID } EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" } EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" } - EndpointDefaultUserAvatar = func(uDiscriminator string) string { - uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator) - return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png" - } - EndpointDefaultUserAvatarMigrated = func(userID string) string { - userIDInt, _ := strconv.Atoi(userID) - return EndpointCDN + "embed/avatars/" + strconv.Itoa((userIDInt>>22)%5) + ".png" + EndpointDefaultUserAvatar = func(idx uint64) string { + return EndpointCDN + "embed/avatars/" + strconv.FormatUint(idx, 10) + ".png" } EndpointUserBanner = func(uID, cID string) string { return EndpointCDNBanners + uID + "/" + cID + ".png" diff --git a/user.go b/user.go index 894d2bf5b..210ad5293 100644 --- a/user.go +++ b/user.go @@ -1,5 +1,9 @@ package discordgo +import ( + "strconv" +) + // UserFlags is the flags of "user" (see UserFlags* consts) // https://discord.com/developers/docs/resources/user#user-object-user-flags type UserFlags int @@ -102,23 +106,14 @@ func (u *User) Mention() string { } // AvatarURL returns a URL to the user's avatar. -// size: The size of the user's avatar as a power of two -// if size is an empty string, no size parameter will -// be added to the URL. // -// Update: https://discord.com/developers/docs/change-log#default-avatars -// Based on the update from new username -// Users on the legacy username will use the discriminator % 5 -// Otherwise, users will use the (user_id >> 22) % 6 +// size: The size of the user's avatar as a power of two +// if size is an empty string, no size parameter will +// be added to the URL. func (u *User) AvatarURL(size string) string { - defaultUserAvatar := EndpointDefaultUserAvatar(u.Discriminator) - if u.isMigrated() { - defaultUserAvatar = EndpointDefaultUserAvatarMigrated(u.ID) - } - return avatarURL( u.Avatar, - defaultUserAvatar, + EndpointDefaultUserAvatar(u.AvatarIndex()), EndpointUserAvatar(u.ID, u.Avatar), EndpointUserAvatarAnimated(u.ID, u.Avatar), size, @@ -126,8 +121,9 @@ func (u *User) AvatarURL(size string) string { } // BannerURL returns the URL of the users's banner image. -// size: The size of the desired banner image as a power of two -// Image size can be any power of two between 16 and 4096. +// +// size: The size of the desired banner image as a power of two +// Image size can be any power of two between 16 and 4096. func (u *User) BannerURL(size string) string { return bannerURL(u.Banner, EndpointUserBanner(u.ID, u.Banner), EndpointUserBannerAnimated(u.ID, u.Banner), size) } @@ -137,3 +133,14 @@ func (u *User) BannerURL(size string) string { func (u *User) isMigrated() bool { return u.Discriminator == "" || u.Discriminator == "0" } + +// AvatarIndex returns the index of the user's avatar +func (u *User) AvatarIndex() uint64 { + if u.isMigrated() { + id, _ := strconv.ParseUint(u.ID, 10, 64) + return (id >> 22) % 6 + } + + id, _ := strconv.Atoi(u.Discriminator) + return uint64(id % 5) +} From d38ce103797b871ceeb99264cf0043e77122a92c Mon Sep 17 00:00:00 2001 From: Fedor Lapshin Date: Mon, 3 Jul 2023 17:57:36 +0300 Subject: [PATCH 15/18] refactor: rename AvatarIndex to DefaultAvatarIndex --- user.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user.go b/user.go index b13cc54af..0a4cbb584 100644 --- a/user.go +++ b/user.go @@ -113,7 +113,7 @@ func (u *User) Mention() string { func (u *User) AvatarURL(size string) string { return avatarURL( u.Avatar, - EndpointDefaultUserAvatar(u.AvatarIndex()), + EndpointDefaultUserAvatar(u.DefaultAvatarIndex()), EndpointUserAvatar(u.ID, u.Avatar), EndpointUserAvatarAnimated(u.ID, u.Avatar), size, @@ -134,8 +134,8 @@ func (u *User) isMigrated() bool { return u.Discriminator == "" || u.Discriminator == "0" } -// AvatarIndex returns the index of the user's avatar -func (u *User) AvatarIndex() uint64 { +// DefaultAvatarIndex returns the index of the user's default avatar. +func (u *User) DefaultAvatarIndex() uint64 { if u.isMigrated() { id, _ := strconv.ParseUint(u.ID, 10, 64) return (id >> 22) % 6 From f91c081ecb687b586288f3363e15d7291f83c688 Mon Sep 17 00:00:00 2001 From: Fedor Lapshin Date: Tue, 4 Jul 2023 02:26:17 +0300 Subject: [PATCH 16/18] feat: use int instead of uint64 for indexes Use regular int instead of uint64 for return value of User.DefaultAvatarIndex and idx parameter of EndpointDefaultUserAvatar. --- endpoints.go | 4 ++-- user.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/endpoints.go b/endpoints.go index f61a51975..22e391f19 100644 --- a/endpoints.go +++ b/endpoints.go @@ -49,8 +49,8 @@ var ( EndpointUser = func(uID string) string { return EndpointUsers + uID } EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" } EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" } - EndpointDefaultUserAvatar = func(idx uint64) string { - return EndpointCDN + "embed/avatars/" + strconv.FormatUint(idx, 10) + ".png" + EndpointDefaultUserAvatar = func(idx int) string { + return EndpointCDN + "embed/avatars/" + strconv.Itoa(idx) + ".png" } EndpointUserBanner = func(uID, cID string) string { return EndpointCDNBanners + uID + "/" + cID + ".png" diff --git a/user.go b/user.go index 0a4cbb584..958066dec 100644 --- a/user.go +++ b/user.go @@ -135,12 +135,12 @@ func (u *User) isMigrated() bool { } // DefaultAvatarIndex returns the index of the user's default avatar. -func (u *User) DefaultAvatarIndex() uint64 { +func (u *User) DefaultAvatarIndex() int { if u.isMigrated() { id, _ := strconv.ParseUint(u.ID, 10, 64) - return (id >> 22) % 6 + return int((id >> 22) % 6) } id, _ := strconv.Atoi(u.Discriminator) - return uint64(id % 5) + return id % 5 } From f4d63d034c2cf1f1bbd6bf5e24b2392c0ac0ded3 Mon Sep 17 00:00:00 2001 From: Fedor Lapshin Date: Wed, 5 Jul 2023 01:51:31 +0300 Subject: [PATCH 17/18] feat: remove handling of empty discriminator --- user.go | 16 +++++----------- user_test.go | 7 ------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/user.go b/user.go index 958066dec..b80a6512c 100644 --- a/user.go +++ b/user.go @@ -89,14 +89,14 @@ type User struct { } // String returns a unique identifier of the form username#discriminator -// or just username, if the discriminator is empty or "0". +// or just username, if the discriminator is set to "0". func (u *User) String() string { - if u.isMigrated() { + // If the user has been migrated from the legacy username system, their discriminator is "0". + // See https://support.discord.com/hc/articles/12620128861463 + if u.Discriminator == "0" { return u.Username } - // The code below handles applications and users without a migrated username. - // https://support-dev.discord.com/hc/en-us/articles/13667755828631 return u.Username + "#" + u.Discriminator } @@ -128,15 +128,9 @@ func (u *User) BannerURL(size string) string { return bannerURL(u.Banner, EndpointUserBanner(u.ID, u.Banner), EndpointUserBannerAnimated(u.ID, u.Banner), size) } -// isMigrated returns true if the user is migrated from the legacy username system -// https://discord.com/developers/docs/change-log#identifying-migrated-users -func (u *User) isMigrated() bool { - return u.Discriminator == "" || u.Discriminator == "0" -} - // DefaultAvatarIndex returns the index of the user's default avatar. func (u *User) DefaultAvatarIndex() int { - if u.isMigrated() { + if u.Discriminator == "0" { id, _ := strconv.ParseUint(u.ID, 10, 64) return int((id >> 22) % 6) } diff --git a/user_test.go b/user_test.go index edbf14d4a..22b1bd775 100644 --- a/user_test.go +++ b/user_test.go @@ -18,13 +18,6 @@ func TestUser_String(t *testing.T) { }, want: "bob#8192", }, - { - name: "User without a discriminator", - u: &User{ - Username: "aldiwildan", - }, - want: "aldiwildan", - }, { name: "User with discriminator set to 0", u: &User{ From e9835e717485b6d1b45b8b875bba8bb981f8b3ef Mon Sep 17 00:00:00 2001 From: Fedor Lapshin Date: Wed, 5 Jul 2023 02:06:45 +0300 Subject: [PATCH 18/18] chore: change username update article to developer version --- user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user.go b/user.go index b80a6512c..5a91adf70 100644 --- a/user.go +++ b/user.go @@ -92,7 +92,7 @@ type User struct { // or just username, if the discriminator is set to "0". func (u *User) String() string { // If the user has been migrated from the legacy username system, their discriminator is "0". - // See https://support.discord.com/hc/articles/12620128861463 + // See https://support-dev.discord.com/hc/en-us/articles/13667755828631 if u.Discriminator == "0" { return u.Username }