Repository: hikari-py/hikari
Pull request #975 (2022-01-22 02:56:02)

Add display_avatar_url property for Member & User
### Summary
It is somewhat tedious to get an `avatar_url` that is guaranteed to always exist, this property aims to rectify this issue, and simplify getting avatars somewhat.

### Checklist
- [x] I have run `nox` and all the pipelines have passed.
- [ ] I have made unittests according to the code I have added/modified/deleted.


HyperGH: > Thank you for the PR. Could you please add tests and the change log fragment file for this change?
> 
> If you have any questions on how to do that, please check CONTRIBUTING.md or ask us :)

Done!

Patch:
diff --git a/changes/975.feature.md b/changes/975.feature.md
new file mode 100644
index 00000000..5850bb9d
--- /dev/null
+++ b/changes/975.feature.md
@@ -0,0 +1,1 @@
+Add display_avatar_url property to hikari.Member and hikari.User
diff --git a/changes/982.bugfix.md b/changes/982.bugfix.md
new file mode 100644
index 00000000..387d2650
--- /dev/null
+++ b/changes/982.bugfix.md
@@ -0,0 +1,1 @@
+Fix magic methods for `UserImpl` and its subclasses.
diff --git a/changes/984.feature.md b/changes/984.feature.md
new file mode 100644
index 00000000..3136f576
--- /dev/null
+++ b/changes/984.feature.md
@@ -0,0 +1,1 @@
+old_x keyword arguments in the event factory now default to None.
diff --git a/hikari/api/event_factory.py b/hikari/api/event_factory.py
index 22b9f634..adb543c6 100644
--- a/hikari/api/event_factory.py
+++ b/hikari/api/event_factory.py
@@ -89,7 +89,7 @@ def deserialize_guild_channel_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_channel: typing.Optional[channel_models.GuildChannel],
+        old_channel: typing.Optional[channel_models.GuildChannel] = None,
     ) -> channel_events.GuildChannelUpdateEvent:
         """Parse a raw payload from Discord into a channel update event object.
 
@@ -99,6 +99,9 @@ def deserialize_guild_channel_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_channel : typing.Optional[hikari.channels.GuildChannel]
             The guild channel object or `builtins.None`.
 
@@ -190,7 +193,7 @@ def deserialize_invite_delete_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_invite: typing.Optional[invite_models.InviteWithMetadata],
+        old_invite: typing.Optional[invite_models.InviteWithMetadata] = None,
     ) -> channel_events.InviteDeleteEvent:
         """Parse a raw payload from Discord into an invite delete event object.
 
@@ -200,6 +203,9 @@ def deserialize_invite_delete_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_invite: typing.Optional[hikari.invites.InviteWithMetadata]
             The invite object or `builtins.None`.
 
@@ -280,7 +286,7 @@ def deserialize_guild_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_guild: typing.Optional[guild_models.GatewayGuild],
+        old_guild: typing.Optional[guild_models.GatewayGuild] = None,
     ) -> guild_events.GuildUpdateEvent:
         """Parse a raw payload from Discord into a guild update event object.
 
@@ -290,6 +296,9 @@ def deserialize_guild_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_guild : typing.Optional[hikari.guilds.GatewayGuild]
             The guild object or `builtins.None`.
 
@@ -305,7 +314,7 @@ def deserialize_guild_leave_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_guild: typing.Optional[guild_models.GatewayGuild],
+        old_guild: typing.Optional[guild_models.GatewayGuild] = None,
     ) -> guild_events.GuildLeaveEvent:
         """Parse a raw payload from Discord into a guild leave event object.
 
@@ -315,6 +324,9 @@ def deserialize_guild_leave_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_guild : typing.Optional[hikari.guilds.GatewayGuild]
             The guild object or `builtins.None`.
 
