mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-23 19:44:20 +00:00
Dictnetwork seems to be working
This commit is contained in:
parent
c0a698e8a3
commit
b4b2f74fb2
9 changed files with 77 additions and 51 deletions
|
@ -1,17 +0,0 @@
|
||||||
from . import audio, \
|
|
||||||
bots, \
|
|
||||||
commands, \
|
|
||||||
database, \
|
|
||||||
network, \
|
|
||||||
utils, \
|
|
||||||
error
|
|
||||||
|
|
||||||
version = "5.0a7"
|
|
||||||
|
|
||||||
__all__ = ["audio",
|
|
||||||
"bots",
|
|
||||||
"commands",
|
|
||||||
"database",
|
|
||||||
"network",
|
|
||||||
"utils",
|
|
||||||
"error"]
|
|
|
@ -3,7 +3,7 @@ import asyncio
|
||||||
import youtube_dl
|
import youtube_dl
|
||||||
import ffmpeg
|
import ffmpeg
|
||||||
from ..utils import Command, Call, NetworkHandler, asyncify
|
from ..utils import Command, Call, NetworkHandler, asyncify
|
||||||
from ..network import Message, RequestSuccessful
|
from ..network import Request, Data
|
||||||
from ..error import TooManyFoundError, NoneFoundError
|
from ..error import TooManyFoundError, NoneFoundError
|
||||||
from ..audio import RoyalPCMAudio, YtdlInfo
|
from ..audio import RoyalPCMAudio, YtdlInfo
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
|
@ -13,14 +13,16 @@ if typing.TYPE_CHECKING:
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
|
||||||
class PlayMessage(Message):
|
class PlayMessage(Data):
|
||||||
def __init__(self, url: str, guild_name: typing.Optional[str] = None):
|
def __init__(self, url: str, guild_name: typing.Optional[str] = None):
|
||||||
|
super().__init__()
|
||||||
self.url: str = url
|
self.url: str = url
|
||||||
self.guild_name: typing.Optional[str] = guild_name
|
self.guild_name: typing.Optional[str] = guild_name
|
||||||
|
|
||||||
|
|
||||||
class PlaySuccessful(RequestSuccessful):
|
class PlaySuccessful(Data):
|
||||||
def __init__(self, info_list: typing.List[YtdlInfo]):
|
def __init__(self, info_list: typing.List[YtdlInfo]):
|
||||||
|
super().__init__()
|
||||||
self.info_list: typing.List[YtdlInfo] = info_list
|
self.info_list: typing.List[YtdlInfo] = info_list
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Royalnet realated classes."""
|
"""Royalnet realated classes."""
|
||||||
|
from .data import Data, Request
|
||||||
from .packages import Package
|
from .package import Package
|
||||||
from .royalnetlink import RoyalnetLink, NetworkError, NotConnectedError, NotIdentifiedError, ConnectionClosedError
|
from .royalnetlink import RoyalnetLink, NetworkError, NotConnectedError, NotIdentifiedError, ConnectionClosedError
|
||||||
from .royalnetserver import RoyalnetServer
|
from .royalnetserver import RoyalnetServer
|
||||||
from .royalnetconfig import RoyalnetConfig
|
from .royalnetconfig import RoyalnetConfig
|
||||||
|
@ -12,4 +12,6 @@ __all__ = ["RoyalnetLink",
|
||||||
"Package",
|
"Package",
|
||||||
"RoyalnetServer",
|
"RoyalnetServer",
|
||||||
"RoyalnetConfig",
|
"RoyalnetConfig",
|
||||||
"ConnectionClosedError"]
|
"ConnectionClosedError",
|
||||||
|
"Data",
|
||||||
|
"Request"]
|
||||||
|
|
25
royalnet/network/data.py
Normal file
25
royalnet/network/data.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
class Data:
|
||||||
|
"""Royalnet data. All fields in this class will be converted to a dict when about to be sent."""
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
|
||||||
|
class Request(Data):
|
||||||
|
"""A Royalnet request. It contains the name of the requested handler, in addition to the data."""
|
||||||
|
|
||||||
|
def __init__(self, handler: str, data: dict):
|
||||||
|
super().__init__()
|
||||||
|
self.handler: str = handler
|
||||||
|
self.data: dict = data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_dict(d: dict):
|
||||||
|
return Request(**d)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, Request):
|
||||||
|
return self.handler == other.handler and self.data == other.data
|
||||||
|
return False
|
|
@ -1,7 +0,0 @@
|
||||||
from ..utils import classdictjanitor
|
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
|
||||||
"""A Royalnet message. All fields of this class will be converted in a dict."""
|
|
||||||
|
|
||||||
# idk use classdictjanitor
|
|
|
@ -4,7 +4,8 @@ import typing
|
||||||
|
|
||||||
|
|
||||||
class Package:
|
class Package:
|
||||||
"""A Royalnet package, the data type with which a :py:class:`royalnet.network.RoyalnetLink` communicates with a :py:class:`royalnet.network.RoyalnetServer` or another link. """
|
"""A Royalnet package, the data type with which a :py:class:`royalnet.network.RoyalnetLink` communicates with a :py:class:`royalnet.network.RoyalnetServer` or another link.
|
||||||
|
Contains info about the source and the destination."""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
data: dict,
|
data: dict,
|
|
@ -6,7 +6,7 @@ import math
|
||||||
import numbers
|
import numbers
|
||||||
import logging as _logging
|
import logging as _logging
|
||||||
import typing
|
import typing
|
||||||
from .packages import Package
|
from .package import Package
|
||||||
|
|
||||||
default_loop = asyncio.get_event_loop()
|
default_loop = asyncio.get_event_loop()
|
||||||
log = _logging.getLogger(__name__)
|
log = _logging.getLogger(__name__)
|
||||||
|
@ -24,6 +24,10 @@ class ConnectionClosedError(Exception):
|
||||||
"""The :py:class:`royalnet.network.RoyalnetLink`'s connection was closed unexpectedly. The link can't be used anymore."""
|
"""The :py:class:`royalnet.network.RoyalnetLink`'s connection was closed unexpectedly. The link can't be used anymore."""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidServerResponseError(Exception):
|
||||||
|
"""The :py:class:`royalnet.network.RoyalnetServer` sent invalid data to the :py:class:`royalnet.network.RoyalnetLink`."""
|
||||||
|
|
||||||
|
|
||||||
class NetworkError(Exception):
|
class NetworkError(Exception):
|
||||||
def __init__(self, error_data: dict, *args):
|
def __init__(self, error_data: dict, *args):
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
|
@ -100,7 +104,8 @@ class RoyalnetLink:
|
||||||
log.info(f"Connection to {self.master_uri} was closed.")
|
log.info(f"Connection to {self.master_uri} was closed.")
|
||||||
# What to do now? Let's just reraise.
|
# What to do now? Let's just reraise.
|
||||||
raise ConnectionClosedError()
|
raise ConnectionClosedError()
|
||||||
assert package.destination == self.nid
|
if self.identify_event.is_set() and package.destination != self.nid:
|
||||||
|
raise InvalidServerResponseError("Package is not addressed to this RoyalnetLink.")
|
||||||
log.debug(f"Received package: {package}")
|
log.debug(f"Received package: {package}")
|
||||||
return package
|
return package
|
||||||
|
|
||||||
|
@ -109,10 +114,13 @@ class RoyalnetLink:
|
||||||
log.info(f"Identifying to {self.master_uri}...")
|
log.info(f"Identifying to {self.master_uri}...")
|
||||||
await self.websocket.send(f"Identify {self.nid}:{self.link_type}:{self.secret}")
|
await self.websocket.send(f"Identify {self.nid}:{self.link_type}:{self.secret}")
|
||||||
response: Package = await self.receive()
|
response: Package = await self.receive()
|
||||||
assert response.source == "<server>"
|
if not response.source == "<server>":
|
||||||
if "error" in response.data:
|
raise InvalidServerResponseError("Received a non-service package before identification.")
|
||||||
raise ConnectionClosedError(f"Identification error: {response.data['error']}")
|
if "type" not in response.data:
|
||||||
assert "success" in response.data
|
raise InvalidServerResponseError("Missing 'type' in response data")
|
||||||
|
if response.data["type"] == "error":
|
||||||
|
raise ConnectionClosedError(f"Identification error: {response.data['type']}")
|
||||||
|
assert response.data["type"] == "success"
|
||||||
self.identify_event.set()
|
self.identify_event.set()
|
||||||
log.info(f"Identified successfully!")
|
log.info(f"Identified successfully!")
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import datetime
|
||||||
import uuid
|
import uuid
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging as _logging
|
import logging as _logging
|
||||||
from .packages import Package
|
from .package import Package
|
||||||
|
|
||||||
default_loop = asyncio.get_event_loop()
|
default_loop = asyncio.get_event_loop()
|
||||||
log = _logging.getLogger(__name__)
|
log = _logging.getLogger(__name__)
|
||||||
|
@ -52,22 +52,22 @@ class RoyalnetServer:
|
||||||
matching = [client for client in self.identified_clients if client.link_type == link_type]
|
matching = [client for client in self.identified_clients if client.link_type == link_type]
|
||||||
return matching or []
|
return matching or []
|
||||||
|
|
||||||
async def listener(self, websocket: websockets.server.WebSocketServerProtocol):
|
async def listener(self, websocket: websockets.server.WebSocketServerProtocol, path):
|
||||||
log.info(f"{websocket.remote_address} connected to the server.")
|
log.info(f"{websocket.remote_address} connected to the server.")
|
||||||
connected_client = ConnectedClient(websocket)
|
connected_client = ConnectedClient(websocket)
|
||||||
# Wait for identification
|
# Wait for identification
|
||||||
identify_msg = await websocket.recv()
|
identify_msg = await websocket.recv()
|
||||||
log.debug(f"{websocket.remote_address} identified itself with: {identify_msg}.")
|
log.debug(f"{websocket.remote_address} identified itself with: {identify_msg}.")
|
||||||
if not isinstance(identify_msg, str):
|
if not isinstance(identify_msg, str):
|
||||||
await websocket.send(connected_client.send_service("error", "Invalid identification message (not a str)"))
|
await connected_client.send_service("error", "Invalid identification message (not a str)")
|
||||||
return
|
return
|
||||||
identification = re.match(r"Identify ([^:\s]+):([^:\s]+):([^:\s]+)", identify_msg)
|
identification = re.match(r"Identify ([^:\s]+):([^:\s]+):([^:\s]+)", identify_msg)
|
||||||
if identification is None:
|
if identification is None:
|
||||||
await websocket.send(connected_client.send_service("error", "Invalid identification message (regex failed)"))
|
await connected_client.send_service("error", "Invalid identification message (regex failed)")
|
||||||
return
|
return
|
||||||
secret = identification.group(3)
|
secret = identification.group(3)
|
||||||
if secret != self.required_secret:
|
if secret != self.required_secret:
|
||||||
await websocket.send(connected_client.send_service("error", "Invalid secret"))
|
await connected_client.send_service("error", "Invalid secret")
|
||||||
return
|
return
|
||||||
# Identification successful
|
# Identification successful
|
||||||
connected_client.nid = identification.group(1)
|
connected_client.nid = identification.group(1)
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
import pytest
|
import pytest
|
||||||
import uuid
|
import uuid
|
||||||
import asyncio
|
import asyncio
|
||||||
from royalnet.network import Package, RoyalnetLink, RoyalnetServer, ConnectionClosedError
|
import logging
|
||||||
|
from royalnet.network import Package, RoyalnetLink, RoyalnetServer, ConnectionClosedError, Request
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.root
|
||||||
|
stream_handler = logging.StreamHandler()
|
||||||
|
stream_handler.formatter = logging.Formatter("{asctime}\t{name}\t{levelname}\t{message}", style="{")
|
||||||
|
log.addHandler(stream_handler)
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -11,8 +19,7 @@ def async_loop():
|
||||||
loop.close()
|
loop.close()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip("Not a test")
|
async def echo_request_handler(message):
|
||||||
def echo_request_handler(message):
|
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,19 +34,24 @@ def test_package_serialization():
|
||||||
assert pkg == Package.from_json_bytes(pkg.to_json_bytes())
|
assert pkg == Package.from_json_bytes(pkg.to_json_bytes())
|
||||||
|
|
||||||
|
|
||||||
|
def test_request_creation():
|
||||||
|
request = Request("pytest", {"testing": "is fun", "bugs": "are less fun"})
|
||||||
|
assert request == Request.from_dict(request.to_dict())
|
||||||
|
|
||||||
|
|
||||||
def test_links(async_loop: asyncio.AbstractEventLoop):
|
def test_links(async_loop: asyncio.AbstractEventLoop):
|
||||||
address, port = "127.0.0.1", 1234
|
address, port = "127.0.0.1", 1235
|
||||||
master = RoyalnetServer(address, port, "test")
|
master = RoyalnetServer(address, port, "test")
|
||||||
async_loop.run_until_complete(master.start())
|
async_loop.run_until_complete(master.start())
|
||||||
# Test invalid secret
|
# Test invalid secret
|
||||||
wrong_secret_link = RoyalnetLink("ws://127.0.0.1:1234", "invalid", "test", echo_request_handler, loop=async_loop)
|
wrong_secret_link = RoyalnetLink("ws://127.0.0.1:1235", "invalid", "test", echo_request_handler, loop=async_loop)
|
||||||
with pytest.raises(ConnectionClosedError):
|
with pytest.raises(ConnectionClosedError):
|
||||||
async_loop.run_until_complete(wrong_secret_link.run())
|
async_loop.run_until_complete(wrong_secret_link.run())
|
||||||
# Test regular connection
|
# Test regular connection
|
||||||
link1 = RoyalnetLink("ws://127.0.0.1:1234", "test", "one", echo_request_handler, loop=async_loop)
|
link1 = RoyalnetLink("ws://127.0.0.1:1235", "test", "one", echo_request_handler, loop=async_loop)
|
||||||
link1_run_task = async_loop.create_task(link1.run())
|
async_loop.create_task(link1.run())
|
||||||
link2 = RoyalnetLink("ws://127.0.0.1:1234", "test", "two", echo_request_handler, loop=async_loop)
|
link2 = RoyalnetLink("ws://127.0.0.1:1235", "test", "two", echo_request_handler, loop=async_loop)
|
||||||
link2_run_task = async_loop.create_task(link2.run())
|
async_loop.create_task(link2.run())
|
||||||
message = {"ciao": "ciao"}
|
message = {"ciao": "ciao"}
|
||||||
response = async_loop.run_until_complete(link1.request(message, "two"))
|
response = async_loop.run_until_complete(link1.request(message, "two"))
|
||||||
assert message == response
|
assert message == response
|
||||||
|
|
Loading…
Reference in a new issue