diff --git a/royalgames.py b/royalgames.py index 5563c102..bb7293aa 100644 --- a/royalgames.py +++ b/royalgames.py @@ -20,11 +20,11 @@ commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziComm AuthorCommand, DiarioCommand, RageCommand, DateparserCommand, ReminderCommand, KvactiveCommand, KvCommand, KvrollCommand, VideoinfoCommand, SummonCommand, PlayCommand] -master = RoyalnetServer("localhost", 1234, "sas") +master = RoyalnetServer("localhost", 2468, "sas") tg_db_cfg = DatabaseConfig(os.environ["DB_PATH"], Royal, Telegram, "tg_id") -tg_bot = TelegramBot(os.environ["TG_AK"], "ws://localhost:1234", "sas", commands, NullCommand, ErrorHandlerCommand, tg_db_cfg) +tg_bot = TelegramBot(os.environ["TG_AK"], "ws://localhost:2468", "sas", commands, NullCommand, ErrorHandlerCommand, tg_db_cfg) ds_db_cfg = DatabaseConfig(os.environ["DB_PATH"], Royal, Discord, "discord_id") -ds_bot = DiscordBot(os.environ["DS_AK"], "ws://localhost:1234", "sas", commands, NullCommand, ErrorHandlerCommand, ds_db_cfg) +ds_bot = DiscordBot(os.environ["DS_AK"], "ws://localhost:2468", "sas", commands, NullCommand, ErrorHandlerCommand, ds_db_cfg) loop.run_until_complete(master.run()) # Dirty hack, remove me asap loop.create_task(tg_bot.run()) diff --git a/royalnet/bots/discord.py b/royalnet/bots/discord.py index 40d33525..83f52ebb 100644 --- a/royalnet/bots/discord.py +++ b/royalnet/bots/discord.py @@ -28,18 +28,6 @@ class DiscordBot: error_command: typing.Type[Command] = NullCommand, database_config: typing.Optional[DatabaseConfig] = None): self.token = token - # Generate commands - self.missing_command = missing_command - self.error_command = error_command - self.commands = {} - required_tables = set() - for command in commands: - self.commands[f"!{command.command_name}"] = command - required_tables = required_tables.union(command.require_alchemy_tables) - # Generate network handlers - self.network_handlers: typing.Dict[typing.Type[Message], typing.Type[NetworkHandler]] = {} - for command in commands: - self.network_handlers = {**self.network_handlers, **command.network_handler_dict()} # Generate the Alchemy database if database_config: self.alchemy = Alchemy(database_config.database_uri, required_tables) @@ -49,16 +37,12 @@ class DiscordBot: self.identity_chain = relationshiplinkchain(self.master_table, self.identity_table) else: if required_tables: - raise InvalidConfigError("Tables are required by the commands, but Alchemy is not configured") + raise InvalidConfigError("Tables are required by the _commands, but Alchemy is not configured") self.alchemy = None self.master_table = None self.identity_table = None self.identity_column = None self.identity_chain = None - # Connect to Royalnet - self.network: RoyalnetLink = RoyalnetLink(master_server_uri, master_server_secret, "discord", - self.network_handler) - loop.create_task(self.network.run()) # Create the PlayModes dictionary self.music_data: typing.Dict[discord.Guild, PlayMode] = {} @@ -134,8 +118,9 @@ class DiscordBot: try: selected_command = self.commands[command_text] except KeyError: - # Skip inexistent commands + # Skip inexistent _commands selected_command = self.missing_command + log.error(f"Running {selected_command}") # Call the command try: return await self.DiscordCall(message.channel, selected_command, parameters, log, @@ -208,11 +193,6 @@ class DiscordBot: return voice_client raise NoneFoundError("No voice clients found") - async def network_handler(self, message: Message) -> Message: - """Handle a Royalnet request.""" - log.debug(f"Received {message} from Royalnet") - return await self.network_handlers[message.__class__].discord(message) - async def add_to_music_data(self, url: str, guild: discord.Guild): """Add a file to the corresponding music_data object.""" log.debug(f"Downloading {url} to add to music_data") diff --git a/royalnet/bots/telegram.py b/royalnet/bots/telegram.py index 64c44e76..f204283f 100644 --- a/royalnet/bots/telegram.py +++ b/royalnet/bots/telegram.py @@ -33,7 +33,7 @@ class TelegramBot: self.error_command = error_command self.network: RoyalnetLink = RoyalnetLink(master_server_uri, master_server_secret, "telegram", todo) loop.create_task(self.network.run()) - # Generate commands + # Generate _commands self.commands = {} required_tables = set() for command in commands: @@ -48,7 +48,7 @@ class TelegramBot: self.identity_chain = relationshiplinkchain(self.master_table, self.identity_table) else: if required_tables: - raise InvalidConfigError("Tables are required by the commands, but Alchemy is not configured") + raise InvalidConfigError("Tables are required by the _commands, but Alchemy is not configured") self.alchemy = None self.master_table = None self.identity_table = None @@ -135,7 +135,7 @@ class TelegramBot: try: command = self.commands[command_text] except KeyError: - # Skip inexistent commands + # Skip inexistent _commands command = self.missing_command # Call the command # noinspection PyBroadException diff --git a/royalnet/commands/play.py b/royalnet/commands/play.py index c4c05c91..e74715f6 100644 --- a/royalnet/commands/play.py +++ b/royalnet/commands/play.py @@ -35,6 +35,7 @@ class PlayNH(NetworkHandler): # TODO: change Exception raise Exception("No music_data for this guild") # Start downloading + # noinspection PyAsyncCall loop.create_task(bot.add_to_music_data(message.url, guild)) return RequestSuccessful() diff --git a/royalnet/network/__init__.py b/royalnet/network/__init__.py index af1615d7..93081500 100644 --- a/royalnet/network/__init__.py +++ b/royalnet/network/__init__.py @@ -2,6 +2,7 @@ from .messages import Message, ServerErrorMessage, InvalidSecretEM, InvalidDesti from .packages import Package from .royalnetlink import RoyalnetLink, NetworkError, NotConnectedError, NotIdentifiedError from .royalnetserver import RoyalnetServer +from .royalnetconfig import RoyalnetConfig __all__ = ["Message", "ServerErrorMessage", @@ -15,4 +16,5 @@ __all__ = ["Message", "Package", "RoyalnetServer", "RequestSuccessful", - "RequestError"] + "RequestError", + "RoyalnetConfig"] diff --git a/royalnet/network/royalnetconfig.py b/royalnet/network/royalnetconfig.py new file mode 100644 index 00000000..8c0e355c --- /dev/null +++ b/royalnet/network/royalnetconfig.py @@ -0,0 +1,12 @@ +import typing + + +class RoyalnetConfig: + def __init__(self, + master_uri: str, + master_secret: str): + if not (master_uri.startswith("ws://") + or master_uri.startswith("wss://")): + raise ValueError("Invalid protocol (must be ws:// or wss://)") + self.master_uri = master_uri + self.master_secret = master_secret diff --git a/royalnet/utils/bot.py b/royalnet/utils/bot.py new file mode 100644 index 00000000..6d46de38 --- /dev/null +++ b/royalnet/utils/bot.py @@ -0,0 +1,75 @@ +import typing +import asyncio +import logging +from ..utils import Command, NetworkHandler +from ..commands import NullCommand +from ..network import RoyalnetLink, Message, RequestError, RoyalnetConfig +from ..database import Alchemy, DatabaseConfig, relationshiplinkchain + +loop = asyncio.get_event_loop() +log = logging.getLogger(__name__) + + +class GenericBot: + def _init_commands(self, + commands: typing.List[typing.Type[Command]], + missing_command: typing.Type[Command], + error_command: typing.Type[Command]): + log.debug(f"Now generating commands") + self.commands: typing.Dict[str, typing.Type[Command]] = {} + self.network_handlers: typing.Dict[typing.Type[Message], typing.Type[NetworkHandler]] = {} + for command in commands: + self.commands[f"!{command.command_name}"] = command + self.network_handlers = {**self.network_handlers, **command.network_handler_dict()} + self.missing_command: typing.Type[Command] = missing_command + self.error_command: typing.Type[Command] = error_command + log.debug(f"Successfully generated commands") + + def _init_royalnet(self, royalnet_config: RoyalnetConfig): + self.network: RoyalnetLink = RoyalnetLink(royalnet_config.master_uri, royalnet_config.master_secret, "discord", + self._network_handler) + log.debug(f"Running RoyalnetLink {self.network}") + loop.create_task(self.network.run()) + + def _network_handler(self, message: Message) -> Message: + log.debug(f"Received {message} from the RoyalnetLink") + try: + network_handler = self.network_handlers[message.__class__] + except KeyError as exc: + log.debug(f"Missing network_handler for {message}") + return RequestError(KeyError("Missing network_handler")) + try: + log.debug(f"Using {network_handler} as handler for {message}") + return await network_handler.discord(message) + except Exception as exc: + log.debug(f"Exception {exc} in {network_handler}") + return RequestError(exc) + + def _init_database(self, commands: typing.List[typing.Type[Command]], database_config: DatabaseConfig): + required_tables = set() + for command in commands: + required_tables = required_tables.union(command.require_alchemy_tables) + self.alchemy = Alchemy(database_config.database_uri, required_tables) + self.master_table = self.alchemy.__getattribute__(database_config.master_table.__name__) + self.identity_table = self.alchemy.__getattribute__(database_config.identity_table.__name__) + self.identity_column = self.identity_table.__getattribute__(self.identity_table, + database_config.identity_column_name) + self.identity_chain = relationshiplinkchain(self.master_table, self.identity_table) + + def __init__(self, *, + royalnet_config: RoyalnetConfig, + database_config: typing.Optional[DatabaseConfig] = None, + commands: typing.Optional[typing.List[typing.Type[Command]]] = None, + missing_command: typing.Type[Command] = NullCommand, + error_command: typing.Type[Command] = NullCommand): + if commands is None: + commands = [] + self._init_commands(commands, missing_command=missing_command, error_command=error_command) + self._init_royalnet(royalnet_config=royalnet_config) + if database_config is None: + self.alchemy = None + self.master_table = None + self.identity_table = None + self.identity_column = None + else: + self._init_database(commands=commands, database_config=database_config) diff --git a/royalnet/utils/call.py b/royalnet/utils/call.py index eb68ae6f..e5fb007d 100644 --- a/royalnet/utils/call.py +++ b/royalnet/utils/call.py @@ -56,7 +56,10 @@ class Call: async def run(self): await self.session_init() - coroutine = getattr(self.command, self.interface_name) + try: + coroutine = getattr(self.command, self.interface_name) + except AttributeError: + coroutine = self.command.common try: result = await coroutine(self) finally: diff --git a/royalnet/utils/command.py b/royalnet/utils/command.py index a406746e..03584ce5 100644 --- a/royalnet/utils/command.py +++ b/royalnet/utils/command.py @@ -27,9 +27,3 @@ class Command: for network_handler in cls.network_handlers: d[network_handler.message_type] = network_handler return d - - def __getattribute__(self, item: str): - try: - return self.__dict__[item] - except KeyError: - return self.common diff --git a/royalnet/utils/networkhandler.py b/royalnet/utils/networkhandler.py index 36cf4b49..73114bcd 100644 --- a/royalnet/utils/networkhandler.py +++ b/royalnet/utils/networkhandler.py @@ -6,9 +6,3 @@ class NetworkHandler: """The NetworkHandler functions are called when a specific Message type is received.""" message_type = NotImplemented - - def __getattribute__(self, item: str): - try: - return self.__dict__[item] - except KeyError: - raise UnsupportedError()