Skip to content

Commit e59e4d0

Browse files
committed
feat: add on_raw_member_update event
1 parent 9914770 commit e59e4d0

File tree

5 files changed

+84
-24
lines changed

5 files changed

+84
-24
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ These changes are available on the `master` branch, but have not yet been releas
1717
- Added `Attachment.read_chunked` and added optional `chunksize` argument to
1818
`Attachment.save` for retrieving attachments in chunks.
1919
([#2956](https://github.com/Pycord-Development/pycord/pull/2956))
20+
- Added a new event called `on_raw_member_update` that is dispatched when a member is updated,
21+
regardless of cache status. ([#3011](https://github.com/Pycord-Development/pycord/pull/3011))
2022

2123
### Changed
2224

discord/member.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
from .types.member import Member as MemberPayload
6565
from .types.member import MemberWithUser as MemberWithUserPayload
6666
from .types.member import UserWithMember as UserWithMemberPayload
67+
from .types.member import MemberUpdateEvent as MemberUpdateEventPayload
6768
from .types.user import User as UserPayload
6869
from .types.voice import GuildVoiceState as GuildVoiceStatePayload
6970
from .types.voice import VoiceState as VoiceStatePayload
@@ -424,27 +425,28 @@ async def _get_channel(self):
424425
ch = await self.create_dm()
425426
return ch
426427

427-
def _update(self, data: MemberPayload) -> None:
428+
def _update(self, data: MemberPayload | MemberUpdateEventPayload) -> None:
428429
# the nickname change is optional,
429430
# if it isn't in the payload then it didn't change
430431
try:
431-
self.nick = data["nick"]
432+
self.nick = data["nick"] # type: ignore # handled by the type-except
432433
except KeyError:
433434
pass
434435

435436
try:
436-
self.pending = data["pending"]
437+
self.pending = data["pending"] # type: ignore # handled by the type-except
437438
except KeyError:
438439
pass
439440

440441
self.premium_since = utils.parse_time(data.get("premium_since"))
441-
self._roles = utils.SnowflakeList(map(int, data["roles"]))
442+
self._roles = utils.SnowflakeList(map(int, data["roles"])) # type: ignore # the API is the same
442443
self._avatar = data.get("avatar")
443444
self._banner = data.get("banner")
444445
self.communication_disabled_until = utils.parse_time(
445446
data.get("communication_disabled_until")
446447
)
447448
self.flags = MemberFlags._from_value(data.get("flags", 0))
449+
self.joined_at = utils.parse_time(data.get("joined_at")) or self.joined_at
448450

449451
def _presence_update(
450452
self, data: PartialPresenceUpdate, user: UserPayload

discord/raw_models.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
TypingEvent,
6969
VoiceChannelStatusUpdateEvent,
7070
)
71+
from .types.member import MemberUpdateEvent
7172
from .user import User
7273

7374

@@ -90,6 +91,7 @@
9091
"RawVoiceChannelStatusUpdateEvent",
9192
"RawMessagePollVoteEvent",
9293
"RawSoundboardSoundDeleteEvent",
94+
"RawMemberUpdateEvent",
9395
)
9496

9597

@@ -870,3 +872,28 @@ def __init__(self, data: PartialSoundboardSound) -> None:
870872
self.sound_id: int = int(data["sound_id"])
871873
self.guild_id: int = int(data["guild_id"])
872874
self.data: PartialSoundboardSound = data
875+
876+
877+
class RawMemberUpdateEvent(_RawReprMixin):
878+
"""Represents the payload for a :func:`on_raw_member_update` event.
879+
880+
.. versionadded:: 2.7
881+
882+
Attributes
883+
----------
884+
data: :class:`dict`
885+
The raw data sent by the `gateway <https://discord.com/developers/docs/topics/gateway-events#guild-member-update>`_
886+
cached_member: Optional[:class:`Member`]
887+
The cached member, if found in the internal member cache.
888+
member: :class:`Member`
889+
The new member object after the update.
890+
"""
891+
892+
__slots__ = ("guild_id", "user_id", "data", "cached_member", "member")
893+
894+
def __init__(self, data: MemberUpdateEvent, member: Member) -> None:
895+
self.guild_id: int = int(data["guild_id"])
896+
self.user_id: int = int(data["user"]["id"])
897+
self.data: MemberUpdateEvent = data
898+
self.cached_member: Member | None = None
899+
self.member: Member = member

discord/state.py

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
from .types.sticker import GuildSticker as GuildStickerPayload
9191
from .types.user import User as UserPayload
9292
from .voice_client import VoiceClient
93+
from .types.member import MemberUpdateEvent
9394

9495
T = TypeVar("T")
9596
CS = TypeVar("CS", bound="ConnectionState")
@@ -910,9 +911,11 @@ def parse_message_poll_vote_add(self, data) -> None:
910911
if answer.id in counts:
911912
counts[answer.id].count += 1
912913
else:
913-
counts[answer.id] = PollAnswerCount(
914-
{"id": answer.id, "count": 1, "me_voted": False}
915-
)
914+
counts[answer.id] = PollAnswerCount({
915+
"id": answer.id,
916+
"count": 1,
917+
"me_voted": False,
918+
})
916919
if poll is not None and user is not None:
917920
answer = poll.get_answer(raw.answer_id)
918921
if answer is not None:
@@ -1322,7 +1325,7 @@ def parse_guild_member_remove(self, data) -> None:
13221325
)
13231326
self.dispatch("raw_member_remove", raw)
13241327

1325-
def parse_guild_member_update(self, data) -> None:
1328+
def parse_guild_member_update(self, data: MemberUpdateEvent) -> None:
13261329
guild = self._get_guild(int(data["guild_id"]))
13271330
user = data["user"]
13281331
user_id = int(user["id"])
@@ -1333,29 +1336,38 @@ def parse_guild_member_update(self, data) -> None:
13331336
)
13341337
return
13351338

1336-
member = guild.get_member(user_id)
1337-
if member is not None:
1338-
old_member = Member._copy(member)
1339-
member._update(data)
1340-
user_update = member._update_inner_user(user)
1339+
if not self.member_cache_flags.joined:
1340+
old_member = guild._members.pop(user_id, None)
1341+
else:
1342+
old_member = guild.get_member(user_id)
1343+
1344+
old_member = Member._copy(old_member) if old_member is not None else None
1345+
1346+
if old_member is not None:
1347+
new_member = old_member
1348+
new_member._update(data)
1349+
1350+
# handle user_update if necessary
1351+
user_update = old_member._update_inner_user(user)
13411352
if user_update:
13421353
self.dispatch("user_update", user_update[0], user_update[1])
13431354

1344-
self.dispatch("member_update", old_member, member)
1355+
self.dispatch("member_update", old_member, new_member)
13451356
else:
1346-
if self.member_cache_flags.joined:
1347-
member = Member(data=data, guild=guild, state=self)
1357+
new_member = Member(guild=guild, data=data, state=self) # type: ignore
13481358

1359+
if self.member_cache_flags.joined:
13491360
# Force an update on the inner user if necessary
1350-
user_update = member._update_inner_user(user)
1361+
user_update = new_member._update_inner_user(user)
13511362
if user_update:
13521363
self.dispatch("user_update", user_update[0], user_update[1])
13531364

1354-
guild._add_member(member)
1355-
_log.debug(
1356-
"GUILD_MEMBER_UPDATE referencing an unknown member ID: %s. Discarding.",
1357-
user_id,
1358-
)
1365+
print("adding new member to cache:", new_member)
1366+
guild._add_member(new_member)
1367+
1368+
raw = RawMemberUpdateEvent(data, new_member)
1369+
raw.cached_member = old_member
1370+
self.dispatch("raw_member_update", raw)
13591371

13601372
def parse_guild_emojis_update(self, data) -> None:
13611373
guild = self._get_guild(int(data["guild_id"]))

discord/types/member.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
DEALINGS IN THE SOFTWARE.
2424
"""
2525

26-
from typing import TypedDict
26+
from typing import NotRequired, TypedDict
2727

28-
from .snowflake import SnowflakeList
28+
from .snowflake import Snowflake, SnowflakeList
2929
from .user import User
3030

3131

@@ -66,3 +66,20 @@ class MemberWithUser(_OptionalMemberWithUser):
6666

6767
class UserWithMember(User, total=False):
6868
member: _OptionalMemberWithUser
69+
70+
71+
class MemberUpdateEvent(TypedDict):
72+
guild_id: Snowflake
73+
user: User
74+
roles: list[Snowflake]
75+
nick: NotRequired[str | None]
76+
avatar: NotRequired[str | None]
77+
banner: NotRequired[str | None]
78+
joined_at: NotRequired[str | None]
79+
premium_since: NotRequired[str | None]
80+
deaf: NotRequired[bool | None]
81+
mute: NotRequired[bool | None]
82+
pending: NotRequired[bool | None]
83+
communication_disabled_until: NotRequired[str | None]
84+
flags: NotRequired[int | None]
85+
avatar_decoration_data: NotRequired[dict | None]

0 commit comments

Comments
 (0)