diff --git a/royalnet/commands/__init__.py b/royalnet/commands/__init__.py index eaef11cd..8e4fdb75 100644 --- a/royalnet/commands/__init__.py +++ b/royalnet/commands/__init__.py @@ -8,7 +8,8 @@ from .errors import CommandError, \ UnsupportedError, \ ConfigurationError, \ ExternalError, \ - UserError + UserError, \ + ProgramError __all__ = [ "CommandInterface", @@ -21,5 +22,6 @@ __all__ = [ "ConfigurationError", "ExternalError", "UserError", + "ProgramError", "Event" ] diff --git a/royalnet/commands/errors.py b/royalnet/commands/errors.py index 45baa5b3..95dff99f 100644 --- a/royalnet/commands/errors.py +++ b/royalnet/commands/errors.py @@ -28,3 +28,7 @@ class ConfigurationError(CommandError): class ExternalError(CommandError): """The command failed to execute, but the problem was because of an external factor (such as an external API going down).""" + + +class ProgramError(CommandError): + """The command encountered an error in the program.""" diff --git a/royalnet/serf/serf.py b/royalnet/serf/serf.py index 5b1721ad..bfb0f0ef 100644 --- a/royalnet/serf/serf.py +++ b/royalnet/serf/serf.py @@ -167,8 +167,7 @@ class Serf: if isinstance(response, rh.ResponseFailure): if response.name == "no_event": raise CommandError(f"There is no event named {event_name} in {destination}.") - elif response.name == "exception_in_event": - # TODO: pretty sure there's a better way to do this + elif response.name == "error_in_event": if response.extra_info["type"] == "CommandError": raise CommandError(response.extra_info["message"]) elif response.extra_info["type"] == "UserError": @@ -182,13 +181,16 @@ class Serf: elif response.extra_info["type"] == "ExternalError": raise ExternalError(response.extra_info["message"]) else: - raise ValueError(f"Herald action call returned invalid error:\n" - f"[p]{response}[/p]") + raise ProgramError(f"Invalid error in Herald event '{event_name}':\n" + f"[p]{response}[/p]") + elif response.name == "unhandled_exception_in_event": + raise ProgramError(f"Unhandled exception in Herald event '{event_name}':\n" + f"[p]{response}[/p]") elif isinstance(response, rh.ResponseSuccess): return response.data else: - raise ValueError(f"Other Herald Link returned unknown response:\n" - f"[p]{response}[/p]") + raise ProgramError(f"Other Herald Link returned unknown response:\n" + f"[p]{response}[/p]") return GenericInterface @@ -266,10 +268,18 @@ class Serf: try: response_data = await event.run(**message.data) return rh.ResponseSuccess(data=response_data) + except CommandError as e: + return rh.ResponseFailure("error_in_event", + f"The event '{message.handler}' raised a {e.__class__.__qualname__}.", + extra_info={ + "type": e.__class__.__qualname__, + "message": str(e) + }) except Exception as e: ru.sentry_exc(e) - return rh.ResponseFailure("exception_in_event", - f"An exception was raised in the event for '{message.handler}'.", + return rh.ResponseFailure("unhandled_exception_in_event", + f"The event '{message.handler}' raised an unhandled" + f" {e.__class__.__qualname__}.", extra_info={ "type": e.__class__.__qualname__, "message": str(e) @@ -293,12 +303,13 @@ class Serf: await data.reply(f"⚠️ {e.message}") except ConfigurationError as e: await data.reply(f"⚠️ {e.message}") + except ProgramError as e: + await data.reply(f"⛔️ {e.message}") except CommandError as e: await data.reply(f"⚠️ {e.message}") except Exception as e: ru.sentry_exc(e) - error_message = f"⛔️ [b]{e.__class__.__name__}[/b]\n" + '\n'.join(e.args) - await data.reply(error_message) + await data.reply(f"⛔️ [b]{e.__class__.__name__}[/b]\n" + '\n'.join(e.args)) async def run(self): """A coroutine that starts the event loop and handles command calls."""