-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: initial commit, as im not gonna stop if i dont commit
- Loading branch information
Showing
10 changed files
with
444 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
# SPDX-License-Identifier: MIT | ||
|
||
from __future__ import annotations | ||
|
||
from asyncio import create_task, sleep | ||
from logging import getLogger | ||
from typing import TYPE_CHECKING | ||
|
||
from aiohttp import ClientSession, WSMsgType | ||
|
||
from .__libraries import ExponentialBackoff, dumps, loads | ||
|
||
if TYPE_CHECKING: | ||
from asyncio import Task | ||
from typing import Any | ||
|
||
from aiohttp import ClientWebSocketResponse | ||
|
||
from .__libraries import Client, VoiceServerUpdatePayload | ||
from .typings import Coro, OutgoingMessage | ||
|
||
_log = getLogger(__name__) | ||
|
||
|
||
class Node: | ||
def __init__( | ||
self, | ||
*, | ||
host: str, | ||
port: int, | ||
label: str, | ||
password: str, | ||
client: Client, | ||
secure: bool = False, | ||
heartbeat: int = 30, | ||
timeout: float = 10, | ||
session: ClientSession | None = None, | ||
) -> None: | ||
self._host = host | ||
self._port = port | ||
self._label = label | ||
self.__password = password | ||
self._secure = secure | ||
self._heartbeat = heartbeat | ||
self._timeout = timeout | ||
self._client = client | ||
self.__session = session | ||
|
||
self._rest_uri = f"http{'s' if secure else ''}://{host}:{port}" | ||
self._ws_uri = f"ws{'s' if secure else ''}://{host}:{port}" | ||
|
||
self._ws: ClientWebSocketResponse | None = None | ||
self._ws_task: Task[None] | None = None | ||
|
||
self._available = False | ||
self._backoff = ExponentialBackoff() | ||
|
||
@property | ||
def host(self) -> str: | ||
return self._host | ||
|
||
@property | ||
def port(self) -> int: | ||
return self._port | ||
|
||
@property | ||
def label(self) -> str: | ||
return self._label | ||
|
||
@property | ||
def client(self) -> Client: | ||
return self._client | ||
|
||
@property | ||
def secure(self) -> bool: | ||
return self._secure | ||
|
||
async def _connect(self) -> None: | ||
await self._client.wait_until_ready() | ||
assert self._client.user is not None | ||
|
||
if self.__session is None: | ||
self.__session = ClientSession() | ||
|
||
session = self.__session | ||
|
||
headers: dict[str, str] = { | ||
"Authorization": self.__password, | ||
"User-Id": str(self._client.user.id), | ||
"Client-Name": f"Mafic/{__import__('mafic').__version__}", | ||
} | ||
|
||
self._ws = await session.ws_connect( # pyright: ignore[reportUnknownMemberType] | ||
self._ws_uri, | ||
timeout=self._timeout, | ||
heartbeat=self._heartbeat, | ||
headers=headers, | ||
) | ||
self._ws_task = create_task( | ||
self._ws_listener(), name=f"mafic node {self._label}" | ||
) | ||
|
||
self._available = True | ||
|
||
await sleep(1) | ||
if self._available: | ||
self._backoff = ExponentialBackoff() | ||
|
||
async def _ws_listener(self) -> None: | ||
if self._ws is None: | ||
raise RuntimeError( | ||
"Websocket is not connected but attempted to listen, report this." | ||
) | ||
|
||
async for message in self._ws: | ||
# Please aiohttp, fix your typehints. | ||
_type: WSMsgType = message.type # pyright: ignore[reportUnknownMemberType] | ||
|
||
if _type is WSMsgType.CLOSED: | ||
self._available = False | ||
self._ws = None | ||
|
||
wait_time = self._backoff.delay() | ||
_log.warning("Websocket closed, reconnecting in %.2f...", wait_time) | ||
|
||
await sleep(wait_time) | ||
create_task(self._connect()) | ||
return | ||
else: | ||
create_task(self._handle_msg(message.json(loads=loads))) | ||
|
||
async def __send(self, data: OutgoingMessage) -> None: | ||
if self._ws is None: | ||
raise RuntimeError( | ||
"Websocket is not connected but attempted to send, report this." | ||
) | ||
|
||
await self._ws.send_json(data, dumps=dumps) | ||
|
||
def send_voice_server_update( | ||
self, guild_id: int, session_id: str, data: VoiceServerUpdatePayload | ||
) -> Coro[None]: | ||
return self.__send( | ||
{ | ||
"op": "voiceUpdate", | ||
"guildId": str(guild_id), | ||
"sessionId": session_id, | ||
"event": data, | ||
} | ||
) | ||
|
||
async def _handle_msg(self, data: dict[str, Any]) -> None: | ||
raise NotImplementedError |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# SPDX-License-Identifier: MIT | ||
|
||
from __future__ import annotations | ||
|
||
from random import choice | ||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from typing import ClassVar, Optional | ||
|
||
from aiohttp import ClientSession | ||
|
||
from .__libraries import Client | ||
from .node import Node | ||
|
||
|
||
class NodePool: | ||
_nodes: ClassVar[dict[str, Node]] = {} | ||
|
||
@property | ||
def nodes(self) -> dict[str, Node]: | ||
return self._nodes | ||
|
||
@classmethod | ||
def create_node( | ||
cls, | ||
*, | ||
host: str, | ||
port: int, | ||
label: str, | ||
password: str, | ||
client: Client, | ||
secure: bool = False, | ||
heartbeat: int = 30, | ||
timeout: float = 10, | ||
session: ClientSession | None = None, | ||
) -> Node: | ||
node = Node( | ||
host=host, | ||
port=port, | ||
label=label, | ||
password=password, | ||
client=client, | ||
secure=secure, | ||
heartbeat=heartbeat, | ||
timeout=timeout, | ||
session=session, | ||
) | ||
|
||
# TODO: assign dicts for regions and such | ||
cls._nodes[label] = node | ||
|
||
return node | ||
|
||
@classmethod | ||
def get_node(cls, *, guild_id: str | int, endpoint: Optional[str]) -> Node: | ||
# TODO: use guild id, endpoint and other stuff like usage to determine node | ||
|
||
return choice(list(cls._nodes.values())) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# SPDX-License-Identifier: MIT | ||
|
||
from .misc import * | ||
from .outgoing import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# SPDX-License-Identifier: MIT | ||
|
||
from __future__ import annotations | ||
|
||
from typing import Any, Coroutine, TypeVar | ||
|
||
__all__ = ("Coro",) | ||
T = TypeVar("T") | ||
|
||
Coro = Coroutine[Any, Any, T] |
Oops, something went wrong.