Fix long-standing issue with user updates not dispatching properly.

This fix is long coming. For a long time due to the addition of a
global user cache, the on_member_update event would only have the
updated user in the very first dispatch due to a quirk in the reference
only being updated once.

In order to fix this issue two things had to change:

1. There had to be a new event, `on_user_update` to complement
   the equivalent member event.
2. Unnecessary copies of User had to be removed to compensate for the
   performance hit from the diffing.

While doing these two fixes I also re-evaluated some more unnecessary
copies done during the PRESENCE_UPDATE to add member case while
fetch_offline_members=False is set or due to chunking issues. The
number of copies was brought down from 2 to 1, discounting the original
Member creation. Unsure on the benefits of this one, however.

N.B: this doesn't change the pre-existing behaviour of on_member_update
This commit is contained in:
Rapptz
2019-04-08 07:40:26 -04:00
parent 1a9726087a
commit f8999b63ae
3 changed files with 43 additions and 8 deletions

View File

@ -189,6 +189,17 @@ class Member(discord.abc.Messageable, _BaseUser):
}
return cls(data=data, guild=message.guild, state=message._state)
@classmethod
def _from_presence_update(cls, *, data, guild, state):
clone = cls(data=data, guild=guild, state=state)
to_return = cls(data=data, guild=guild, state=state)
to_return._client_status = {
key: value
for key, value in data.get('client_status', {}).items()
}
to_return._client_status[None] = data['status']
return to_return, clone
@classmethod
def _copy(cls, member):
self = cls.__new__(cls) # to bypass __init__
@ -200,7 +211,10 @@ class Member(discord.abc.Messageable, _BaseUser):
self.nick = member.nick
self.activities = member.activities
self._state = member._state
self._user = User._copy(member._user)
# Reference will not be copied unless necessary by PRESENCE_UPDATE
# See below
self._user = member._user
return self
async def _get_channel(self):
@ -230,9 +244,15 @@ class Member(discord.abc.Messageable, _BaseUser):
if len(user) > 1:
u = self._user
u.name = user.get('username', u.name)
u.avatar = user.get('avatar', u.avatar)
u.discriminator = user.get('discriminator', u.discriminator)
original = (u.name, u.avatar, u.discriminator)
# These keys seem to always be available
modified = (user['username'], user['avatar'], user['discriminator'])
if original != modified:
to_return = User._copy(self._user)
u.name, u.avatar, u.discriminator = modified
# Signal to dispatch on_user_update
return to_return, u
return False
@property
def status(self):