diff --git a/novus/api/gateway/dispatch.py b/novus/api/gateway/dispatch.py index f3b6d4dd..81decd50 100644 --- a/novus/api/gateway/dispatch.py +++ b/novus/api/gateway/dispatch.py @@ -296,8 +296,6 @@ def try_delete(dict: dict, key: int) -> None: for id in list(current._threads.keys()): try_delete(self.cache.channels, id) del current._threads[id] - for id in list(current._voice_states.keys()): - pass # TODO for id in list(current._channels.keys()): try_delete(self.cache.channels, id) del current._channels[id] @@ -430,9 +428,15 @@ async def _handle_message_delete( async def _handle_presence_update( self, - data: dict[Any, Any]) -> None: + data: payloads.Presence) -> None: """Handle updating presences for users.""" - log.debug("Ignoring presence update %s" % dump(data)) # TODO + + user = self.cache.get_user(data["user"]["id"]) + if user is None: + return # Can't do anything with the presence user object really + before = copy(user) + user._update_presence(data) + self.dispatch("PRESENCE_UPDATE", before, user) async def _handle_channel_generic( self, diff --git a/novus/enums/presence.py b/novus/enums/presence.py index 5b2a5334..0e9f33bc 100644 --- a/novus/enums/presence.py +++ b/novus/enums/presence.py @@ -35,10 +35,10 @@ class ActivityType(Enum): class Status(Enum): - ONLINE = "ONLINE" - DND = "DND" - DO_NOT_DISTURB = "DND" - IDLE = "IDLE" - AFK = "IDLE" - INVISIBLE = "INVISIBLE" - OFFLINE = "OFFLINE" + ONLINE = "online" + DND = "dnd" + DO_NOT_DISTURB = "dnd" + IDLE = "idle" + AFK = "idle" + INVISIBLE = "invisible" + OFFLINE = "offline" diff --git a/novus/models/user.py b/novus/models/user.py index f9d2507d..34f9b74f 100644 --- a/novus/models/user.py +++ b/novus/models/user.py @@ -22,8 +22,9 @@ from typing_extensions import Self +from ..enums import Status from ..flags import UserFlags -from ..utils import cached_slot_property, generate_repr, try_snowflake +from ..utils import DiscordDatetime, cached_slot_property, generate_repr, try_snowflake from .abc import Hashable, Messageable from .asset import Asset @@ -85,6 +86,12 @@ class User(Hashable, Messageable): The premium type associated with the account. .. seealso:: `novus.UserPremiumType` + status : str + The status of the user. + + .. seealso:: `novus.Status` + activities : list[novus.Activity] + The activites of the user. """ __slots__ = ( @@ -104,6 +111,8 @@ class User(Hashable, Messageable): 'email', 'flags', 'premium_type', + 'status', + 'activites', '_cs_avatar', '_cs_default_avatar', '_cs_banner', @@ -197,6 +206,20 @@ def _update(self, data: payloads.User | payloads.PartialUser) -> Self: data.get('flags', 0) | data.get('public_flags', 0) ) self.premium_type = data.get('premium_type', 0) + self.status = Status.ONLINE + self.activites: list[Activity] = [] + return self + + def _update_presence(self, data: payloads.Presence) -> Self: + """ + Update the presence for this user. + """ + + self.status = data.get("status") or Status.ONLINE + self.activites = [ + Activity(data=d) + for d in data.get("activities", []) + ] return self # API methods @@ -304,3 +327,14 @@ async def _get_send_method(self) -> Callable[..., Awaitable[Any]]: if self._dm_channel is None: self._dm_channel = await self.create_dm_channel() return functools.partial(self.state.channel.create_message, self._dm_channel.id) + + +class Activity: + """ + A user activity for their presence. + """ + + def __init__(self, *, data: payloads.Activity): + self.name = data["name"] + self.type = data["type"] + self.created_at = DiscordDatetime.fromtimestamp(data["created_at"] / 1_000) diff --git a/novus/payloads/user.py b/novus/payloads/user.py index 4e405c30..81106069 100644 --- a/novus/payloads/user.py +++ b/novus/payloads/user.py @@ -164,7 +164,7 @@ class _ActivityOptional(TypedDict, total=False): class Activity(_ActivityOptional): name: str - type: Literal[0, 1, 2, 3, 4, 5] + type: int created_at: Timestamp