From 512315258281a59e80ab79beca3f55966d103e4a Mon Sep 17 00:00:00 2001 From: shiftinv Date: Wed, 3 May 2023 21:17:42 +0200 Subject: [PATCH 01/25] docs: deprecate `discriminator` fields --- disnake/abc.py | 6 ++++++ disnake/team.py | 6 ++++++ disnake/user.py | 12 ++++++++++++ disnake/widget.py | 5 +++++ 4 files changed, 29 insertions(+) diff --git a/disnake/abc.py b/disnake/abc.py index 567189e9b9..eaee0b8013 100644 --- a/disnake/abc.py +++ b/disnake/abc.py @@ -132,6 +132,12 @@ class User(Snowflake, Protocol): The user's username. discriminator: :class:`str` The user's discriminator. + + .. deprecated:: 2.9 + This is being phased out by Discord; the username system is moving away from ``username#discriminator`` + to users having a globally unique ``@username``. + The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. + See the :ddocs:`changelog ` for details. avatar: :class:`~disnake.Asset` The avatar asset the user has. bot: :class:`bool` diff --git a/disnake/team.py b/disnake/team.py index f9b78e0cae..fa8d080eb6 100644 --- a/disnake/team.py +++ b/disnake/team.py @@ -96,6 +96,12 @@ class TeamMember(BaseUser): The team member's unique ID. discriminator: :class:`str` The team member's discriminator. This is given when the username has conflicts. + + .. deprecated:: 2.9 + This is being phased out by Discord; the username system is moving away from ``username#discriminator`` + to users having a globally unique ``@username``. + The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. + See the :ddocs:`changelog ` for details. avatar: Optional[:class:`str`] The avatar hash the team member has. Could be None. bot: :class:`bool` diff --git a/disnake/user.py b/disnake/user.py index fafad0fb33..dab62b3541 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -287,6 +287,12 @@ class ClientUser(BaseUser): The user's unique ID. discriminator: :class:`str` The user's discriminator. This is given when the username has conflicts. + + .. deprecated:: 2.9 + This is being phased out by Discord; the username system is moving away from ``username#discriminator`` + to users having a globally unique ``@username``. + The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. + See the :ddocs:`changelog ` for details. bot: :class:`bool` Specifies if the user is a bot account. system: :class:`bool` @@ -418,6 +424,12 @@ class User(BaseUser, disnake.abc.Messageable): The user's unique ID. discriminator: :class:`str` The user's discriminator. This is given when the username has conflicts. + + .. deprecated:: 2.9 + This is being phased out by Discord; the username system is moving away from ``username#discriminator`` + to users having a globally unique ``@username``. + The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. + See the :ddocs:`changelog ` for details. bot: :class:`bool` Specifies if the user is a bot account. system: :class:`bool` diff --git a/disnake/widget.py b/disnake/widget.py index 2ecfec0145..5b6c0c61cd 100644 --- a/disnake/widget.py +++ b/disnake/widget.py @@ -115,6 +115,11 @@ class WidgetMember(BaseUser): The member's nickname (if set in the guild) or username. discriminator: :class:`str` The member's anonymized discriminator. + + .. deprecated:: 2.9 + This is being phased out by Discord; the username system is moving away from ``username#discriminator`` + to users having a globally unique ``@username``. + See the :ddocs:`changelog ` for details. status: :class:`Status` The member's status. activity: Optional[Union[:class:`BaseActivity`, :class:`Spotify`]] From 8f21161ee5d3144a940990d8953e01210668016d Mon Sep 17 00:00:00 2001 From: shiftinv Date: Fri, 5 May 2023 16:07:06 +0200 Subject: [PATCH 02/25] docs: add docs to `Member.discriminator`/`.tag` --- disnake/member.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/disnake/member.py b/disnake/member.py index df240463c3..2955886007 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -483,10 +483,23 @@ def status(self, value: Status) -> None: @property def tag(self) -> str: + """:class:`str`: An alias of :attr:`.discriminator`. + + .. deprecated:: 2.9 + See :attr:`.discriminator`. + """ return self._user.discriminator @property def discriminator(self) -> str: + """:class:`str`:The user's discriminator. + + .. deprecated:: 2.9 + This is being phased out by Discord; the username system is moving away from ``username#discriminator`` + to users having a globally unique ``@username``. + The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. + See the :ddocs:`changelog ` for details. + """ return self._user.discriminator @property From 218e3ba9713f9a0e81084e631bfe88b9bf4ee3a5 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Fri, 5 May 2023 16:20:14 +0200 Subject: [PATCH 03/25] feat: update `User.__str__` to handle `"0"` discriminator --- disnake/member.py | 2 +- disnake/team.py | 2 +- disnake/types/user.py | 2 +- disnake/user.py | 11 ++++++++--- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/disnake/member.py b/disnake/member.py index 2955886007..363d14add0 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -230,7 +230,7 @@ class Member(disnake.abc.Messageable, _UserTag): .. describe:: str(x) - Returns the member's name with the discriminator. + Returns the member's name (with discriminator, if not migrated to new system yet). Attributes ---------- diff --git a/disnake/team.py b/disnake/team.py index fa8d080eb6..dcd382047f 100644 --- a/disnake/team.py +++ b/disnake/team.py @@ -84,7 +84,7 @@ class TeamMember(BaseUser): .. describe:: str(x) - Returns the team member's name with discriminator. + Returns the team member's name (with discriminator, if not migrated to new system yet). .. versionadded:: 1.3 diff --git a/disnake/types/user.py b/disnake/types/user.py index 8c7abd2de4..5a1491c06b 100644 --- a/disnake/types/user.py +++ b/disnake/types/user.py @@ -8,7 +8,7 @@ class PartialUser(TypedDict): id: Snowflake username: str - discriminator: str + discriminator: str # may be removed in future API versions avatar: Optional[str] diff --git a/disnake/user.py b/disnake/user.py index dab62b3541..bccbfb7fa8 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -76,7 +76,12 @@ def __repr__(self) -> str: ) def __str__(self) -> str: - return f"{self.name}#{self.discriminator}" + discriminator = self.discriminator + if discriminator == "0": + # TODO: add "@" prefix? + return self.name + # legacy behavior + return f"{self.name}#{discriminator}" def __eq__(self, other: Any) -> bool: return isinstance(other, _UserTag) and other.id == self.id @@ -277,7 +282,7 @@ class ClientUser(BaseUser): .. describe:: str(x) - Returns the user's name with discriminator. + Returns the user's name (with discriminator, if not migrated to new system yet). Attributes ---------- @@ -414,7 +419,7 @@ class User(BaseUser, disnake.abc.Messageable): .. describe:: str(x) - Returns the user's name with discriminator. + Returns the user's name (with discriminator, if not migrated to new system yet). Attributes ---------- From cda5bf9d8d94d87ac4d76be24739fb091461c06d Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 6 May 2023 17:31:05 +0200 Subject: [PATCH 04/25] feat: add `global_name` field --- disnake/abc.py | 6 ++++++ disnake/member.py | 8 +++++--- disnake/types/user.py | 3 +++ disnake/user.py | 26 ++++++++++++++++++++++---- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/disnake/abc.py b/disnake/abc.py index eaee0b8013..d0b2efe1c3 100644 --- a/disnake/abc.py +++ b/disnake/abc.py @@ -138,6 +138,11 @@ class User(Snowflake, Protocol): to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the :ddocs:`changelog ` for details. + global_name: Optional[:class:`str`] + The user's global display name, if set. + For bots, this is the application name. + + .. versionadded:: 2.9 avatar: :class:`~disnake.Asset` The avatar asset the user has. bot: :class:`bool` @@ -148,6 +153,7 @@ class User(Snowflake, Protocol): name: str discriminator: str + global_name: Optional[str] avatar: Asset bot: bool diff --git a/disnake/member.py b/disnake/member.py index 363d14add0..736051da6a 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -278,6 +278,7 @@ class Member(disnake.abc.Messageable, _UserTag): if TYPE_CHECKING: name: str id: int + global_name: Optional[str] bot: bool system: bool created_at: datetime.datetime @@ -345,7 +346,7 @@ def __str__(self) -> str: def __repr__(self) -> str: return ( - f"" ) @@ -449,17 +450,18 @@ def _presence_update( def _update_inner_user(self, user: UserPayload) -> Optional[Tuple[User, User]]: u = self._user - original = (u.name, u._avatar, u.discriminator, u._public_flags) + original = (u.name, u._avatar, u.discriminator, u.global_name, u._public_flags) # These keys seem to always be available modified = ( user["username"], user["avatar"], user["discriminator"], + user.get("global_name"), user.get("public_flags", 0), ) if original != modified: to_return = User._copy(self._user) - u.name, u._avatar, u.discriminator, u._public_flags = modified + u.name, u._avatar, u.discriminator, u.global_name, u._public_flags = modified # Signal to dispatch on_user_update return to_return, u diff --git a/disnake/types/user.py b/disnake/types/user.py index 5a1491c06b..ab913f9e82 100644 --- a/disnake/types/user.py +++ b/disnake/types/user.py @@ -2,6 +2,8 @@ from typing import Literal, Optional, TypedDict +from typing_extensions import NotRequired + from .snowflake import Snowflake @@ -9,6 +11,7 @@ class PartialUser(TypedDict): id: Snowflake username: str discriminator: str # may be removed in future API versions + global_name: NotRequired[Optional[str]] avatar: Optional[str] diff --git a/disnake/user.py b/disnake/user.py index bccbfb7fa8..8a527e366d 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -42,6 +42,7 @@ class BaseUser(_UserTag): "name", "id", "discriminator", + "global_name", "_avatar", "_banner", "_accent_colour", @@ -55,6 +56,7 @@ class BaseUser(_UserTag): name: str id: int discriminator: str + global_name: Optional[str] bot: bool system: bool _state: ConnectionState @@ -71,8 +73,8 @@ def __init__( def __repr__(self) -> str: return ( - f"" + f"" ) def __str__(self) -> str: @@ -96,6 +98,7 @@ def _update(self, data: Union[UserPayload, PartialUserPayload]) -> None: self.name = data["username"] self.id = int(data["id"]) self.discriminator = data["discriminator"] + self.global_name = data.get("global_name") self._avatar = data["avatar"] self._banner = data.get("banner", None) self._accent_colour = data.get("accent_color", None) @@ -110,6 +113,7 @@ def _copy(cls, user: BaseUser) -> Self: self.name = user.name self.id = user.id self.discriminator = user.discriminator + self.global_name = user.global_name self._avatar = user._avatar self._banner = user._banner self._accent_colour = user._accent_colour @@ -125,6 +129,7 @@ def _to_minimal_user_json(self) -> UserPayload: "id": self.id, "avatar": self._avatar, "discriminator": self.discriminator, + "global_name": self.global_name, "bot": self.bot, "public_flags": self._public_flags, } @@ -298,6 +303,11 @@ class ClientUser(BaseUser): to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the :ddocs:`changelog ` for details. + global_name: Optional[:class:`str`] + The user's global display name, if set. + For bots, this is the application name. + + .. versionadded:: 2.9 bot: :class:`bool` Specifies if the user is a bot account. system: :class:`bool` @@ -330,7 +340,7 @@ def __init__(self, *, state: ConnectionState, data: UserPayload) -> None: def __repr__(self) -> str: return ( - f"" ) @@ -435,6 +445,11 @@ class User(BaseUser, disnake.abc.Messageable): to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the :ddocs:`changelog ` for details. + global_name: Optional[:class:`str`] + The user's global display name, if set. + For bots, this is the application name. + + .. versionadded:: 2.9 bot: :class:`bool` Specifies if the user is a bot account. system: :class:`bool` @@ -444,7 +459,10 @@ class User(BaseUser, disnake.abc.Messageable): __slots__ = ("__weakref__",) def __repr__(self) -> str: - return f"" + return ( + f"" + ) async def _get_channel(self) -> DMChannel: ch = await self.create_dm() From c356a52d9c1197807ea85678ee7f30dfbb3918a2 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 6 May 2023 17:31:36 +0200 Subject: [PATCH 05/25] feat: update `display_name` handling --- disnake/ext/commands/converter.py | 4 +++- disnake/member.py | 8 ++++---- disnake/user.py | 7 +++---- disnake/widget.py | 3 ++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/disnake/ext/commands/converter.py b/disnake/ext/commands/converter.py index ea966b0079..bf4cd701a1 100644 --- a/disnake/ext/commands/converter.py +++ b/disnake/ext/commands/converter.py @@ -1000,7 +1000,8 @@ class clean_content(Converter[str]): fix_channel_mentions: :class:`bool` Whether to clean channel mentions. use_nicknames: :class:`bool` - Whether to use nicknames when transforming mentions. + Whether to use :attr:`nicknames <.Member.nick>` and + :attr:`global names <.Member.global_name>` when transforming mentions. escape_markdown: :class:`bool` Whether to also escape special markdown characters. remove_markdown: :class:`bool` @@ -1030,6 +1031,7 @@ def resolve_user(id: int) -> str: m = (msg and _utils_get(msg.mentions, id=id)) or bot.get_user(id) if m is None and ctx.guild: m = ctx.guild.get_member(id) + # TODO: add a separate option for `global_name`s? return f"@{m.display_name if self.use_nicknames else m.name}" if m else "@deleted-user" def resolve_role(id: int) -> str: diff --git a/disnake/member.py b/disnake/member.py index 736051da6a..8615d75f5f 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -581,11 +581,11 @@ def mention(self) -> str: def display_name(self) -> str: """:class:`str`: Returns the user's display name. - For regular users this is just their username, but - if they have a guild specific nickname then that - is returned instead. + If they have a guild-specific :attr:`nickname <.nick>`, then + that is returned. If not, this is their :attr:`global name <.global_name>` + if set, or their :attr:`username <.name>` otherwise. """ - return self.nick or self.name + return self.nick or self.global_name or self.name @property def display_avatar(self) -> Asset: diff --git a/disnake/user.py b/disnake/user.py index 8a527e366d..28e0d162f4 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -243,11 +243,10 @@ def created_at(self) -> datetime: def display_name(self) -> str: """:class:`str`: Returns the user's display name. - For regular users this is just their username, but - if they have a guild specific nickname then that - is returned instead. + This is their :attr:`global name <.global_name>` if set, + or their :attr:`username <.name>` otherwise. """ - return self.name + return self.global_name or self.name def mentioned_in(self, message: Message) -> bool: """Checks if the user is mentioned in the specified message. diff --git a/disnake/widget.py b/disnake/widget.py index 5b6c0c61cd..ee1d7b5708 100644 --- a/disnake/widget.py +++ b/disnake/widget.py @@ -179,10 +179,11 @@ def avatar(self) -> Optional[Asset]: return Asset(self._state, url=url, key=url, animated=False) return None + # TODO: it's not yet known if widget members include global_name or if it's already handled through `name` @property def display_name(self) -> str: """:class:`str`: Returns the member's name.""" - return self.name + return self.global_name or self.name class WidgetSettings: From 049b44e3f594ed03367236899cace9bd0c7ef913 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 6 May 2023 18:13:06 +0200 Subject: [PATCH 06/25] fix: update default avatar handling --- disnake/user.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/disnake/user.py b/disnake/user.py index 28e0d162f4..46b05e0d57 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -152,8 +152,13 @@ def avatar(self) -> Optional[Asset]: @property def default_avatar(self) -> Asset: - """:class:`Asset`: Returns the default avatar for a given user. This is calculated by the user's discriminator.""" - return Asset._from_default_avatar(self._state, int(self.discriminator) % len(DefaultAvatar)) + """:class:`Asset`: Returns the default avatar for a given user.""" + if self.discriminator == "0": + num = self.id >> 22 + else: + # legacy behavior + num = int(self.discriminator) + return Asset._from_default_avatar(self._state, num % len(DefaultAvatar)) @property def display_avatar(self) -> Asset: From d031d6ce41d1d97b7bffa97ac9ac887aeca8fbda Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 6 May 2023 18:16:21 +0200 Subject: [PATCH 07/25] fix: assume widget members don't have a `global_name` for now --- disnake/widget.py | 2 +- docs/api/widgets.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/disnake/widget.py b/disnake/widget.py index ee1d7b5708..3b133a6a8f 100644 --- a/disnake/widget.py +++ b/disnake/widget.py @@ -183,7 +183,7 @@ def avatar(self) -> Optional[Asset]: @property def display_name(self) -> str: """:class:`str`: Returns the member's name.""" - return self.global_name or self.name + return self.name class WidgetSettings: diff --git a/docs/api/widgets.rst b/docs/api/widgets.rst index 936d7bc9d7..b0565bed04 100644 --- a/docs/api/widgets.rst +++ b/docs/api/widgets.rst @@ -44,7 +44,7 @@ WidgetMember .. autoclass:: WidgetMember() :members: :inherited-members: - :exclude-members: public_flags, default_avatar, banner, accent_colour, accent_color, colour, color, mention, created_at, mentioned_in + :exclude-members: global_name, public_flags, default_avatar, banner, accent_colour, accent_color, colour, color, mention, created_at, mentioned_in Enumerations ------------ From 430288d876a4dec976a1b227e82c4ef02af47b6f Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 6 May 2023 20:46:34 +0200 Subject: [PATCH 08/25] fix: update converters and `get_member_named` --- disnake/ext/commands/converter.py | 33 ++++++++++++++++++++++++------- disnake/guild.py | 25 +++++++++++++---------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/disnake/ext/commands/converter.py b/disnake/ext/commands/converter.py index bf4cd701a1..b39fe22c0f 100644 --- a/disnake/ext/commands/converter.py +++ b/disnake/ext/commands/converter.py @@ -188,9 +188,12 @@ class MemberConverter(IDConverter[disnake.Member]): 1. Lookup by ID. 2. Lookup by mention. - 3. Lookup by name#discrim - 4. Lookup by name - 5. Lookup by nickname + 3. Lookup by name#discrim. + 4. Lookup by nickname. + 5. Lookup by global name. + 6. Lookup by name. + + The name resolution order matches the one used by :meth:`.Guild.get_member_named`. .. versionchanged:: 1.5 Raise :exc:`.MemberNotFound` instead of generic :exc:`.BadArgument` @@ -198,6 +201,10 @@ class MemberConverter(IDConverter[disnake.Member]): .. versionchanged:: 1.5.1 This converter now lazily fetches members from the gateway and HTTP APIs, optionally caching the result if :attr:`.MemberCacheFlags.joined` is enabled. + + .. versionchanged:: 2.9 + Name resolution order changed from ``name > nick`` to ``nick > global_name > name`` + to account for the username migration. """ async def query_member_named( @@ -205,12 +212,16 @@ async def query_member_named( ) -> Optional[disnake.Member]: cache = guild._state.member_cache_flags.joined if len(argument) > 5 and argument[-5] == "#": + # legacy behavior for non-migrated users username, _, discriminator = argument.rpartition("#") members = await guild.query_members(username, limit=100, cache=cache) return _utils_get(members, name=username, discriminator=discriminator) else: members = await guild.query_members(argument, limit=100, cache=cache) - return disnake.utils.find(lambda m: m.name == argument or m.nick == argument, members) + return disnake.utils.find( + lambda m: m.nick == argument or m.global_name == argument or m.name == argument, + members, + ) async def query_member_by_id( self, bot: disnake.Client, guild: disnake.Guild, user_id: int @@ -286,8 +297,9 @@ class UserConverter(IDConverter[disnake.User]): 1. Lookup by ID. 2. Lookup by mention. - 3. Lookup by name#discrim - 4. Lookup by name + 3. Lookup by name#discrim. + 4. Lookup by global name. + 5. Lookup by name. .. versionchanged:: 1.5 Raise :exc:`.UserNotFound` instead of generic :exc:`.BadArgument` @@ -295,6 +307,9 @@ class UserConverter(IDConverter[disnake.User]): .. versionchanged:: 1.6 This converter now lazily fetches users from the HTTP APIs if an ID is passed and it's not available in cache. + + .. versionchanged:: 2.9 + Now takes :attr:`.User.global_name` into account. """ async def convert(self, ctx: AnyContext, argument: str) -> disnake.User: @@ -332,6 +347,7 @@ async def convert(self, ctx: AnyContext, argument: str) -> disnake.User: # check for discriminator if it exists, if len(arg) > 5 and arg[-5] == "#": + # legacy behavior for non-migrated users discrim = arg[-4:] name = arg[:-5] result = disnake.utils.find( @@ -340,7 +356,10 @@ async def convert(self, ctx: AnyContext, argument: str) -> disnake.User: if result is not None: return result - result = disnake.utils.find(lambda u: u.name == arg, state._users.values()) + result = disnake.utils.find( + lambda u: u.global_name == arg or u.name == arg, + state._users.values(), + ) if result is None: raise UserNotFound(argument) diff --git a/disnake/guild.py b/disnake/guild.py index 163ddcc47d..8bd23505e6 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -1117,18 +1117,22 @@ def created_at(self) -> datetime.datetime: def get_member_named(self, name: str, /) -> Optional[Member]: """Returns the first member found that matches the name provided. - The name can have an optional discriminator argument, e.g. "Jake#0001" - or "Jake" will both do the lookup. However the former will give a more - precise result. Note that the discriminator must have all 4 digits - for this to work. + The lookup strategy is as follows (in order): - If a nickname is passed, then it is looked up via the nickname. Note - however, that a nickname + discriminator combo will not lookup the nickname - but rather the username + discriminator combo due to nickname + discriminator - not being unique. + 1. Lookup by nickname. + 2. Lookup by global name. + 3. Lookup by name. + + While the migration away from discriminators is still ongoing, + the name can have an optional discriminator argument, e.g. "Jake#0001", + in which case it will be treated as a username + discriminator combo + (note: this only works with usernames, not nicknames). If no member is found, ``None`` is returned. + .. versionchanged:: 2.9 + Now takes :attr:`User.global_name` into account. + Parameters ---------- name: :class:`str` @@ -1140,8 +1144,9 @@ def get_member_named(self, name: str, /) -> Optional[Member]: The member in this guild with the associated name. If not found then ``None`` is returned. """ - result = None members = self.members + + # legacy behavior for non-migrated users if len(name) > 5 and name[-5] == "#": # The 5 length is checking to see if #0000 is in the string, # as a#0000 has a length of 6, the minimum for a potential @@ -1155,7 +1160,7 @@ def get_member_named(self, name: str, /) -> Optional[Member]: return result def pred(m: Member) -> bool: - return m.nick == name or m.name == name + return m.nick == name or m.global_name == name or m.name == name return utils.find(pred, members) From 73f0b5527f8aa1b73fc5a2fef7498b2631c9047a Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 6 May 2023 21:04:20 +0200 Subject: [PATCH 09/25] docs: add changelog entry --- changelog/1025.feature.rst | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog/1025.feature.rst diff --git a/changelog/1025.feature.rst b/changelog/1025.feature.rst new file mode 100644 index 0000000000..81f116a12c --- /dev/null +++ b/changelog/1025.feature.rst @@ -0,0 +1,7 @@ +Add support for new username system - see the official :ddocs:`changelog ` for details. Existing functionality is kept backwards-compatible while the migration is still ongoing. +- Add :attr:`User.global_name`, and update attributes/methods to account for it: + - :attr:`User.display_name` and :attr:`Member.display_name` + - :meth:`Guild.get_member_named` + - |commands| :class:`~ext.commands.UserConverter` and :class:`~ext.commands.MemberConverter`, now largely matching the behavior of :meth:`Guild.get_member_named` +- Update ``str(user)`` and ``str(member)`` to not include ``#0`` discriminator of migrated users. +- Adjust :attr:`User.default_avatar` to account for new default avatar handling. From 3ce1c9dfd0eea7de6df70dbf693cf4f389217765 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 6 May 2023 21:43:31 +0200 Subject: [PATCH 10/25] docs: cleanup/clarify/stuff/things --- disnake/abc.py | 2 ++ disnake/flags.py | 1 + disnake/guild.py | 2 +- disnake/member.py | 3 ++- disnake/team.py | 4 ++-- disnake/user.py | 12 ++++++++---- disnake/widget.py | 9 ++++----- docs/api/events.rst | 1 + docs/ext/commands/commands.rst | 4 ++-- 9 files changed, 23 insertions(+), 15 deletions(-) diff --git a/disnake/abc.py b/disnake/abc.py index d0b2efe1c3..cae6ef1395 100644 --- a/disnake/abc.py +++ b/disnake/abc.py @@ -140,6 +140,8 @@ class User(Snowflake, Protocol): See the :ddocs:`changelog ` for details. global_name: Optional[:class:`str`] The user's global display name, if set. + This takes precedence over :attr:`.name` when shown. + For bots, this is the application name. .. versionadded:: 2.9 diff --git a/disnake/flags.py b/disnake/flags.py index 5879a01618..3e8ded9acd 100644 --- a/disnake/flags.py +++ b/disnake/flags.py @@ -1136,6 +1136,7 @@ def members(self): - :attr:`User.name` - :attr:`User.avatar` - :attr:`User.discriminator` + - :attr:`User.global_name` For more information go to the :ref:`member intent documentation `. diff --git a/disnake/guild.py b/disnake/guild.py index 8bd23505e6..b64ab190be 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -1136,7 +1136,7 @@ def get_member_named(self, name: str, /) -> Optional[Member]: Parameters ---------- name: :class:`str` - The name of the member to lookup with an optional discriminator. + The name of the member to lookup (with an optional discriminator). Returns ------- diff --git a/disnake/member.py b/disnake/member.py index 8615d75f5f..0860909f70 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -230,7 +230,7 @@ class Member(disnake.abc.Messageable, _UserTag): .. describe:: str(x) - Returns the member's name (with discriminator, if not migrated to new system yet). + Returns the member's username (with discriminator, if not migrated to new system yet), or global name if set. Attributes ---------- @@ -249,6 +249,7 @@ class Member(disnake.abc.Messageable, _UserTag): The guild that the member belongs to. nick: Optional[:class:`str`] The guild specific nickname of the user. + This takes precedence over :attr:`.global_name` and :attr:`.name` when shown. pending: :class:`bool` Whether the member is pending member verification. diff --git a/disnake/team.py b/disnake/team.py index dcd382047f..f0caf4d859 100644 --- a/disnake/team.py +++ b/disnake/team.py @@ -84,7 +84,7 @@ class TeamMember(BaseUser): .. describe:: str(x) - Returns the team member's name (with discriminator, if not migrated to new system yet). + Returns the team member's username (with discriminator, if not migrated to new system yet), or global name if set. .. versionadded:: 1.3 @@ -95,7 +95,7 @@ class TeamMember(BaseUser): id: :class:`int` The team member's unique ID. discriminator: :class:`str` - The team member's discriminator. This is given when the username has conflicts. + The team member's discriminator. .. deprecated:: 2.9 This is being phased out by Discord; the username system is moving away from ``username#discriminator`` diff --git a/disnake/user.py b/disnake/user.py index 46b05e0d57..db4f1ec13f 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -291,7 +291,7 @@ class ClientUser(BaseUser): .. describe:: str(x) - Returns the user's name (with discriminator, if not migrated to new system yet). + Returns the user's username (with discriminator, if not migrated to new system yet), or global name if set. Attributes ---------- @@ -300,7 +300,7 @@ class ClientUser(BaseUser): id: :class:`int` The user's unique ID. discriminator: :class:`str` - The user's discriminator. This is given when the username has conflicts. + The user's discriminator. .. deprecated:: 2.9 This is being phased out by Discord; the username system is moving away from ``username#discriminator`` @@ -309,6 +309,8 @@ class ClientUser(BaseUser): See the :ddocs:`changelog ` for details. global_name: Optional[:class:`str`] The user's global display name, if set. + This takes precedence over :attr:`.name` when shown. + For bots, this is the application name. .. versionadded:: 2.9 @@ -433,7 +435,7 @@ class User(BaseUser, disnake.abc.Messageable): .. describe:: str(x) - Returns the user's name (with discriminator, if not migrated to new system yet). + Returns the user's username (with discriminator, if not migrated to new system yet), or global name if set. Attributes ---------- @@ -442,7 +444,7 @@ class User(BaseUser, disnake.abc.Messageable): id: :class:`int` The user's unique ID. discriminator: :class:`str` - The user's discriminator. This is given when the username has conflicts. + The user's discriminator. .. deprecated:: 2.9 This is being phased out by Discord; the username system is moving away from ``username#discriminator`` @@ -451,6 +453,8 @@ class User(BaseUser, disnake.abc.Messageable): See the :ddocs:`changelog ` for details. global_name: Optional[:class:`str`] The user's global display name, if set. + This takes precedence over :attr:`.name` when shown. + For bots, this is the application name. .. versionadded:: 2.9 diff --git a/disnake/widget.py b/disnake/widget.py index 3b133a6a8f..0f85bd5ba9 100644 --- a/disnake/widget.py +++ b/disnake/widget.py @@ -105,14 +105,14 @@ class WidgetMember(BaseUser): .. describe:: str(x) - Returns the widget member's `name#discriminator`. + Returns the widget member's name. Attributes ---------- id: :class:`int` The member's anonymized ID. name: :class:`str` - The member's nickname (if set in the guild) or username. + The member's nickname (if set in the guild), global name (if set) or username. discriminator: :class:`str` The member's anonymized discriminator. @@ -292,9 +292,8 @@ class Widget: .. note:: Due to a Discord limitation, if this data is available - the users will be "anonymized" with linear IDs and discriminator - information being incorrect. Likewise, the number of members - retrieved is capped. + the users will be "anonymized" with linear IDs. + Likewise, the number of members retrieved is capped. presence_count: :class:`int` The number of online members in the server. diff --git a/docs/api/events.rst b/docs/api/events.rst index 4727a69ef7..791202d535 100644 --- a/docs/api/events.rst +++ b/docs/api/events.rst @@ -866,6 +866,7 @@ Members - avatar - discriminator - name + - global_name - public_flags This requires :attr:`Intents.members` to be enabled. diff --git a/docs/ext/commands/commands.rst b/docs/ext/commands/commands.rst index fbc71b4ad3..c54f3b21ae 100644 --- a/docs/ext/commands/commands.rst +++ b/docs/ext/commands/commands.rst @@ -371,8 +371,8 @@ For example, to receive a :class:`Member` you can just pass it as a converter: await ctx.send(f'{member} joined on {member.joined_at}') When this command is executed, it attempts to convert the string given into a :class:`Member` and then passes it as a -parameter for the function. This works by checking if the string is a mention, an ID, a nickname, a username + discriminator, -or just a regular username. The default set of converters have been written to be as easy to use as possible. +parameter for the function. This works by checking if the string is a mention, an ID, a username + discriminator, +a nickname, a global name, or just a regular username. The default set of converters have been written to be as easy to use as possible. A lot of Discord models work out of the gate as a parameter: From c17260888f360318de8bd6096ce7c9bac1f56105 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 6 May 2023 21:44:51 +0200 Subject: [PATCH 11/25] fix: add `TeamMember.global_name` docs and repr --- disnake/team.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/disnake/team.py b/disnake/team.py index f0caf4d859..2806c014a4 100644 --- a/disnake/team.py +++ b/disnake/team.py @@ -102,6 +102,11 @@ class TeamMember(BaseUser): to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the :ddocs:`changelog ` for details. + global_name: Optional[:class:`str`] + The team members's global display name, if set. + This takes precedence over :attr:`.name` when shown. + + .. versionadded:: 2.9 avatar: Optional[:class:`str`] The avatar hash the team member has. Could be None. bot: :class:`bool` @@ -124,6 +129,6 @@ def __init__(self, team: Team, state: ConnectionState, data: TeamMemberPayload) def __repr__(self) -> str: return ( - f"<{self.__class__.__name__} id={self.id} name={self.name!r} " - f"discriminator={self.discriminator!r} membership_state={self.membership_state!r}>" + f"<{self.__class__.__name__} id={self.id} name={self.name!r} global_name={self.global_name!r}" + f" discriminator={self.discriminator!r} membership_state={self.membership_state!r}>" ) From a9acb1ef24b32cabd8685c797cef7ef19180fb28 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 6 May 2023 21:45:08 +0200 Subject: [PATCH 12/25] fix: include `global_name` in follower webhooks --- disnake/webhook/async_.py | 1 + 1 file changed, 1 insertion(+) diff --git a/disnake/webhook/async_.py b/disnake/webhook/async_.py index 1c64327415..edd9ec3dcd 100644 --- a/disnake/webhook/async_.py +++ b/disnake/webhook/async_.py @@ -1210,6 +1210,7 @@ def _as_follower(cls, data, *, channel, user) -> Webhook: "username": user.name, "discriminator": user.discriminator, "id": user.id, + "global_name": user.global_name, "avatar": user._avatar, }, } From 4269e8a4912da87f059aa90487ad8ecd853270c2 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sun, 7 May 2023 12:59:08 +0200 Subject: [PATCH 13/25] docs: fix broken reference --- disnake/ext/commands/converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disnake/ext/commands/converter.py b/disnake/ext/commands/converter.py index b39fe22c0f..a8172093f9 100644 --- a/disnake/ext/commands/converter.py +++ b/disnake/ext/commands/converter.py @@ -309,7 +309,7 @@ class UserConverter(IDConverter[disnake.User]): and it's not available in cache. .. versionchanged:: 2.9 - Now takes :attr:`.User.global_name` into account. + Now takes :attr:`~disnake.User.global_name` into account. """ async def convert(self, ctx: AnyContext, argument: str) -> disnake.User: From 56cbc15e6c207c53ee37d6b61bade7822305abe6 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Tue, 9 May 2023 19:38:47 +0200 Subject: [PATCH 14/25] fix(docs): it doesn't actually return the global name, oops --- disnake/member.py | 2 +- disnake/team.py | 2 +- disnake/user.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/disnake/member.py b/disnake/member.py index 0860909f70..183ab58065 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -230,7 +230,7 @@ class Member(disnake.abc.Messageable, _UserTag): .. describe:: str(x) - Returns the member's username (with discriminator, if not migrated to new system yet), or global name if set. + Returns the member's username (with discriminator, if not migrated to new system yet). Attributes ---------- diff --git a/disnake/team.py b/disnake/team.py index 2806c014a4..c94a56ef4e 100644 --- a/disnake/team.py +++ b/disnake/team.py @@ -84,7 +84,7 @@ class TeamMember(BaseUser): .. describe:: str(x) - Returns the team member's username (with discriminator, if not migrated to new system yet), or global name if set. + Returns the team member's username (with discriminator, if not migrated to new system yet). .. versionadded:: 1.3 diff --git a/disnake/user.py b/disnake/user.py index db4f1ec13f..07cdb473db 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -291,7 +291,7 @@ class ClientUser(BaseUser): .. describe:: str(x) - Returns the user's username (with discriminator, if not migrated to new system yet), or global name if set. + Returns the user's username (with discriminator, if not migrated to new system yet). Attributes ---------- @@ -435,7 +435,7 @@ class User(BaseUser, disnake.abc.Messageable): .. describe:: str(x) - Returns the user's username (with discriminator, if not migrated to new system yet), or global name if set. + Returns the user's username (with discriminator, if not migrated to new system yet). Attributes ---------- From f1000aac3e9277c744caf6da707b2d02b1ca5c50 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Tue, 9 May 2023 19:29:37 +0200 Subject: [PATCH 15/25] docs: add more `versionchanged` --- disnake/member.py | 3 +++ disnake/user.py | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/disnake/member.py b/disnake/member.py index 183ab58065..a474ad7b4b 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -585,6 +585,9 @@ def display_name(self) -> str: If they have a guild-specific :attr:`nickname <.nick>`, then that is returned. If not, this is their :attr:`global name <.global_name>` if set, or their :attr:`username <.name>` otherwise. + + .. versionchanged:: 2.9 + Added :attr:`.global_name`. """ return self.nick or self.global_name or self.name diff --git a/disnake/user.py b/disnake/user.py index 07cdb473db..f3a12f696f 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -152,7 +152,11 @@ def avatar(self) -> Optional[Asset]: @property def default_avatar(self) -> Asset: - """:class:`Asset`: Returns the default avatar for a given user.""" + """:class:`Asset`: Returns the default avatar for a given user. + + .. versionchanged:: 2.9 + Added handling for users migrated to the new username system without discriminators. + """ if self.discriminator == "0": num = self.id >> 22 else: @@ -250,6 +254,9 @@ def display_name(self) -> str: This is their :attr:`global name <.global_name>` if set, or their :attr:`username <.name>` otherwise. + + .. versionchanged:: 2.9 + Added :attr:`.global_name`. """ return self.global_name or self.name From 88b1e6e015238fc9298dce6da40105243a3c3f15 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Fri, 19 May 2023 00:06:47 +0200 Subject: [PATCH 16/25] docs: link to helpdesk article instead of not yet existent changelog entry --- changelog/1025.feature.rst | 2 +- disnake/abc.py | 2 +- disnake/member.py | 2 +- disnake/team.py | 2 +- disnake/user.py | 4 ++-- disnake/widget.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/changelog/1025.feature.rst b/changelog/1025.feature.rst index 81f116a12c..7a063ba158 100644 --- a/changelog/1025.feature.rst +++ b/changelog/1025.feature.rst @@ -1,4 +1,4 @@ -Add support for new username system - see the official :ddocs:`changelog ` for details. Existing functionality is kept backwards-compatible while the migration is still ongoing. +Add support for new username system - see the official `help article `__ for details. Existing functionality is kept backwards-compatible while the migration is still ongoing. - Add :attr:`User.global_name`, and update attributes/methods to account for it: - :attr:`User.display_name` and :attr:`Member.display_name` - :meth:`Guild.get_member_named` diff --git a/disnake/abc.py b/disnake/abc.py index cae6ef1395..6ba22bf2e1 100644 --- a/disnake/abc.py +++ b/disnake/abc.py @@ -137,7 +137,7 @@ class User(Snowflake, Protocol): This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. - See the :ddocs:`changelog ` for details. + See the `help article `__ for details. global_name: Optional[:class:`str`] The user's global display name, if set. This takes precedence over :attr:`.name` when shown. diff --git a/disnake/member.py b/disnake/member.py index a474ad7b4b..5fd7f4741d 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -501,7 +501,7 @@ def discriminator(self) -> str: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. - See the :ddocs:`changelog ` for details. + See the `help article `__ for details. """ return self._user.discriminator diff --git a/disnake/team.py b/disnake/team.py index c94a56ef4e..b713377540 100644 --- a/disnake/team.py +++ b/disnake/team.py @@ -101,7 +101,7 @@ class TeamMember(BaseUser): This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. - See the :ddocs:`changelog ` for details. + See the `help article `__ for details. global_name: Optional[:class:`str`] The team members's global display name, if set. This takes precedence over :attr:`.name` when shown. diff --git a/disnake/user.py b/disnake/user.py index f3a12f696f..9aac8d4025 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -313,7 +313,7 @@ class ClientUser(BaseUser): This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. - See the :ddocs:`changelog ` for details. + See the `help article `__ for details. global_name: Optional[:class:`str`] The user's global display name, if set. This takes precedence over :attr:`.name` when shown. @@ -457,7 +457,7 @@ class User(BaseUser, disnake.abc.Messageable): This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. - See the :ddocs:`changelog ` for details. + See the `help article `__ for details. global_name: Optional[:class:`str`] The user's global display name, if set. This takes precedence over :attr:`.name` when shown. diff --git a/disnake/widget.py b/disnake/widget.py index 0f85bd5ba9..4813bd011e 100644 --- a/disnake/widget.py +++ b/disnake/widget.py @@ -119,7 +119,7 @@ class WidgetMember(BaseUser): .. deprecated:: 2.9 This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. - See the :ddocs:`changelog ` for details. + See the `help article `__ for details. status: :class:`Status` The member's status. activity: Optional[Union[:class:`BaseActivity`, :class:`Spotify`]] From 83d24ff6aac2bcb77ca8982174ce4d63b02ee703 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Fri, 19 May 2023 00:19:39 +0200 Subject: [PATCH 17/25] docs: un-deprecate discrims for now --- disnake/abc.py | 2 +- disnake/member.py | 8 ++------ disnake/team.py | 2 +- disnake/user.py | 4 ++-- disnake/widget.py | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/disnake/abc.py b/disnake/abc.py index 6ba22bf2e1..aa22fd2010 100644 --- a/disnake/abc.py +++ b/disnake/abc.py @@ -133,7 +133,7 @@ class User(Snowflake, Protocol): discriminator: :class:`str` The user's discriminator. - .. deprecated:: 2.9 + .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. diff --git a/disnake/member.py b/disnake/member.py index 5fd7f4741d..25ca593197 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -486,18 +486,14 @@ def status(self, value: Status) -> None: @property def tag(self) -> str: - """:class:`str`: An alias of :attr:`.discriminator`. - - .. deprecated:: 2.9 - See :attr:`.discriminator`. - """ + """:class:`str`: An alias of :attr:`.discriminator`.""" return self._user.discriminator @property def discriminator(self) -> str: """:class:`str`:The user's discriminator. - .. deprecated:: 2.9 + .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. diff --git a/disnake/team.py b/disnake/team.py index b713377540..56753ca896 100644 --- a/disnake/team.py +++ b/disnake/team.py @@ -97,7 +97,7 @@ class TeamMember(BaseUser): discriminator: :class:`str` The team member's discriminator. - .. deprecated:: 2.9 + .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. diff --git a/disnake/user.py b/disnake/user.py index 9aac8d4025..dbbb0396a2 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -309,7 +309,7 @@ class ClientUser(BaseUser): discriminator: :class:`str` The user's discriminator. - .. deprecated:: 2.9 + .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. @@ -453,7 +453,7 @@ class User(BaseUser, disnake.abc.Messageable): discriminator: :class:`str` The user's discriminator. - .. deprecated:: 2.9 + .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. diff --git a/disnake/widget.py b/disnake/widget.py index 4813bd011e..1608891254 100644 --- a/disnake/widget.py +++ b/disnake/widget.py @@ -116,7 +116,7 @@ class WidgetMember(BaseUser): discriminator: :class:`str` The member's anonymized discriminator. - .. deprecated:: 2.9 + .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` to users having a globally unique ``@username``. See the `help article `__ for details. From df7904639e29cf82d0adc34f5d3376dcab637444 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 20 May 2023 22:44:10 +0200 Subject: [PATCH 18/25] feat: support `username#0` in lookup methods --- disnake/ext/commands/converter.py | 59 ++++++++++++++----------------- disnake/guild.py | 20 ++++------- 2 files changed, 33 insertions(+), 46 deletions(-) diff --git a/disnake/ext/commands/converter.py b/disnake/ext/commands/converter.py index a8172093f9..bb3efe4d2e 100644 --- a/disnake/ext/commands/converter.py +++ b/disnake/ext/commands/converter.py @@ -186,12 +186,13 @@ class MemberConverter(IDConverter[disnake.Member]): The lookup strategy is as follows (in order): - 1. Lookup by ID. - 2. Lookup by mention. - 3. Lookup by name#discrim. - 4. Lookup by nickname. - 5. Lookup by global name. - 6. Lookup by name. + 1. Lookup by ID + 2. Lookup by mention + 3. Lookup by username#discrim + 4. Lookup by username#0 + 5. Lookup by nickname + 6. Lookup by global name + 7. Lookup by username The name resolution order matches the one used by :meth:`.Guild.get_member_named`. @@ -203,17 +204,18 @@ class MemberConverter(IDConverter[disnake.Member]): optionally caching the result if :attr:`.MemberCacheFlags.joined` is enabled. .. versionchanged:: 2.9 - Name resolution order changed from ``name > nick`` to ``nick > global_name > name`` - to account for the username migration. + Name resolution order changed from ``username > nick`` to + ``nick > global_name > username`` to account for the username migration. """ async def query_member_named( self, guild: disnake.Guild, argument: str ) -> Optional[disnake.Member]: cache = guild._state.member_cache_flags.joined - if len(argument) > 5 and argument[-5] == "#": - # legacy behavior for non-migrated users - username, _, discriminator = argument.rpartition("#") + + username, _, discriminator = argument.rpartition("#") + if discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()): + # legacy behavior members = await guild.query_members(username, limit=100, cache=cache) return _utils_get(members, name=username, discriminator=discriminator) else: @@ -295,11 +297,12 @@ class UserConverter(IDConverter[disnake.User]): The lookup strategy is as follows (in order): - 1. Lookup by ID. - 2. Lookup by mention. - 3. Lookup by name#discrim. - 4. Lookup by global name. - 5. Lookup by name. + 1. Lookup by ID + 2. Lookup by mention + 3. Lookup by username#discrim + 4. Lookup by username#0 + 5. Lookup by global name + 6. Lookup by username .. versionchanged:: 1.5 Raise :exc:`.UserNotFound` instead of generic :exc:`.BadArgument` @@ -310,6 +313,7 @@ class UserConverter(IDConverter[disnake.User]): .. versionchanged:: 2.9 Now takes :attr:`~disnake.User.global_name` into account. + No longer automatically removes ``"@"`` prefix from arguments. """ async def convert(self, ctx: AnyContext, argument: str) -> disnake.User: @@ -338,26 +342,17 @@ async def convert(self, ctx: AnyContext, argument: str) -> disnake.User: return result._user return result - arg = argument - - # Remove the '@' character if this is the first character from the argument - if arg[0] == "@": - # Remove first character - arg = arg[1:] - - # check for discriminator if it exists, - if len(arg) > 5 and arg[-5] == "#": - # legacy behavior for non-migrated users - discrim = arg[-4:] - name = arg[:-5] - result = disnake.utils.find( - lambda u: u.name == name and u.discriminator == discrim, state._users.values() - ) + username, _, discriminator = argument.rpartition("#") + # n.b. there's no builtin method that only matches arabic digits, `isdecimal` is the closest one. + # it really doesn't matter much, worst case is unnecessary computations + if discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()): + # legacy behavior + result = _utils_get(state._users.values(), name=username, discriminator=discriminator) if result is not None: return result result = disnake.utils.find( - lambda u: u.global_name == arg or u.name == arg, + lambda u: u.global_name == argument or u.name == argument, state._users.values(), ) diff --git a/disnake/guild.py b/disnake/guild.py index b64ab190be..7e9ff911cc 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -1121,7 +1121,7 @@ def get_member_named(self, name: str, /) -> Optional[Member]: 1. Lookup by nickname. 2. Lookup by global name. - 3. Lookup by name. + 3. Lookup by username. While the migration away from discriminators is still ongoing, the name can have an optional discriminator argument, e.g. "Jake#0001", @@ -1144,25 +1144,17 @@ def get_member_named(self, name: str, /) -> Optional[Member]: The member in this guild with the associated name. If not found then ``None`` is returned. """ - members = self.members - - # legacy behavior for non-migrated users - if len(name) > 5 and name[-5] == "#": - # The 5 length is checking to see if #0000 is in the string, - # as a#0000 has a length of 6, the minimum for a potential - # discriminator lookup. - potential_discriminator = name[-4:] - - # do the actual lookup and return if found - # if it isn't found then we'll do a full name lookup below. - result = utils.get(members, name=name[:-5], discriminator=potential_discriminator) + username, _, discriminator = name.rpartition("#") + if discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()): + # legacy behavior + result = utils.get(self._members.values(), name=username, discriminator=discriminator) if result is not None: return result def pred(m: Member) -> bool: return m.nick == name or m.global_name == name or m.name == name - return utils.find(pred, members) + return utils.find(pred, self._members.values()) def _create_channel( self, From c8d8d8d753700a174fdf303cadc3441846367b10 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 20 May 2023 23:00:19 +0200 Subject: [PATCH 19/25] fix: check for separator --- disnake/ext/commands/converter.py | 12 ++++++++---- disnake/guild.py | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/disnake/ext/commands/converter.py b/disnake/ext/commands/converter.py index bb3efe4d2e..3962e9d723 100644 --- a/disnake/ext/commands/converter.py +++ b/disnake/ext/commands/converter.py @@ -213,8 +213,10 @@ async def query_member_named( ) -> Optional[disnake.Member]: cache = guild._state.member_cache_flags.joined - username, _, discriminator = argument.rpartition("#") - if discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()): + username, sep, discriminator = argument.rpartition("#") + if sep and ( + discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()) + ): # legacy behavior members = await guild.query_members(username, limit=100, cache=cache) return _utils_get(members, name=username, discriminator=discriminator) @@ -342,10 +344,12 @@ async def convert(self, ctx: AnyContext, argument: str) -> disnake.User: return result._user return result - username, _, discriminator = argument.rpartition("#") + username, sep, discriminator = argument.rpartition("#") # n.b. there's no builtin method that only matches arabic digits, `isdecimal` is the closest one. # it really doesn't matter much, worst case is unnecessary computations - if discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()): + if sep and ( + discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()) + ): # legacy behavior result = _utils_get(state._users.values(), name=username, discriminator=discriminator) if result is not None: diff --git a/disnake/guild.py b/disnake/guild.py index 7e9ff911cc..6e3cf92bb7 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -1144,8 +1144,10 @@ def get_member_named(self, name: str, /) -> Optional[Member]: The member in this guild with the associated name. If not found then ``None`` is returned. """ - username, _, discriminator = name.rpartition("#") - if discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()): + username, sep, discriminator = name.rpartition("#") + if sep and ( + discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()) + ): # legacy behavior result = utils.get(self._members.values(), name=username, discriminator=discriminator) if result is not None: From 7a9392ea401b54b17573793db6088404604fa48e Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 20 May 2023 23:17:18 +0200 Subject: [PATCH 20/25] chore: update widget name stuff --- disnake/widget.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/disnake/widget.py b/disnake/widget.py index 1608891254..b0bee70eb4 100644 --- a/disnake/widget.py +++ b/disnake/widget.py @@ -112,7 +112,7 @@ class WidgetMember(BaseUser): id: :class:`int` The member's anonymized ID. name: :class:`str` - The member's nickname (if set in the guild), global name (if set) or username. + The member's name. discriminator: :class:`str` The member's anonymized discriminator. @@ -179,7 +179,6 @@ def avatar(self) -> Optional[Asset]: return Asset(self._state, url=url, key=url, animated=False) return None - # TODO: it's not yet known if widget members include global_name or if it's already handled through `name` @property def display_name(self) -> str: """:class:`str`: Returns the member's name.""" From c7f05b032d8dbc16bc15287d36f486b07eea529d Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 20 May 2023 23:19:21 +0200 Subject: [PATCH 21/25] docs: no `@` prefix --- disnake/abc.py | 2 +- disnake/member.py | 2 +- disnake/team.py | 2 +- disnake/user.py | 5 ++--- disnake/widget.py | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/disnake/abc.py b/disnake/abc.py index aa22fd2010..27b400c2ba 100644 --- a/disnake/abc.py +++ b/disnake/abc.py @@ -135,7 +135,7 @@ class User(Snowflake, Protocol): .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` - to users having a globally unique ``@username``. + to users having a globally unique username. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the `help article `__ for details. global_name: Optional[:class:`str`] diff --git a/disnake/member.py b/disnake/member.py index 25ca593197..96db64f907 100644 --- a/disnake/member.py +++ b/disnake/member.py @@ -495,7 +495,7 @@ def discriminator(self) -> str: .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` - to users having a globally unique ``@username``. + to users having a globally unique username. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the `help article `__ for details. """ diff --git a/disnake/team.py b/disnake/team.py index 56753ca896..1dd758f26d 100644 --- a/disnake/team.py +++ b/disnake/team.py @@ -99,7 +99,7 @@ class TeamMember(BaseUser): .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` - to users having a globally unique ``@username``. + to users having a globally unique username. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the `help article `__ for details. global_name: Optional[:class:`str`] diff --git a/disnake/user.py b/disnake/user.py index dbbb0396a2..22fd6c4877 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -80,7 +80,6 @@ def __repr__(self) -> str: def __str__(self) -> str: discriminator = self.discriminator if discriminator == "0": - # TODO: add "@" prefix? return self.name # legacy behavior return f"{self.name}#{discriminator}" @@ -311,7 +310,7 @@ class ClientUser(BaseUser): .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` - to users having a globally unique ``@username``. + to users having a globally unique username. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the `help article `__ for details. global_name: Optional[:class:`str`] @@ -455,7 +454,7 @@ class User(BaseUser, disnake.abc.Messageable): .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` - to users having a globally unique ``@username``. + to users having a globally unique username. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the `help article `__ for details. global_name: Optional[:class:`str`] diff --git a/disnake/widget.py b/disnake/widget.py index b0bee70eb4..d5056d82dc 100644 --- a/disnake/widget.py +++ b/disnake/widget.py @@ -118,7 +118,7 @@ class WidgetMember(BaseUser): .. note:: This is being phased out by Discord; the username system is moving away from ``username#discriminator`` - to users having a globally unique ``@username``. + to users having a globally unique username. See the `help article `__ for details. status: :class:`Status` The member's status. From bdc4a12732eb79cbd0651cfedc9bf991289b3f27 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sat, 20 May 2023 23:59:19 +0200 Subject: [PATCH 22/25] docs: improve gw member query docs --- disnake/guild.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/disnake/guild.py b/disnake/guild.py index 6e3cf92bb7..3296630028 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -4123,7 +4123,7 @@ async def query_members( ) -> List[Member]: """|coro| - Request members that belong to this guild whose username starts with + Request members that belong to this guild whose name starts with the query given. This is a websocket operation and can be slow. @@ -4135,7 +4135,7 @@ async def query_members( Parameters ---------- query: Optional[:class:`str`] - The string that the username's start with. + The string that the names start with. limit: :class:`int` The maximum number of members to send back. This must be a number between 5 and 100. @@ -4198,7 +4198,7 @@ async def search_members( ): """|coro| - Retrieves members that belong to this guild whose username or nickname starts with + Retrieves members that belong to this guild whose name starts with the query given. Note that unlike :func:`query_members`, this is not a websocket operation, but an HTTP operation. @@ -4210,7 +4210,7 @@ async def search_members( Parameters ---------- query: :class:`str` - The string that the usernames or nicknames start with. + The string that the names start with. limit: :class:`int` The maximum number of members to send back. This must be a number between 1 and 1000. From 841578c0f2d83e58450943ca383a5e37a2abd4f4 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sun, 21 May 2023 16:26:34 +0200 Subject: [PATCH 23/25] fix: check for username instead of separator --- disnake/ext/commands/converter.py | 8 ++++---- disnake/guild.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/disnake/ext/commands/converter.py b/disnake/ext/commands/converter.py index 3962e9d723..26b93ef909 100644 --- a/disnake/ext/commands/converter.py +++ b/disnake/ext/commands/converter.py @@ -213,8 +213,8 @@ async def query_member_named( ) -> Optional[disnake.Member]: cache = guild._state.member_cache_flags.joined - username, sep, discriminator = argument.rpartition("#") - if sep and ( + username, _, discriminator = argument.rpartition("#") + if username and ( discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()) ): # legacy behavior @@ -344,10 +344,10 @@ async def convert(self, ctx: AnyContext, argument: str) -> disnake.User: return result._user return result - username, sep, discriminator = argument.rpartition("#") + username, _, discriminator = argument.rpartition("#") # n.b. there's no builtin method that only matches arabic digits, `isdecimal` is the closest one. # it really doesn't matter much, worst case is unnecessary computations - if sep and ( + if username and ( discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()) ): # legacy behavior diff --git a/disnake/guild.py b/disnake/guild.py index 3296630028..400b522548 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -1144,8 +1144,8 @@ def get_member_named(self, name: str, /) -> Optional[Member]: The member in this guild with the associated name. If not found then ``None`` is returned. """ - username, sep, discriminator = name.rpartition("#") - if sep and ( + username, _, discriminator = name.rpartition("#") + if username and ( discriminator == "0" or (len(discriminator) == 4 and discriminator.isdecimal()) ): # legacy behavior From 2407171a3899c9b3f28ca08e27388a24e5d8be68 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sun, 21 May 2023 16:26:54 +0200 Subject: [PATCH 24/25] chore: remove todo --- disnake/ext/commands/converter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/disnake/ext/commands/converter.py b/disnake/ext/commands/converter.py index 26b93ef909..6ce1be35ab 100644 --- a/disnake/ext/commands/converter.py +++ b/disnake/ext/commands/converter.py @@ -1049,7 +1049,6 @@ def resolve_user(id: int) -> str: m = (msg and _utils_get(msg.mentions, id=id)) or bot.get_user(id) if m is None and ctx.guild: m = ctx.guild.get_member(id) - # TODO: add a separate option for `global_name`s? return f"@{m.display_name if self.use_nicknames else m.name}" if m else "@deleted-user" def resolve_role(id: int) -> str: From de1d328f3cf92d9c51af73a81ea804272acab754 Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sun, 21 May 2023 19:05:28 +0200 Subject: [PATCH 25/25] chore(docs): spacing --- disnake/abc.py | 2 ++ disnake/team.py | 2 ++ disnake/user.py | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/disnake/abc.py b/disnake/abc.py index 27b400c2ba..54066623bb 100644 --- a/disnake/abc.py +++ b/disnake/abc.py @@ -138,6 +138,7 @@ class User(Snowflake, Protocol): to users having a globally unique username. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the `help article `__ for details. + global_name: Optional[:class:`str`] The user's global display name, if set. This takes precedence over :attr:`.name` when shown. @@ -145,6 +146,7 @@ class User(Snowflake, Protocol): For bots, this is the application name. .. versionadded:: 2.9 + avatar: :class:`~disnake.Asset` The avatar asset the user has. bot: :class:`bool` diff --git a/disnake/team.py b/disnake/team.py index 1dd758f26d..6465128ffe 100644 --- a/disnake/team.py +++ b/disnake/team.py @@ -102,11 +102,13 @@ class TeamMember(BaseUser): to users having a globally unique username. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the `help article `__ for details. + global_name: Optional[:class:`str`] The team members's global display name, if set. This takes precedence over :attr:`.name` when shown. .. versionadded:: 2.9 + avatar: Optional[:class:`str`] The avatar hash the team member has. Could be None. bot: :class:`bool` diff --git a/disnake/user.py b/disnake/user.py index 22fd6c4877..8f9333b9cd 100644 --- a/disnake/user.py +++ b/disnake/user.py @@ -313,6 +313,7 @@ class ClientUser(BaseUser): to users having a globally unique username. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the `help article `__ for details. + global_name: Optional[:class:`str`] The user's global display name, if set. This takes precedence over :attr:`.name` when shown. @@ -320,6 +321,7 @@ class ClientUser(BaseUser): For bots, this is the application name. .. versionadded:: 2.9 + bot: :class:`bool` Specifies if the user is a bot account. system: :class:`bool` @@ -457,6 +459,7 @@ class User(BaseUser, disnake.abc.Messageable): to users having a globally unique username. The value of a single zero (``"0"``) indicates that the user has been migrated to the new system. See the `help article `__ for details. + global_name: Optional[:class:`str`] The user's global display name, if set. This takes precedence over :attr:`.name` when shown. @@ -464,6 +467,7 @@ class User(BaseUser, disnake.abc.Messageable): For bots, this is the application name. .. versionadded:: 2.9 + bot: :class:`bool` Specifies if the user is a bot account. system: :class:`bool`