1
Fork 0
mirror of https://github.com/RYGhub/royalnet.git synced 2024-11-24 03:54:20 +00:00
This commit is contained in:
Steffo 2019-04-19 19:43:24 +02:00
parent 350d754683
commit 10c77a20d0
10 changed files with 134 additions and 126 deletions

View file

@ -1,7 +1,7 @@
import os import os
import asyncio import asyncio
import logging import logging
from royalnet.bots import DiscordBot, DiscordConfig from royalnet.bots import DiscordBot, DiscordConfig, TelegramBot, TelegramConfig
from royalnet.commands import * from royalnet.commands import *
from royalnet.commands.debug_create import DebugCreateCommand from royalnet.commands.debug_create import DebugCreateCommand
from royalnet.commands.error_handler import ErrorHandlerCommand from royalnet.commands.error_handler import ErrorHandlerCommand
@ -12,8 +12,12 @@ from royalnet.database.tables import Royal, Telegram, Discord
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
log = logging.root log = logging.root
log.addHandler(logging.StreamHandler()) stream_handler = logging.StreamHandler()
stream_handler.formatter = logging.Formatter("{asctime}\t{name}\t{levelname}\t{message}", style="{")
log.addHandler(stream_handler)
logging.getLogger("royalnet.bots.generic").setLevel(logging.DEBUG) logging.getLogger("royalnet.bots.generic").setLevel(logging.DEBUG)
logging.getLogger("royalnet.bots.discord").setLevel(logging.DEBUG)
logging.getLogger("royalnet.bots.telegram").setLevel(logging.DEBUG)
commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, DebugCreateCommand, SyncCommand, commands = [PingCommand, ShipCommand, SmecdsCommand, ColorCommand, CiaoruoziCommand, DebugCreateCommand, SyncCommand,
AuthorCommand, DiarioCommand, RageCommand, DateparserCommand, ReminderCommand, KvactiveCommand, KvCommand, AuthorCommand, DiarioCommand, RageCommand, DateparserCommand, ReminderCommand, KvactiveCommand, KvCommand,
@ -27,7 +31,13 @@ ds_bot = DiscordBot(discord_config=DiscordConfig(os.environ["DS_AK"]),
database_config=DatabaseConfig(os.environ["DB_PATH"], Royal, Discord, "discord_id"), database_config=DatabaseConfig(os.environ["DB_PATH"], Royal, Discord, "discord_id"),
commands=commands, commands=commands,
error_command=ErrorHandlerCommand) error_command=ErrorHandlerCommand)
tg_bot = TelegramBot(telegram_config=TelegramConfig(os.environ["TG_AK"]),
royalnet_config=RoyalnetConfig(f"ws://{address}:{port}", "sas"),
database_config=DatabaseConfig(os.environ["DB_PATH"], Royal, Telegram, "tg_id"),
commands=commands,
error_command=ErrorHandlerCommand)
loop.run_until_complete(master.run()) loop.run_until_complete(master.run())
loop.create_task(tg_bot.run())
loop.create_task(ds_bot.run()) loop.create_task(ds_bot.run())
print("Starting loop...") print("Starting loop...")
loop.run_forever() loop.run_forever()

View file

@ -1,4 +1,4 @@
from .telegram import TelegramBot from .telegram import TelegramBot, TelegramConfig
from .discord import DiscordBot, DiscordConfig from .discord import DiscordBot, DiscordConfig
__all__ = ["TelegramBot", "DiscordBot", "DiscordConfig"] __all__ = ["TelegramBot", "TelegramConfig", "DiscordBot", "DiscordConfig"]

View file

