From c375ae0e10473a73556cf94bf562aa13eb0428de Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sun, 27 Dec 2020 11:17:39 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Create=20Command=20class=20(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Create Command class * ✨ Create Command class --- docs/source/conf.py | 5 +++ docs/source/index.rst | 5 +++ royalnet/engineer/command.py | 77 ++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 royalnet/engineer/command.py diff --git a/docs/source/conf.py b/docs/source/conf.py index fcc5a94e..2db0a871 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -34,6 +34,7 @@ release = '6.0.0a12' extensions = [ "sphinx.ext.autodoc", "sphinx.ext.intersphinx", + 'sphinx.ext.todo', ] # Add any paths that contain templates here, relative to this directory. @@ -91,3 +92,7 @@ autodoc_default_options = { 'special-members': '__init__', 'undoc-members': True, } + +# -- Automodule settings ----------------------------------------------------- + +todo_include_todos = True diff --git a/docs/source/index.rst b/docs/source/index.rst index 9d722c51..2b367982 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -25,3 +25,8 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` + +To do +----- + +.. todolist:: diff --git a/royalnet/engineer/command.py b/royalnet/engineer/command.py new file mode 100644 index 00000000..98f6b63f --- /dev/null +++ b/royalnet/engineer/command.py @@ -0,0 +1,77 @@ +""" +Commands are used to quickly create single-message conversations +""" + +from __future__ import annotations +import royalnet.royaltyping as t + +import logging +import functools +import re + +from . import bullet +from . import teleporter + +log = logging.getLogger(__name__) + + +class Command: + """ + A decorator to create a command that can be called from the chat by entering a certain :attr:`.pattern` of + characters: + + .. code-block:: text + + /echo Hello! + # Hello! + + """ + + def __init__(self, + prefix: str, + name: str, + syntax: str, + *, + pattern: str = r"^{prefix}{name} {syntax}", + doc: str = ""): + self.prefix: str = prefix + """ + The prefix used in the command (usually ``/`` or ``!``). + """ + + self.name: str = name + """ + The name of the command, usually all lowercase. + """ + + self.syntax: str = syntax + """ + A regex describing the syntax of the command, using named capture groups ``(?P...)`` to capture arguments + that should be passed to the function. + """ + + self.pattern: re.Pattern = re.compile(pattern.format(prefix=prefix, name=name, syntax=syntax)) + """ + The compiled regex pattern. + + By default, it :meth:`str.format` the passed string with the ``prefix``, ``name`` and ``syntax`` keyword + arguments, but this behaviour can be changed by passing a different ``pattern`` to :meth:`__init__`. + """ + + self.doc: str = doc + """ + A string explaining how this command should be used. Useful for command lists or help commands. + """ + + def __call__(self, f): + """ + The decorator interface of the command. + """ + @functools.wraps(f) + async def decorated(_msg: bullet.Message, **original_kwargs) -> t.Conversation: + text: str = await _msg.text() + match: re.Match = self.pattern.search(text) + match_kwargs: dict = match.groupdict() + teleported: t.Callable = teleporter.teleporter(is_async=True, validate_output=False)(f) + return await teleported(_msg=_msg, **original_kwargs, **match_kwargs) + return decorated