Skip to content

Commit

Permalink
Add new usernames (#1631)
Browse files Browse the repository at this point in the history
Signed-off-by: Leo Developer <[email protected]>
Signed-off-by: davfsa <[email protected]>
Co-authored-by: davfsa <[email protected]>
  • Loading branch information
Le0Developer and davfsa authored Jun 7, 2023
1 parent ded4833 commit cf28ba9
Show file tree
Hide file tree
Showing 14 changed files with 90 additions and 8 deletions.
1 change: 1 addition & 0 deletions changes/1631.deprecation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecate `User.discriminator`
4 changes: 4 additions & 0 deletions changes/1631.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Implement username changes:
- Add `global_name`
- `User.__str__()` respects `global_name` now
- `User.default_avatar_url` returns correct URL for migrated accounts
4 changes: 4 additions & 0 deletions hikari/applications.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,10 @@ def mention(self) -> str:
def username(self) -> str:
return self.user.username

@property
def global_name(self) -> typing.Optional[str]:
return self.user.global_name

def __str__(self) -> str:
return str(self.user)

Expand Down
10 changes: 8 additions & 2 deletions hikari/guilds.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,14 +455,16 @@ def display_name(self) -> str:
"""Return the member's display name.
If the member has a nickname, this will return that nickname.
Otherwise, it will return the username instead.
If the user has a global name, this will return that global name.
If the user has neither, the username will be returned instead.
See Also
--------
Nickname: `Member.nickname`.
Username: `Member.username`.
Globalname: `Member.global_name`.
"""
return self.nickname if isinstance(self.nickname, str) else self.username
return self.nickname or self.global_name or self.username

@property
def flags(self) -> users.UserFlag:
Expand Down Expand Up @@ -572,6 +574,10 @@ def get_top_role(self) -> typing.Optional[Role]:
def username(self) -> str:
return self.user.username

@property
def global_name(self) -> typing.Optional[str]:
return self.user.global_name

def make_avatar_url(self, *, ext: typing.Optional[str] = None, size: int = 4096) -> typing.Optional[files.URL]:
return self.user.make_avatar_url(ext=ext, size=size)

Expand Down
4 changes: 4 additions & 0 deletions hikari/impl/entity_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ class _UserFields:
id: snowflakes.Snowflake = attrs.field()
discriminator: str = attrs.field()
username: str = attrs.field()
global_name: typing.Optional[str] = attrs.field()
avatar_hash: str = attrs.field()
banner_hash: typing.Optional[str] = attrs.field()
accent_color: typing.Optional[color_models.Color] = attrs.field()
Expand Down Expand Up @@ -3519,6 +3520,7 @@ def _set_user_attrsibutes(payload: data_binding.JSONObject) -> _UserFields:
id=snowflakes.Snowflake(payload["id"]),
discriminator=payload["discriminator"],
username=payload["username"],
global_name=payload.get("global_name"),
avatar_hash=payload["avatar"],
banner_hash=payload.get("banner", None),
accent_color=color_models.Color(accent_color) if accent_color is not None else None,
Expand All @@ -3536,6 +3538,7 @@ def deserialize_user(self, payload: data_binding.JSONObject) -> user_models.User
id=user_fields.id,
discriminator=user_fields.discriminator,
username=user_fields.username,
global_name=payload.get("global_name"),
avatar_hash=user_fields.avatar_hash,
banner_hash=user_fields.banner_hash,
accent_color=user_fields.accent_color,
Expand All @@ -3551,6 +3554,7 @@ def deserialize_my_user(self, payload: data_binding.JSONObject) -> user_models.O
id=user_fields.id,
discriminator=user_fields.discriminator,
username=user_fields.username,
global_name=payload.get("global_name"),
avatar_hash=user_fields.avatar_hash,
banner_hash=user_fields.banner_hash,
accent_color=user_fields.accent_color,
Expand Down
1 change: 1 addition & 0 deletions hikari/impl/event_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ def deserialize_presence_update_event(
id=snowflakes.Snowflake(user_payload["id"]),
discriminator=discriminator,
username=user_payload.get("username", undefined.UNDEFINED),
global_name=user_payload.get("global_name", undefined.UNDEFINED),
avatar_hash=user_payload.get("avatar", undefined.UNDEFINED),
banner_hash=user_payload.get("banner", undefined.UNDEFINED),
accent_color=accent_color,
Expand Down
6 changes: 5 additions & 1 deletion hikari/impl/shard.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,10 +725,14 @@ async def _poll_events(self) -> None:
user_pl = data["user"]
self._user_id = snowflakes.Snowflake(user_pl["id"])

# TODO: Remove when rollout finishes
user = user_pl["username"] + (
"#" + user_pl["discriminator"] if user_pl["discriminator"] != "0" else ""
)
self._logger.info(
"shard is ready: %s guilds, %s (%s), session %r on v%s gateway",
len(data["guilds"]),
f"{user_pl['username']}#{user_pl['discriminator']}",
user,
self._user_id,
self._session_id,
data["v"],
Expand Down
55 changes: 51 additions & 4 deletions hikari/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,24 @@ def accent_colour(self) -> undefined.UndefinedNoneOr[colors.Color]:
@property
@abc.abstractmethod
def discriminator(self) -> undefined.UndefinedOr[str]:
"""Discriminator for the user."""
"""Discriminator for the user.
.. deprecation:: 2.0.0.dev120
Discriminators are deprecated and being replaced with "0" by Discord
during username migration. This field will be removed after migration is complete.
Learn more here: https://dis.gd/usernames
"""

@property
@abc.abstractmethod
def username(self) -> undefined.UndefinedOr[str]:
"""Username for the user."""

@property
@abc.abstractmethod
def global_name(self) -> undefined.UndefinedNoneOr[str]:
"""Global name for the user, if they have one, otherwise `None`."""

@property
@abc.abstractmethod
def is_bot(self) -> undefined.UndefinedOr[bool]:
Expand Down Expand Up @@ -484,6 +495,11 @@ def banner_url(self) -> typing.Optional[files.URL]:
@property
def default_avatar_url(self) -> files.URL:
"""Default avatar URL for this user."""
if self.discriminator == "0": # migrated account
return routes.CDN_DEFAULT_USER_AVATAR.compile_to_file(
urls.CDN_URL, discriminator=self.id.created_at.timestamp() % 6, file_format="png"
)

return routes.CDN_DEFAULT_USER_AVATAR.compile_to_file(
urls.CDN_URL, discriminator=int(self.discriminator) % 5, file_format="png"
)
Expand All @@ -496,7 +512,13 @@ def display_avatar_url(self) -> files.URL:
@property
@abc.abstractmethod
def discriminator(self) -> str:
"""Discriminator for the user."""
"""Discriminator for the user.
.. deprecation:: 2.0.0.dev120
Discriminators are deprecated and being replaced with "0" by Discord
during username migration. This field will be removed after migration is complete.
Learn more here: https://dis.gd/usernames
"""

@property
@abc.abstractmethod
Expand Down Expand Up @@ -531,6 +553,11 @@ def mention(self) -> str:
def username(self) -> str:
"""Username for the user."""

@property
@abc.abstractmethod
def global_name(self) -> typing.Optional[str]:
"""Global name for the user, if they have one, otherwise `None`."""

def make_avatar_url(self, *, ext: typing.Optional[str] = None, size: int = 4096) -> typing.Optional[files.URL]:
"""Generate the avatar URL for this user, if set.
Expand Down Expand Up @@ -636,11 +663,20 @@ class PartialUserImpl(PartialUser):
"""Client application that models may use for procedures."""

discriminator: undefined.UndefinedOr[str] = attrs.field(eq=False, hash=False, repr=True)
"""Four-digit discriminator for the user."""
"""Four-digit discriminator for the user if unmigrated.
.. deprecation:: 2.0.0.dev120
Discriminators are deprecated and being replaced with "0" by Discord
during username migration. This field will be removed after migration is complete.
Learn more here: https://dis.gd/usernames
"""

username: undefined.UndefinedOr[str] = attrs.field(eq=False, hash=False, repr=True)
"""Username of the user."""

global_name: undefined.UndefinedNoneOr[str] = attrs.field(eq=False, hash=False, repr=True)
"""Global name of the user."""

avatar_hash: undefined.UndefinedNoneOr[str] = attrs.field(eq=False, hash=False, repr=False)
"""Avatar hash of the user, if a custom avatar is set."""

Expand Down Expand Up @@ -678,6 +714,8 @@ def mention(self) -> str:
def __str__(self) -> str:
if self.username is undefined.UNDEFINED or self.discriminator is undefined.UNDEFINED:
return f"Partial user ID {self.id}"
elif self.discriminator == "0": # migrated account
return self.username
return f"{self.username}#{self.discriminator}"


Expand All @@ -686,11 +724,20 @@ class UserImpl(PartialUserImpl, User):
"""Concrete implementation of user information."""

discriminator: str = attrs.field(eq=False, hash=False, repr=True)
"""The user's discriminator."""
"""The user's discriminator.
.. deprecation:: 2.0.0.dev120
Discriminators are deprecated and being replaced with "0" by Discord
during username migration. This field will be removed after migration is complete.
Learn more here: https://dis.gd/usernames
"""

username: str = attrs.field(eq=False, hash=False, repr=True)
"""The user's username."""

global_name: typing.Optional[str] = attrs.field(eq=False, hash=False, repr=True)
"""The user's global name."""

avatar_hash: typing.Optional[str] = attrs.field(eq=False, hash=False, repr=False)
"""The user's avatar hash, if they have one, otherwise `None`."""

Expand Down
2 changes: 2 additions & 0 deletions scripts/gateway_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

me_user_id = "1234567890"
me_username = "nekokatt"
me_global_name = None
me_discriminator = "6945"
me_avatar = None
me_bot = True
Expand Down Expand Up @@ -71,6 +72,7 @@ def v8_get_my_user(_):
body = {
"id": me_user_id,
"username": me_username,
"global_name": me_global_name,
"discriminator": me_discriminator,
"avatar": me_avatar,
"bot": me_bot,
Expand Down
3 changes: 3 additions & 0 deletions tests/hikari/impl/test_entity_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -6692,6 +6692,7 @@ def my_user_payload(self):
return {
"id": "379953393319542784",
"username": "qt pi",
"global_name": "blahaj",
"avatar": "820d0e50543216e812ad94e6ab7",
"banner": "a_221313e1e2edsncsncsmcndsc",
"accent_color": 231321,
Expand All @@ -6712,6 +6713,7 @@ def test_deserialize_my_user(self, entity_factory_impl, mock_app, my_user_payloa
assert my_user.app is mock_app
assert my_user.id == 379953393319542784
assert my_user.username == "qt pi"
assert my_user.global_name == "blahaj"
assert my_user.avatar_hash == "820d0e50543216e812ad94e6ab7"
assert my_user.banner_hash == "a_221313e1e2edsncsncsmcndsc"
assert my_user.accent_color == 231321
Expand Down Expand Up @@ -6740,6 +6742,7 @@ def test_deserialize_my_user_with_unset_fields(self, entity_factory_impl, mock_a
"premium_type": 1,
}
)
assert my_user.global_name is None
assert my_user.app is mock_app
assert my_user.banner_hash is None
assert my_user.accent_color is None
Expand Down
3 changes: 3 additions & 0 deletions tests/hikari/impl/test_event_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ def test_deserialize_presence_update_event_with_full_user_object(self, event_fac
"user": {
"id": "1231312",
"username": "OK",
"global_name": "blahaj",
"avatar": "NOK",
"banner": "12122hssjamanmdd",
"accent_color": 12342,
Expand All @@ -674,6 +675,7 @@ def test_deserialize_presence_update_event_with_full_user_object(self, event_fac
assert isinstance(event.user, user_models.PartialUser)
assert event.user.id == 1231312
assert event.user.username == "OK"
assert event.user.global_name == "blahaj"
assert event.user.discriminator == "1231"
assert event.user.avatar_hash == "NOK"
assert event.user.banner_hash == "12122hssjamanmdd"
Expand Down Expand Up @@ -701,6 +703,7 @@ def test_deserialize_presence_update_event_with_partial_user_object(self, event_
assert isinstance(event.user, user_models.PartialUser)
assert event.user.id == 1231312
assert event.user.username is undefined.UNDEFINED
assert event.user.global_name is undefined.UNDEFINED
assert event.user.discriminator is undefined.UNDEFINED
assert event.user.avatar_hash is undefined.UNDEFINED
assert event.user.banner_hash is undefined.UNDEFINED
Expand Down
1 change: 1 addition & 0 deletions tests/hikari/integration/test_users_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def make_user(user_id, username):
id=snowflakes.Snowflake(user_id),
discriminator="0001",
username=username,
global_name=None,
avatar_hash=None,
banner_hash=None,
accent_color=None,
Expand Down
2 changes: 1 addition & 1 deletion tests/hikari/test_guilds.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ def test_display_name_property_when_nickname(self, model):

def test_display_name_property_when_no_nickname(self, model, mock_user):
model.nickname = None
assert model.display_name is mock_user.username
assert model.display_name is mock_user.global_name

def test_mention_property(self, model, mock_user):
assert model.mention == mock_user.mention
Expand Down
2 changes: 2 additions & 0 deletions tests/hikari/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ def obj(self):
app=mock.Mock(),
discriminator="8637",
username="thomm.o",
global_name=None,
avatar_hash=None,
banner_hash=None,
accent_color=None,
Expand Down Expand Up @@ -348,6 +349,7 @@ def obj(self):
app=mock.Mock(),
discriminator="1234",
username="foobar",
global_name=None,
avatar_hash="69420",
banner_hash="42069",
accent_color=123456,
Expand Down

0 comments on commit cf28ba9

Please sign in to comment.