Browse Source

Support hook decorators

dave/xdcc
dave 5 years ago
parent
commit
65459051da
  1. 33
      docs/module_guide/_module_guide.rst
  2. 41
      pyircbot/modulebase.py
  3. 4
      pyircbot/modules/ASCII.py

33
docs/module_guide/_module_guide.rst

@ -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.
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.
.. code-block:: python
Adding interaction
------------------
self.hooks=[ModuleHook("PRIVMSG", self.echo)]
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`.
Then, a handler for this hook:
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)

41
pyircbot/modulebase.py

@ -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

4
pyircbot/modules/ASCII.py

@ -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

Loading…
Cancel
Save