SmIRC 0.70

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 sending a greeting message to anyone who joins a channel. A bot can also look for anyone saying the word "beer" on a channel, and immediately replying with a message "* A gives B a can of beer.".

Bots are generally frowned upon. But they're an inseparable part of the IRC culture. 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 your favorite language. SmIRC does NOT come with a scripting language of its own. Why should it? Just use any UNIX language 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, can become a bot.

Ok, what exactly is a bot, in SmIRC?

Specifically, when a bot program is running, SmIRC sends to the bot a copy of everything that appears in a window, format in a special way. 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, shows the message, then sends the message to every bot running in that window.

Each bot can be thought of as running in one window, which can be either a server window, or a channel window. But, you can have multiple copies of the same bot program, each copy running in a separate window.

The bot receives, on standard input, a copy of every message that SmIRC displays. Unless otherwise stated, each message is formatted as follows:

In other words, each message is one or more tab-separated fields. The message, and the fields, directly correspond to a stringTable resource entry in SmIRC's resource file. Take a look at /usr/lib/X11R6/app-defaults/Smirc, starting with the section labeled "Various messages". That section contains a list of all possible messages that can appear. NOTE: the stringTable resources will also include various other text messages, such as window titles.

The first field is the name of the resource, such as "LOOKINGUP", or "CONNECTING". The remaining fields 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.

A bot program causes things to happen simply by printing messages. Anything that the bot program prints to standard output will be interpreted EXACTLY as if it were typed in the command buffer. For example, printing "/join #newbies", followed by a newline, will join this channel. Printing "Hello!", followed by a newline, sends a 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 can't keep up, or if it 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.

Output from your bot program will - some form or fashion - reappear on input. That's because anything that's typed in usually results in some message to be displayed, and subsequently sent back to the bot. If you're not careful, you'll send your bot program into an infinite 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/etc/smirc
/usr/local/share/smirc
$HOME/.smirc
The name of the bot program must end with a ".bot" extension. To run a bot in a window, simply type:

/botname arguments

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

Starting with version 0.62 of SmIRC, you can also type:

/RUN botname arguments

Arguments received by the bot process

The bot process is executed with the following arguments:
 

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.

Everything in $HOME/.smirc/privbots is started every time a private chat window is created.

Hints for writing bots

There is a new subdirectory, /usr/local/share/smirc/samples, starting with the 0.60 release. This subdirectory contains some useful, generic, bots, written in Perl. Use them as a guide for writing your own.

Comparing nicknames

If you are comparing two nickname variables for equivalence, 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.

A bot will get either a NICKCHANGE, or a MYNICKCHANGE message if someone's nick changes.

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 canceled. 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 canceling the timer.

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

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 /usr/local/share/smirc/samples subdirectory contains some sample bots, written in Perl.