@ -6,7 +6,7 @@ from .generic import GenericBot
from ..commands import NullCommand from ..commands import NullCommand
from ..utils import asyncify, Call, Command from ..utils import asyncify, Call, Command
from ..error import UnregisteredError, NoneFoundError, TooManyFoundError, InvalidConfigError from ..error import UnregisteredError, NoneFoundError, TooManyFoundError, InvalidConfigError
from ..network import Message, RequestError, RoyalnetConfig from ..network import Message, RoyalnetConfig
from ..database import DatabaseConfig from ..database import DatabaseConfig
from ..audio import PlayMode, Playlist from ..audio import PlayMode, Playlist
@ -27,9 +27,12 @@ class DiscordBot(GenericBot):
interface_name = "discord" interface_name = "discord"
def _init_voice(self): def _init_voice(self):
log.debug(f"Creating music_data dict")
self.music_data: typing.Dict[discord.Guild, PlayMode] = {} self.music_data: typing.Dict[discord.Guild, PlayMode] = {}
def _call_factory(self) -> typing.Type[Call]: def _call_factory(self) -> typing.Type[Call]:
log.debug(f"Creating DiscordCall")
# noinspection PyMethodParameters # noinspection PyMethodParameters
class DiscordCall(Call): class DiscordCall(Call):
interface_name = self.interface_name interface_name = self.interface_name
@ -56,9 +59,8 @@ class DiscordBot(GenericBot):
async def net_request(call, message: Message, destination: str): async def net_request(call, message: Message, destination: str):
if self.network is None: if self.network is None:
raise InvalidConfigError("Royalnet is not enabled on this bot") raise InvalidConfigError("Royalnet is not enabled on this bot")
response = await self.network.request(message, destination) response: Message = await self.network.request(message, destination)
if isinstance(response, RequestError): response.raise_on_error()
raise response.exc
return response return response
async def get_author(call, error_if_none=False): async def get_author(call, error_if_none=False):
@ -77,12 +79,15 @@ class DiscordBot(GenericBot):
def _bot_factory(self) -> typing.Type[discord.Client]: def _bot_factory(self) -> typing.Type[discord.Client]:
"""Create a new DiscordClient class based on this DiscordBot.""" """Create a new DiscordClient class based on this DiscordBot."""
log.debug(f"Creating DiscordClient")
# noinspection PyMethodParameters # noinspection PyMethodParameters
class DiscordClient(discord.Client): class DiscordClient(discord.Client):
async def vc_connect_or_move(cli, channel: discord.VoiceChannel): async def vc_connect_or_move(cli, channel: discord.VoiceChannel):
# Connect to voice chat # Connect to voice chat
try: try:
await channel.connect() await channel.connect()
log.debug(f"Connecting to Voice in {channel}")
except discord.errors.ClientException: except discord.errors.ClientException:
# Move to the selected channel, instead of connecting # Move to the selected channel, instead of connecting
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
@ -91,8 +96,10 @@ class DiscordBot(GenericBot):
if voice_client.guild != channel.guild: if voice_client.guild != channel.guild:
continue continue
await voice_client.move_to(channel) await voice_client.move_to(channel)
log.debug(f"Moved {voice_client} to {channel}")
# Create a music_data entry, if it doesn't exist; default is a Playlist # Create a music_data entry, if it doesn't exist; default is a Playlist
if not self.music_data.get(channel.guild): if not self.music_data.get(channel.guild):
log.debug(f"Creating music_data for {channel.guild}")
self.music_data[channel.guild] = Playlist() self.music_data[channel.guild] = Playlist()
@staticmethod # Not really static because of the self reference @staticmethod # Not really static because of the self reference
@ -157,17 +164,21 @@ class DiscordBot(GenericBot):
def _init_client(self): def _init_client(self):
"""Create a bot instance.""" """Create a bot instance."""
self.client = self._bot_factory()() log.debug(f"Creating DiscordClient instance")
self._Client = self._bot_factory()
self.client = self._Client()
def __init__(self, *, def __init__(self, *,
discord_config: DiscordConfig, discord_config: DiscordConfig,
royalnet_config: RoyalnetConfig, royalnet_config: typing.Optional[RoyalnetConfig] = None,
database_config: typing.Optional[DatabaseConfig] = None, database_config: typing.Optional[DatabaseConfig] = None,
command_prefix: str = "!",
commands: typing.List[typing.Type[Command]] = None, commands: typing.List[typing.Type[Command]] = None,
missing_command: typing.Type[Command] = NullCommand, missing_command: typing.Type[Command] = NullCommand,
error_command: typing.Type[Command] = NullCommand): error_command: typing.Type[Command] = NullCommand):
super().__init__(royalnet_config=royalnet_config, super().__init__(royalnet_config=royalnet_config,
database_config=database_config, database_config=database_config,
command_prefix=command_prefix,
commands=commands, commands=commands,
missing_command=missing_command, missing_command=missing_command,
error_command=error_command) error_command=error_command)
@ -176,7 +187,9 @@ class DiscordBot(GenericBot):
self._init_voice() self._init_voice()
async def run(self): async def run(self):
log.debug(f"Logging in to Discord")
await self.client.login(self._discord_config.token) await self.client.login(self._discord_config.token)
log.debug(f"Connecting to Discord")
await self.client.connect() await self.client.connect()
# TODO: how to stop? # TODO: how to stop?

