OC's rbot Recipes and tips

This page will be somewhat of a documentations page covering stuff I do. Hopefully someone will make use out of it

Tips

Login

The old auth command is now replaced by login owner ! More information in RbotSecurity


Recipes

Create a HL2 server query module

As the sample modules might be a bit tricky to understand I've here commented my first module, a Half-Life 2 Source Engine query script.

Step 1, document your plugin! It's important to know what the plugin does and is for immediately, so follow the best practice, and add a short header to your plugin file. rbot is released under GPL v2, you should do this aswell, however MIT, BSD, Apache 2 and Mozilla based licenses should be ok too.

# Plugin for the Ruby IRC bot (http://linuxbrit.co.uk/rbot/)
#
# Simple Half-Life 2 (Source Engine) plugin to query online
# servers to see if its online and kicking and how many users.
#
# Added 2 seconds timeout to the response. And sockets are now
# closing properly.
#
# (c) 2006 Ole Christian Rynning <oc@rynning.no>
# Licensed under GPL V2.

Step 2, Create your plugin. As you might have read before, rbot has a Plugin interface that your plugins should inherit. My example plugin uses two libraries, Socket for (UDP) socket handling and Timeout for ensuring that requests time out properly.

require 'socket'
require 'timeout'
class HL2Plugin < Plugin

Use constants! This is programming best practise, and consistent with DRY thought. A2S_INFO is the Source Engine server information query string documented at  http://developer.valvesoftware.com/wiki/Source_Server_Queries in case you were wondering.

I also use a constant for the TIMEOUT value, here, 2 seconds.

  A2S_INFO = "\xFF\xFF\xFF\xFF\x54\x53\x6F\x75\x72\x63\x65\x20\x45\x6E\x67\x69\x6E\x65\x20\x51\x75\x65\x72\x79\x00"
  TIMEOUT = 2

Now for the actions. Try to split up functionality, this action performs the a2s_info query. Since the plugin for now only support one function ;; a2s_info queries, I have put all of the socket handling, send and receive in one action: Note that the action handles the timeout exceptions by simply ignoring it.

  def a2s_info(addr, port)
    socket = UDPSocket.new()
    socket.send(A2S_INFO, 0, addr, port.to_i)
    response = nil

    begin
      timeout(TIMEOUT) do
        response = socket.recvfrom(1400,0)
      end
    rescue Exception
    end

    socket.close()
    response ? response.first.unpack("iACZ*Z*Z*Z*sCCCaaCCZ*") : nil
  end

A plugin should override a simple help action, so that the user can easily know how to use the plugin.

  def help(plugin, topic="")
    "hl2 'server:port' => show basic information about the given server"
  end

Now for the "magic action", the hl2 action handles simple mappings (mappings without a specified target name). As long as you do not specify an action in the mapping (further below) it will automatically use the action with the same name. My action is currently very simple, it takes only one parameter, a connection string in the format hostname:port or ip:port, for example '217.78.96.31:26015'.

You should also note that I make use of Threading for the server request, as the UDPSocket.recvfrom() action will freeze up the bot otherwise. This threading ensures that your bot will still run smoothly if you run an action that requires alot of time to process or CPU, memory, and other resources.

  def hl2(m, params)
    addr, port = params[:conn_str].split(':')
    Thread.start do
      info = a2s_info(addr, port)
      if info != nil
        m.reply "#{info[3]} is online with #{info[8]}/#{info[9]} players."
      else
        m.reply "Couldn't connect to #{params[:conn_str]}"
      end
    end
  end

Note that m is a special RBOT::Plugin variable that handles the requesters information, such as who invoked the trigger for the plugin.

Finally theres invoking the plugin, and mapping it to a trigger.

The line 'plugin.map 'hl2 :conn_str' simply means that it will execute the hl2 action with a parameter :conn_str if it is triggered.

You could map 'anything' to be the trigger, by i.e.

plugin.map 'anything :conn_str', :action => 'hl2'

end
plugin = HL2Plugin.new
plugin.map 'hl2 :conn_str'