Support hook decorators
This commit is contained in:
parent
d2c3261398
commit
65459051da
|
@ -13,13 +13,14 @@ file's name.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase,ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
class EchoExample(ModuleBase):
|
class EchoExample(ModuleBase):
|
||||||
|
|
||||||
The class's ``__init__`` method accepts 2 args - a reference to the bot's API
|
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
|
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
|
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
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -32,20 +33,29 @@ calling :py:meth:`pyircbot.modulebase.ModuleBase.loadConfig`:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
self.loadConfig()
|
|
||||||
print(self.config)
|
print(self.config)
|
||||||
|
self.loadConfig() # Manually reload config
|
||||||
|
|
||||||
In ``__init__``, the module lists what hooks it wants to listen for. Hooks are
|
Any other setup code should be placed in ``__init__``. Note that ``__init__``
|
||||||
executed when the corresponding IRC protocol command is received.
|
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)]
|
------------------
|
||||||
|
|
||||||
Then, a handler for this hook:
|
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
|
.. code-block:: python
|
||||||
|
|
||||||
|
@hook("PRIVMSG")
|
||||||
def echo(self, event):
|
def echo(self, event):
|
||||||
|
|
||||||
The handler is passed and IRCEvent object containing the data sent by the irc
|
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
|
EchoExample module
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
This is the snippets above combined into a usable module.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase,ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
|
|
||||||
class EchoExample(ModuleBase):
|
class EchoExample(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.loadConfig()
|
|
||||||
print(self.config)
|
print(self.config)
|
||||||
self.hooks=[ModuleHook("PRIVMSG", self.echo)]
|
|
||||||
|
|
||||||
|
@hook("PRIVMSG")
|
||||||
def echo(self, event):
|
def echo(self, event):
|
||||||
self.bot.act_PRIVMSG(event.args[0], event.trailing)
|
self.bot.act_PRIVMSG(event.args[0], event.trailing)
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,21 @@ class ModuleBase:
|
||||||
# Autoload config if available
|
# Autoload config if available
|
||||||
self.loadConfig()
|
self.loadConfig()
|
||||||
|
|
||||||
|
# Prepare any function hooking
|
||||||
|
self.init_hooks()
|
||||||
|
|
||||||
self.log.info("Loaded module %s" % self.moduleName)
|
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):
|
def loadConfig(self):
|
||||||
"""
|
"""
|
||||||
Loads this module's config into self.config. The bot's main config is checked for a section matching the module
|
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):
|
def __init__(self, hook, method):
|
||||||
self.hook = hook
|
self.hook = hook
|
||||||
self.method = method
|
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
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
:synopsis: Spam chat with awesome ascii texts
|
:synopsis: Spam chat with awesome ascii texts
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from glob import iglob
|
from glob import iglob
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
@ -20,10 +20,10 @@ RE_ASCII_FNAME = re.compile(r'^[a-zA-Z0-9\-_]+$')
|
||||||
class ASCII(ModuleBase):
|
class ASCII(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks.append(ModuleHook("PRIVMSG", self.listen_msg))
|
|
||||||
self.running_asciis = defaultdict(lambda: None)
|
self.running_asciis = defaultdict(lambda: None)
|
||||||
self.killed_channels = defaultdict(lambda: False)
|
self.killed_channels = defaultdict(lambda: False)
|
||||||
|
|
||||||
|
@hook("PRIVMSG")
|
||||||
def listen_msg(self, msg):
|
def listen_msg(self, msg):
|
||||||
"""
|
"""
|
||||||
Handle commands
|
Handle commands
|
||||||
|
|
Loading…
Reference in New Issue