View file

@ -16,6 +16,7 @@ class GenericBot:
interface_name = NotImplemented interface_name = NotImplemented
def _init_commands(self, def _init_commands(self,
command_prefix: str,
commands: typing.List[typing.Type[Command]], commands: typing.List[typing.Type[Command]],
missing_command: typing.Type[Command], missing_command: typing.Type[Command],
error_command: typing.Type[Command]): error_command: typing.Type[Command]):
@ -24,7 +25,7 @@ class GenericBot:
self.commands: typing.Dict[str, typing.Type[Command]] = {} self.commands: typing.Dict[str, typing.Type[Command]] = {}
self.network_handlers: typing.Dict[typing.Type[Message], typing.Type[NetworkHandler]] = {} self.network_handlers: typing.Dict[typing.Type[Message], typing.Type[NetworkHandler]] = {}
for command in commands: for command in commands:
self.commands[f"!{command.command_name}"] = command self.commands[f"{command_prefix}{command.command_name}"] = command
self.network_handlers = {**self.network_handlers, **command.network_handler_dict()} self.network_handlers = {**self.network_handlers, **command.network_handler_dict()}
self.missing_command: typing.Type[Command] = missing_command self.missing_command: typing.Type[Command] = missing_command
self.error_command: typing.Type[Command] = error_command self.error_command: typing.Type[Command] = error_command
@ -36,7 +37,7 @@ class GenericBot:
def _init_royalnet(self, royalnet_config: RoyalnetConfig): def _init_royalnet(self, royalnet_config: RoyalnetConfig):
"""Create a RoyalnetLink, and run it as a task.""" """Create a RoyalnetLink, and run it as a task."""
self.network: RoyalnetLink = RoyalnetLink(royalnet_config.master_uri, royalnet_config.master_secret, "discord", self.network: RoyalnetLink = RoyalnetLink(royalnet_config.master_uri, royalnet_config.master_secret, self.interface_name,
self._network_handler) self._network_handler)
log.debug(f"Running RoyalnetLink {self.network}") log.debug(f"Running RoyalnetLink {self.network}")
loop.create_task(self.network.run()) loop.create_task(self.network.run())
@ -46,15 +47,17 @@ class GenericBot:
log.debug(f"Received {message} from the RoyalnetLink") log.debug(f"Received {message} from the RoyalnetLink")
try: try:
network_handler = self.network_handlers[message.__class__] network_handler = self.network_handlers[message.__class__]
except KeyError as exc: except KeyError:
_, exc, tb = sys.exc_info()
log.debug(f"Missing network_handler for {message}") log.debug(f"Missing network_handler for {message}")
return RequestError(KeyError("Missing network_handler")) return RequestError(exc=exc)
try: try:
log.debug(f"Using {network_handler} as handler for {message}") log.debug(f"Using {network_handler} as handler for {message}")
return await getattr(network_handler, self.interface_name)(message) return await getattr(network_handler, self.interface_name)(self, message)
except Exception as exc: except Exception:
_, exc, tb = sys.exc_info()
log.debug(f"Exception {exc} in {network_handler}") log.debug(f"Exception {exc} in {network_handler}")
return RequestError(exc) return RequestError(exc=exc)
def _init_database(self, commands: typing.List[typing.Type[Command]], database_config: DatabaseConfig): def _init_database(self, commands: typing.List[typing.Type[Command]], database_config: DatabaseConfig):
"""Connect to the database, and create the missing tables required by the selected commands.""" """Connect to the database, and create the missing tables required by the selected commands."""
@ -74,6 +77,7 @@ class GenericBot:
def __init__(self, *, def __init__(self, *,
royalnet_config: typing.Optional[RoyalnetConfig] = None, royalnet_config: typing.Optional[RoyalnetConfig] = None,
database_config: typing.Optional[DatabaseConfig] = None, database_config: typing.Optional[DatabaseConfig] = None,
command_prefix: str,
commands: typing.List[typing.Type[Command]] = None, commands: typing.List[typing.Type[Command]] = None,
missing_command: typing.Type[Command] = NullCommand, missing_command: typing.Type[Command] = NullCommand,
error_command: typing.Type[Command] = NullCommand): error_command: typing.Type[Command] = NullCommand):
@ -86,7 +90,7 @@ class GenericBot:
self._init_database(commands=commands, database_config=database_config) self._init_database(commands=commands, database_config=database_config)
if commands is None: if commands is None:
commands = [] commands = []
self._init_commands(commands, missing_command=missing_command, error_command=error_command) self._init_commands(command_prefix, commands, missing_command=missing_command, error_command=error_command)
self._Call = self._call_factory() self._Call = self._call_factory()
if royalnet_config is None: if royalnet_config is None:
self.network = None self.network = None
@ -95,15 +99,18 @@ class GenericBot:
async def call(self, command_name: str, channel, parameters: typing.List[str] = None, **kwargs): async def call(self, command_name: str, channel, parameters: typing.List[str] = None, **kwargs):
"""Call a command by its string, or missing_command if it doesn't exists, or error_command if an exception is raised during the execution.""" """Call a command by its string, or missing_command if it doesn't exists, or error_command if an exception is raised during the execution."""
log.debug(f"Trying to call {command_name}")
if parameters is None: if parameters is None:
parameters = [] parameters = []
try: try:
command: typing.Type[Command] = self.commands[command_name] command: typing.Type[Command] = self.commands[command_name]
except KeyError: except KeyError:
log.debug(f"Calling missing_command because {command_name} does not exist")
command = self.missing_command command = self.missing_command
try: try:
await self._Call(channel, command, parameters, **kwargs).run() await self._Call(channel, command, parameters, **kwargs).run()
except Exception as exc: except Exception as exc:
log.debug(f"Calling error_command because of an error in {command_name}")
await self._Call(channel, self.error_command, await self._Call(channel, self.error_command,
exception_info=sys.exc_info(), exception_info=sys.exc_info(),
previous_command=command, **kwargs).run() previous_command=command, **kwargs).run()