@@ -387,7 +399,7 @@ def deserialize_guild_emojis_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_emojis: typing.Optional[typing.Sequence[emojis_models.KnownCustomEmoji]],
+        old_emojis: typing.Optional[typing.Sequence[emojis_models.KnownCustomEmoji]] = None,
     ) -> guild_events.EmojisUpdateEvent:
         """Parse a raw payload from Discord into a guild emojis update event object.
 
@@ -397,6 +409,9 @@ def deserialize_guild_emojis_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_emojis : typing.Optional[typing.Sequence[hikari.emojis.KnownCustomEmoji]]
             The sequence of emojis or `builtins.None`.
 
@@ -469,7 +484,7 @@ def deserialize_presence_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_presence: typing.Optional[presences_models.MemberPresence],
+        old_presence: typing.Optional[presences_models.MemberPresence] = None,
     ) -> guild_events.PresenceUpdateEvent:
         """Parse a raw payload from Discord into a presence update event object.
 
@@ -479,6 +494,9 @@ def deserialize_presence_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_presence: typing.Optional[hikari.presences.MemberPresence]
             The presence object or `builtins.None`.
 
@@ -542,7 +560,7 @@ def deserialize_guild_member_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_member: typing.Optional[guild_models.Member],
+        old_member: typing.Optional[guild_models.Member] = None,
     ) -> member_events.MemberUpdateEvent:
         """Parse a raw payload from Discord into a guild member update event object.
 
@@ -552,6 +570,9 @@ def deserialize_guild_member_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_member: typing.Optional[hikari.guilds.Member]
             The member object or `builtins.None`.
 
@@ -567,7 +588,7 @@ def deserialize_guild_member_remove_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_member: typing.Optional[guild_models.Member],
+        old_member: typing.Optional[guild_models.Member] = None,
     ) -> member_events.MemberDeleteEvent:
         """Parse a raw payload from Discord into a guild member remove event object.
 
@@ -577,6 +598,9 @@ def deserialize_guild_member_remove_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_member: typing.Optional[hikari.guilds.Member]
             The member object or `builtins.None`.
 
@@ -615,7 +639,7 @@ def deserialize_guild_role_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_role: typing.Optional[guild_models.Role],
+        old_role: typing.Optional[guild_models.Role] = None,
     ) -> role_events.RoleUpdateEvent:
         """Parse a raw payload from Discord into a guild role update event object.
 
@@ -625,6 +649,9 @@ def deserialize_guild_role_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_role: typing.Optional[hikari.guilds.Role]
             The role object or `builtins.None`.
 
@@ -640,7 +667,7 @@ def deserialize_guild_role_delete_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_role: typing.Optional[guild_models.Role],
+        old_role: typing.Optional[guild_models.Role] = None,
     ) -> role_events.RoleDeleteEvent:
         """Parse a raw payload from Discord into a guild role delete event object.
 
@@ -650,6 +677,9 @@ def deserialize_guild_role_delete_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_role: typing.Optional[hikari.guilds.Role]
             The role object or `builtins.None`.
 
@@ -732,7 +762,7 @@ def deserialize_message_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_message: typing.Optional[messages_models.PartialMessage],
+        old_message: typing.Optional[messages_models.PartialMessage] = None,
     ) -> message_events.MessageUpdateEvent:
         """Parse a raw payload from Discord into a message update event object.
 
@@ -742,6 +772,9 @@ def deserialize_message_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_message: typing.Optional[hikari.messages.PartialMessage]
             The message object or `builtins.None`.
 
@@ -757,7 +790,7 @@ def deserialize_message_delete_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_message: typing.Optional[messages_models.Message],
+        old_message: typing.Optional[messages_models.Message] = None,
     ) -> message_events.MessageDeleteEvent:
         """Parse a raw payload from Discord into a message delete event object.
 
@@ -767,6 +800,9 @@ def deserialize_message_delete_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_message : typing.Optional[hikari.messages.Message]
             The old message object.
 
