The following is a series of articles written exploring the new (to be) paradigm of evented programming (mostly using EventMachine) and distributed applications and services. Along the way, I will be developing an IRC bot that takes advantage of these paradigms to the best of its ability. As it is an exploration, not all avenues will be followed, some will be followed and abandoned (because it didn't work out, or because it was the way I chose) and what results is a series of articles that touches a smorgasbord of Ruby libraries. The libraries used will be listed in bold in the descriptions.
IRC Introduction and Bots
IRC is the main informal communication medium for a lot of people, programmers and Rubyists among them. The IRC protocol itself came about in the late 80's to provide a common protocol for people to chat in realtime over the Internet (a relatively new thing at that time). IRC is not like a more "modern" service you may be used to like Twitter, where a single company or entity controls the entire service, IRC is just a protocol. Any server running the IRC protocol can be connected to using an IRC client. Popular servers (or "networks," as these exist as multiple servers connected behind the scenes) include:
- EFNet - One of the oldest and largest networks.
- FreeNode - Formerly OpenProjects, this is a very popular network for Open Source software, and hosts the #ruby-lang official Ruby channel.
- QuakeNet - A network with popular gaming channels.
- OFTC - Another network popular with Open Source and Free Software programmers.
IRC bots have kind of a sketchy past. IRC has always been full of drama, where flame wars escalate to bans, ban evasions, channel wars and takeovers. Bots played a big role here, and this type of bot is full of mischief and malice. However, this is not the type of bot we're talking about here. IRC bots can also be useful little machines, channels often have a resident bot that provides useful features such as the collection of links, quotes, keeps track of who has been in the channel and when they were last seen, etc. This is the type of bot we'll be focusing on here.
Onto the main event. Each link below is an article. They appear in no particular order, there is no "start" or even a "finish," each is an exploration in some tangent in making a distributed IRC bot. So feel free to skip around.
Named Groups - We first take a look at a novel way to parse text data in a human readable way using extended regular expressions and named groups. If you've ever made any complicated regular expressions, you'll know it gets real hairy real fast. Coming back to the regular expression some months later, you have to carefully analyze what it does, and good luck if the thing is broken. But by using these two techniques, you can keep your regular expressions DRY and readable.
IRC Messages - We'll take some time and look at the IRC protocol itself. We'll look at how IRC messages are formatted, and we'll connect to an IRC server using telnet and interact with it manually. We'll also get a log of traffic we can parse in the next section.
Parsing IRC Messages - Before you can even think about writing an IRC bot, you need to first start parsing IRC messages. IRC is a simple plaintext protocol with a relatively rigid structure, but we'll be using the features presented in the previous article to parse them almost like a full tokenizer and parser.
Using Sockets in Ruby - Another essential bit before we can really dig in is to explore sockets. Sockets are used to connect to services on remote computers. While you use them all the time, if you mainly deal with high-level APIs and interfaces (such as consuming a RESTful service) you may have never dealt with sockets alone before. And, of course, you can't build an IRC bot without connecting to remote servers.
Plugins - The core of the IRC bot should be "clean." It should be quite small really, all the "real" functionality being implemented in plugins. This makes it very easy to customize the IRC bot, add or remove functionality, develop new functionality, etc. We'll look at a naive but simple way to implement plugins.
Dynamic Plugins - Finally, to the "meat" of the project: distributing the plugins. Instead of loading all the plugins into the main Ruby interpreter, we'll fire up more processes and use interprocess communication (in this case, simple pipes using popen) and work on dynamically loading plugins when we change their source.