@@ -2,6 +2,8 @@ v0.2 (unreleased): | |||||
- Added a new command syntax allowing the caller to redirect replies to another | - Added a new command syntax allowing the caller to redirect replies to another | ||||
user. Example: "!dictionary >Fred earwig" | user. Example: "!dictionary >Fred earwig" | ||||
- Added unload() hooks to commands and tasks, called when they are killed | |||||
during a reload. | |||||
- Added 'rc' hook type to allow IRC commands to respond to RC watcher events. | - Added 'rc' hook type to allow IRC commands to respond to RC watcher events. | ||||
- Added 'part' hook type as a counterpart to 'join'. | - Added 'part' hook type as a counterpart to 'join'. | ||||
- Added !stalk/!watch. | - Added !stalk/!watch. | ||||
@@ -133,8 +133,8 @@ Custom IRC commands | |||||
~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~ | ||||
Custom commands are subclasses of `earwigbot.commands.Command`_ that override | Custom commands are subclasses of `earwigbot.commands.Command`_ that override | ||||
``Command``'s ``process()`` (and optionally ``check()`` or ``setup()``) | |||||
methods. | |||||
``Command``'s ``process()`` (and optionally ``check()``, ``setup()``, or | |||||
``unload()``) methods. | |||||
The bot has a wide selection of built-in commands and plugins to act as sample | The bot has a wide selection of built-in commands and plugins to act as sample | ||||
code and/or to give ideas. Start with test_, and then check out chanops_ and | code and/or to give ideas. Start with test_, and then check out chanops_ and | ||||
@@ -144,7 +144,7 @@ Custom bot tasks | |||||
~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~ | ||||
Custom tasks are subclasses of `earwigbot.tasks.Task`_ that override ``Task``'s | Custom tasks are subclasses of `earwigbot.tasks.Task`_ that override ``Task``'s | ||||
``run()`` (and optionally ``setup()``) methods. | |||||
``run()`` (and optionally ``setup()`` or ``unload()``) methods. | |||||
See the built-in wikiproject_tagger_ task for a relatively straightforward | See the built-in wikiproject_tagger_ task for a relatively straightforward | ||||
task, or the afc_statistics_ plugin for a more complicated one. | task, or the afc_statistics_ plugin for a more complicated one. | ||||
@@ -86,8 +86,9 @@ Custom IRC commands | |||||
Custom commands are subclasses of :py:class:`earwigbot.commands.Command` that | Custom commands are subclasses of :py:class:`earwigbot.commands.Command` that | ||||
override :py:class:`~earwigbot.commands.Command`'s | override :py:class:`~earwigbot.commands.Command`'s | ||||
:py:meth:`~earwigbot.commands.Command.process` (and optionally | :py:meth:`~earwigbot.commands.Command.process` (and optionally | ||||
:py:meth:`~earwigbot.commands.Command.check` or | |||||
:py:meth:`~earwigbot.commands.Command.setup`) methods. | |||||
:py:meth:`~earwigbot.commands.Command.check`, | |||||
:py:meth:`~earwigbot.commands.Command.setup`, or | |||||
:py:meth:`~earwigbot.commands.Command.unload`) methods. | |||||
:py:class:`~earwigbot.commands.Command`'s docstrings should explain what each | :py:class:`~earwigbot.commands.Command`'s docstrings should explain what each | ||||
attribute and method is for and what they should be overridden with, but these | attribute and method is for and what they should be overridden with, but these | ||||
@@ -154,6 +155,10 @@ are the basics: | |||||
<earwigbot.irc.connection.IRCConnection.join>`, and | <earwigbot.irc.connection.IRCConnection.join>`, and | ||||
:py:meth:`part(chan) <earwigbot.irc.connection.IRCConnection.part>`. | :py:meth:`part(chan) <earwigbot.irc.connection.IRCConnection.part>`. | ||||
- Method :py:meth:`~earwigbot.commands.Command.unload` is called *once* with no | |||||
arguments immediately before the command is unloaded, such as when someone | |||||
uses ``!reload``. Does nothing by default. | |||||
Commands have access to :py:attr:`config.commands[command_name]` for config | Commands have access to :py:attr:`config.commands[command_name]` for config | ||||
information, which is a node in :file:`config.yml` like every other attribute | information, which is a node in :file:`config.yml` like every other attribute | ||||
of :py:attr:`bot.config`. This can be used to store, for example, API keys or | of :py:attr:`bot.config`. This can be used to store, for example, API keys or | ||||
@@ -175,7 +180,8 @@ Custom bot tasks | |||||
Custom tasks are subclasses of :py:class:`earwigbot.tasks.Task` that | Custom tasks are subclasses of :py:class:`earwigbot.tasks.Task` that | ||||
override :py:class:`~earwigbot.tasks.Task`'s | override :py:class:`~earwigbot.tasks.Task`'s | ||||
:py:meth:`~earwigbot.tasks.Task.run` (and optionally | :py:meth:`~earwigbot.tasks.Task.run` (and optionally | ||||
:py:meth:`~earwigbot.tasks.Task.setup`) methods. | |||||
:py:meth:`~earwigbot.tasks.Task.setup` or | |||||
:py:meth:`~earwigbot.tasks.Task.unload`) methods. | |||||
:py:class:`~earwigbot.tasks.Task`'s docstrings should explain what each | :py:class:`~earwigbot.tasks.Task`'s docstrings should explain what each | ||||
attribute and method is for and what they should be overridden with, but these | attribute and method is for and what they should be overridden with, but these | ||||
@@ -220,6 +226,10 @@ are the basics: | |||||
the task's code goes. For interfacing with MediaWiki sites, read up on the | the task's code goes. For interfacing with MediaWiki sites, read up on the | ||||
:doc:`Wiki Toolset <toolset>`. | :doc:`Wiki Toolset <toolset>`. | ||||
- Method :py:meth:`~earwigbot.tasks.Task.unload` is called *once* with no | |||||
arguments immediately before the task is unloaded. Does nothing by | |||||
default. | |||||
Tasks have access to :py:attr:`config.tasks[task_name]` for config information, | Tasks have access to :py:attr:`config.tasks[task_name]` for config information, | ||||
which is a node in :file:`config.yml` like every other attribute of | which is a node in :file:`config.yml` like every other attribute of | ||||
:py:attr:`bot.config`. This can be used to store, for example, edit summaries | :py:attr:`bot.config`. This can be used to store, for example, edit summaries | ||||
@@ -120,3 +120,10 @@ class Command(object): | |||||
command's body here. | command's body here. | ||||
""" | """ | ||||
pass | pass | ||||
def unload(self): | |||||
"""Hook called immediately before a command is unloaded. | |||||
Does nothing by default; feel free to override. | |||||
""" | |||||
pass |
@@ -137,6 +137,19 @@ class _ResourceManager(object): | |||||
self._load_module(modname, dir) | self._load_module(modname, dir) | ||||
processed.append(modname) | processed.append(modname) | ||||
def _unload_resources(self): | |||||
"""Unload all resources, calling their unload hooks in the process.""" | |||||
res_type = self._resource_name[:-1] # e.g. "command" or "task" | |||||
for resource in self: | |||||
if not hasattr(resource, "unload"): | |||||
continue | |||||
try: | |||||
resource.unload() | |||||
except Exception: | |||||
e = "Error unloading {0} '{1}'" | |||||
self.logger.exception(e.format(res_type, resource.name)) | |||||
self._resources.clear() | |||||
@property | @property | ||||
def lock(self): | def lock(self): | ||||
"""The resource access/modify lock.""" | """The resource access/modify lock.""" | ||||
@@ -146,7 +159,7 @@ class _ResourceManager(object): | |||||
"""Load (or reload) all valid resources into :py:attr:`_resources`.""" | """Load (or reload) all valid resources into :py:attr:`_resources`.""" | ||||
name = self._resource_name # e.g. "commands" or "tasks" | name = self._resource_name # e.g. "commands" or "tasks" | ||||
with self.lock: | with self.lock: | ||||
self._resources.clear() | |||||
self._unload_resources() | |||||
builtin_dir = path.join(path.dirname(__file__), name) | builtin_dir = path.join(path.dirname(__file__), name) | ||||
plugins_dir = path.join(self.bot.config.root_dir, name) | plugins_dir = path.join(self.bot.config.root_dir, name) | ||||
if getattr(self.bot.config, name).get("disable") is True: | if getattr(self.bot.config, name).get("disable") is True: | ||||
@@ -84,6 +84,13 @@ class Task(object): | |||||
""" | """ | ||||
pass | pass | ||||
def unload(self): | |||||
"""Hook called immediately before the task is unloaded. | |||||
Does nothing by default; feel free to override. | |||||
""" | |||||
pass | |||||
def make_summary(self, comment): | def make_summary(self, comment): | ||||
"""Make an edit summary by filling in variables in a config value. | """Make an edit summary by filling in variables in a config value. | ||||