Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Client Presence implementation. #543

Merged
merged 1 commit into from Feb 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 12 additions & 10 deletions interactions/api/gateway.py
Expand Up @@ -19,7 +19,6 @@

from aiohttp import WSMessage

from ..api.models.gw import Presence
from ..base import get_logger
from ..enums import InteractionType, OptionType
from ..models.command import Option
Expand All @@ -29,6 +28,7 @@
from .http import HTTPClient
from .models.flags import Intents
from .models.misc import MISSING
from .models.presence import ClientPresence

log: Logger = get_logger("gateway")

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

async def _establish_connection(
self, shard: Optional[List[Tuple[int]]] = MISSING, presence: Optional[Presence] = MISSING
self,
shard: Optional[List[Tuple[int]]] = MISSING,
presence: Optional[ClientPresence] = MISSING,
) -> None:
"""
Establishes a client connection with the Gateway.

:param shard?: The shards to establish a connection with. Defaults to ``None``.
:type shard: Optional[List[Tuple[int]]]
:param presence: The presence to carry with. Defaults to ``None``.
:type presence: Optional[Presence]
:type presence: Optional[ClientPresence]
"""
self._options["headers"] = {"User-Agent": self._http._req._headers["User-Agent"]}
url = await self._http.get_gateway()
Expand All @@ -193,7 +195,7 @@ async def _handle_connection(
self,
stream: Dict[str, Any],
shard: Optional[List[Tuple[int]]] = MISSING,
presence: Optional[Presence] = MISSING,
presence: Optional[ClientPresence] = MISSING,
) -> None:
"""
Handles the client's connection with the Gateway.
Expand All @@ -203,7 +205,7 @@ async def _handle_connection(
:param shard?: The shards to establish a connection with. Defaults to ``None``.
:type shard: Optional[List[Tuple[int]]]
:param presence: The presence to carry with. Defaults to ``None``.
:type presence: Optional[Presence]
:type presence: Optional[ClientPresence]
"""
op: Optional[int] = stream.get("op")
event: Optional[str] = stream.get("t")
Expand Down Expand Up @@ -462,15 +464,15 @@ async def _send_packet(self, data: Dict[str, Any]) -> None:
log.debug(packet)