@@ -782,7 +818,7 @@ def deserialize_guild_message_delete_bulk_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_messages: typing.Mapping[snowflakes.Snowflake, messages_models.Message],
+        old_messages: typing.Optional[typing.Mapping[snowflakes.Snowflake, messages_models.Message]] = None,
     ) -> message_events.GuildBulkMessageDeleteEvent:
         """Parse a raw payload from Discord into a guild message delete bulk event object.
 
@@ -792,7 +828,10 @@ def deserialize_guild_message_delete_bulk_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
-        old_messages : typing.Mapping[hikari.snowflakes.Snowflake, hikari.messages.Message]
+
+        Other Parameters
+        ----------------
+        old_messages : typing.Optional[typing.Mapping[hikari.snowflakes.Snowflake, hikari.messages.Message]]
             A mapping of the old message objects.
 
         Returns
@@ -1001,7 +1040,7 @@ def deserialize_own_user_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_user: typing.Optional[user_models.OwnUser],
+        old_user: typing.Optional[user_models.OwnUser] = None,
     ) -> user_events.OwnUserUpdateEvent:
         """Parse a raw payload from Discord into a own user update event object.
 
@@ -1011,6 +1050,9 @@ def deserialize_own_user_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_user: typing.Optional[hikari.users.OwnUser]
             The OwnUser object or `builtins.None`.
 
