SmIRC 0.60beta

SmIRC bots

Starting with version 0.60, SmIRC now provides generic bot support.

What is a "bot"?

A bot is an IRC program, that automatically responds to predefined events on IRC. Short for a "robot", a bot can do things like automatically saying hello to anyone who joins a channel, or, if anyone says the word "beer" on a channel, immediately send a message "* A gives B a can of beer.".

Bots are generally frowned upon, but they've become a part of the IRC culture, so as long as you stick to harmless fun like that, nobody will mind.

What language do I need to use in order to write a bot?

You can write a bot using whatever tools you want. SmIRC does NOT provide you with a scripting language of its own. Why should it? Just use anything in UNIX that you're comfortable with. Write a Perl script, or write a C program. Anything that can read from standard input, or write to standard output or error, can become a bot.

Ok, what exactly is a bot, in SmIRC?

Specifically, when a bot program is running, SmIRC will send to this program a copy of everything that it sends to a window, in a certain format. Most standalone bot programs use a separate IRC connection, with a separate nickname. All bots running in SmIRC share the same IRC connection, with SmIRC taking care of all the dirty low-level details. You don't need to know anything about the IRC protocol.

SmIRC receives IRC messages from the IRC server, figures out in which window the message should be displayed, and dispatches the message to this window.

Each bot can be thought of as running in one window, which would be either a server window, or a channel window. You can have a separate copy of each bot running for multiple channels, one in each window.

The bot program will receive, on standard input, a copy of every message that SmIRC shows in its window. Unless otherwise stated, each message will be formatted as follows:

In other words, each message is one or more tab-separated fields. The message, and the fields, directly correspond to the stringTable resource entries in SmIRC's resource file. Take a look at /usr/lib/X11/app-defaults/Smirc, starting with the section labeled "Various messages". What follows is a list of all the possible messages that can appear in a window. NOTE: the stringTable resources also include various other text messages, such as window titles. The first record is the name of the resource, such as "LOOKINGUP", or "CONNECTING". The remaining records are any fill-in parameters for this message. For example, for the NICKCHANGE message, the second field is the old nickname, and the third field is the new nickname.

Based upon these messages, a bot program can cause things to happen simply by sending output. Anything that the bot program prints to standard output will be interpreted EXACTLY as if it was typed into the command buffer. For example, printing "/join #newbies", followed by a newline, will join this channel. Printing "Hello!", followed by a newline, sends this hello message to the channel.

Anything that the bot program prints to standard error is simply displayed in the window.

Important things you should know about bots

Your bot program must be able to keep up with its input. If your bot program locks up, SmIRC will dutifully save all messages pending for the bot, internally. This will cause SmIRC to grow in size, using up all available memory.

Generally, I'm giving you enough rope to hang yourself. Don't blame me if you do something silly, and crash your machine.

Standard error, and standard output from your bot program will, in one way or another, reappear on standard input, since they will usually cause some kind of message to be displayed in the window. If you're not carefull, you'll send your bot program into a tight loop.

When the window is closed, SmIRC will close the standard input, output, and error to every bot program running in that window. Your bot program is expected to be able to detect it, and go away. SmIRC will not kill the process, nor wait for it to finish.

Ok, ok, I've read all of the above, and I have my bot ready, now what?

SmIRC expects to find bots in several places:

/usr/local/lib/smirc

/usr/lib/smirc

$HOME/.smirc

The name of the bot program should end with a ".bot" extension. To run a bot in a window, simply type:

/botname

In the command buffer, where botname is the name of the bot program, without the ".bot" extension.

How to automatically start bots for a new channel, or a server window

SmIRC will run everything in the $HOME/.smirc/chanbots subdirectory, every time you join a new channel.

Everything in $HOME/.smirc/serverbots subdirectory is started every time a new server window is opened, which includes the first server window displayed when you start SmIRC.

Hints for writing bots

There is a new "samples" subdirectory, starting with the 0.60 release. This subdirectory contains some usefull, generic, bots, written in Perl. Use them as a guide for writing your own.

Comparing nicknames

If you are comparing two nickname variables for equivalency, consider the fact that nicknames are case-insensitive. Also, keep in mind that according to RFC1459, the characters "{", "}", and "|" are lowercase equivalents of the characters "[", "]", and "\".

