Skip to content

Commit 4421264

Browse files
authored
feat: Client Presence implementation. (#543)
1 parent 347c245 commit 4421264

File tree

6 files changed

+62
-21
lines changed

6 files changed

+62
-21
lines changed

interactions/api/gateway.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
from aiohttp import WSMessage
2121

22-
from ..api.models.gw import Presence
2322
from ..base import get_logger
2423
from ..enums import InteractionType, OptionType
2524
from ..models.command import Option
@@ -29,6 +28,7 @@
2928
from .http import HTTPClient
3029
from .models.flags import Intents
3130
from .models.misc import MISSING
31+
from .models.presence import ClientPresence
3232

3333
log: Logger = get_logger("gateway")
3434

@@ -68,7 +68,7 @@ class WebSocketClient:
6868
:ivar dict _ready: The contents of the application returned when ready.
6969
:ivar _Heartbeat __heartbeater: The context state of a "heartbeat" made to the Gateway.
7070
:ivar Optional[List[Tuple[int]]] __shard: The shards used during connection.
71-
:ivar Optional[Presence] __presence: The presence used in connection.
71+
:ivar Optional[ClientPresence] __presence: The presence used in connection.
7272
:ivar Task __task: The closing task for ending connections.
7373
:ivar int session_id: The ID of the ongoing session.
7474
:ivar str sequence: The sequence identifier of the ongoing session.
@@ -161,15 +161,17 @@ async def __restart(self):
161161
await self._establish_connection()
162162

163163
async def _establish_connection(
164-
self, shard: Optional[List[Tuple[int]]] = MISSING, presence: Optional[Presence] = MISSING
164+
self,
165+
shard: Optional[List[Tuple[int]]] = MISSING,
166+
presence: Optional[ClientPresence] = MISSING,
165167
) -> None:
166168
"""
167169
Establishes a client connection with the Gateway.
168170
169171
:param shard?: The shards to establish a connection with. Defaults to ``None``.
170172
:type shard: Optional[List[Tuple[int]]]
171173
:param presence: The presence to carry with. Defaults to ``None``.
172-
:type presence: Optional[Presence]
174+
:type presence: Optional[ClientPresence]
173175
"""
174176
self._options["headers"] = {"User-Agent": self._http._req._headers["User-Agent"]}
175177
url = await self._http.get_gateway()
@@ -193,7 +195,7 @@ async def _handle_connection(
193195
self,
194196
stream: Dict[str, Any],
195197
shard: Optional[List[Tuple[int]]] = MISSING,
196-
presence: Optional[Presence] = MISSING,
198+
presence: Optional[ClientPresence] = MISSING,
197199
) -> None:
198200
"""
199201
Handles the client's connection with the Gateway.
@@ -203,7 +205,7 @@ async def _handle_connection(
203205
:param shard?: The shards to establish a connection with. Defaults to ``None``.
204206
:type shard: Optional[List[Tuple[int]]]
205207
:param presence: The presence to carry with. Defaults to ``None``.
206-
:type presence: Optional[Presence]
208+
:type presence: Optional[ClientPresence]
207209
"""
208210
op: Optional[int] = stream.get("op")
209211
event: Optional[str] = stream.get("t")
@@ -462,15 +464,15 @@ async def _send_packet(self, data: Dict[str, Any]) -> None:
462464
log.debug(packet)
463465

464466
async def __identify(
465-
self, shard: Optional[List[Tuple[int]]] = None, presence: Optional[Presence] = None
467+
self, shard: Optional[List[Tuple[int]]] = None, presence: Optional[ClientPresence] = None
466468
) -> None:
467469
"""
468470
Sends an ``IDENTIFY`` packet to the gateway.
469471
470472
:param shard?: The shard ID to identify under.
471473
:type shard: Optional[List[Tuple[int]]]
472474
:param presence?: The presence to change the bot to on identify.
473-
:type presence: Optional[Presence]
475+
:type presence: Optional[ClientPresence]
474476
"""
475477
self.__shard = shard
476478
self.__presence = presence
@@ -489,7 +491,7 @@ async def __identify(
489491

490492
if isinstance(shard, List) and len(shard) >= 1:
491493
payload["d"]["shard"] = shard
492-
if isinstance(presence, Presence):
494+
if isinstance(presence, ClientPresence):
493495
payload["d"]["presence"] = presence._json
494496

495497
log.debug(f"IDENTIFYING: {payload}")
@@ -518,6 +520,6 @@ def shard(self) -> Optional[List[Tuple[int]]]:
518520
return self.__shard
519521

520522
@property
521-
def presence(self) -> Optional[Presence]:
523+
def presence(self) -> Optional[ClientPresence]:
522524
"""Returns the current presence."""
523525
return self.__presence

interactions/api/gateway.pyi

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ from typing import Any, Dict, List, Optional, Tuple
88

99
from aiohttp import ClientWebSocketResponse
1010

11-
from ..api.models.gw import Presence
1211
from ..base import get_logger
13-
from ..models.misc import MISSING
12+
from ..api.models.misc import MISSING
13+
from ..api.models.presence import ClientPresence
1414
from .dispatch import Listener
1515
from .http import HTTPClient
1616
from .models.flags import Intents
@@ -35,7 +35,7 @@ class WebSocketClient:
3535
_ready: dict
3636
__heartbeater: _Heartbeat
3737
__shard: Optional[List[Tuple[int]]]
38-
__presence: Optional[Presence]
38+
__presence: Optional[ClientPresence]
3939
__task: Optional[Task]
4040
session_id: int
4141
sequence: str
@@ -49,19 +49,19 @@ class WebSocketClient:
4949
async def _manage_heartbeat(self) -> None: ...
5050
async def __restart(self): ...
5151
async def _establish_connection(
52-
self, shard: Optional[List[Tuple[int]]] = MISSING, presence: Optional[Presence] = MISSING
52+
self, shard: Optional[List[Tuple[int]]] = MISSING, presence: Optional[ClientPresence] = MISSING
5353
) -> None: ...
5454
async def _handle_connection(
5555
self,
5656
stream: Dict[str, Any],
5757
shard: Optional[List[Tuple[int]]] = MISSING,
58-
presence: Optional[Presence] = MISSING,
58+
presence: Optional[ClientPresence] = MISSING,
5959
) -> None: ...
6060
@property
6161
async def __receive_packet_stream(self) -> Optional[Dict[str, Any]]: ...
6262
async def _send_packet(self, data: Dict[str, Any]) -> None: ...
6363
async def __identify(
64-
self, shard: Optional[List[Tuple[int]]] = None, presence: Optional[Presence] = None
64+
self, shard: Optional[List[Tuple[int]]] = None, presence: Optional[ClientPresence] = None
6565
) -> None: ...
6666
async def __resume(self) -> None: ...
6767
async def __heartbeat(self) -> None: ...

interactions/api/models/presence.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import time
2+
13
from .misc import DictSerializerMixin, Snowflake
24

35

@@ -140,3 +142,33 @@ def __init__(self, **kwargs):
140142
self.party = PresenceParty(**self.party) if self._json.get("party") else None
141143
self.assets = PresenceAssets(**self.assets) if self._json.get("assets") else None
142144
self.secrets = PresenceSecrets(**self.secrets) if self._json.get("secrets") else None
145+
146+
147+
class ClientPresence(DictSerializerMixin):
148+
"""
149+
An object that symbolizes the presence of the current client's session upon creation.
150+
151+
:ivar Optional[int] since?: Unix time in milliseconds of when the client went idle. None if it is not idle.
152+
:ivar Optional[List[PresenceActivity]] activities: Array of activity objects.
153+
:ivar str status: The client's new status.
154+
:ivar bool afk: Whether the client is afk or not.
155+
"""
156+
157+
__slots__ = ("_json", "since", "activities", "status", "afk")
158+
159+
def __init__(self, **kwargs):
160+
super().__init__(**kwargs)
161+
self.activities = (
162+
[
163+
PresenceActivity(**(activity if isinstance(activity, dict) else activity._json))
164+
for activity in self._json.get("activities")
165+
]
166+
if self._json.get("activities")
167+
else None
168+
)
169+
if self.activities:
170+
self._json["activities"] = [activity._json for activity in self.activities]
171+
if self.status == "idle" and not self._json.get("since"):
172+
# If since is not provided by the developer...
173+
self.since = int(time.time() * 1000)
174+
self._json["since"] = self.since

interactions/api/models/presence.pyi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,11 @@ class PresenceActivity(DictSerializerMixin):
5454
flags: Optional[int]
5555
buttons: Optional[List[PresenceButtons]]
5656
def __init__(self, **kwargs): ...
57+
58+
class ClientPresence(DictSerializerMixin):
59+
_json: dict
60+
since: Optional[int]
61+
activities: Optional[List[PresenceActivity]]
62+
status: str
63+
afk: bool
64+
def __init__(self, **kwargs): ...

interactions/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class Client:
3939
:ivar WebSocketClient _websocket: An object-orientation of a websocket server connection to the Gateway.
4040
:ivar Intents _intents: The Gateway intents of the application. Defaults to ``Intents.DEFAULT``.
4141
:ivar Optional[List[Tuple[int]]] _shard: The list of bucketed shards for the application's connection.
42-
:ivar Optional[Presence] _presence: The RPC-like presence shown on an application once connected.
42+
:ivar Optional[ClientPresence] _presence: The RPC-like presence shown on an application once connected.
4343
:ivar str _token: The token of the application used for authentication when connecting.
4444
:ivar Optional[Dict[str, ModuleType]] _extensions: The "extensions" or cog equivalence registered to the main client.
4545
:ivar Application me: The application representation of the client.
@@ -69,7 +69,7 @@ def __init__(
6969
# Defaults to ``Intents.DEFAULT``.
7070
# shards? : Optional[List[Tuple[int]]]
7171
# Dictates and controls the shards that the application connects under.
72-
# presence? : Optional[Presence]
72+
# presence? : Optional[ClientPresence]
7373
# Sets an RPC-like presence on the application when connected to the Gateway.
7474
# disable_sync? : Optional[bool]
7575
# Controls whether synchronization in the user-facing API should be automatic or not.

interactions/client.pyi

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ from .api.gateway import WebSocketClient
77
from .api.http import HTTPClient
88
from .api.models.flags import Intents
99
from .api.models.guild import Guild
10-
from .api.models.gw import Presence
1110
from .api.models.misc import MISSING, Snowflake
11+
from .api.models.presence import ClientPresence
1212
from .api.models.team import Application
1313
from .enums import ApplicationCommandType
1414
from .models.command import ApplicationCommand, Option
1515
from .models.component import Button, Modal, SelectMenu
16-
from .models.misc import MISSING
1716

1817
_token: str = "" # noqa
1918
_cache: Optional[Cache] = None
@@ -24,7 +23,7 @@ class Client:
2423
_websocket: WebSocketClient
2524
_intents: Intents
2625
_shard: Optional[List[Tuple[int]]]
27-
_presence: Optional[Presence]
26+
_presence: Optional[ClientPresence]
2827
_token: str
2928
_scopes: set[List[Union[int, Snowflake]]]
3029
_automate_sync: bool

0 commit comments

Comments
 (0)