@@ -1030,7 +1072,7 @@ def deserialize_voice_state_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_state: typing.Optional[voices_models.VoiceState],
+        old_state: typing.Optional[voices_models.VoiceState] = None,
     ) -> voice_events.VoiceStateUpdateEvent:
         """Parse a raw payload from Discord into a voice state update event object.
 
@@ -1040,6 +1082,9 @@ def deserialize_voice_state_update_event(
             The shard that emitted this event.
         payload : hikari.internal.data_binding.JSONObject
             The dict payload to parse.
+
+        Other Parameters
+        ----------------
         old_state: typing.Optional[hikari.voices.VoiceState]
             The VoiceState object or `builtins.None`.
 
diff --git a/hikari/guilds.py b/hikari/guilds.py
index c4511a85..47a27f2c 100644
--- a/hikari/guilds.py
+++ b/hikari/guilds.py
@@ -425,6 +425,10 @@ def guild_avatar_url(self) -> typing.Optional[files.URL]:
     def default_avatar_url(self) -> files.URL:
         return self.user.default_avatar_url
 
+    @property
+    def display_avatar_url(self) -> files.URL:
+        return self.make_guild_avatar_url() or super().display_avatar_url
+
     @property
     def banner_hash(self) -> typing.Optional[str]:
         return self.user.banner_hash
diff --git a/hikari/impl/event_factory.py b/hikari/impl/event_factory.py
index 6d29b111..e754e4f7 100644
--- a/hikari/impl/event_factory.py
+++ b/hikari/impl/event_factory.py
@@ -88,7 +88,7 @@ def deserialize_guild_channel_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_channel: typing.Optional[channel_models.GuildChannel],
+        old_channel: typing.Optional[channel_models.GuildChannel] = None,
     ) -> channel_events.GuildChannelUpdateEvent:
         channel = self._app.entity_factory.deserialize_channel(payload)
         assert isinstance(channel, channel_models.GuildChannel), "DM channel update events are undocumented behaviour"
@@ -148,7 +148,7 @@ def deserialize_invite_delete_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_invite: typing.Optional[invite_models.InviteWithMetadata],
+        old_invite: typing.Optional[invite_models.InviteWithMetadata] = None,
     ) -> channel_events.InviteDeleteEvent:
         return channel_events.InviteDeleteEvent(
             app=self._app,
@@ -233,7 +233,7 @@ def deserialize_guild_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_guild: typing.Optional[guild_models.GatewayGuild],
+        old_guild: typing.Optional[guild_models.GatewayGuild] = None,
     ) -> guild_events.GuildUpdateEvent:
         guild_information = self._app.entity_factory.deserialize_gateway_guild(payload)
         return guild_events.GuildUpdateEvent(
@@ -249,7 +249,7 @@ def deserialize_guild_leave_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_guild: typing.Optional[guild_models.GatewayGuild],
+        old_guild: typing.Optional[guild_models.GatewayGuild] = None,
     ) -> guild_events.GuildLeaveEvent:
         return guild_events.GuildLeaveEvent(
             app=self._app, shard=shard, guild_id=snowflakes.Snowflake(payload["id"]), old_guild=old_guild
@@ -285,7 +285,7 @@ def deserialize_guild_emojis_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_emojis: typing.Optional[typing.Sequence[emojis_models.KnownCustomEmoji]],
+        old_emojis: typing.Optional[typing.Sequence[emojis_models.KnownCustomEmoji]] = None,
     ) -> guild_events.EmojisUpdateEvent:
         guild_id = snowflakes.Snowflake(payload["guild_id"])
         emojis = [
@@ -330,7 +330,7 @@ def deserialize_presence_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_presence: typing.Optional[presences_models.MemberPresence],
+        old_presence: typing.Optional[presences_models.MemberPresence] = None,
     ) -> guild_events.PresenceUpdateEvent:
         presence = self._app.entity_factory.deserialize_member_presence(payload)
 
@@ -393,7 +393,7 @@ def deserialize_guild_member_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_member: typing.Optional[guild_models.Member],
+        old_member: typing.Optional[guild_models.Member] = None,
     ) -> member_events.MemberUpdateEvent:
         member = self._app.entity_factory.deserialize_member(payload)
         return member_events.MemberUpdateEvent(shard=shard, member=member, old_member=old_member)
@@ -403,7 +403,7 @@ def deserialize_guild_member_remove_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_member: typing.Optional[guild_models.Member],
+        old_member: typing.Optional[guild_models.Member] = None,
     ) -> member_events.MemberDeleteEvent:
         guild_id = snowflakes.Snowflake(payload["guild_id"])
         user = self._app.entity_factory.deserialize_user(payload["user"])
@@ -427,7 +427,7 @@ def deserialize_guild_role_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_role: typing.Optional[guild_models.Role],
+        old_role: typing.Optional[guild_models.Role] = None,
     ) -> role_events.RoleUpdateEvent:
         role = self._app.entity_factory.deserialize_role(
             payload["role"],
@@ -440,7 +440,7 @@ def deserialize_guild_role_delete_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_role: typing.Optional[guild_models.Role],
+        old_role: typing.Optional[guild_models.Role] = None,
     ) -> role_events.RoleDeleteEvent:
         return role_events.RoleDeleteEvent(
             app=self._app,
@@ -485,7 +485,7 @@ def deserialize_message_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_message: typing.Optional[messages_models.PartialMessage],
+        old_message: typing.Optional[messages_models.PartialMessage] = None,
     ) -> message_events.MessageUpdateEvent:
         message = self._app.entity_factory.deserialize_partial_message(payload)
 
@@ -499,7 +499,7 @@ def deserialize_message_delete_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_message: typing.Optional[messages_models.Message],
+        old_message: typing.Optional[messages_models.Message] = None,
     ) -> message_events.MessageDeleteEvent:
         channel_id = snowflakes.Snowflake(payload["channel_id"])
         message_id = snowflakes.Snowflake(payload["id"])
@@ -526,7 +526,8 @@ def deserialize_guild_message_delete_bulk_event(
         self,
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
-        old_messages: typing.Mapping[snowflakes.Snowflake, messages_models.Message],
+        *,
+        old_messages: typing.Optional[typing.Mapping[snowflakes.Snowflake, messages_models.Message]] = None,
     ) -> message_events.GuildBulkMessageDeleteEvent:
         return message_events.GuildBulkMessageDeleteEvent(
             app=self._app,
@@ -534,7 +535,7 @@ def deserialize_guild_message_delete_bulk_event(
             channel_id=snowflakes.Snowflake(payload["channel_id"]),
             guild_id=snowflakes.Snowflake(payload["guild_id"]),
             message_ids=collections.SnowflakeSet(*(snowflakes.Snowflake(message_id) for message_id in payload["ids"])),
-            old_messages=old_messages,
+            old_messages=old_messages or {},
         )
 
     ###################
@@ -746,7 +747,7 @@ def deserialize_own_user_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_user: typing.Optional[user_models.OwnUser],
+        old_user: typing.Optional[user_models.OwnUser] = None,
     ) -> user_events.OwnUserUpdateEvent:
         return user_events.OwnUserUpdateEvent(
             shard=shard, user=self._app.entity_factory.deserialize_my_user(payload), old_user=old_user
@@ -761,7 +762,7 @@ def deserialize_voice_state_update_event(
         shard: gateway_shard.GatewayShard,
         payload: data_binding.JSONObject,
         *,
-        old_state: typing.Optional[voices_models.VoiceState],
+        old_state: typing.Optional[voices_models.VoiceState] = None,
     ) -> voice_events.VoiceStateUpdateEvent:
         state = self._app.entity_factory.deserialize_voice_state(payload)
         return voice_events.VoiceStateUpdateEvent(shard=shard, state=state, old_state=old_state)
diff --git a/hikari/users.py b/hikari/users.py
index 90c62233..0fd6acf8 100644
--- a/hikari/users.py
+++ b/hikari/users.py
@@ -512,6 +512,11 @@ def default_avatar_url(self) -> files.URL:
             file_format="png",
         )
 
+    @property
+    def display_avatar_url(self) -> files.URL:
+        """Display avatar URL for this user."""
+        return self.make_avatar_url() or self.default_avatar_url
+
     @property
     @abc.abstractmethod
     def discriminator(self) -> str:
@@ -689,10 +694,10 @@ class PartialUserImpl(PartialUser):
     is_bot: undefined.UndefinedOr[bool] = attr.field(eq=False, hash=False, repr=True)
     """Whether this user is a bot account."""
 
-    is_system: undefined.UndefinedOr[bool] = attr.field(eq=False, hash=False, repr=False)
+    is_system: undefined.UndefinedOr[bool] = attr.field(eq=False, hash=False, repr=True)
     """Whether this user is a system account."""
 
-    flags: undefined.UndefinedOr[UserFlag] = attr.field(eq=False, hash=False)
+    flags: undefined.UndefinedOr[UserFlag] = attr.field(eq=False, hash=False, repr=True)
     """Public flags for this user."""
 
     @property
@@ -724,35 +729,31 @@ def __str__(self) -> str:
 class UserImpl(PartialUserImpl, User):
     """Concrete implementation of user information."""
 
-    # These are not attribs on purpose. The idea is to narrow the types of
-    # these fields without redefining them twice in the slots. This is
-    # compatible with MYPY, hence why I have done it like this...
-
-    discriminator: str
+    discriminator: str = attr.field(eq=False, hash=False, repr=True)
     """The user's discriminator."""
 