Determining when someone joins, or leaves, the channel

When a bot is started from ~/.smirc/chanbots, the bot will get a series of RPLDEFAULT messages for "353" messages from the IRC server, which contain the list of nicknames on the channel. That is followed by a RPLDEFAULT "366" message. The bot program can use these messages to build a list of nicknames on the channel. At any time, the bot can issue a /NAMES command to receive a fresh set of nickname listings. NOTE: if you define a RPL353, or a RPL366 resource in the stringTable portion of the resource file, the bot will get these messages instead.

A bot will get a USERJOIN message whenever someone joins the channel, and either a USERPART, USERKICK, or USERQUIT message whenever someone leaves the channel.

Determining when SmIRC connects or disconnects from a server

A bot script may need to track whether or not SmIRC is connected to a server. This is not straightforward, because there are quite a few messages involved. In putting together the 0.60 release, I added some messages to help in this task, and I suggest that bots consider the following approach:

Extra commands for bot programs

Also, there are some commands that can be sent by a bot, to standard output, that are handled internally by SmIRC. These commands are only available to bots, you can't enter them in the input buffer.

/_CTCPHANDLER cmd

Notifies SmIRC that this bot can handle this CTCP command. Since SmIRC displays CTCP messages in a channel window, all bots running in the window will always receive CTCP messages. After displaying the message, SmIRC will acknowledge it with an CTCPUNKNOWNERRMSG message.

This command, available to bots only, will suppress the CTCPUNKNOWNERRMSG reply. The bot will always receive either a CTCPCMD, or a CTCPCMDACK message (see the resources file), this just suppresses the error message.

Also, there are some interesting side effects here. If there's a CTCP message to a channel where you have a handler bot running, a CTCPUNKNOWNERRMSG message will not be sent. However, if some wag /PRIVMSGs you a CTCP command, since it will not go into the channel window, SmIRC will reply with a CTCPUNKNOWNERRMSG. /_CTCPHANDLER command is window-specific.

/_TIMER nnn

Sometimes it may be necessary for a bot to pause for a period of time, or to wait for a period of time for an expected response to arrive, and, if it doesn't come, time out. Actually pausing the bot process IS NOT RECOMMENDED. While a bot process is paused, SmIRC will continue to queue up output for the bot process, eating up memory.

The /_TIMER command (note the leading underscore) sets a timer in SmIRC. The parameter to the /_TIMER command is the number of seconds for the timer to run. When the time expires, the bot process will receive a _TIMEOUT command on standard input (note the leading underscore). The basic idea is to send the /_TIMER command, then wait either for the expected response, or a _TIMEOUT message.

If the number of seconds is 0, any existing timer is cancelled. HOWEVER it is possible that the /_TIMER 0 command is sent just after the timer expires, and SmIRC already prepared the _TIMEOUT message to be sent to the bot, but the bot hasn't received it yet, so you may STILL receive a _TIMEOUT message after supposedly cancelling the timer.

If the number of seconds is 0, after any existing timer is cancelled, SmIRC will send a _CANCEL message (note the leading underscore) to the bot process, signifying the timer being cancelled.

This has a possibly useful side-effect. SmIRC will ALWAYS send a _CANCEL message in response to a /_TIMER 0 command. Therefore, if a bot process find itself in a situation where it knows it may have a lot of unprocessed messages from SmIRC waiting, it can quickly flush them out by sending a /_TIMER 0 command, and discarding any messages it received until it gets a _CANCEL. (Note, that this will NOT work if one of the unprocessed messages is a response to a prior /TIMER 0.)

Each bot process has only ONE timer it can set, however, each separate bot process has its own, private, timer. If you attempt to set a timer before the previous one goes off, the timer is simply reset for a new expiration time. HOWEVER, because of a previously-mentioned race condition, a _TIMEOUT response may or may not be sent to the bot process, therefore, you will lose track of your timer, and you should ALWAYS reset the timer first.

Examples???

The samples subdirectory in the SmIRC source code tarball contains some sample bots, written in Perl. Make sure your perl can be found in /usr/bin, and feel free to experiment with them.