[QUIZ] IRC Teams (#221)

The main idea behind this quiz was to show how easy it is to get an
irc bot up and running in ruby, especially given the number of bot
frameworks out there. I chose to base my bot on rif
[http://gitorious.org/ruby-irc], a nice lightweight library with
everything I needed to get started immediately.

The code consists of a Teams class, which does all the actual work of
maintaining teams, and a TeamBot, which inherits from RIF::Bot and
handles the IRC part of it. For the sake of simplicity, the bot
frontend does no real validation; it just unpacks an incoming message
into a command and arguments, and blindly sends those arguments to the
Teams object. All public methods on the Teams object accept a player
name as a first argument, whether they need it or not, and return
either a string or an array of strings, or raise an exception. The bot
sends the return value to the channel; an array is sent one message at
a time.



gem 'rif'
require 'rif/bot'

class TeambotError < Exception

def TeambotError(foo)

class Teams
  attr_accessor :teams, :members

  def initialize
    @teams = {}
    @members = {}

  def create(player, team)
    raise TeambotError("team #{team} already created") if teams[team]
    teams[team] = true
    "created team #{team}"

  def delete(player, team)
    members.delete_if {|k,v| v == team}
    "deleted team #{team}"

  def join(player, team)
    raise TeambotError("no such team: #{team}") if not teams[team]
    members[player] = team
    "#{player} has joined team #{team}"

  def leave(player, *args)
    team = members.delete(player)
    "#{player} has left team #{team}"

  def reset(*args)
    @members = {}
    @teams = {}
    "deleted all teams"

  def show(player, *args)
    if args[0] == 'my'
    elsif args[0] == 'teams'
      teams.map {|team, _| "#{team}: #{show_players(team)}"}
    elsif args[0] == 'team'

  def players(team)
    members.select {|k,v| v == team}.keys

  def show_players(team)
    players(team).join(" ")

class TeamBot < RIF::Bot
  attr_reader :teams, :channel
  def initialize(channel, nick, server, port, username)
    @teams = Teams.new
    @channel = channel
    super(nick, server, port, username)

  def on_endofmotd(event)

  def on_message(event)
    return unless event.channel == channel
    msg, *args = event.message.split(" ")
    player = event.nick
      *ret = teams.send(msg, player, *args)
    rescue NameError
      ret = nil
    rescue TeambotError => e
      ret = [e.message]
    ret.each {|m| send_message(channel, m)} if ret

if __FILE__ == $0
  channel = "##{ARGV[0]}"
  bot = TeamBot.new(channel, "teambot", "irc.freenode.net", 6667, "RIF Bot")


This week's quiz was solved by Martin DeMello.

Martin uses the [rif gem][1] to handle the nitty gritty details of
IRC, like establishing a connection and sending and receiving

The `TeamBot#on_message` method receives the messages from the users
and dispatches them to an instance of the Teams class. This is
accomplished by simply splitting the the message that was sent and
using the first part as the method to invoke. The first argument is
always the player, which is taken from the message event's `nick`
field. The rest of the message, if any, is passed as the remaining

Let's look at a full pass through of the following sample message from user Bob:

    <Bob> create ruby

This will land in the bot's `on_message` method. The `event` will have
"Bob" as the nick and "create ruby" as the message. That is parsed
into the method to call and the arguments like so:

    msg, *args = event.message.split(" ")

The `teams` object is then sent the parsed message arguments with the
specified method as follows:

    *ret = teams.send(msg, player, *args)

This takes us into the create method and the team is created (if all
goes according to plan):

    def create(player, team)
      raise TeambotError("team #{team} already created") if teams[team]
      teams[team] = true
      "created team #{team}"

The other actions that users may wish to perform are taken care of in
the same way. This simple and flexible solution allows for easy
modification and extension.

Thank you Martin for your solution (and also for the quiz suggestion)!

[IRC Teams (#221) - Solutions][2]

[1]: http://gitorious.org/ruby-irc
[2]: http://rubyquiz.strd6.com/quizzes/221.tar.gz

221.tar.gz (10.7 KB)


- Daniel