-    username: str
+    username: str = attr.field(eq=False, hash=False, repr=True)
     """The user's username."""
 
-    avatar_hash: typing.Optional[str]
+    avatar_hash: typing.Optional[str] = attr.field(eq=False, hash=False, repr=False)
     """The user's avatar hash, if they have one, otherwise `builtins.None`."""
 
-    banner_hash: typing.Optional[str]
+    banner_hash: typing.Optional[str] = attr.field(eq=False, hash=False, repr=False)
     """Banner hash of the user, if they have one, otherwise `builtins.None`"""
 
-    accent_color: typing.Optional[colors.Color]
+    accent_color: typing.Optional[colors.Color] = attr.field(eq=False, hash=False, repr=False)
     """The custom banner color for the user, if set.
 
     The official client will decide the default color if not set.
     """  # noqa: D401 - Imperative mood
 
-    is_bot: bool
+    is_bot: bool = attr.field(eq=False, hash=False, repr=True)
     """`builtins.True` if this user is a bot account, `builtins.False` otherwise."""
 
-    is_system: bool
+    is_system: bool = attr.field(eq=False, hash=False, repr=True)
     """`builtins.True` if this user is a system account, `builtins.False` otherwise."""
 
-    flags: UserFlag
+    flags: UserFlag = attr.field(eq=False, hash=False, repr=True)
     """The public flags for this user."""
 
 
