Support hook decorators

This commit is contained in:
dave 2017-05-14 12:44:14 -07:00
parent d2c3261398
commit 65459051da
3 changed files with 68 additions and 16 deletions

View File

@ -13,13 +13,14 @@ file's name.
.. code-block:: python
from pyircbot.modulebase import ModuleBase,ModuleHook
from pyircbot.modulebase import ModuleBase, hook
class EchoExample(ModuleBase):
The class's ``__init__`` method accepts 2 args - a reference to the bot's API
and what the bot has decided to name this module. These are passed to
ModuleBase. Module's init method should be as quick as possible. The bot loads
modules one after the other so a long delay slows bot startup.
modules sequentially on startup and you should avoid long operations here as it
will undesirably slow bot startup time.
.. code-block:: python
@ -32,20 +33,29 @@ calling :py:meth:`pyircbot.modulebase.ModuleBase.loadConfig`:
.. code-block:: python
self.loadConfig()
print(self.config)
self.loadConfig() # Manually reload config
In ``__init__``, the module lists what hooks it wants to listen for. Hooks are
executed when the corresponding IRC protocol command is received.
.. code-block:: python
self.hooks=[ModuleHook("PRIVMSG", self.echo)]
Then, a handler for this hook:
Any other setup code should be placed in ``__init__``. Note that ``__init__``
will be executed when the bot is starting up and before the irc connection is
opened.
Adding interaction
------------------
In order to make your module respond to various IRC commands, pyircbot uses a
system of "hooks", which can be defined using decorators or programmatically.
Note: in this case, "commands" refers to IRC protocol commands such as PRIVMSG,
KICK, JOIN, etc. Pyircbot also provides some meta-events that are accessed in
the same way. The complete list of supported commands can be seen in the source
of :py:meth:`pyircbot.irccore.IRCCore.initHooks`.
The easiest method is to use the ``hook`` decorator on any function your want
called when an IRC command is received.
.. code-block:: python
@hook("PRIVMSG")
def echo(self, event):
The handler is passed and IRCEvent object containing the data sent by the irc
@ -88,17 +98,18 @@ shutdown handler is needed to ensure a clean shutdown.
EchoExample module
------------------
This is the snippets above combined into a usable module.
.. code-block:: python
from pyircbot.modulebase import ModuleBase,ModuleHook
from pyircbot.modulebase import ModuleBase, hook
class EchoExample(ModuleBase):
def __init__(self, bot, moduleName):
ModuleBase.__init__(self, bot, moduleName)
self.loadConfig()
print(self.config)
self.hooks=[ModuleHook("PRIVMSG", self.echo)]
@hook("PRIVMSG")
def echo(self, event):
self.bot.act_PRIVMSG(event.args[0], event.trailing)

View File

@ -42,8 +42,21 @@ class ModuleBase:
# Autoload config if available
self.loadConfig()
# Prepare any function hooking
self.init_hooks()
self.log.info("Loaded module %s" % self.moduleName)
def init_hooks(self):
"""
Scan the module for tagged methods and set up appropriate protocol hooks.
"""
for attr_name in dir(self):
attr = getattr(self, attr_name)
if callable(attr) and hasattr(attr, ATTR_ACTION_HOOK):
for action in getattr(attr, ATTR_ACTION_HOOK):
self.hooks.append(ModuleHook(action, attr))
def loadConfig(self):
"""
Loads this module's config into self.config. The bot's main config is checked for a section matching the module
@ -79,3 +92,31 @@ class ModuleHook:
def __init__(self, hook, method):
self.hook = hook
self.method = method
ATTR_ACTION_HOOK = "__tag_hooks"
class hook(object):
"""
Decorating for calling module methods in response to IRC actions. Example:
```
@hook("PRIVMSG")
def mymyethod(self, message):
print("IRC server sent PRIVMSG")
```
This stores a list of IRC actions each function is tagged for in method.__tag_hooks. This attribute is scanned
during module init and appropriate hooks are set up.
"""
def __init__(self, *args):
self.commands = args
def __call__(self, func):
"""
"""
if not hasattr(func, ATTR_ACTION_HOOK):
setattr(func, ATTR_ACTION_HOOK, list(self.commands))
else:
getattr(func, ATTR_ACTION_HOOK).extend(self.commands)
return func

View File

@ -5,7 +5,7 @@
:synopsis: Spam chat with awesome ascii texts
"""
from pyircbot.modulebase import ModuleBase, ModuleHook
from pyircbot.modulebase import ModuleBase, hook
from threading import Thread
from glob import iglob
from collections import defaultdict
@ -20,10 +20,10 @@ RE_ASCII_FNAME = re.compile(r'^[a-zA-Z0-9\-_]+$')
class ASCII(ModuleBase):
def __init__(self, bot, moduleName):
ModuleBase.__init__(self, bot, moduleName)
self.hooks.append(ModuleHook("PRIVMSG", self.listen_msg))
self.running_asciis = defaultdict(lambda: None)
self.killed_channels = defaultdict(lambda: False)
@hook("PRIVMSG")
def listen_msg(self, msg):
"""
Handle commands