View file

@ -3,10 +3,11 @@ import asyncio
import typing import typing
import logging as _logging import logging as _logging
import sys import sys
from .generic import GenericBot
from ..commands import NullCommand from ..commands import NullCommand
from ..utils import asyncify, Call, Command from ..utils import asyncify, Call, Command
from ..error import UnregisteredError, InvalidConfigError from ..error import UnregisteredError, InvalidConfigError
from ..network import RoyalnetLink, Message, RequestError from ..network import RoyalnetLink, Message, RequestError, RoyalnetConfig
from ..database import Alchemy, relationshiplinkchain, DatabaseConfig from ..database import Alchemy, relationshiplinkchain, DatabaseConfig
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
@ -17,48 +18,25 @@ async def todo(message: Message):
log.warning(f"Skipped {message} because handling isn't supported yet.") log.warning(f"Skipped {message} because handling isn't supported yet.")
class TelegramBot: class TelegramConfig:
def __init__(self, def __init__(self, token: str):
api_key: str, self.token: str = token
master_server_uri: str,
master_server_secret: str,
commands: typing.List[typing.Type[Command]], class TelegramBot(GenericBot):
missing_command: typing.Type[Command] = NullCommand, interface_name = "telegram"
error_command: typing.Type[Command] = NullCommand,
database_config: typing.Optional[DatabaseConfig] = None): def _init_client(self):
self.bot: telegram.Bot = telegram.Bot(api_key) self.client = telegram.Bot(self._telegram_config.token)
self.should_run: bool = False self._offset: int = -100
self.offset: int = -100
self.missing_command = missing_command def _call_factory(self) -> typing.Type[Call]:
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
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 the Alchemy database
if database_config:
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)
else:
if required_tables:
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
# noinspection PyMethodParameters # noinspection PyMethodParameters
class TelegramCall(Call): class TelegramCall(Call):
interface_name = "telegram" interface_name = self.interface_name
interface_obj = self interface_obj = self
interface_prefix = "/" interface_prefix = "/"
alchemy = self.alchemy alchemy = self.alchemy
async def reply(call, text: str): async def reply(call, text: str):
@ -75,9 +53,10 @@ class TelegramBot:
await asyncify(call.channel.send_message, escaped_text, parse_mode="HTML") await asyncify(call.channel.send_message, escaped_text, parse_mode="HTML")
async def net_request(call, message: Message, destination: str): async def net_request(call, message: Message, destination: str):
response = await self.network.request(message, destination) if self.network is None:
if isinstance(response, RequestError): raise InvalidConfigError("Royalnet is not enabled on this bot")
raise response.exc response: Message = await self.network.request(message, destination)
response.raise_on_error()
return response return response
async def get_author(call, error_if_none=False): async def get_author(call, error_if_none=False):
@ -94,29 +73,26 @@ class TelegramBot:
result = await asyncify(query.one_or_none) result = await asyncify(query.one_or_none)
if result is None and error_if_none: if result is None and error_if_none:
raise UnregisteredError("Author is not registered") raise UnregisteredError("Author is not registered")
return result return TelegramCall
self.TelegramCall = TelegramCall def __init__(self, *,
telegram_config: TelegramConfig,
royalnet_config: typing.Optional[RoyalnetConfig] = None,
database_config: typing.Optional[DatabaseConfig] = None,
command_prefix: str = "/",
commands: typing.List[typing.Type[Command]] = None,
missing_command: typing.Type[Command] = NullCommand,
error_command: typing.Type[Command] = NullCommand):
super().__init__(royalnet_config=royalnet_config,
database_config=database_config,
command_prefix=command_prefix,
commands=commands,
missing_command=missing_command,
error_command=error_command)
self._telegram_config = telegram_config
self._init_client()
async def run(self): async def _handle_update(self, update: telegram.Update):
self.should_run = True
while self.should_run:
# Get the latest 100 updates
try:
last_updates: typing.List[telegram.Update] = await asyncify(self.bot.get_updates, offset=self.offset, timeout=60)
except telegram.error.TimedOut:
continue
# Handle updates
for update in last_updates:
# noinspection PyAsyncCall
asyncio.create_task(self.handle_update(update))
# Recalculate offset
try:
self.offset = last_updates[-1].update_id + 1
except IndexError:
pass
async def handle_update(self, update: telegram.Update):
# Skip non-message updates # Skip non-message updates
if update.message is None: if update.message is None:
return return
@ -130,33 +106,31 @@ class TelegramBot:
return return
# Find and clean parameters # Find and clean parameters
command_text, *parameters = text.split(" ") command_text, *parameters = text.split(" ")
command_text.replace(f"@{self.bot.username}", "") command_text.replace(f"@{self.client.username}", "")
# Find the function
try:
command = self.commands[command_text]
except KeyError:
# Skip inexistent _commands
command = self.missing_command
# Call the command # Call the command
# noinspection PyBroadException await self.call(command_text, update.message.chat, parameters, update=update)
try:
return await self.TelegramCall(message.chat, command, parameters, log,
update=update).run()
except Exception as exc:
try:
return await self.TelegramCall(message.chat, self.error_command, parameters, log,
update=update,
exception_info=sys.exc_info(),
previous_command=command).run()
except Exception as exc2:
log.error(f"Exception in error handler command: {exc2}")
def generate_botfather_command_string(self): async def run(self):
while True:
# Get the latest 100 updates
try:
last_updates: typing.List[telegram.Update] = await asyncify(self.client.get_updates, offset=self._offset, timeout=60)
except telegram.error.TimedOut:
continue
# Handle updates
for update in last_updates:
# noinspection PyAsyncCall
loop.create_task(self._handle_update(update))
# Recalculate offset
try:
self._offset = last_updates[-1].update_id + 1
except IndexError:
pass
@property
def botfather_command_string(self) -> str:
string = "" string = ""
for command_key in self.commands: for command_key in self.commands:
command = self.commands[command_key] command = self.commands[command_key]
string += f"{command.command_name} - {command.command_description}\n" string += f"{command.command_name} - {command.command_description}\n"
return string return string
async def handle_net_request(self, message: Message):
pass

