Dominik Honnef

Are lightweight IRC frameworks too lightweight?

Published:
Last modified:
by

Recently I’ve been looking for a small framework for writing IRC bots in Ruby. Admittedly, I have been successfully running a bot based on Butler for quite some time now, but I have always been looking for a more lightweight solution, which does not come with a predefined plugin or authentication structure.

Now there are probably three major players in the field of IRC bots, namely Autumn, Isaac and just recently Cinch.

I won’t further examine Autumn here, as it is to IRC bots what Rails is to web development: Anything but lightweight.

Isaac and Cinch are quite similar: They both feature a small DSL for quickly developing bots, while Cinch puts more emphasis on the idea of plugins (Isaac is more about parsing messages in general) and its own pattern language to match input.

But there is another thing they have in common: No proper object-oriented interface… Instead of passing around channel and user objects, all the developer sees are strings. This also means that instead of doing something as intuitive as

some_channel.part
# or
some_channel.kick(user, "some reason")
# or
some_channel.topic # retrieve the topic
some_channel.topic = "new topic" # set a new topic
# or
some_user.hostname

one will have to fall back to stuff like this (the examples are based on Cinch but will be similar for Isaac):

bot.part(some_channel) # now this still makes sense
bot.kick(some_channel, user, "some reason") # not too nice anymore
bot.topic(some_channel) # plain wrong
bot.topic(some_channel, "new topic") # plain wrong
# there simply is no method of retrieving a user's hostname,
# except from doing a WHOIS and parsing the output yourself

Of course one might argue that those frameworks/DSLs are lightweight after all and that the user could write helper methods if he really wanted to have a rich interface, but that would also mean that I had to use those helper methods every other line, just to deal with proper objects.

It is quite questionable if “lightweight” is even allowed to mean “not properly abstracted” or if abstraction is something that can be taken for granted if working with Ruby.

Either way, as nice as the DSLs of Isaac and Cinch look, they are still pretty useless to me in their current state. Instead of reinventing the wheel, however, I will probably fork Isaac or Cinch and create a “still lightweight” framework based on that. In the end, I am planning to have an API which effectively allows code like the following:

on :channel, /^print my host$/ do
  # in this example, we will work with an instance of User,
  # instead of a plain string with just a nickname. Same goes for `channel`
  channel.send "Your host is #{user.host}"
end

on :channel, /!annoy (.+)$/ do |target|
  # indeed a helper for cases in which we have to deal with strings
  Channel(target).each_user do |victim|
    victim.send "hi, you have been annoyed!"
  end
end