Skip to content

Commit

Permalink
module: add action_commands decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
deathbybandaid committed Oct 5, 2019
1 parent 0de7ad9 commit 9911b97
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 3 deletions.
11 changes: 8 additions & 3 deletions sopel/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import re
import sys

from sopel.tools import compile_rule, itervalues, get_command_regexp, get_nickname_command_regexp
from sopel.tools import (compile_rule, itervalues, get_command_regexp,
get_nickname_command_regexp, get_action_command_regexp)
from sopel.config import core_section

default_prefix = core_section.CoreSection.help_prefix.default
Expand Down Expand Up @@ -67,7 +68,7 @@ def clean_callable(func, config):
func.rule = [func.rule]
func.rule = [compile_rule(nick, rule, alias_nicks) for rule in func.rule]

if hasattr(func, 'commands') or hasattr(func, 'nickname_commands'):
if hasattr(func, 'commands') or hasattr(func, 'nickname_commands') or hasattr(func, 'action_commands'):
func.rule = getattr(func, 'rule', [])
for command in getattr(func, 'commands', []):
regexp = get_command_regexp(prefix, command)
Expand All @@ -77,6 +78,10 @@ def clean_callable(func, config):
regexp = get_nickname_command_regexp(nick, command, alias_nicks)
if regexp not in func.rule:
func.rule.append(regexp)
for command in getattr(func, 'action_commands', []):
regexp = get_action_command_regexp(command)
if regexp not in func.rule:
func.rule.append(regexp)
if hasattr(func, 'example'):
# If no examples are flagged as user-facing, just show the first one like Sopel<7 did
examples = [rec["example"] for rec in func.example if rec["help"]] or [func.example[0]["example"]]
Expand Down Expand Up @@ -107,7 +112,7 @@ def is_triggerable(obj):
return any(
hasattr(obj, attr)
for attr in ('rule', 'event', 'intents', 'commands',
'nickname_commands'))
'nickname_commands', 'action_commands'))


def clean_module(module, config):
Expand Down
28 changes: 28 additions & 0 deletions sopel/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,34 @@ def add_attribute(function):
return add_attribute


def action_commands(*command_list):
"""Decorate a function to trigger on CTCP ACTION lines.
This decorator can be used multiple times to add multiple rules. The
resulting match object will have the command as the first group, rest of
the line, excluding leading whitespace, as the second group. Parameters 1
through 4, separated by whitespace, will be groups 3-6.
Args:
command: A string, which can be a regular expression.
Returns:
A function with a new regular expression appended to the rule
attribute. If there is no rule attribute, it is added.
Example:
@action_commands("hello!"):
Would trigger on "/me hello!"
"""
def add_attribute(function):
function.intents = ['ACTION']
if not hasattr(function, "action_commands"):
function.action_commands = []
function.action_commands.extend(command_list)
return function
return add_attribute


def priority(value):
"""Decorate a function to be executed with higher or lower priority.
Expand Down
39 changes: 39 additions & 0 deletions sopel/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,45 @@ def get_nickname_command_pattern(command):
""".format(command=command)


def get_action_command_regexp(command):
"""Get a compiled regexp object that implements the command.
:param str command: the name of the command
:return: a compiled regexp object that implements the command
:rtype: :py:class:`re.Pattern`
"""
pattern = get_action_command_pattern(command)
return re.compile(pattern, re.IGNORECASE | re.VERBOSE)


def get_action_command_pattern(command):
"""Get the uncompiled regex pattern for action commands.
:param str command: the command name
:return: a regex pattern that will match the given command
:rtype: str
"""
# This regexp matches equivalently and produces the same
# groups 1 and 2 as the old regexp: r'^%s(%s)(?: +(.*))?$'
# The only differences should be handling all whitespace
# like spaces and the addition of groups 3-6.
return r"""
({command}) # Command as group 1.
(?:\s+ # Whitespace to end command.
( # Rest of the line as group 2.
(?:(\S+))? # Parameters 1-4 as groups 3-6.
(?:\s+(\S+))?
(?:\s+(\S+))?
(?:\s+(\S+))?
.* # Accept anything after the parameters.
# Leave it up to the module to parse
# the line.
))? # Group 2 must be None, if there are no
# parameters.
$ # EoL, so there are no partial matches.
""".format(command=command)


def get_sendable_message(text, max_length=400):
"""Get a sendable ``text`` message, with its excess when needed.
Expand Down

0 comments on commit 9911b97

Please sign in to comment.