View file

@ -1,5 +1,4 @@
import logging as _logging import logging as _logging
import traceback
from ..utils import Command, Call from ..utils import Command, Call
from ..error import NoneFoundError, \ from ..error import NoneFoundError, \
TooManyFoundError, \ TooManyFoundError, \
@ -21,33 +20,28 @@ class ErrorHandlerCommand(Command):
@classmethod @classmethod
async def common(cls, call: Call): async def common(cls, call: Call):
try: exception: Exception = call.kwargs["exception"]
e_type, e_value, e_tb = call.kwargs["exception_info"] if isinstance(exception, NoneFoundError):
except InvalidInputError:
await call.reply("⚠️ Questo comando non può essere chiamato da solo.")
return
if e_type == NoneFoundError:
await call.reply("⚠️ L'elemento richiesto non è stato trovato.") await call.reply("⚠️ L'elemento richiesto non è stato trovato.")
return return
if e_type == TooManyFoundError: if isinstance(exception, TooManyFoundError):
await call.reply("⚠️ La richiesta effettuata è ambigua, pertanto è stata annullata.") await call.reply("⚠️ La richiesta effettuata è ambigua, pertanto è stata annullata.")
return return
if e_type == UnregisteredError: if isinstance(exception, UnregisteredError):
await call.reply("⚠️ Devi essere registrato a Royalnet per usare questo comando!") await call.reply("⚠️ Devi essere registrato a Royalnet per usare questo comando!")
return return
if e_type == UnsupportedError: if isinstance(exception, UnsupportedError):
await call.reply("⚠️ Il comando richiesto non è disponibile tramite questa interfaccia.") await call.reply("⚠️ Il comando richiesto non è disponibile tramite questa interfaccia.")
return return
if e_type == InvalidInputError: if isinstance(exception, InvalidInputError):
command = call.kwargs["previous_command"] command = call.kwargs["previous_command"]
await call.reply(f"⚠️ Sintassi non valida.\nSintassi corretta: [c]{call.interface_prefix}{command.command_name} {command.command_syntax}[/c]") await call.reply(f"⚠️ Sintassi non valida.\nSintassi corretta: [c]{call.interface_prefix}{command.command_name} {command.command_syntax}[/c]")
return return
if e_type == InvalidConfigError: if isinstance(exception, InvalidConfigError):
await call.reply("⚠️ Il bot non è stato configurato correttamente, quindi questo comando non può essere eseguito. L'errore è stato segnalato all'amministratore.") await call.reply("⚠️ Il bot non è stato configurato correttamente, quindi questo comando non può essere eseguito. L'errore è stato segnalato all'amministratore.")
return return
if e_type == ExternalError: if isinstance(exception, ExternalError):
await call.reply("⚠️ Una risorsa esterna necessaria per l'esecuzione del comando non ha funzionato correttamente, quindi il comando è stato annullato.") await call.reply("⚠️ Una risorsa esterna necessaria per l'esecuzione del comando non ha funzionato correttamente, quindi il comando è stato annullato.")
return return
await call.reply(f"❌ Eccezione non gestita durante l'esecuzione del comando:\n[b]{e_type.__name__}[/b]\n{e_value}") await call.reply(f"❌ Eccezione non gestita durante l'esecuzione del comando:\n[b]{exception.__class__.__name__}[/b]\n{exception}")
formatted_tb: str = '\n'.join(traceback.format_tb(e_tb)) log.error(f"Unhandled exception - {exception.__class__.__name__}: {exception}")
log.error(f"Unhandled exception - {e_type.__name__}: {e_value}\n{formatted_tb}")

