This package contains a framework for an easily set-up, multi-purpose IRC bot. The intent with this package is not that it be included with other packages, but rather be run as a standalone program. However, it is intended (and encouraged) that users write new “modules” for it. This documentation is intended to serve that goal.
Due to the high level of customization encouraged by this bot, there are a few different versions of it floating around. For this documentation, we will cover three: the original “phenny” by Sean B. Palmer the “jenni” fork by Michael Yanovich, and the “Willie” fork by Edward Powell et al. While all recieve periodic updates, the above lists them in a generally ascending order of recentness.
For clarity’s sake, this documentation will refer to these forks as different “API levels” or “API versions”. The original phenny fork will be termed 1.x, the jenni fork will be termed 2.x, and Willie, which this guide will focus on, will be termed 3.x.
At its most basic, writing a Willie module involves creating a Python file with some number of functions in it. Each of these functions will be passed a “Willie” object (“Phenny” in 1.x) and a “Trigger” object (CommandInput in 1.x and 2.x). By convention, these are named phenny and input in 1.x, jenni and input in 2.x, and willie and trigger in 3.x. For the purposes of this guide, the 3.x names will be used.
A Willie module contains one or more callables. It may optionally contain a configure or setup function. callables are given a number of attributes, which determine when they will be executed. Syntactically, this is done at the same indentation level as the function’s def line, following the last line of the function.
This is the general function format, called by Willie when a command is used, a rule is matched, or an event is seen, as determined by the attributes of the function. The details of what this function does are entirely up to the module writer - the only hard requirement from the bot is that it be callable with a Willie object and a Trigger object , as noted above. Usually, methods of the Willie object will be used in reply to the trigger, but this isn’t a requirement.
Note that the name can, and should, be anything - it doesn’t need to be called callable.
A list of commands which will trigger the function. If the Willie instance is in a channel, or sent a PRIVMSG, where one of these strings is said, preceeded only by the configured prefix (a period, by default), the function will execute.
A regular expression which will trigger the function. If the Willie instance is in a channel, or sent a PRIVMSG, where a string matching this expression is said, the function will execute. Note that captured groups here will be retrievable through the Trigger object later.
Inside the regular expression, some special directives can be used. $nick will be replaced with the nick of the bot and , or :, and $nickname will be replaced with the nick of the bot.
Prior to 3.1, rules could also be made one of three formats of tuple. The values would be joined together to form a singular regular expression. However, these kinds of rules add no functionality over simple regular expressions, and are considered deprecated in 3.1.
This is one of a number of events, such as 'JOIN', 'PART', 'QUIT', etc. (More details can be found in RFC 1459.) When the Willie bot is sent one of these events, the function will execute. Note that functions with an event must also be given a rule to match (though it may be '.*', which will always match) or they will not be triggered.
Availability: 2+
This limits the frequency with which a single user may use the function. If a function is given a rate of 20, a single user may only use that function once every 20 seconds. This limit applies to each user individually. Users on the admin list in Willie’s configuration are exempted from rate limits.
Priority can be one of high, medium, low. It allows you to control the order of callable execution, if your module needs it. Defaults to medium
This is an optional function of a module, which will be called while the module is being loaded. Note that this normally occurs prior to connection to the server, so the behavior of the Willie object’s messaging functions is undefined. The purpose of this function is to perform whatever actions are needed to allow a module to function properly (e.g, ensuring that the appropriate configuration variables are set and exist).
The bot will not continue loading modules or connecting during the execution of this function. As such, an infinite loop (such as an unthreaded polling loop) will cause the bot to hang.
Availability: 3+
This is an optional function of a module, which will be called during the user’s setup of the bot. It’s intended purpose is to use the methods of the passed Config object in order to create the configuration variables it needs to function properly.
In 3.1+, the docstring of this function can be used to document the configuration variables that the module uses. This is not currently used by the bot itself; it is merely convention.
In a module function, send text to the channel in which the function was triggered, preceeded by the nick of the user who triggered it.
This function is not available outside of module functions. It can not be used, for example, in a module’s setup function.
The same behavior regarding loop detection and length restrictions apply to reply as to msg.
In a module function, send text to the channel in which the function was triggered.
This function is not available outside of module functions. It can not be used, for example, in a module’s configure function.
The same behavior regarding loop detection and length restrictions apply to say as to msg and action.
In a module function, send text to the channel in which the function was triggered preceeded by CTCP ACTION directive (result identical to using /me in most clients).
This function is not available outside of module functions. It can not be used, for example, in a module’s configure function.
The same behavior regarding loop detection and length restrictions apply to action as to msg and say.
Gracefully quit and shutdown, using message as the quit message
Part channel
Join a channel named channel.
Willie’s current nick. Changing this while Willie is running is unsupported.
Willie’s “real name”, as used for whois.
Willie’s NickServ password
A list of Willie’s initial channels. This list will initially be the same as the one given in the config file, but is not guaranteed to be kept up-to-date.
Availability: 3+
Dictionary mapping channels to a list of their ops, and half-ops and ops respectively.
Send a command to the server. In the simplest case, args is a list containing just the command you wish to send, and text the argument to that command {e.g. write([‘JOIN’], ‘#channel’)}
More specifically, args will be joined together, separated by a space. If text is given, it will be added preceeded by a space and a colon (‘ :’).
Newlines and carriage returns (‘\n’ and ‘\r’) are removed before sending. Additionally, if the joined args and text are more than 510 characters, any remaining characters will not be sent.
Send a PRIVMSG of text to recipient. If the same text was the message in 5 or more of the last 8 calls to msg, '...' will be sent instead. If this condition is met, and '...' is more than 3 of the last 8 calls, no message will be sent. This is intended to prevent Willie from being caught in an infinite loop with another bot, or being used to spam.
After loop detection, the message is checked for length. If the sum of the lengths of text and recipient are greater than 500, text will be truncated to fit this limit.
Availability: 3+
Send text to Willie’s configured debug_target. This can be either an IRC channel (starting with #) or stdio. Suppress the message if the given level is lower than Willie’s configured verbose setting. Acceptable values for level are 'verbose' (only send if Willie is in verbose mode), 'warning' (send if Willie is in verbose or warning mode), always (send debug message regardless of the configured debug level). Returns True if the message is sent or printed, and False if it is not.
If debug_target is a channel, the same behavior regarding loop detection and length restrictions apply to debug as to msg.
Availability: 3+
Add name to channel‘s entry in the ops or halfplus dictionaries, respectively.
Availability: 3+
Remove name from channel‘s entry in the ops or halfplus dictionaries, respectively.
Availability: 3+
Re-initialize and empty the ops and halfops entry for channel.
Availability: 3+
Create an empty entry in ops and halfops for channel. This will not damage existing entries, but must be done before users can be added to either dictionary.
Availability: 3.1+
A simple thread-safe dict implementation. In order to prevent exceptions when iterating over the values and changing them at the same time from different threads, we use a blocking lock on __setitem__ and contains.
Check if a key is in the dict. Use this instead of the in keyword if you want to be thread-safe.
The Config for the current Willie instance.
Sends an error to Willie’s configured debug_target.
Removed in 3.1.2
A dictionary of module functions to their docstring and example, if declared. As of 3.1.2, this dict will be empty, and not updated.
A thread-safe dict for storage of runtime data to be shared between modules. See WillieMemory
With the __dict__ attribute from a Willie module, update or add the trigger commands and rules to allow the function to be triggered.
A dictionary which maps a tuple of a function name and where it was used to the nuber of times it was used there.
A dictionary mapping lower-case’d nicks to dictionaries which map funtion names to the time which they were last used by that nick.
The channel (or nick, in a private message) from which the message was sent.
The nick of the person who sent the message.
The event which triggered the message.
The line which triggered the message.
The regular expression MatchObject for the triggering line.
The arguments given to a command.
True if the nick which triggered the command is in Willie’s admin list as defined in the config file.
True if the nick which triggered the command is the owner stated in the config file.
The host which sent the triggering message.
Availability: 3+
True if the nick which triggered the command is an op on the channel it was triggered in. Will always be False if the command was triggered by a private message
The willie object has, among others, the attributes db and config. These can be used for a number of functions and features.
Availability: 3.1+
Note: This supersedes the SettingsDB object of v3. Within Willie modules, simmilar functionallity can be found using willie.db.preferences.
This class defines an interface for a semi-arbitrary database type. It is meant to allow module writers to operate without regard to how the end user has decided to set up the database.
Return an object which represents a table in the given WillieDB, with the given attributes. This will not check if db already has a table with the given name; the db‘s add_table provides that functionality.
key must be a string, which is in the list of strings columns, or an Exception will be thrown.
Insert a new column into the table, and add it to the column cache. This is the preferred way to add new columns to the database.
Returns the number of users (entries starting with # or &) in the table’s key column.
Return True if this table has a row where the key value is equal to key, else False.
key in db will also work, where db is your SettingsDB object.
Deletes the row for row in the database, removing its values in all columns.
Retrieve the value(s) in one or more columns in the row where the key column(s) match the value(s) given in row. This is basically equivalent to executing SELECT <columns> FROM <self> WHERE <key> = <row>.
The key can be either the name of one column as a string, or a tuple of the names of multiple columns. row is the value or values of this column or columns for which data will be retrieved. If multiple columns are being used, the order in which the columns are presented should match between row and key. A KeyError will be raised if no have values matching row in key. If key is not passed, it will default to the table’s primary key.
columns can either be a single column name, or a tuple of column names. If one name is passed, a single string will be returned. If a tuple of names is passed, the return value will be a tuple in the same order.
Each Table contains a cached list of its columns. hascolumn(column) checks this list, and returns True if it contains column. If column is an iterable, this returns true if all of the values in column are in the column cache. Note that this will not check the database itself; it’s meant for speed, not accuracy. However, unless you have multiple bots using the same database, or are adding columns while the bot is running, you are unlikely to encounter errors.
Return an iterator over the keys and values in the table.
In a for each loop, you can use for key in table:, where key will be the value of the key column(s), which defaults to the primary key, and table is the Table. This may be deprecated in future versions.
Returns the total number of rows in the table.
Update the row where the values in row match the key columns. If the row does not exist, it will be created. The same rules regarding the type and length of key and row apply for update as for get.
The given values must be a dict of column name to new value.
Returns the number of users (entries not starting with # or &) in the table’s key column.
Return a WillieDB object configured with the options in the given Config object. The exact settgins used vary depending on the type of database chosen to back the SettingsDB, as determined by the userdb_type attribute of config.
Currently, two values for userdb_type are supported: sqlite and mysql. The sqlite type requires that userdb_file be set in config, and refer to a writeable sqlite database. The mysql type requires userdb_host, userdb_user, userdb_pass, and userdb_name to be set, and provide the host and name of a MySQL database, as well as a username and password for a user able to write to said database.
Upon creation of the object, the tables currently existing in the given database will be registered, as though added through add_table.
Add a column with the given name and key, which has the given columns. Each element in columns may be either a string giving the name of the column, or a tuple containing the name of the column and its type (using SQL type names). If the former, the type will be assumed as string.
This will attempt to create the table within the database. If an error is encountered while adding the table, it will not be added to the WillieDB object. If a table with the same name and key already exists, the given columns will be added (if they don’t already exist).
The given name can not be the same as any function or attribute (with the exception of other tables) of the WillieDB object, nor may it start with '_'. If it does not meet this requirement, or if the name matches that of an existing table with a different key, a ValueError will be thrown.
When a table is created, the column key will be declared as the primary key of the table. If it is desired that there be no primary key, this can be achieved by creating the table manually, or with a custom query, and then creating the WillieDB object.
Return True if the WillieDB contains a table with the same name and key, and which contains a column with the same name as each element in the given list columns.
Create a database connection object. This functions essentially the same as the connect function of the appropriate database type, allowing for custom queries to be executed.
Interactively create configuration options and add the attributes to the Config object config.
Availability: 3+ for all functions; attributes may vary.
The config class is an abstraction class for accessing the active Willie configuration file.
The Willie config file is divided to sections, and each section contains keys and values. A section is an attribute of the config class, and is of type ConfigSection. Each section contains the keys as attributes. for example, if you want to access key example from section test, use config.test.example.
The core section will always be present, and contains configuration used by the Willie core. Modules are allowed to read those, but must not change them.
The config file can store strings, booleans and lists. If you need to store a number, cast it to int() when reading.
For backwards compatibility, every key in the core section is an attribute of the config class as well as of config.core. For new code, always specify the name of the section, because this behavior might be removed in the future.
Running the config.py file directly will give the user an interactive series of dialogs to create the configuration file. This will guide the user through creating settings for the Willie core, the settings database, and any modules which have a configuration function.
The configuration function, if used, must be declared with the signature configure(config). To add options, use interactive_add, add_list and add_option.
Represents a section of the config file, contains all keys in the section as attributes
Ask user in terminal for a list to assign to option. If option is already defined under section, show the user the current values and ask if the user would like to keep them. If so, additional values can be entered.
Show user in terminal a “y/n” prompt, and set option to True or False based on the response. If default is passed as true, the default will be shown as [y], else it will be [n]. question should be phrased as a question, but without a question mark at the end. If option is already defined, it will be used instead of default, regardless of wheather default is passed.
Add a section to the config file, returns False if already exists
The config object’s associated file, as noted above.
Check if option name exists under section section
Check if section name exists
Ask user in terminal for the value to assign to option under section. If default is passed, it will be shown as the default value in the prompt. If option is already defined in section, it will be used instead of default, regardless of wheather default is passed.
Show user in terminal a “y/n” prompt, and return true or false based on the response. If default is passed as true, the default will be shown as [y], else it will be [n]. question should be phrased as a question, but without a question mark at the end.
Save all changes to the config file
Exception type for configuration errors
Availability: 3+
The web class contains essential web-related functions for interaction with web applications or websites in your modules. It supports HTTP GET, HTTP POST and HTTP HEAD.
Execute an HTTP GET query on uri, and return the result. timeout is an optional argument, which represents how much time we should wait before throwing a timeout exception. It defualts to 20, but can be set to higher values if you are communicating with a slow web application.
Return a urllib2 object for uri and timeout. This is better than using urrlib2 directly, for it handles redirects, makes sure URI is utf8, and is shorter and easier to use. Modules may use this if they need a urllib2 object to execute .read() on. For more information, refer to the urllib2 documentation.
Execute an HTTP GET query on uri, and return the headers. timeout is an optional argument, which represents how much time we should wait before throwing a timeout exception. It defualts to 20, but can be set to higher values if you are communicating with a slow web application.
Execute an HTTP POST query. uri is the target URI, and query is the POST data.
Identical to urllib2.quote. Use this if you already importing web in your module and don’t want to import urllib2 just to use the quote function.
Identical to urllib.urlencode. Use this if you already importing web in your module and don’t want to import urllib just to use the urlencode function.