Skip to content
zoni edited this page Dec 23, 2012 · 28 revisions

Content

Tips before you get started

Add a development directory in the plugin searchpath

Make a directory to host your new plugin or plugins and set it in config.py

BOT_EXTRA_PLUGIN_DIR = '/home/path/to/plugin/root'

Start the bot in test mode (optional but it helps)

You can start the bot with the "-t" command line parameter. It runs the bot in test mode, a kind of single user mode on pure console (no XMPP server involved here).

It will prompt you :

# err.py -t
[...]
INFO:Plugin activation done.
Talk to  me >> _

Then you can enter all the available commands and it will trust you as an admin. Combine it with the debug mode of an IDE and you have a super snappy environment to try out and debug your code.

Use our plugin skeleton

We've written a minimal plugin which shows the basic layout of a simple plugin. Save yourself some time by using this template as a starting point.

Writing a simple plugin

Let say you want to make an helloWorld plugin. First define a class implementing BotPlugin with a method decorated by @botcmd as follow :

helloWorld.py

from errbot import BotPlugin, botcmd # Note: if you use a version < 1.6.0 see below.

class HelloWorld(BotPlugin):
    @botcmd                               # this tag this method as a command
    def hello(self, mess, args):          # it will respond to !hello
        """ this command says hello """   # this will be the answer of !help hello
        return 'Hello World !'            # the bot will answer that

Then you need to put some metadescription in a .plug file.

helloWorld.plug

[Core]
Name = HelloWorld
Module = helloWorld
    
[Documentation]
Description = let's say hello !

Start the bot or restart it if it is already live with the command !restart.

Note : Module must match the name of the python module your main class is

That's it ! You can check if the plugin correctly load with the !status command. Then you can check if the hello command is correctly bound with !help. Then you can try it : !hello

If something goes wrong to can inspect the last lines of the log instantly with !log tail

imports for versions [<v1.6.x]

For v1.0.0 to v1.5.1:

from errbot.botplugin import BotPlugin
from errbot.jabberbot import botcmd

Actually those still work under 1.6.x but they will generate a deprecation warning.

Sending a message asynchronously while computing a command

Often, with commands that takes long, you want to be able to send some feedback to the user that the command is progressing. You can use the helper send to achieve that.

plugin_example.py

    from errbot import BotPlugin, botcmd

    class PluginExample(BotPlugin):
        @botcmd
        def longcompute(self, mess, args):
            self.send(mess.getFrom(), "/me is computing ... ", message_type=mess.getType())
            [...] # long processing
            return "Done !"

make err split the arguments for you [v1.2.1]

With the split_args_with argument to botcmd, you can specify a delimiter of the arguments and it will give you an array of strings instead of a string

@botcmd(split_args_with=' ')
    def action(self, mess, args):
        # if you send it !action one two three
        # args will be ['one', 'two', 'three']

subcommands [v1.2.0]

If you put an _ in the name of the function, err will create for you a subcommand. It is useful to categorize a feature

@botcmd
    def basket_add(self, mess, args):
        # it will respond to !basket add

    def basket_remove(self, mess, args):
        # it will respond to !basket remove

Note: for backward compatibility it will also answer to !basket_add and !basket_remove

Version checking [v1.3.0]

You can enforce a minimal and maximal version of err if you know that your plugin won't be compatible outside the range. You just have to define min_err_version and/or max_err_version as a property or field. Note: the version is inclusive. It MUST be defined as a string with 3 numbers dotted like '1.3.0'

plugin_example.py

from errbot import BotPlugin

class PluginExample(BotPlugin):
    min_err_version = '1.2.2'
    [...]

Allow the admin to configure your plugin from the chat [v1.3.0]

Err can keep a simple python object for the configuration of your plugin. I avoids asking for the admin to edit config.py, restart the bot...

In order to enable this feature, you need to give a configuration template (ie. a config example) by overriding the get_configuration_template method, for example a plugin requesting a dictionary with 2 entries in there.

plugin_example.py

from errbot import BotPlugin

class PluginExample(BotPlugin):
    def get_configuration_template(self):
        return {'ID_TOKEN' : '00112233445566778899aabbccddeeff', 'USERNAME':'changeme'}
    

This is the minimal implementation.

Then from the admin you will be able to request the default template with !config PluginExample. Then it will instruct you to do a !config PluginExample {'ID_TOKEN' : '00112233445566778899aabbccddeeff', 'USERNAME':'changeme'} with the real values in there.

You will be able to recall the config set with again: !config PluginExample

From within your code, your config will be in self.config

    @botcmd
    def mycommand(self, mess, args):
        # oh I need my TOKEN !
        token = self.config['ID_TOKEN'] # <- Note: this is already a real python dict here

[new in v1.4.1]

Now the framework will do be default a strict check on structure and types or the config : by default it will do only a BASIC check. You need to override it if you want to do more complex checks. it will be called before the configure callback. Note if the config_template is None, it will never be called

It means recusively:

  1. in case of a dictionary, it will check if all the entries and from the same type are there and not more
  2. in case of an array or tuple, it will assume array members of the same type of first element of the template (no mix typed is supported)

If you need a more complex validation you need to override check_configuration :

def check_configuration(self, configuration):
    # ... and here check the configuration as in v1.3.0 for the configure (see below)
    # if you encounter a validation error you need to throw a errbot.util.ValidationException).

[otherwise if you are still on v1.3.0 you need to do that....]

But be careful, you might want to be more defensive towards the user input to be sure the plugin will run with it. For that, override the method configure. Note: if you have no configuration set yet, it will pass you None as parameter, you code has to take care of this case. Note 2: Don't forget to call super otherwise the framework will not store it.

For example :

plugin_example.py

from errbot import BotPlugin

class PluginExample(BotPlugin):
    def configure(self, configuration):
        if configuration: # if it is not configured ignore
            if type(configuration) != dict:
                raise Exception('Wrong configuration type')
            if not configuration.has_key('ID_TOKEN') or not configuration.has_key('USERNAME'):
                raise Exception('Wrong configuration type, it should contain ID_TOKEN and USERNAME')
            if len(configuration) > 2:
                raise Exception('What else did you try to insert in my config ?')
        super(PluginExample, self).configure(configuration)

Implementing a callback to listen to every messages in the chatrooms

You can add a specific callback that will be called on any message sent on the chatroom. It is useful to react at specific keywords even the the bot is not called explicitely with the ! commands.

plugin_example.py

from errbot import BotPlugin

class PluginExample(BotPlugin):
    def callback_message(self, conn, mess):
        if mess.getBody().find('cookie') != -1:
            self.send(mess.getFrom(), "What what somebody said cookie !?", message_type=mess.getType())

Note: Up to and including version 1.6.7, Err suffered from a bug where this could cause an infinite loop when called from within a MUC

Make the bot poll for a specific function [v1.4.0]

Simply register your polling function at activation time:

plugin_example.py

from errbot import BotPlugin
import logging

class PluginExample(BotPlugin):
    def my_callback(self):
        logging.debug('I am called every minute')

    def activate(self):
        super(PluginExample, self).activate()
        self.start_poller(60, self.my_callback)

This is the simplest case. See Botplugin for added parameters, starting several pollers, stopping pollers etc ...