View file

@ -50,6 +50,5 @@ class PlayCommand(Command):
@classmethod @classmethod
async def common(cls, call: Call): async def common(cls, call: Call):
guild, url = call.args.match(r"(?:\[(.+)])?\s*(\S+)\s*") guild, url = call.args.match(r"(?:\[(.+)])?\s*(\S+)\s*")
response: typing.Union[RequestSuccessful, RequestError] = await call.net_request(PlayMessage(url, guild), "discord") response: RequestSuccessful = await call.net_request(PlayMessage(url, guild), "discord")
response.raise_on_error()
await call.reply(f"✅ Richiesta la riproduzione di [c]{url}[/c].") await call.reply(f"✅ Richiesta la riproduzione di [c]{url}[/c].")

View file

@ -14,7 +14,7 @@ loop = asyncio.get_event_loop()
class SummonMessage(Message): class SummonMessage(Message):
def __init__(self, channel_identifier: typing.Union[int, str], def __init__(self, channel_identifier: typing.Union[int, str],
guild_identifier: typing.Optional[typing.Union[int, str]] = None): guild_identifier: typing.Optional[typing.Union[int, str]] = None):
self.channel_identifier = channel_identifier self.channel_name = channel_identifier
self.guild_identifier = guild_identifier self.guild_identifier = guild_identifier
@ -24,7 +24,7 @@ class SummonNH(NetworkHandler):
@classmethod @classmethod
async def discord(cls, bot: "DiscordBot", message: SummonMessage): async def discord(cls, bot: "DiscordBot", message: SummonMessage):
"""Handle a summon Royalnet request. That is, join a voice channel, or move to a different one if that is not possible.""" """Handle a summon Royalnet request. That is, join a voice channel, or move to a different one if that is not possible."""
channel = bot.client.find_channel(message.channel_identifier) channel = bot.client.find_channel_by_name(message.channel_name)
if not isinstance(channel, discord.VoiceChannel): if not isinstance(channel, discord.VoiceChannel):
raise NoneFoundError("Channel is not a voice channel") raise NoneFoundError("Channel is not a voice channel")
loop.create_task(bot.client.vc_connect_or_move(channel)) loop.create_task(bot.client.vc_connect_or_move(channel))

View file

@ -22,5 +22,12 @@ class InvalidConfigError(Exception):
"""The bot has not been configured correctly, therefore the command can not function.""" """The bot has not been configured correctly, therefore the command can not function."""
class RoyalnetError(Exception):
"""An error was raised while handling the Royalnet request.
This exception contains the exception that was raised during the handling."""
def __init__(self, exc):
self.exc = exc
class ExternalError(Exception): class ExternalError(Exception):
"""Something went wrong in a non-Royalnet component and the command execution cannot be completed.""" """Something went wrong in a non-Royalnet component and the command execution cannot be completed."""

View file

@ -1,3 +1,7 @@
import traceback
from ..error import RoyalnetError
class Message: class Message:
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__}>" return f"<{self.__class__.__name__}>"
@ -34,7 +38,7 @@ class RequestSuccessful(Message):
class RequestError(Message): class RequestError(Message):
def __init__(self, exc: Exception): def __init__(self, exc: Exception):
self.exc = exc self.exc: Exception = exc
def raise_on_error(self): def raise_on_error(self):
raise self.exc raise RoyalnetError(exc=self.exc)