async def __identify(
self, shard: Optional[List[Tuple[int]]] = None, presence: Optional[Presence] = None
self, shard: Optional[List[Tuple[int]]] = None, presence: Optional[ClientPresence] = None
) -> None:
"""
Sends an ``IDENTIFY`` packet to the gateway.

:param shard?: The shard ID to identify under.
:type shard: Optional[List[Tuple[int]]]
:param presence?: The presence to change the bot to on identify.
:type presence: Optional[Presence]
:type presence: Optional[ClientPresence]
"""
self.__shard = shard
self.__presence = presence
Expand All @@ -489,7 +491,7 @@ async def __identify(

if isinstance(shard, List) and len(shard) >= 1:
payload["d"]["shard"] = shard
if isinstance(presence, Presence):
if isinstance(presence, ClientPresence):
payload["d"]["presence"] = presence._json

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

@property
def presence(self) -> Optional[Presence]:
def presence(self) -> Optional[ClientPresence]:
"""Returns the current presence."""
return self.__presence
12 changes: 6 additions & 6 deletions interactions/api/gateway.pyi
Expand Up @@ -8,9 +8,9 @@ from typing import Any, Dict, List, Optional, Tuple

from aiohttp import ClientWebSocketResponse

from ..api.models.gw import Presence
from ..base import get_logger
from ..models.misc import MISSING
from ..api.models.misc import MISSING
from ..api.models.presence import ClientPresence
from .dispatch import Listener
from .http import HTTPClient
from .models.flags import Intents
Expand All @@ -35,7 +35,7 @@ class WebSocketClient:
_ready: dict
__heartbeater: _Heartbeat
__shard: Optional[List[Tuple[int]]]
__presence: Optional[Presence]
__presence: Optional[ClientPresence]
__task: Optional[Task]
session_id: int
sequence: str
Expand All @@ -49,19 +49,19 @@ class WebSocketClient:
async def _manage_heartbeat(self) -> None: ...
async def __restart(self): ...
async def _establish_connection(
self, shard: Optional[List[Tuple[int]]] = MISSING, presence: Optional[Presence] = MISSING
self, shard: Optional[List[Tuple[int]]] = MISSING, presence: Optional[ClientPresence] = MISSING
) -> None: ...
async def _handle_connection(
self,
stream: Dict[str, Any],
shard: Optional[List[Tuple[int]]] = MISSING,
presence: Optional[Presence] = MISSING,
presence: Optional[ClientPresence] = MISSING,
) -> None: ...
@property
async def __receive_packet_stream(self) -> Optional[Dict[str, Any]]: ...
async def _send_packet(self, data: Dict[str, Any]) -> None: ...
async def __identify(
self, shard: Optional[List[Tuple[int]]] = None, presence: Optional[Presence] = None
self, shard: Optional[List[Tuple[int]]] = None, presence: Optional[ClientPresence] = None
) -> None: ...
async def __resume(self) -> None: ...
async def __heartbeat(self) -> None: ...
Expand Down
32 changes: 32 additions & 0 deletions interactions/api/models/presence.py
@@ -1,3 +1,5 @@
import time

from .misc import DictSerializerMixin, Snowflake


Expand Down Expand Up @@ -140,3 +142,33 @@ def __init__(self, **kwargs):
self.party = PresenceParty(**self.party) if self._json.get("party") else None
self.assets = PresenceAssets(**self.assets) if self._json.get("assets") else None
self.secrets = PresenceSecrets(**self.secrets) if self._json.get("secrets") else None


class ClientPresence(DictSerializerMixin):
"""
An object that symbolizes the presence of the current client's session upon creation.

:ivar Optional[int] since?: Unix time in milliseconds of when the client went idle. None if it is not idle.
:ivar Optional[List[PresenceActivity]] activities: Array of activity objects.
:ivar str status: The client's new status.
:ivar bool afk: Whether the client is afk or not.
"""

__slots__ = ("_json", "since", "activities", "status", "afk")

def __init__(self, **kwargs):
super().__init__(**kwargs)
self.activities = (
[
PresenceActivity(**(activity if isinstance(activity, dict) else activity._json))
for activity in self._json.get("activities")
]
if self._json.get("activities")
else None
)
if self.activities:
self._json["activities"] = [activity._json for activity in self.activities]
if self.status == "idle" and not self._json.get("since"):
# If since is not provided by the developer...
self.since = int(time.time() * 1000)
self._json["since"] = self.since
8 changes: 8 additions & 0 deletions interactions/api/models/presence.pyi
Expand Up @@ -54,3 +54,11 @@ class PresenceActivity(DictSerializerMixin):
flags: Optional[int]
buttons: Optional[List[PresenceButtons]]
def __init__(self, **kwargs): ...

class ClientPresence(DictSerializerMixin):
_json: dict
since: Optional[int]
activities: Optional[List[PresenceActivity]]
status: str
afk: bool
def __init__(self, **kwargs): ...
4 changes: 2 additions & 2 deletions interactions/client.py
Expand Up @@ -39,7 +39,7 @@ class Client:
:ivar WebSocketClient _websocket: An object-orientation of a websocket server connection to the Gateway.
:ivar Intents _intents: The Gateway intents of the application. Defaults to ``Intents.DEFAULT``.
:ivar Optional[List[Tuple[int]]] _shard: The list of bucketed shards for the application's connection.
:ivar Optional[Presence] _presence: The RPC-like presence shown on an application once connected.
:ivar Optional[ClientPresence] _presence: The RPC-like presence shown on an application once connected.
:ivar str _token: The token of the application used for authentication when connecting.
:ivar Optional[Dict[str, ModuleType]] _extensions: The "extensions" or cog equivalence registered to the main client.
:ivar Application me: The application representation of the client.
Expand Down Expand Up @@ -69,7 +69,7 @@ def __init__(
# Defaults to ``Intents.DEFAULT``.
# shards? : Optional[List[Tuple[int]]]
# Dictates and controls the shards that the application connects under.
# presence? : Optional[Presence]
# presence? : Optional[ClientPresence]
# Sets an RPC-like presence on the application when connected to the Gateway.
# disable_sync? : Optional[bool]
# Controls whether synchronization in the user-facing API should be automatic or not.
Expand Down
5 changes: 2 additions & 3 deletions interactions/client.pyi
Expand Up @@ -7,13 +7,12 @@ from .api.gateway import WebSocketClient
from .api.http import HTTPClient
from .api.models.flags import Intents
from .api.models.guild import Guild
from .api.models.gw import Presence
from .api.models.misc import MISSING, Snowflake
from .api.models.presence import ClientPresence
from .api.models.team import Application
from .enums import ApplicationCommandType
from .models.command import ApplicationCommand, Option
from .models.component import Button, Modal, SelectMenu
from .models.misc import MISSING

_token: str = "" # noqa
_cache: Optional[Cache] = None
Expand All @@ -24,7 +23,7 @@ class Client:
_websocket: WebSocketClient
_intents: Intents
_shard: Optional[List[Tuple[int]]]
_presence: Optional[Presence]
_presence: Optional[ClientPresence]
_token: str
_scopes: set[List[Union[int, Snowflake]]]
_automate_sync: bool
Expand Down