diff --git a/royalnet/__init__.py b/royalnet/__init__.py index 773fc2e6..3a58404e 100644 --- a/royalnet/__init__.py +++ b/royalnet/__init__.py @@ -1,5 +1,17 @@ -from . import audio, bots, commands, database, network, utils, error +from . import audio, \ + bots, \ + commands, \ + database, \ + network, \ + utils, \ + error version = "5.0a7" -__all__ = ["audio", "bots", "commands", "database", "network", "utils", "error"] +__all__ = ["audio", + "bots", + "commands", + "database", + "network", + "utils", + "error"] diff --git a/royalnet/network/message.py b/royalnet/network/message.py new file mode 100644 index 00000000..091f8249 --- /dev/null +++ b/royalnet/network/message.py @@ -0,0 +1,7 @@ +from ..utils import classdictjanitor + + +class Message: + """A Royalnet message. All fields of this class will be converted in a dict.""" + + # idk use classdictjanitor diff --git a/royalnet/network/royalnetlink.py b/royalnet/network/royalnetlink.py index e52fbc48..ab673c45 100644 --- a/royalnet/network/royalnetlink.py +++ b/royalnet/network/royalnetlink.py @@ -91,7 +91,7 @@ class RoyalnetLink: Raises: :py:exc:`royalnet.network.royalnetlink.ConnectionClosedError` if the connection closes.""" try: - jbytes = await self.websocket.recv() + jbytes: bytes = await self.websocket.recv() package: Package = Package.from_json_bytes(jbytes) except websockets.ConnectionClosed: self.error_event.set() @@ -99,7 +99,7 @@ class RoyalnetLink: self.identify_event.clear() log.info(f"Connection to {self.master_uri} was closed.") # What to do now? Let's just reraise. - raise ConnectionClosedError("") + raise ConnectionClosedError() assert package.destination == self.nid log.debug(f"Received package: {package}") return package @@ -108,7 +108,11 @@ class RoyalnetLink: async def identify(self) -> None: log.info(f"Identifying to {self.master_uri}...") await self.websocket.send(f"Identify {self.nid}:{self.link_type}:{self.secret}") - response_package: dict = await self.receive() + response: Package = await self.receive() + assert response.source == "" + if "error" in response.data: + raise ConnectionClosedError(f"Identification error: {response.data['error']}") + assert "success" in response.data self.identify_event.set() log.info(f"Identified successfully!") @@ -125,11 +129,9 @@ class RoyalnetLink: await self.send(package) log.debug(f"Sent request: {message} -> {destination}") await request.event.wait() - result: Message = request.data - log.debug(f"Received response: {request} -> {result}") - if isinstance(result, ServerErrorMessage): - raise NetworkError(result, "Server returned error while requesting something") - return result + response: dict = request.data + log.debug(f"Received response: {request} -> {response}") + return response async def run(self, loops: numbers.Real = math.inf): """Blockingly run the Link.""" @@ -153,9 +155,9 @@ class RoyalnetLink: log.debug(f"Received request {package.source_conv_id}: {package}") try: response = await self.request_handler(package.data) - assert isinstance(response, Message) except Exception as exc: - response = RequestError(exc=exc) + response = {"error": "Exception in request_handler", + "exception": exc.__class__.__name__} response_package: Package = package.reply(response) await self.send(response_package) log.debug(f"Replied to request {response_package.source_conv_id}: {response_package}") diff --git a/royalnet/network/royalnetserver.py b/royalnet/network/royalnetserver.py index 2a55f14a..72e52c4b 100644 --- a/royalnet/network/royalnetserver.py +++ b/royalnet/network/royalnetserver.py @@ -2,7 +2,6 @@ import typing import websockets import re import datetime -import pickle import uuid import asyncio import logging as _logging @@ -25,8 +24,8 @@ class ConnectedClient: """Has the client sent a valid identification package?""" return bool(self.nid) - async def send_server_error(self, reason: str): - await self.send(Package({"error": reason}, + async def send_service(self, msg_type: str, message: str): + await self.send(Package({"type": msg_type, "service": message}, source="", destination=self.nid)) @@ -60,31 +59,31 @@ class RoyalnetServer: identify_msg = await websocket.recv() log.debug(f"{websocket.remote_address} identified itself with: {identify_msg}.") if not isinstance(identify_msg, str): - await websocket.send(connected_client.send_server_error("Invalid identification message (not a str)")) + await websocket.send(connected_client.send_service("error", "Invalid identification message (not a str)")) return identification = re.match(r"Identify ([^:\s]+):([^:\s]+):([^:\s]+)", identify_msg) if identification is None: - await websocket.send(connected_client.send_server_error("Invalid identification message (regex failed)")) + await websocket.send(connected_client.send_service("error", "Invalid identification message (regex failed)")) return secret = identification.group(3) if secret != self.required_secret: - await websocket.send(connected_client.send_server_error("Invalid secret")) + await websocket.send(connected_client.send_service("error", "Invalid secret")) return # Identification successful connected_client.nid = identification.group(1) connected_client.link_type = identification.group(2) self.identified_clients.append(connected_client) log.debug(f"{websocket.remote_address} identified successfully as {connected_client.nid} ({connected_client.link_type}).") - await connected_client.send(Package(IdentifySuccessfulMessage(), connected_client.nid, "__master__")) + await connected_client.send_service("success", "Identification successful!") log.debug(f"{connected_client.nid}'s identification confirmed.") # Main loop while True: # Receive packages - raw_pickle = await websocket.recv() - package: Package = pickle.loads(raw_pickle) + raw_bytes = await websocket.recv() + package: Package = Package.from_json_bytes(raw_bytes) log.debug(f"Received package: {package}") # Check if the package destination is the server itself. - if package.destination == "__master__": + if package.destination == "": # TODO: do stuff pass # Otherwise, route the package to its destination @@ -101,7 +100,7 @@ class RoyalnetServer: A :py:class:`list` of :py:class:`ConnectedClients` to send the package to.""" # Parse destination # Is it nothing? - if package.destination == "NULL": + if package.destination == "": return [] # Is it a valid nid? try: @@ -118,7 +117,10 @@ class RoyalnetServer: destinations = self.find_destination(package) log.debug(f"Routing package: {package} -> {destinations}") for destination in destinations: - specific_package = Package(package.data, destination.nid, package.source, + # This may have some consequences + specific_package = Package(package.data, + source=package.source, + destination=destination.nid, source_conv_id=package.source_conv_id, destination_conv_id=package.destination_conv_id) await destination.send(specific_package) @@ -127,7 +129,7 @@ class RoyalnetServer: await websockets.serve(self.listener, host=self.address, port=self.port) async def start(self): - log.debug(f"Starting main server loop for __master__ on ws://{self.address}:{self.port}") + log.debug(f"Starting main server loop for on ws://{self.address}:{self.port}") # noinspection PyAsyncCall self._loop.create_task(self.serve()) # Just to be sure it has started on Linux diff --git a/royalnet/utils/call.py b/royalnet/utils/call.py index b90c1f32..ea8799d2 100644 --- a/royalnet/utils/call.py +++ b/royalnet/utils/call.py @@ -1,6 +1,5 @@ import typing import asyncio -from ..network import Message, Reply from .command import Command from .commandargs import CommandArgs if typing.TYPE_CHECKING: @@ -26,7 +25,7 @@ class Call: text: The text to be sent, possibly formatted in the weird undescribed markup that I'm using.""" raise NotImplementedError() - async def net_request(self, message, destination: str) -> Reply: + async def net_request(self, message, destination: str) -> dict: """Send data through a :py:class:`royalnet.network.RoyalnetLink` and wait for a :py:class:`royalnet.network.Reply`. Parameters: