From 9b1c8704bd3d0a603164a4e9716610212b1b24dc Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 28 Sep 2019 18:04:35 +0200 Subject: [PATCH] 5.0a59: beeg update --- README.md | 16 +- requirements.txt | 4 +- royalnet/__main__.py | 134 +++++++++++++++++ royalnet/audio/ytdldiscord.py | 2 +- royalnet/audio/ytdlmp3.py | 2 +- royalnet/bots/__init__.py | 6 +- royalnet/bots/discord.py | 28 +--- royalnet/bots/generic.py | 138 ++++++++++-------- royalnet/bots/telegram.py | 33 ++--- royalnet/commands/commandinterface.py | 2 +- royalnet/commands/royalgames/__init__.py | 2 +- royalnet/commands/royalgames/ciaoruozi.py | 2 +- royalnet/commands/royalgames/diario.py | 17 +-- royalnet/commands/royalgames/mm.py | 5 +- royalnet/configurator.py | 27 ++++ royalnet/database/__init__.py | 3 +- royalnet/database/tables/__init__.py | 4 +- royalnet/database/tables/activekvgroups.py | 6 +- royalnet/database/tables/aliases.py | 6 +- royalnet/database/tables/bios.py | 6 +- royalnet/database/tables/diario.py | 10 +- royalnet/database/tables/discord.py | 6 +- royalnet/database/tables/medalawards.py | 6 +- royalnet/database/tables/mmdecisions.py | 6 +- royalnet/database/tables/mmevents.py | 6 +- royalnet/database/tables/mmresponse.py | 6 +- royalnet/database/tables/reminders.py | 6 +- royalnet/database/tables/royals.py | 8 +- royalnet/database/tables/telegram.py | 6 +- royalnet/database/tables/triviascores.py | 6 +- royalnet/database/tables/wikirevisions.py | 6 +- royalnet/network/__init__.py | 12 +- .../{royalnetconfig.py => networkconfig.py} | 2 +- .../{royalnetlink.py => networklink.py} | 16 +- .../{royalnetserver.py => networkserver.py} | 11 +- royalnet/network/package.py | 2 +- royalnet/network/request.py | 2 +- royalnet/run.py | 26 ---- royalnet/utils/wikiport.py | 4 +- royalnet/version.py | 2 +- .../web/royalprints/diarioview/__init__.py | 6 +- royalnet/web/royalprints/home/__init__.py | 2 +- royalnet/web/royalprints/login/__init__.py | 4 +- .../web/royalprints/newaccount/__init__.py | 4 +- royalnet/web/royalprints/profile/__init__.py | 2 +- royalnet/web/royalprints/tglogin/__init__.py | 6 +- royalnet/web/royalprints/wikiedit/__init__.py | 6 +- royalnet/web/royalprints/wikiview/__init__.py | 6 +- setup.py | 5 +- tests/test_network.py | 10 +- 50 files changed, 384 insertions(+), 259 deletions(-) create mode 100644 royalnet/__main__.py create mode 100644 royalnet/configurator.py rename royalnet/network/{royalnetconfig.py => networkconfig.py} (94%) rename royalnet/network/{royalnetlink.py => networklink.py} (90%) rename royalnet/network/{royalnetserver.py => networkserver.py} (95%) delete mode 100644 royalnet/run.py diff --git a/README.md b/README.md index a23b0c88..19440be1 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,10 @@ It has a lot of submodules, many of which may be used in other bots. [Documentation available here](https://royal-games.github.io/royalnet/html/index.html). -## Installation for the Royal Games community +## Quick install -With `python3.7` and `pip` installed, run: - -```bash -pip install royalnet -pip install websockets --upgrade -export TG_AK="Telegram API Key" -export DS_AK="Discord API Key" -export DB_PATH="sqlalchemy database path to postgresql" -export MASTER_KEY="A secret password to connect to the network" -python3.7 -m royalnet.royalgames -OO ``` +pip3.7 install royalnet +python3.7 -m royalnet.configurator +python3.7 -m royalnet -c (YOUR_COMMAND_PACK) +``` \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index aa73f73c..9571e829 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -bcrypt +bcrypt>=3.1.7 python-telegram-bot>=11.1.0 websockets>=7.0 pytest>=4.3.1 @@ -20,3 +20,5 @@ mcstatus>=2.2.1 sortedcontainers>=2.1.0 sentry-sdk>=0.11.1 click>=7.0 +keyring>=19.2.0 +urllib3>=1.25.6 diff --git a/royalnet/__main__.py b/royalnet/__main__.py new file mode 100644 index 00000000..2fd5281d --- /dev/null +++ b/royalnet/__main__.py @@ -0,0 +1,134 @@ +import click +import typing +import importlib +import royalnet as r +import multiprocessing +import keyring + + +@click.command() +@click.option("--telegram/--no-telegram", default=None, + help="Enable/disable the Telegram module.") +@click.option("--discord/--no-discord", default=None, + help="Enable/disable the Discord module.") +@click.option("-d", "--database", type=str, default=None, + help="The PostgreSQL database path.") +@click.option("-c", "--command-packs", type=str, multiple=True, default=[], + help="The names of the command pack modules that should be imported.") +@click.option("-n", "--network-address", type=str, default=None, + help="The Network server URL to connect to.") +@click.option("--local-network-server/--no-local-network-server", default=True, + help="Locally run a Network server, bind it to port 44444.") +@click.option("-s", "--secrets-name", type=str, default="__default__", + help="The name in the keyring that the secrets are stored with.") +def run(telegram: typing.Optional[bool], + discord: typing.Optional[bool], + database: typing.Optional[str], + command_packs: typing.List[str], + network_address: typing.Optional[str], + local_network_server: bool, + secrets_name: str): + + # Get the network password + network_password = keyring.get_password(f"Royalnet/{secrets_name}", "network") + + # Get the sentry dsn + sentry_dsn = keyring.get_password(f"Royalnet/{secrets_name}", "sentry") + + # Enable / Disable interfaces + interfaces = { + "telegram": telegram, + "discord": discord + } + # If any interface is True, then the undefined ones should be False + if any(interfaces[name] is True for name in interfaces): + for name in interfaces: + if interfaces[name] is None: + interfaces[name] = False + # Likewise, if any interface is False, then the undefined ones should be True + elif any(interfaces[name] is False for name in interfaces): + for name in interfaces: + if interfaces[name] is None: + interfaces[name] = True + # Otherwise, if no interfaces are specified, all should be enabled + else: + assert all(interfaces[name] is None for name in interfaces) + for name in interfaces: + interfaces[name] = True + + server_process: typing.Optional[multiprocessing.Process] = None + # Start the network server + if local_network_server: + server_process = multiprocessing.Process(name="Network Server", + target=r.network.NetworkServer("0.0.0.0", + 44444, + network_password).run_blocking(), + daemon=True) + server_process.start() + network_address = "ws://127.0.0.1:44444/" + + # Create a Royalnet configuration + network_config: typing.Optional[r.network.NetworkConfig] = None + if network_address is not None: + network_config = r.network.NetworkConfig(network_address, network_password) + + # Create a Alchemy configuration + telegram_db_config: typing.Optional[r.database.DatabaseConfig] = None + discord_db_config: typing.Optional[r.database.DatabaseConfig] = None + if database is not None: + telegram_db_config = r.database.DatabaseConfig(database, + r.database.tables.User, + r.database.tables.Telegram, + "tg_id") + discord_db_config = r.database.DatabaseConfig(database, + r.database.tables.User, + r.database.tables.Discord, + "discord_id") + + # Import command packs + if not command_packs: + raise click.ClickException("No command packs were specified.") + enabled_commands = [] + for pack in command_packs: + imported = importlib.import_module(pack) + try: + imported_commands = imported.commands + except AttributeError: + raise click.ClickException(f"{pack} isn't a Royalnet command pack.") + enabled_commands = [*enabled_commands, *imported_commands] + + telegram_process: typing.Optional[multiprocessing.Process] = None + if interfaces["telegram"]: + telegram_bot = r.bots.TelegramBot(network_config=network_config, + database_config=telegram_db_config, + sentry_dsn=sentry_dsn, + commands=enabled_commands, + secrets_name=secrets_name) + telegram_process = multiprocessing.Process(name="Telegram Interface", + target=telegram_bot.run_blocking, + daemon=True) + telegram_process.start() + + discord_process: typing.Optional[multiprocessing.Process] = None + if interfaces["discord"]: + discord_bot = r.bots.DiscordBot(network_config=network_config, + database_config=discord_db_config, + sentry_dsn=sentry_dsn, + commands=enabled_commands, + secrets_name=secrets_name) + discord_process = multiprocessing.Process(name="Discord Interface", + target=discord_bot.run_blocking, + daemon=True) + discord_process.start() + + click.echo("Royalnet processes have been started. You can force-quit by pressing Ctrl+C.") + if server_process is not None: + server_process.join() + if telegram_process is not None: + telegram_process.join() + if discord_process is not None: + discord_process.join() + + +if __name__ == "__main__": + run() diff --git a/royalnet/audio/ytdldiscord.py b/royalnet/audio/ytdldiscord.py index db31074b..1a5bb93f 100644 --- a/royalnet/audio/ytdldiscord.py +++ b/royalnet/audio/ytdldiscord.py @@ -24,7 +24,7 @@ class YtdlDiscord: ffmpeg.input(self.ytdl_file.filename) .output(destination_filename, format="s16le", ac=2, ar="48000") .overwrite_output() - .run(quiet=True) + .run_async(quiet=True) ) self.pcm_filename = destination_filename diff --git a/royalnet/audio/ytdlmp3.py b/royalnet/audio/ytdlmp3.py index 81e86b7f..f4607b5f 100644 --- a/royalnet/audio/ytdlmp3.py +++ b/royalnet/audio/ytdlmp3.py @@ -24,7 +24,7 @@ class YtdlMp3: ffmpeg.input(self.ytdl_file.filename) .output(destination_filename, format="mp3") .overwrite_output() - .run() + .run_async() ) self.mp3_filename = destination_filename diff --git a/royalnet/bots/__init__.py b/royalnet/bots/__init__.py index e2574a04..75bf4e5a 100644 --- a/royalnet/bots/__init__.py +++ b/royalnet/bots/__init__.py @@ -1,7 +1,7 @@ """Various bot interfaces, and a generic class to create new ones.""" from .generic import GenericBot -from .telegram import TelegramBot, TelegramConfig -from .discord import DiscordBot, DiscordConfig +from .telegram import TelegramBot +from .discord import DiscordBot -__all__ = ["TelegramBot", "TelegramConfig", "DiscordBot", "DiscordConfig", "GenericBot"] +__all__ = ["TelegramBot", "DiscordBot", "GenericBot"] diff --git a/royalnet/bots/discord.py b/royalnet/bots/discord.py index fa948795..ce0d65db 100644 --- a/royalnet/bots/discord.py +++ b/royalnet/bots/discord.py @@ -1,12 +1,9 @@ import discord -import typing import sentry_sdk import logging as _logging from .generic import GenericBot from ..utils import * from ..error import * -from ..network import * -from ..database import * from ..audio import * from ..commands import * @@ -17,12 +14,6 @@ if not discord.opus.is_loaded(): log.error("Opus is not loaded. Weird behaviour might emerge.") -class DiscordConfig: - """The specific configuration to be used for :py:class:`royalnet.bots.DiscordBot`.""" - def __init__(self, token: str): - self.token = token - - class DiscordBot(GenericBot): """A bot that connects to `Discord `_.""" interface_name = "discord" @@ -204,24 +195,19 @@ class DiscordBot(GenericBot): self._Client = self._bot_factory() self.client = self._Client() - def __init__(self, *, - discord_config: DiscordConfig, - royalnet_config: typing.Optional[RoyalnetConfig] = None, - database_config: typing.Optional[DatabaseConfig] = None, - sentry_dsn: typing.Optional[str] = None, - commands: typing.List[typing.Type[Command]] = None): - super().__init__(royalnet_config=royalnet_config, - database_config=database_config, - sentry_dsn=sentry_dsn, - commands=commands) - self._discord_config = discord_config + def _initialize(self): + super()._initialize() self._init_client() self._init_voice() async def run(self): """Login to Discord, then run the bot.""" + if not self.initialized: + self._initialize() + log.debug("Getting Discord secret") + token = self.get_secret("discord") log.info(f"Logging in to Discord") - await self.client.login(self._discord_config.token) + await self.client.login(token) log.info(f"Connecting to Discord") await self.client.connect() # TODO: how to stop? diff --git a/royalnet/bots/generic.py b/royalnet/bots/generic.py index 04b32451..6eca5297 100644 --- a/royalnet/bots/generic.py +++ b/royalnet/bots/generic.py @@ -1,8 +1,8 @@ import sys -import typing import asyncio import logging import sentry_sdk +import keyring from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration from sentry_sdk.integrations.aiohttp import AioHttpIntegration from sentry_sdk.integrations.logging import LoggingIntegration @@ -21,7 +21,7 @@ class GenericBot: :py:class:`royalnet.bots.TelegramBot` and :py:class:`royalnet.bots.DiscordBot`. """ interface_name = NotImplemented - def _init_commands(self, commands: typing.List[typing.Type[Command]]) -> None: + def _init_commands(self) -> None: """Generate the ``commands`` dictionary required to handle incoming messages, and the ``network_handlers`` dictionary required to handle incoming requests. """ log.debug(f"Now binding commands") @@ -29,13 +29,13 @@ class GenericBot: self._Data = self._data_factory() self.commands = {} self.network_handlers: typing.Dict[str, typing.Type[NetworkHandler]] = {} - for SelectedCommand in commands: + for SelectedCommand in self.uninitialized_commands: log.debug(f"Binding {SelectedCommand.name}...") interface = self._Interface() try: self.commands[f"{interface.prefix}{SelectedCommand.name}"] = SelectedCommand(interface) except Exception as e: - log.error(f"{e} during the initialization of {SelectedCommand.name}, skipping...") + log.error(f"{e.__class__.__name__} during the initialization of {SelectedCommand.name}, skipping...") log.debug(f"Successfully bound commands") def _interface_factory(self) -> typing.Type[CommandInterface]: @@ -71,15 +71,17 @@ class GenericBot: def _data_factory(self) -> typing.Type[CommandData]: raise NotImplementedError() - def _init_royalnet(self, royalnet_config: RoyalnetConfig): - """Create a :py:class:`royalnet.network.RoyalnetLink`, and run it as a :py:class:`asyncio.Task`.""" - self.network: RoyalnetLink = RoyalnetLink(royalnet_config.master_uri, royalnet_config.master_secret, - self.interface_name, self._network_handler) - log.debug(f"Running RoyalnetLink {self.network}") - self.loop.create_task(self.network.run()) + def _init_network(self): + """Create a :py:class:`royalnet.network.NetworkLink`, and run it as a :py:class:`asyncio.Task`.""" + if self.uninitialized_network_config is not None: + self.network: NetworkLink = NetworkLink(self.uninitialized_network_config.master_uri, + self.uninitialized_network_config.master_secret, + self.interface_name, self._network_handler) + log.debug(f"Running NetworkLink {self.network}") + self.loop.create_task(self.network.run()) async def _network_handler(self, request_dict: dict) -> dict: - """Handle a single :py:class:`dict` received from the :py:class:`royalnet.network.RoyalnetLink`. + """Handle a single :py:class:`dict` received from the :py:class:`royalnet.network.NetworkLink`. Returns: Another :py:class:`dict`, formatted as a :py:class:`royalnet.network.Response`.""" @@ -91,7 +93,7 @@ class GenericBot: return ResponseError("invalid_request", f"The Request that you sent was invalid. Check extra_info to see what you sent.", extra_info={"you_sent": request_dict}).to_dict() - log.debug(f"Received {request} from the RoyalnetLink") + log.debug(f"Received {request} from the NetworkLink") try: network_handler = self.network_handlers[request.handler] except KeyError: @@ -115,59 +117,79 @@ class GenericBot: "str": str(exc) }).to_dict() - def _init_database(self, commands: typing.List[typing.Type[Command]], database_config: DatabaseConfig): + def _init_database(self): """Create an :py:class:`royalnet.database.Alchemy` with the tables required by the commands. Then, find the chain that links the ``master_table`` to the ``identity_table``. """ - log.debug(f"Initializing database") - required_tables = {database_config.master_table, database_config.identity_table} - for command in commands: - required_tables = required_tables.union(command.require_alchemy_tables) - log.debug(f"Found {len(required_tables)} required 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) - log.debug(f"Identity chain is {self.identity_chain}") + if self.uninitialized_database_config: + log.debug(f"Initializing database") + required_tables = {self.uninitialized_database_config.master_table, self.uninitialized_database_config.identity_table} + for command in self.uninitialized_commands: + required_tables = required_tables.union(command.require_alchemy_tables) + log.debug(f"Found {len(required_tables)} required tables") + self.alchemy = Alchemy(self.uninitialized_database_config.database_uri, required_tables) + self.master_table = self.alchemy.__getattribute__(self.uninitialized_database_config.master_table.__name__) + self.identity_table = self.alchemy.__getattribute__(self.uninitialized_database_config.identity_table.__name__) + self.identity_column = self.identity_table.__getattribute__(self.identity_table, + self.uninitialized_database_config.identity_column_name) + self.identity_chain = relationshiplinkchain(self.master_table, self.identity_table) + log.debug(f"Identity chain is {self.identity_chain}") + else: + log.debug(f"Database is not enabled, setting everything to None") + self.alchemy = None + self.master_table = None + self.identity_table = None + self.identity_column = None + + def _init_sentry(self): + if self.uninitialized_sentry_dsn: + log.debug("Sentry integration enabled") + self.sentry = sentry_sdk.init(self.uninitialized_sentry_dsn, + integrations=[AioHttpIntegration(), + SqlalchemyIntegration(), + LoggingIntegration(event_level=None)]) + else: + log.debug("Sentry integration disabled") + + def _init_loop(self): + if self.uninitialized_loop is None: + self.loop = asyncio.get_event_loop() + else: + self.loop = self.uninitialized_loop def __init__(self, *, - royalnet_config: typing.Optional[RoyalnetConfig] = None, + network_config: typing.Optional[NetworkConfig] = None, database_config: typing.Optional[DatabaseConfig] = None, commands: typing.List[typing.Type[Command]] = None, sentry_dsn: typing.Optional[str] = None, - loop: asyncio.AbstractEventLoop = None): - if loop is None: - self.loop = asyncio.get_event_loop() - else: - self.loop = loop - if sentry_dsn: - log.debug("Sentry integration enabled") - self.sentry = sentry_sdk.init(sentry_dsn, integrations=[AioHttpIntegration(), - SqlalchemyIntegration(), - LoggingIntegration(event_level=None)]) - else: - log.debug("Sentry integration disabled") - try: - 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) - if commands is None: - commands = [] - self._init_commands(commands) - if royalnet_config is None: - self.network = None - else: - self._init_royalnet(royalnet_config=royalnet_config) - except Exception as e: - sentry_sdk.capture_exception(e) - log.error(f"{e.__class__.__name__} while initializing Royalnet: {' | '.join(e.args)}") - raise + loop: asyncio.AbstractEventLoop = None, + secrets_name: str = "__default__"): + self.initialized = False + self.uninitialized_network_config = network_config + self.uninitialized_database_config = database_config + self.uninitialized_commands = commands + self.uninitialized_sentry_dsn = sentry_dsn + self.uninitialized_loop = loop + self.secrets_name = secrets_name - async def run(self): + def get_secret(self, username: str): + return keyring.get_password(f"Royalnet/{self.secrets_name}", username) + + def set_secret(self, username: str, password: str): + return keyring.set_password(f"Royalnet/{self.secrets_name}", username, password) + + def _initialize(self): + if not self.initialized: + self._init_sentry() + self._init_loop() + self._init_database() + self._init_commands() + self._init_network() + self.initialized = True + + def run(self): """A blocking coroutine that should make the bot start listening to commands and requests.""" raise NotImplementedError() + + def run_blocking(self): + self._initialize() + self.loop.run_until_complete(self.run()) diff --git a/royalnet/bots/telegram.py b/royalnet/bots/telegram.py index 893f539b..3c102d1e 100644 --- a/royalnet/bots/telegram.py +++ b/royalnet/bots/telegram.py @@ -1,6 +1,5 @@ import telegram import telegram.utils.request -import typing import uuid import urllib3 import asyncio @@ -9,20 +8,12 @@ import logging as _logging from .generic import GenericBot from ..utils import * from ..error import * -from ..network import * -from ..database import * from ..commands import * log = _logging.getLogger(__name__) -class TelegramConfig: - """The specific configuration to be used for :py:class:`royalnet.database.TelegramBot`.""" - def __init__(self, token: str): - self.token: str = token - - class TelegramBot(GenericBot): """A bot that connects to `Telegram `_.""" interface_name = "telegram" @@ -30,8 +21,9 @@ class TelegramBot(GenericBot): def _init_client(self): """Create the :py:class:`telegram.Bot`, and set the starting offset.""" # https://github.com/python-telegram-bot/python-telegram-bot/issues/341 - request = telegram.utils.request.Request(20, read_timeout=15) - self.client = telegram.Bot(self._telegram_config.token, request=request) + request = telegram.utils.request.Request(5, read_timeout=30) + token = self.get_secret("telegram") + self.client = telegram.Bot(token, request=request) self._offset: int = -100 def _interface_factory(self) -> typing.Type[CommandInterface]: @@ -109,19 +101,6 @@ class TelegramBot(GenericBot): return TelegramData - def __init__(self, *, - telegram_config: TelegramConfig, - royalnet_config: typing.Optional[RoyalnetConfig] = None, - database_config: typing.Optional[DatabaseConfig] = None, - sentry_dsn: typing.Optional[str] = None, - commands: typing.List[typing.Type[Command]] = None): - super().__init__(royalnet_config=royalnet_config, - database_config=database_config, - sentry_dsn=sentry_dsn, - commands=commands) - self._telegram_config = telegram_config - self._init_client() - @staticmethod async def safe_api_call(f: typing.Callable, *args, **kwargs) -> typing.Optional: while True: @@ -225,7 +204,13 @@ class TelegramBot(GenericBot): else: await self.safe_api_call(query.answer, text=response) + def _initialize(self): + super()._initialize() + self._init_client() + async def run(self): + if not self.initialized: + self._initialize() while True: # Get the latest 100 updates last_updates: typing.List[telegram.Update] = await self.safe_api_call(self.client.get_updates, diff --git a/royalnet/commands/commandinterface.py b/royalnet/commands/commandinterface.py index 22af3ad6..d687ae55 100644 --- a/royalnet/commands/commandinterface.py +++ b/royalnet/commands/commandinterface.py @@ -28,7 +28,7 @@ class CommandInterface: raise UnsupportedError() async def net_request(self, message, destination: str) -> dict: - """Send data through a :py:class:`royalnet.network.RoyalnetLink` and wait for a + """Send data through a :py:class:`royalnet.network.NetworkLink` and wait for a :py:class:`royalnet.network.Reply`. Parameters: diff --git a/royalnet/commands/royalgames/__init__.py b/royalnet/commands/royalgames/__init__.py index 346f1d9f..aa8b0f85 100644 --- a/royalnet/commands/royalgames/__init__.py +++ b/royalnet/commands/royalgames/__init__.py @@ -1,6 +1,6 @@ """Commands that can be used in bots. -These probably won't suit your needs, as they are tailored for the bots of the Royal Games gaming community, but they +These probably won't suit your needs, as they are tailored for the bots of the User Games gaming community, but they may be useful to develop new ones.""" from .ciaoruozi import CiaoruoziCommand diff --git a/royalnet/commands/royalgames/ciaoruozi.py b/royalnet/commands/royalgames/ciaoruozi.py index 5138f9a8..e6d652c5 100644 --- a/royalnet/commands/royalgames/ciaoruozi.py +++ b/royalnet/commands/royalgames/ciaoruozi.py @@ -8,7 +8,7 @@ from ..commanddata import CommandData class CiaoruoziCommand(Command): name: str = "ciaoruozi" - description: str = "Saluta Ruozi, un leggendario essere che una volta era in Royal Games." + description: str = "Saluta Ruozi, un leggendario essere che una volta era in User Games." syntax: str = "" diff --git a/royalnet/commands/royalgames/diario.py b/royalnet/commands/royalgames/diario.py index 664f67de..f12a9b12 100644 --- a/royalnet/commands/royalgames/diario.py +++ b/royalnet/commands/royalgames/diario.py @@ -2,26 +2,21 @@ import typing import re import datetime import telegram -import os import aiohttp from ..command import Command from ..commandargs import CommandArgs from ..commanddata import CommandData -from ...database.tables import Royal, Diario, Alias +from ...database.tables import User, Diario, Alias from ...utils import asyncify from ...error import * -async def to_imgur(photosizes: typing.List[telegram.PhotoSize], caption="") -> str: +async def to_imgur(imgur_api_key, photosizes: typing.List[telegram.PhotoSize], caption="") -> str: # Select the largest photo largest_photo = sorted(photosizes, key=lambda p: p.width * p.height)[-1] # Get the photo url photo_file: telegram.File = await asyncify(largest_photo.get_file) # Forward the url to imgur, as an upload - try: - imgur_api_key = os.environ["IMGUR_CLIENT_ID"] - except KeyError: - raise InvalidConfigError("Missing IMGUR_CLIENT_ID envvar, can't upload images to imgur.") async with aiohttp.request("post", "https://api.imgur.com/3/upload", data={ "image": photo_file.file_path, "type": "URL", @@ -43,7 +38,7 @@ class DiarioCommand(Command): syntax = "[!] \"(testo)\" --[autore], [contesto]" - require_alchemy_tables = {Royal, Diario, Alias} + require_alchemy_tables = {User, Diario, Alias} async def run(self, args: CommandArgs, data: CommandData) -> None: if self.interface.name == "telegram": @@ -74,7 +69,8 @@ class DiarioCommand(Command): if photosizes: # Text is a caption text = reply.caption - media_url = await to_imgur(photosizes, text if text is not None else "") + media_url = await to_imgur(self.interface.bot.get_secret("imgur"), + photosizes, text if text is not None else "") else: media_url = None # Ensure there is a text or an image @@ -101,7 +97,8 @@ class DiarioCommand(Command): # Check if there's an image associated with the reply photosizes: typing.Optional[typing.List[telegram.PhotoSize]] = message.photo if photosizes: - media_url = await to_imgur(photosizes, raw_text if raw_text is not None else "") + media_url = await to_imgur(self.interface.bot.get_secret("imgur"), + photosizes, raw_text if raw_text is not None else "") else: media_url = None # Parse the text, if it exists diff --git a/royalnet/commands/royalgames/mm.py b/royalnet/commands/royalgames/mm.py index 55b59b80..f3b130c2 100644 --- a/royalnet/commands/royalgames/mm.py +++ b/royalnet/commands/royalgames/mm.py @@ -1,6 +1,5 @@ import datetime import dateparser -import os import telegram import asyncio import re @@ -98,7 +97,7 @@ class MmCommand(Command): try: await self.interface.bot.safe_api_call(client.edit_message_text, text=telegram_escape(self._main_text(mmevent)), - chat_id=os.environ["MM_CHANNEL_ID"], + chat_id=-1001224004974, message_id=mmevent.message_id, parse_mode="HTML", disable_web_page_preview=True, @@ -384,7 +383,7 @@ class MmCommand(Command): await asyncify(self.interface.session.commit) message: telegram.Message = await self.interface.bot.safe_api_call(client.send_message, - chat_id=os.environ["MM_CHANNEL_ID"], + chat_id=-1001224004974, text=telegram_escape(self._main_text(mmevent)), parse_mode="HTML", disable_webpage_preview=True, diff --git a/royalnet/configurator.py b/royalnet/configurator.py new file mode 100644 index 00000000..ba823126 --- /dev/null +++ b/royalnet/configurator.py @@ -0,0 +1,27 @@ +import click +import keyring + + +@click.command() +def run(): + click.echo("Welcome to the Royalnet configuration creator!") + secrets_name = click.prompt("Desired secrets name", default="__default__") + network = click.prompt("Network password", default="") + if network: + keyring.set_password(f"Royalnet/{secrets_name}", "network", network) + telegram = click.prompt("Telegram Bot API token", default="") + if telegram: + keyring.set_password(f"Royalnet/{secrets_name}", "telegram", telegram) + discord = click.prompt("Discord Bot API token", default="") + if discord: + keyring.set_password(f"Royalnet/{secrets_name}", "discord", discord) + imgur = click.prompt("Imgur API token", default="") + if imgur: + keyring.set_password(f"Royalnet/{secrets_name}", "imgur", imgur) + sentry = click.prompt("Sentry DSN", default="") + if sentry: + keyring.set_password(f"Royalnet/{secrets_name}", "sentry", sentry) + + +if __name__ == "__main__": + run() diff --git a/royalnet/database/__init__.py b/royalnet/database/__init__.py index 9434c897..66e22bf6 100644 --- a/royalnet/database/__init__.py +++ b/royalnet/database/__init__.py @@ -3,5 +3,6 @@ from .alchemy import Alchemy from .relationshiplinkchain import relationshiplinkchain from .databaseconfig import DatabaseConfig +from . import tables -__all__ = ["Alchemy", "relationshiplinkchain", "DatabaseConfig"] +__all__ = ["Alchemy", "relationshiplinkchain", "DatabaseConfig", "tables"] diff --git a/royalnet/database/tables/__init__.py b/royalnet/database/tables/__init__.py index 891254b4..de1a083d 100644 --- a/royalnet/database/tables/__init__.py +++ b/royalnet/database/tables/__init__.py @@ -1,4 +1,4 @@ -from .royals import Royal +from .royals import User from .telegram import Telegram from .diario import Diario from .aliases import Alias @@ -17,6 +17,6 @@ from .mmdecisions import MMDecision from .mmevents import MMEvent from .mmresponse import MMResponse -__all__ = ["Royal", "Telegram", "Diario", "Alias", "ActiveKvGroup", "Keyvalue", "Keygroup", "Discord", "WikiPage", +__all__ = ["User", "Telegram", "Diario", "Alias", "ActiveKvGroup", "Keyvalue", "Keygroup", "Discord", "WikiPage", "WikiRevision", "Medal", "MedalAward", "Bio", "Reminder", "TriviaScore", "MMDecision", "MMEvent", "MMResponse"] diff --git a/royalnet/database/tables/activekvgroups.py b/royalnet/database/tables/activekvgroups.py index aa3bd0f7..429003c2 100644 --- a/royalnet/database/tables/activekvgroups.py +++ b/royalnet/database/tables/activekvgroups.py @@ -5,7 +5,7 @@ from sqlalchemy import Column, \ from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr # noinspection PyUnresolvedReferences -from .royals import Royal +from .royals import User # noinspection PyUnresolvedReferences from .keygroups import Keygroup @@ -15,7 +15,7 @@ class ActiveKvGroup: @declared_attr def royal_id(self): - return Column(Integer, ForeignKey("royals.uid"), primary_key=True) + return Column(Integer, ForeignKey("users.uid"), primary_key=True) @declared_attr def group_name(self): @@ -23,7 +23,7 @@ class ActiveKvGroup: @declared_attr def royal(self): - return relationship("Royal", backref="active_kv_group") + return relationship("User", backref="active_kv_group") @declared_attr def group(self): diff --git a/royalnet/database/tables/aliases.py b/royalnet/database/tables/aliases.py index 16a42fe5..afccf47f 100644 --- a/royalnet/database/tables/aliases.py +++ b/royalnet/database/tables/aliases.py @@ -5,7 +5,7 @@ from sqlalchemy import Column, \ from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr # noinspection PyUnresolvedReferences -from .royals import Royal +from .royals import User class Alias: @@ -13,7 +13,7 @@ class Alias: @declared_attr def royal_id(self): - return Column(Integer, ForeignKey("royals.uid")) + return Column(Integer, ForeignKey("users.uid")) @declared_attr def alias(self): @@ -21,7 +21,7 @@ class Alias: @declared_attr def royal(self): - return relationship("Royal", backref="aliases") + return relationship("User", backref="aliases") def __repr__(self): return f"" diff --git a/royalnet/database/tables/bios.py b/royalnet/database/tables/bios.py index 82176c7c..64ac8506 100644 --- a/royalnet/database/tables/bios.py +++ b/royalnet/database/tables/bios.py @@ -4,7 +4,7 @@ from sqlalchemy import Column, \ ForeignKey from sqlalchemy.orm import relationship, backref from sqlalchemy.ext.declarative import declared_attr -from .royals import Royal +from .royals import User class Bio: @@ -12,11 +12,11 @@ class Bio: @declared_attr def royal_id(self): - return Column(Integer, ForeignKey("royals.uid"), primary_key=True) + return Column(Integer, ForeignKey("users.uid"), primary_key=True) @declared_attr def royal(self): - return relationship("Royal", backref=backref("bio", uselist=False)) + return relationship("User", backref=backref("bio", uselist=False)) @declared_attr def contents(self): diff --git a/royalnet/database/tables/diario.py b/royalnet/database/tables/diario.py index 44fc8ebd..060489fd 100644 --- a/royalnet/database/tables/diario.py +++ b/royalnet/database/tables/diario.py @@ -9,7 +9,7 @@ from sqlalchemy import Column, \ from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr # noinspection PyUnresolvedReferences -from .royals import Royal +from .royals import User class Diario: @@ -21,11 +21,11 @@ class Diario: @declared_attr def creator_id(self): - return Column(Integer, ForeignKey("royals.uid")) + return Column(Integer, ForeignKey("users.uid")) @declared_attr def quoted_account_id(self): - return Column(Integer, ForeignKey("royals.uid")) + return Column(Integer, ForeignKey("users.uid")) @declared_attr def quoted(self): @@ -53,11 +53,11 @@ class Diario: @declared_attr def creator(self): - return relationship("Royal", foreign_keys=self.creator_id, backref="diario_created") + return relationship("User", foreign_keys=self.creator_id, backref="diario_created") @declared_attr def quoted_account(self): - return relationship("Royal", foreign_keys=self.quoted_account_id, backref="diario_quoted") + return relationship("User", foreign_keys=self.quoted_account_id, backref="diario_quoted") def __repr__(self): return f"" diff --git a/royalnet/database/tables/medalawards.py b/royalnet/database/tables/medalawards.py index fa16b538..6e54abfd 100644 --- a/royalnet/database/tables/medalawards.py +++ b/royalnet/database/tables/medalawards.py @@ -4,7 +4,7 @@ from sqlalchemy import Column, \ ForeignKey from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import relationship -from .royals import Royal +from .royals import User from .medals import Medal @@ -25,7 +25,7 @@ class MedalAward: @declared_attr def royal_id(self): - return Column(Integer, ForeignKey("royal.uid"), nullable=False) + return Column(Integer, ForeignKey("users.uid"), nullable=False) @declared_attr def medal(self): @@ -33,7 +33,7 @@ class MedalAward: @declared_attr def royal(self): - return relationship("Royal", backref="medals_received") + return relationship("User", backref="medals_received") def __repr__(self): return f"" diff --git a/royalnet/database/tables/mmdecisions.py b/royalnet/database/tables/mmdecisions.py index 12b06cf5..3e2bdc8c 100644 --- a/royalnet/database/tables/mmdecisions.py +++ b/royalnet/database/tables/mmdecisions.py @@ -4,7 +4,7 @@ from sqlalchemy import Column, \ ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr -from .royals import Royal +from .royals import User from .mmevents import MMEvent @@ -13,11 +13,11 @@ class MMDecision: @declared_attr def royal_id(self): - return Column(Integer, ForeignKey("royals.uid"), primary_key=True) + return Column(Integer, ForeignKey("users.uid"), primary_key=True) @declared_attr def royal(self): - return relationship("Royal", backref="mmdecisions_taken") + return relationship("User", backref="mmdecisions_taken") @declared_attr def mmevent_id(self): diff --git a/royalnet/database/tables/mmevents.py b/royalnet/database/tables/mmevents.py index aff80841..774f9242 100644 --- a/royalnet/database/tables/mmevents.py +++ b/royalnet/database/tables/mmevents.py @@ -9,7 +9,7 @@ from sqlalchemy import Column, \ BigInteger from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr -from .royals import Royal +from .royals import User if typing.TYPE_CHECKING: from .mmdecisions import MMDecision from .mmresponse import MMResponse @@ -20,11 +20,11 @@ class MMEvent: @declared_attr def creator_id(self): - return Column(Integer, ForeignKey("royals.uid"), nullable=False) + return Column(Integer, ForeignKey("users.uid"), nullable=False) @declared_attr def creator(self): - return relationship("Royal", backref="mmevents_created") + return relationship("User", backref="mmevents_created") @declared_attr def mmid(self): diff --git a/royalnet/database/tables/mmresponse.py b/royalnet/database/tables/mmresponse.py index 60ae3ff9..a6643586 100644 --- a/royalnet/database/tables/mmresponse.py +++ b/royalnet/database/tables/mmresponse.py @@ -4,7 +4,7 @@ from sqlalchemy import Column, \ ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr -from .royals import Royal +from .royals import User from .mmevents import MMEvent @@ -13,11 +13,11 @@ class MMResponse: @declared_attr def royal_id(self): - return Column(Integer, ForeignKey("royals.uid"), primary_key=True) + return Column(Integer, ForeignKey("users.uid"), primary_key=True) @declared_attr def royal(self): - return relationship("Royal", backref="mmresponses_given") + return relationship("User", backref="mmresponses_given") @declared_attr def mmevent_id(self): diff --git a/royalnet/database/tables/reminders.py b/royalnet/database/tables/reminders.py index 95f6d331..31a36db3 100644 --- a/royalnet/database/tables/reminders.py +++ b/royalnet/database/tables/reminders.py @@ -7,7 +7,7 @@ from sqlalchemy import Column, \ from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr # noinspection PyUnresolvedReferences -from .royals import Royal +from .royals import User class Reminder: @@ -19,11 +19,11 @@ class Reminder: @declared_attr def creator_id(self): - return Column(Integer, ForeignKey("royals.uid")) + return Column(Integer, ForeignKey("users.uid")) @declared_attr def creator(self): - return relationship("Royal", backref="reminders_created") + return relationship("User", backref="reminders_created") @declared_attr def interface_name(self): diff --git a/royalnet/database/tables/royals.py b/royalnet/database/tables/royals.py index 71292c1b..fa2454ed 100644 --- a/royalnet/database/tables/royals.py +++ b/royalnet/database/tables/royals.py @@ -5,8 +5,8 @@ from sqlalchemy import Column, \ from sqlalchemy.ext.declarative import declared_attr -class Royal: - __tablename__ = "royals" +class User: + __tablename__ = "users" @declared_attr def uid(self): @@ -29,7 +29,7 @@ class Royal: return Column(LargeBinary) def __repr__(self): - return f"" + return f"" def __str__(self): - return f"[c]royalnet:{self.username}[/c]" + return self.username diff --git a/royalnet/database/tables/telegram.py b/royalnet/database/tables/telegram.py index f5725c41..5f922659 100644 --- a/royalnet/database/tables/telegram.py +++ b/royalnet/database/tables/telegram.py @@ -6,7 +6,7 @@ from sqlalchemy import Column, \ from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr # noinspection PyUnresolvedReferences -from .royals import Royal +from .royals import User class Telegram: @@ -14,7 +14,7 @@ class Telegram: @declared_attr def royal_id(self): - return Column(Integer, ForeignKey("royals.uid")) + return Column(Integer, ForeignKey("users.uid")) @declared_attr def tg_id(self): @@ -34,7 +34,7 @@ class Telegram: @declared_attr def royal(self): - return relationship("Royal", backref="telegram") + return relationship("User", backref="telegram") def __repr__(self): return f"" diff --git a/royalnet/database/tables/triviascores.py b/royalnet/database/tables/triviascores.py index d0b75b2d..35f00129 100644 --- a/royalnet/database/tables/triviascores.py +++ b/royalnet/database/tables/triviascores.py @@ -3,7 +3,7 @@ from sqlalchemy import Column, \ ForeignKey from sqlalchemy.orm import relationship, backref from sqlalchemy.ext.declarative import declared_attr -from .royals import Royal +from .royals import User class TriviaScore: @@ -11,11 +11,11 @@ class TriviaScore: @declared_attr def royal_id(self): - return Column(Integer, ForeignKey("royals.uid"), primary_key=True) + return Column(Integer, ForeignKey("users.uid"), primary_key=True) @declared_attr def royal(self): - return relationship("Royal", backref=backref("trivia_score", uselist=False)) + return relationship("User", backref=backref("trivia_score", uselist=False)) @declared_attr def correct_answers(self): diff --git a/royalnet/database/tables/wikirevisions.py b/royalnet/database/tables/wikirevisions.py index e4abfc90..8713876e 100644 --- a/royalnet/database/tables/wikirevisions.py +++ b/royalnet/database/tables/wikirevisions.py @@ -9,7 +9,7 @@ from sqlalchemy.ext.declarative import declared_attr # noinspection PyUnresolvedReferences from .wikipages import WikiPage # noinspection PyUnresolvedReferences -from .royals import Royal +from .royals import User class WikiRevision: @@ -33,11 +33,11 @@ class WikiRevision: @declared_attr def author_id(self): - return Column(Integer, ForeignKey("royals.uid"), nullable=False) + return Column(Integer, ForeignKey("users.uid"), nullable=False) @declared_attr def author(self): - return relationship("Royal", foreign_keys=self.author_id, backref="wiki_contributions") + return relationship("User", foreign_keys=self.author_id, backref="wiki_contributions") @declared_attr def timestamp(self): diff --git a/royalnet/network/__init__.py b/royalnet/network/__init__.py index eee8ebc4..ea229aaf 100644 --- a/royalnet/network/__init__.py +++ b/royalnet/network/__init__.py @@ -2,17 +2,17 @@ from .request import Request from .response import Response, ResponseSuccess, ResponseError from .package import Package -from .royalnetlink import RoyalnetLink, NetworkError, NotConnectedError, NotIdentifiedError, ConnectionClosedError -from .royalnetserver import RoyalnetServer -from .royalnetconfig import RoyalnetConfig +from .networklink import NetworkLink, NetworkError, NotConnectedError, NotIdentifiedError, ConnectionClosedError +from .networkserver import NetworkServer +from .networkconfig import NetworkConfig -__all__ = ["RoyalnetLink", +__all__ = ["NetworkLink", "NetworkError", "NotConnectedError", "NotIdentifiedError", "Package", - "RoyalnetServer", - "RoyalnetConfig", + "NetworkServer", + "NetworkConfig", "ConnectionClosedError", "Request", "Response", diff --git a/royalnet/network/royalnetconfig.py b/royalnet/network/networkconfig.py similarity index 94% rename from royalnet/network/royalnetconfig.py rename to royalnet/network/networkconfig.py index 5e35695a..704962b7 100644 --- a/royalnet/network/royalnetconfig.py +++ b/royalnet/network/networkconfig.py @@ -1,4 +1,4 @@ -class RoyalnetConfig: +class NetworkConfig: def __init__(self, master_uri: str, master_secret: str): diff --git a/royalnet/network/royalnetlink.py b/royalnet/network/networklink.py similarity index 90% rename from royalnet/network/royalnetlink.py rename to royalnet/network/networklink.py index 1d0989d6..ef8b8767 100644 --- a/royalnet/network/royalnetlink.py +++ b/royalnet/network/networklink.py @@ -13,19 +13,19 @@ log = _logging.getLogger(__name__) class NotConnectedError(Exception): - """The :py:class:`royalnet.network.RoyalnetLink` is not connected to a :py:class:`royalnet.network.RoyalnetServer`.""" + """The :py:class:`royalnet.network.NetworkLink` is not connected to a :py:class:`royalnet.network.NetworkServer`.""" class NotIdentifiedError(Exception): - """The :py:class:`royalnet.network.RoyalnetLink` has not identified yet to a :py:class:`royalnet.network.RoyalnetServer`.""" + """The :py:class:`royalnet.network.NetworkLink` has not identified yet to a :py:class:`royalnet.network.NetworkServer`.""" 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.NetworkLink`'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`.""" + """The :py:class:`royalnet.network.NetworkServer` sent invalid data to the :py:class:`royalnet.network.NetworkLink`.""" class NetworkError(Exception): @@ -69,7 +69,7 @@ def requires_identification(func): return new_func -class RoyalnetLink: +class NetworkLink: def __init__(self, master_uri: str, secret: str, link_type: str, request_handler, *, loop: asyncio.AbstractEventLoop = None): if ":" in link_type: @@ -90,7 +90,7 @@ class RoyalnetLink: self.identify_event: asyncio.Event = asyncio.Event(loop=self._loop) async def connect(self): - """Connect to the :py:class:`royalnet.network.RoyalnetServer` at ``self.master_uri``.""" + """Connect to the :py:class:`royalnet.network.NetworkServer` at ``self.master_uri``.""" log.info(f"Connecting to {self.master_uri}...") self.websocket = await websockets.connect(self.master_uri, loop=self._loop) self.connect_event.set() @@ -98,7 +98,7 @@ class RoyalnetLink: @requires_connection async def receive(self) -> Package: - """Recieve a :py:class:`Package` from the :py:class:`royalnet.network.RoyalnetServer`. + """Recieve a :py:class:`Package` from the :py:class:`royalnet.network.NetworkServer`. Raises: :py:exc:`royalnet.network.royalnetlink.ConnectionClosedError` if the connection closes.""" @@ -113,7 +113,7 @@ class RoyalnetLink: # What to do now? Let's just reraise. raise ConnectionClosedError() if self.identify_event.is_set() and package.destination != self.nid: - raise InvalidServerResponseError("Package is not addressed to this RoyalnetLink.") + raise InvalidServerResponseError("Package is not addressed to this NetworkLink.") log.debug(f"Received package: {package}") return package diff --git a/royalnet/network/royalnetserver.py b/royalnet/network/networkserver.py similarity index 95% rename from royalnet/network/royalnetserver.py rename to royalnet/network/networkserver.py index 4ad5e3c2..5968a3c0 100644 --- a/royalnet/network/royalnetserver.py +++ b/royalnet/network/networkserver.py @@ -12,7 +12,7 @@ log = _logging.getLogger(__name__) class ConnectedClient: - """The :py:class:`royalnet.network.RoyalnetServer`-side representation of a connected :py:class:`royalnet.network.RoyalnetLink`.""" + """The :py:class:`royalnet.network.NetworkServer`-side representation of a connected :py:class:`royalnet.network.NetworkLink`.""" def __init__(self, socket: websockets.WebSocketServerProtocol): self.socket: websockets.WebSocketServerProtocol = socket self.nid: typing.Optional[str] = None @@ -30,11 +30,11 @@ class ConnectedClient: destination=self.nid)) async def send(self, package: Package): - """Send a :py:class:`royalnet.network.Package` to the :py:class:`royalnet.network.RoyalnetLink`.""" + """Send a :py:class:`royalnet.network.Package` to the :py:class:`royalnet.network.NetworkLink`.""" await self.socket.send(package.to_json_bytes()) -class RoyalnetServer: +class NetworkServer: def __init__(self, address: str, port: int, required_secret: str, *, loop: asyncio.AbstractEventLoop = None): self.address: str = address self.port: int = port @@ -131,9 +131,12 @@ class RoyalnetServer: async def serve(self): await websockets.serve(self.listener, host=self.address, port=self.port) - async def start(self): + async def run(self): 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 await asyncio.sleep(0.5) + + def run_blocking(self): + self._loop.run_until_complete(self.run()) diff --git a/royalnet/network/package.py b/royalnet/network/package.py index 282879ef..cdb0ddd6 100644 --- a/royalnet/network/package.py +++ b/royalnet/network/package.py @@ -4,7 +4,7 @@ import typing 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.NetworkLink` communicates with a :py:class:`royalnet.network.NetworkServer` or another link. Contains info about the source and the destination.""" def __init__(self, diff --git a/royalnet/network/request.py b/royalnet/network/request.py index b7ccad27..657d97ac 100644 --- a/royalnet/network/request.py +++ b/royalnet/network/request.py @@ -1,5 +1,5 @@ class Request: - """A request sent from a :py:class:`royalnet.network.RoyalnetLink` to another. + """A request sent from a :py:class:`royalnet.network.NetworkLink` to another. It contains the name of the requested handler, in addition to the data.""" diff --git a/royalnet/run.py b/royalnet/run.py deleted file mode 100644 index 6f246d80..00000000 --- a/royalnet/run.py +++ /dev/null @@ -1,26 +0,0 @@ -import click -import typing -import royalnet as r - - -@click.command() -@click.option("--telegram/--no-telegram", default=None, - help="Enable/disable the Telegram module.") -@click.option("--discord/--no-discord", default=None, - help="Enable/disable the Discord module.") -@click.option("--database", type=str, default=None, - help="The PostgreSQL database path.") -@click.option("--commands", type=str, multiple=True, default=[], - help="The names of the command pack modules that should be imported.") -@click.option("--network", type=str, default=None, - help="The Royalnet master server uri and password, separated by a pipe.") -def run(telegram: typing.Optional[bool], - discord: typing.Optional[bool], - database: typing.Optional[str], - commands: typing.List[str], - network: typing.Optional[str]): - ... - - -if __name__ == "__main__": - run() diff --git a/royalnet/utils/wikiport.py b/royalnet/utils/wikiport.py index c2d487ce..0d14a744 100644 --- a/royalnet/utils/wikiport.py +++ b/royalnet/utils/wikiport.py @@ -4,11 +4,11 @@ import datetime import difflib import uuid from royalnet.database import Alchemy -from royalnet.database.tables import Royal, WikiPage, WikiRevision +from royalnet.database.tables import User, WikiPage, WikiRevision if __name__ == "__main__": - alchemy = Alchemy(os.environ["DB_PATH"], {Royal, WikiPage, WikiRevision}) + alchemy = Alchemy(os.environ["DB_PATH"], {User, WikiPage, WikiRevision}) with open(r"data.txt") as file, alchemy.session_cm() as session: for line in file.readlines(): match = re.match("^([^\t]+)\t([^\t]+)\t([tf])$", line) diff --git a/royalnet/version.py b/royalnet/version.py index a3e37e46..3477ad1f 100644 --- a/royalnet/version.py +++ b/royalnet/version.py @@ -1 +1 @@ -semantic = "5.0a58" +semantic = "5.0a59" diff --git a/royalnet/web/royalprints/diarioview/__init__.py b/royalnet/web/royalprints/diarioview/__init__.py index 2cb5521b..7c7c46e5 100644 --- a/royalnet/web/royalprints/diarioview/__init__.py +++ b/royalnet/web/royalprints/diarioview/__init__.py @@ -1,15 +1,15 @@ -"""A Royal Games Diario viewer :py:class:`royalnet.web.Royalprint`.""" +"""A User Games Diario viewer :py:class:`royalnet.web.Royalprint`.""" import flask as f import os from ...royalprint import Royalprint from ...shortcuts import error -from ....database.tables import Royal, Diario +from ....database.tables import User, Diario tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') rp = Royalprint("diarioview", __name__, url_prefix="/diario", template_folder=tmpl_dir, - required_tables={Royal, Diario}) + required_tables={User, Diario}) @rp.route("/", defaults={"page": 1}) diff --git a/royalnet/web/royalprints/home/__init__.py b/royalnet/web/royalprints/home/__init__.py index b5739c11..25d01dff 100644 --- a/royalnet/web/royalprints/home/__init__.py +++ b/royalnet/web/royalprints/home/__init__.py @@ -1,4 +1,4 @@ -"""Homepage :py:class:`royalnet.web.Royalprint` of the Royal Games website.""" +"""Homepage :py:class:`royalnet.web.Royalprint` of the User Games website.""" import flask as f import os from ...royalprint import Royalprint diff --git a/royalnet/web/royalprints/login/__init__.py b/royalnet/web/royalprints/login/__init__.py index 0ee00573..a270361a 100644 --- a/royalnet/web/royalprints/login/__init__.py +++ b/royalnet/web/royalprints/login/__init__.py @@ -5,11 +5,11 @@ import datetime import bcrypt from ...royalprint import Royalprint from ...shortcuts import error -from ....database.tables import Royal +from ....database.tables import User tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') -rp = Royalprint("login", __name__, url_prefix="/login/password", required_tables={Royal}, +rp = Royalprint("login", __name__, url_prefix="/login/password", required_tables={User}, template_folder=tmpl_dir) diff --git a/royalnet/web/royalprints/newaccount/__init__.py b/royalnet/web/royalprints/newaccount/__init__.py index 28c3b07d..e7b08cc6 100644 --- a/royalnet/web/royalprints/newaccount/__init__.py +++ b/royalnet/web/royalprints/newaccount/__init__.py @@ -4,11 +4,11 @@ import os import bcrypt from ...royalprint import Royalprint from ...shortcuts import error -from ....database.tables import Royal, Alias +from ....database.tables import User, Alias tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') -rp = Royalprint("newaccount", __name__, url_prefix="/newaccount", required_tables={Royal, Alias}, +rp = Royalprint("newaccount", __name__, url_prefix="/newaccount", required_tables={User, Alias}, template_folder=tmpl_dir) diff --git a/royalnet/web/royalprints/profile/__init__.py b/royalnet/web/royalprints/profile/__init__.py index 21a756db..7ff02eed 100644 --- a/royalnet/web/royalprints/profile/__init__.py +++ b/royalnet/web/royalprints/profile/__init__.py @@ -11,7 +11,7 @@ from ....utils.wikirender import prepare_page_markdown, RenderError # Maybe some of these tables are optional... tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') rp = Royalprint("profile", __name__, url_prefix="/profile", template_folder=tmpl_dir, - required_tables={Royal, ActiveKvGroup, Alias, Diario, Discord, Keygroup, Keyvalue, Telegram, WikiPage, + required_tables={User, ActiveKvGroup, Alias, Diario, Discord, Keygroup, Keyvalue, Telegram, WikiPage, WikiRevision, Bio, TriviaScore}) diff --git a/royalnet/web/royalprints/tglogin/__init__.py b/royalnet/web/royalprints/tglogin/__init__.py index 115cac70..e9b41c96 100644 --- a/royalnet/web/royalprints/tglogin/__init__.py +++ b/royalnet/web/royalprints/tglogin/__init__.py @@ -6,11 +6,11 @@ import datetime import os from ...royalprint import Royalprint from ...shortcuts import error -from ....database.tables import Royal, Telegram +from ....database.tables import User, Telegram tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') -rp = Royalprint("tglogin", __name__, url_prefix="/login/telegram", required_tables={Royal, Telegram}, +rp = Royalprint("tglogin", __name__, url_prefix="/login/telegram", required_tables={User, Telegram}, template_folder=tmpl_dir) @@ -38,7 +38,7 @@ def tglogin_done(): return error(400, "L'autenticazione è fallita: l'hash ricevuto non coincide con quello calcolato.") tg_user = alchemy_session.query(alchemy.Telegram).filter(alchemy.Telegram.tg_id == f.request.args["id"]).one_or_none() if tg_user is None: - return error(404, "L'account Telegram con cui hai fatto il login non è connesso a nessun account Royal Games. Se sei un membro Royal Games, assicurati di aver syncato con il bot il tuo account di Telegram!") + return error(404, "L'account Telegram con cui hai fatto il login non è connesso a nessun account User Games. Se sei un membro User Games, assicurati di aver syncato con il bot il tuo account di Telegram!") royal_user = tg_user.royal f.session["royal"] = { "uid": royal_user.uid, diff --git a/royalnet/web/royalprints/wikiedit/__init__.py b/royalnet/web/royalprints/wikiedit/__init__.py index 13342e27..9a130e9e 100644 --- a/royalnet/web/royalprints/wikiedit/__init__.py +++ b/royalnet/web/royalprints/wikiedit/__init__.py @@ -1,4 +1,4 @@ -"""A Royal Games Wiki viewer :py:class:`royalnet.web.Royalprint`. Doesn't support any kind of edit.""" +"""A User Games Wiki viewer :py:class:`royalnet.web.Royalprint`. Doesn't support any kind of edit.""" import flask as f import uuid import os @@ -6,12 +6,12 @@ import datetime import difflib from ...royalprint import Royalprint from ...shortcuts import error, from_urluuid -from ....database.tables import Royal, WikiPage, WikiRevision +from ....database.tables import User, WikiPage, WikiRevision tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') rp = Royalprint("wikiedit", __name__, url_prefix="/wiki/edit", template_folder=tmpl_dir, - required_tables={Royal, WikiPage, WikiRevision}) + required_tables={User, WikiPage, WikiRevision}) @rp.route("/newpage", methods=["GET", "POST"]) diff --git a/royalnet/web/royalprints/wikiview/__init__.py b/royalnet/web/royalprints/wikiview/__init__.py index 34af9266..a8041333 100644 --- a/royalnet/web/royalprints/wikiview/__init__.py +++ b/royalnet/web/royalprints/wikiview/__init__.py @@ -1,16 +1,16 @@ -"""A Royal Games Wiki viewer :py:class:`royalnet.web.Royalprint`. Doesn't support any kind of edit.""" +"""A User Games Wiki viewer :py:class:`royalnet.web.Royalprint`. Doesn't support any kind of edit.""" import flask as f import os from ...royalprint import Royalprint from ...shortcuts import error, from_urluuid -from ....database.tables import Royal, WikiPage, WikiRevision +from ....database.tables import User, WikiPage, WikiRevision from ....utils.wikirender import prepare_page_markdown, RenderError tmpl_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates') rp = Royalprint("wikiview", __name__, url_prefix="/wiki/view", template_folder=tmpl_dir, - required_tables={Royal, WikiPage, WikiRevision}) + required_tables={User, WikiPage, WikiRevision}) def prepare_page(page): diff --git a/setup.py b/setup.py index 041f7fa2..f266dca4 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setuptools.setup( version=royalnet.version.semantic, author="Stefano Pigozzi", author_email="ste.pigozzi@gmail.com", - description="The great bot network of the Royal Games community", + description="The great bot network of the User Games community", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/royal-games/royalnet", @@ -31,7 +31,8 @@ setuptools.setup( "mcstatus>=2.2.1", "sortedcontainers>=2.1.0", "sentry-sdk>=0.11.1", - "click>=7.0"], + "click>=7.0", + "keyring>=19.2.0"], python_requires=">=3.7", classifiers=[ "Development Status :: 3 - Alpha", diff --git a/tests/test_network.py b/tests/test_network.py index 48c85ba1..e59da7c6 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -2,7 +2,7 @@ import pytest import uuid import asyncio import logging -from royalnet.network import Package, RoyalnetLink, RoyalnetServer, ConnectionClosedError, Request +from royalnet.network import Package, NetworkLink, NetworkServer, ConnectionClosedError, Request log = logging.root @@ -41,16 +41,16 @@ def test_request_creation(): def test_links(async_loop: asyncio.AbstractEventLoop): address, port = "127.0.0.1", 1235 - master = RoyalnetServer(address, port, "test") + master = NetworkServer(address, port, "test") async_loop.run_until_complete(master.start()) # Test invalid secret - wrong_secret_link = RoyalnetLink("ws://127.0.0.1:1235", "invalid", "test", echo_request_handler, loop=async_loop) + wrong_secret_link = NetworkLink("ws://127.0.0.1:1235", "invalid", "test", echo_request_handler, loop=async_loop) with pytest.raises(ConnectionClosedError): async_loop.run_until_complete(wrong_secret_link.run()) # Test regular connection - link1 = RoyalnetLink("ws://127.0.0.1:1235", "test", "one", echo_request_handler, loop=async_loop) + link1 = NetworkLink("ws://127.0.0.1:1235", "test", "one", echo_request_handler, loop=async_loop) async_loop.create_task(link1.run()) - link2 = RoyalnetLink("ws://127.0.0.1:1235", "test", "two", echo_request_handler, loop=async_loop) + link2 = NetworkLink("ws://127.0.0.1:1235", "test", "two", echo_request_handler, loop=async_loop) async_loop.create_task(link2.run()) message = {"ciao": "ciao"} response = async_loop.run_until_complete(link1.request(message, "two"))