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