diff --git a/requirements.txt b/requirements.txt
index de87d488..24712a9d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
 aiohttp~=3.8
 attrs~=21.4
 colorlog~=6.6
-multidict~=5.2
+multidict~=6.0
 pure25519==0.0.1


Test patch:
diff --git a/tests/hikari/impl/test_event_factory.py b/tests/hikari/impl/test_event_factory.py
index 2b7832b7..a596e58b 100644
--- a/tests/hikari/impl/test_event_factory.py
+++ b/tests/hikari/impl/test_event_factory.py
@@ -656,6 +656,16 @@ def test_deserialize_guild_message_delete_bulk_event(self, event_factory, mock_a
         assert event.message_ids == {6523423, 345123}
         assert event.guild_id == 4394949
 
+    def test_deserialize_guild_message_delete_bulk_event_when_old_messages_is_none(
+        self, event_factory, mock_app, mock_shard
+    ):
+        mock_payload = {"ids": ["6523423", "345123"], "channel_id": "564123", "guild_id": "4394949"}
+
+        event = event_factory.deserialize_guild_message_delete_bulk_event(mock_shard, mock_payload)
+
+        assert isinstance(event, message_events.GuildBulkMessageDeleteEvent)
+        assert event.old_messages == {}
+
     ###################
     # REACTION EVENTS #
     ###################
diff --git a/tests/hikari/test_guilds.py b/tests/hikari/test_guilds.py
index 960bee4b..ec90efdd 100644
--- a/tests/hikari/test_guilds.py
+++ b/tests/hikari/test_guilds.py
@@ -31,6 +31,7 @@
 from hikari import snowflakes
 from hikari import undefined
 from hikari import urls
+from hikari import users
 from hikari.impl import bot
 from hikari.internal import routes
 from hikari.internal import time
@@ -261,6 +262,15 @@ def test_flags_property(self, model, mock_user):
     def test_avatar_url_property(self, model, mock_user):
         assert model.avatar_url is mock_user.avatar_url
 
+    def test_display_avatar_url_when_guild_hash_is_None(self, model, mock_user):
+        with mock.patch.object(guilds.Member, "make_guild_avatar_url") as mock_make_guild_avatar_url:
+            assert model.display_avatar_url is mock_make_guild_avatar_url.return_value
+
+    def test_display_guild_avatar_url_when_guild_hash_is_not_None(self, model, mock_user):
+        with mock.patch.object(guilds.Member, "make_guild_avatar_url", return_value=None):
+            with mock.patch.object(users.User, "display_avatar_url") as mock_display_avatar_url:
+                assert model.display_avatar_url is mock_display_avatar_url
+
     def test_banner_hash_property(self, model, mock_user):
         assert model.banner_hash is mock_user.banner_hash
 
diff --git a/tests/hikari/test_users.py b/tests/hikari/test_users.py
index 6690d4d1..7cd19113 100644
--- a/tests/hikari/test_users.py
+++ b/tests/hikari/test_users.py
@@ -244,6 +244,15 @@ def test_make_avatar_url_with_all_args(self, obj):
             file_format="url",
         )
 
+    def test_display_avatar_url_when_avatar_url(self, obj):
+        with mock.patch.object(users.User, "make_avatar_url") as mock_make_avatar_url:
+            assert obj.display_avatar_url is mock_make_avatar_url.return_value
+
+    def test_display_avatar_url_when_no_avatar_url(self, obj):
+        with mock.patch.object(users.User, "make_avatar_url", return_value=None):
+            with mock.patch.object(users.User, "default_avatar_url") as mock_default_avatar_url:
+                assert obj.display_avatar_url is mock_default_avatar_url
+
     def test_default_avatar(self, obj):
         obj.avatar_hash = "18dnf8dfbakfdh"
         obj.discriminator = "1234"