-
Notifications
You must be signed in to change notification settings - Fork 0
Willie tutorial, Part 2
You have Willie up and running. Now it's time to start writing your own modules. This part of the guide will cover how to do some simple modules.
It's not a programming tutorial without a "Hello, world". We start by editing a
file in ~/.willie/modules
. If you've run Willie already, this folder should
already exist. Willie will find modules in here by default, but you can add
other folders for it to look in by modifying your config file.
A "Hello, world" command is very simple in willie. In the folder I mentioned
above, make a file called helloworld.py
. In it, put the following and save:
import willie
@willie.module.commands('helloworld')
def helloworld(bot, trigger):
bot.say('Hello, world!')
The first line imports the willie
library. In this module, we only really
need this for the next line. This is called a decorator, and associates the command "helloworld"
with the function that comes right after it. A command in Willie is triggered when a line said
in a channel starts with a period, followed by the command (the prefix can be changed in the config file).
The next line defines a function, which takes two arguments. This is the
signature you'll see in nearly every function in Willie modules. The first
argument is usually called bot
, and it represents the Willie instance you're
running. It includes functions like msg
and say
, which we used above, that
make the bot interact with the IRC network. It also includes access to the
database and configuration, which will be discussed later. The second argument
is usually called trigger
, and gives you access to information about the line
which "triggered" the "callable", which is what we call the functions that the
bot can call.
The last line does exactly as you'd expect; it says "Hello, world!" in the
channel where the command was triggered. In other words, if someone says
.helloworld
, Willie will say Hello, world!
.
As a side note, willie.module
also has a commands decorator, which you can use to give a callable more than one command. For example, @willie.module.commands('helloworld', 'hiworld')
would make this get triggered by either .helloworld
or .hiworld.
Let's take a look at another short example:
from willie import module
@module.commands('echo')
def echo(bot, trigger):
bot.reply(trigger.group(2))
There are a few new concepts here. One is that I've imported the decorator a
bit differently. This is pretty standard in Python. You could also do
from willie.module import command
, and then just do @command
for the
decorator. They all work the same way.
The next new thing is willie.reply
. This is the same as .say
, but it puts
the name of the person who triggered it at the beginning. Next, we have
trigger.group(2)
. We'll go more into groups in the next section, but for now
you just need to know that, for commands like this, group 1 is the command
(echo
in this case), and group 2 is everything after it.
So if I say .echo Spam and eggs
, Willie will respond
Embolalia, Spam and eggs
.
Regular expressions, or regexes, are incredibly powerful tools for matching patterns in text. I'll explain the regexes I use in this section, but you should probably check out a quick guide to them before continuing. This page gives you a good quick overview, and the same site has a reference sheet which comes in handy. This site has a tool to test regexes, and show you what's matching which part of the pattern.
To make a callable trigger on a rule, use the @willie.module.rule
decorator.
It takes a string with a regex in it, and matches that against the lines it
sees. Here's an example:
from willie import module
@module.rule('hello!?')
def hi(bot, trigger):
bot.say('Hi, ' + trigger.nick)
The rule, hello!?
, matches the word "hello", possibly followed by an
exclaimation point. So if someone says either "hello" or "hello!", this
rule will match, and this function will be called. Willie will then say
Hi, Embolalia
, where "Embolalia" is the nickname of whoever triggered the
callable ( trigger.nick
gives you the triggerer's nickname).
A trick you might want to keep in mind is Python's "raw strings". As you'll
shortly find, regular expressions contain quite a few backslashes, which can
sometimes lead to unexpected results. Putting the letter r
before a string
means Python interprets that string exactly as it looks. So the string r'\n'
,
for example, is actually a backslash and then an n, and not a newline character.
Ignore this section for now. It covers stuff that's new and isn't entirely set in stone yet.
Another decorator that takes rules, @willie.module.nickname_command
, is
provided for convenience. This is basically the same as a regular rule, but it
prefixes it with the name of the bot, followed by either a comma or a colon.
So, for example, if the above command had instead had the decorator
@module.nickname_command('hello!?')
, and if the bot were running with the
nickname "Willie", it would have matched on Willie, hello
, Willie: hello!
,
etc.
Using groups in your regular expressions is very helpful. For example, if you
wanted to match a NANP
phone number, you could use the regular expression
\D?(\d{3})\s?\D(\d{3})\D(\d{4})
. Going through this piece by piece, we have
\D
(which matches anything that isn't a digit 0-9) followed by a question
mark (which makes it optional). Then we have a group which matches exactly
three digits (\d
is any digit 0-9, and {3}
means to repeat the previous
thing three times). Then we have \s
, which matches any space character,
followed by a question mark to make it optional. Then another non-digit,
another 3 digits, another non-digit, and then another four digits. This is a
bit liberal, in that it will accept not just normally formatted numbers like
(614)555-1234
, but also some weird stuff like !614* 555+1234
, and it
doesn't match numbers given without area codes. Finding a perfect regex for
that is left as a reader exercise.
If this were assigned as a rule to a callable, any line which started with a
NANP phone number would be matched. (You could add .*
, which means any number
of any characters, to the beginning to make it match if the number is anywhere
in the line.) To get the pieces of the number out, you could use
trigger.group(1)
, trigger.group(2)
, and trigger.group(3)
. Or, if for
example you wanted to put it into a more common format, you could do
'(%s) %s-%s' % trigger.groups()
, which will give you (for example)
(614) 555-1234
.
The final basic thing to know is how to document your code in a way Willie can use. This is done using Python's "docstrings". When a string is put immediately below a variable or function, or at the top of a file, without being assigned to anything else, it becomes that variable/function/file's docstring. The first one you'll want to do is one for your file. You should include a brief description of what the functions in the file do, and information about the copyright and licensing on the file. Most of the modules that come with Willie have one that looks something like this:
"""
sample.py - A sample Willie module
Copyright 2013, Edward Powell - embolalia.net
Licensed under the Eiffel Forum License 2.
http://willie.dfbta.net
"""
The triple quotes are Python's way of having a string across multiple lines. Yours doesn't have to look exactly like this, of course, but it'll give you a general idea of what to include.
You should also put a docstring on all of the callables you have. This should
be a short message describing what the callable does. This is what Willie will
reply with when you use the .help
command on something.
There is also a @willie.module.example
decorator, which you can give a string
containing an example of a valid command. An example of an example might be
.t America/New_York
for the time command.