Skip to content
Aiden880 edited this page Jul 30, 2014 · 61 revisions

Writing Plugins

Writing plugins for CloudBot is simple! This page seeks to detail all you need to know to write your own plugins.

If you have any trouble understanding the guide, come chat with us in the #CloudBot IRC channel on irc.esper.net.

(We'd also love it if you helped give back to CloudBot! Send us your custom plugins for inclusion :])

Learning by example

plugins/echo.py:

from util import hook

@hook.command
def echo(inp):
    return inp + inp

usage:

<Luke> .echo dance
<MyNewCloudBot> (Luke) dancedance

explanation:

This defines a command that replies with twice its input. It can be invoked by saying phrases in a channel the bot is in, notably ".echo", "MyNewCloudBot: echo", "MyNewCloudBot; echo", and "MyNewCloudBot, echo" (assuming the bot's nick is "MyNewCloudBot").

It is important to note that any output a command returns will be printed in the channel the command was used in. Of course, you don't need to use return as you can just use one of the functions detailed near the bottom of this page.

CloudBot continually scans the ./plugins directory for new or changed .py files. When it finds one, it runs it and examines each function to see whether it is a plugin. If it is, the plugin is loaded.

The decorators found in util/hook.py mark functions as plugins of various types.

Plugin hooks

There are four types of plugin hooks: commands, regexes, events, and sieves. The hook type is assigned to plugin functions using decorators found in 'util/hook.py'.

There is also a secondary hook type: '@hook.singlethread'

It indicates that the function should run in its own thread. Note that, in that case, you can't use the existing database connection object.

Command Hook

@hook.command @hook.command(mycommand)

Commands run when the beginning of a normal chat line matches one of .command, botnick: command, botnick; command, or botnick, command, where command is the command name, and botnick is the bot's nick on the server.

Commands respond to abbreviated forms: a command named "dictionary" will be invoked on both ".dictionary" and ".dict". If an abbreviated command is ambiguous, the bot will return with a list of possibilities: given commands "dictionary" and "dice", attempting to run command ".di" will make the bot say "did you mean dictionary or dice?".

When @hook.command is used without arguments, the command name is set to the function name. When given an argument, it is used as the command name. This allows one function to respond to multiple commands:

from util import hook

@hook.command('hi')
@hook.command
def hello(inp):
    return "Hey there!"

Users can invoke this function with either ".hello" or ".hi" (note that they will have to add (junk) arguments, for a way to fix this, please see the "advanced hook types" section).

The first argument, inp, will be the text that occurs after the command. (e.g., "bar" in ".foo bar"). You can split this with inp.split(" "). For the other arguments, see the "shared arguments

Regex Hook

@hook.regex(myregex)

Takes an argument corresponding to the regex string (not the compiled regex), followed by optional flags. Each line of chat is matched against the provided regex pattern; if it finds a matc, the hooked function will be called with the match object.

Event Hook

@hook.event(myevent)

Event hooks are called whenever a specific IRC command is issued. For example, to run some code whenever a user talks, you can use 'PRIVMSG' as the argument. Using '*' as the argument will hook all IRC activity.

Some useful events to hook onto are:

  • PRIVMSG - called when a user speaks
  • KICK - called when a user is kicked
  • NICK - called when a user changes nick
  • 004 - called when the bot is connected to the network and ready to accept input

The first argument returned for 'PRIVMSG' be a two-element list of the form ["#channel", "text"]. Details on what other types of event return will be added soon.

Here's an example event hook that will make the bot join two password-protected channels:

from util import hook

@hook.event("004")
def onready(paramlist, conn=None):
    conn.send("JOIN #channel1 password1")
    conn.send("JOIN #channel2 password2")

Needs to be expanded

Sieve Hook

@hook.sieve(bot, input, func, type, args)

Allows you filter or cancel different actions.

To cancel an action, return None.

Needs to be expanded

Secondary Hook: Singlethread

@hook.singlethread

This is a secondary hook, used to tell a hook to run on its own thread. (note that the existing database connection will be unusable).

@hook.singlethread
@hook.command('hi', adminonly=True)

Shared Arguments:

These arguments are shared by functions of all hook types:

  • inp -- the command arguments.
  • nick -- string, the nickname of whoever sent the message.
  • channel -- string, the channel the message was sent on. Equal to nick if it's a private message.
  • msg -- string, the line that was sent.
  • raw -- string, the raw full line that was sent.
  • re -- the result of doing re.match(hook, msg).
  • input -- the triggering line of text.
  • bot -- the running bot object.
  • db -- the database connection object.

useful functions:

  • message("msg"): obvious (Luke! Want to dance?)
  • action("action"): makes the bot act something out (* MyNewCloudBot dances with Luke!)
  • reply("msg"): makes the bot say something with the name of the user who triggered the hook ((Luke) That was fun!) )
  • message("msg", target="target"): sends msg to target (MyNewCloudBot => Luke: Psst, let's do it again!)
  • (other irc commands, like mode, topic, etc)

usage:

In order to use these, you must pass them as a Nonetype argument to your function, like so:

from util import hook
@hook.command
def say_something(inp, message=None, action=None, nick=None):
    action("is going to say something that " + nick + " told me to say!")
    message(inp)
<Luke> .say_something I love to dance! :D
* MyNewCloudBot is going to say something that Luke told me to say!
<MyNewCloudBot> I love to dance! :D

(these must be used as arguments to the plugin, not the hook itself)

advanced hook types:

For hooks that should only work for admins, add permissions=["adminonly"] to the hook (not the function). ie:

@hook.command('hi', permissions=["adminonly"])

You can also use other permissions you make up, and add them to the config under permissions.

If a hook should not show help when used without arguments, add autohelp=False to the hook (not the function). ie:

@hook.command('hi', autohelp=True)

(this is only relevant on command hooks)

Getting Help

If you need help with anything on this page, head on over to #CloudBot!