Develop:PluginDev

Matthias Hecker edited this page Mar 9, 2015 · 1 revision

Plugins are little ruby files that are dropped in the plugins subdirectory in the rbot directory, or in the rbot configuration directory (if you want per-configuration plugins - say different plugins for different irc servers or functions).

Plugins are run in anonymous modules which derive from the main rbot module. This allows them access to rbot functions and classes, but also allows them to drop their namespace and be reloaded at runtime. This means a new plugin can be dropped in the directory while rbot is running, and that a plugin can be modified while in use. Then tell rbot to rescan (rbot: rescan), and it will reload its modules dynamically.

A plugin class is provided for you to derive from and you must use it. There are two modes of plugin currently. One that registers itself for one of more commands. A command being the first word of a line addressed to the bot (either public or private), the rest of the line are parameters. The second class is a listener plugin, which gets to see all messages the bot sees, addressed or not - regardless of the first word of the line. These plugins can react to more events, but should be careful not to respond willy nilly to them.

Plugins are also wrapped in exception handling to prevent and uncaught exception in the plugin from causing the bot to crash.

Command plugin example

This is example code for a very simple plugin. It responds to a message of the form length .* and returns the length of the following string:

class LengthPlugin < Plugin

  # return a help string when the bot is asked for help on this plugin
  def help(plugin, topic="")
    return "length <string> => return length of string <string>"
  end

  # reply to a private message that we've registered for
  def privmsg(m)

    # m.params contains the rest of the message, m.plugin contains the first
    # word (useful because it's possible to register for multiple commands)
    unless(m.params)
      m.reply "incorrect usage. " + help(m.plugin)
    end

    length = m.params.length
    m.reply "length is #{length}"
  end
end

# create an instance of our plugin class and register for the "length" command
plugin = LengthPlugin.new
plugin.register("length")

Note that you can do a lot more than just reply to a message. The plugin class inherits a @bot member, which gives access to the bot data and functionality. @bot.say "#channel", "message", allows you to talk to channels, @bot.say "nick", "message" allows for private messages, @bot.action lets you perform /me-style actions, and then there's lots of other goodies, @bot.nick, @bot.quit, @bot.join, etc, etc. A link to the full API docs can be found at the top of this page.

The message class contains members like .target, the target of the message (which could be the bot's nick, or a channel name), it has functions like public? and private? to see if the message was private or in a channel, and address?, to see if the message was addressed to you (either privately messaged or in a channel in the form "bot-nick: message"). There's also .replyto, which contains the nick or channel you should reply to, which depends on the manner of address. The reply member function is just a shortcut for @bot.say m.replyto "message".

To create a listener plugin, define a listen() method, which takes a message object. Bear in mind that the message may not be addressed to you, and that a reply may not therefor be expected :)

For examples of plugins (probably the best way to see right now), just look at the plugins that come with rbot - they vary in complexity and the quotes plugin is a listener.

Plugin Routing

There's now a much easier way of connecting your Plugin methods to the commands that a user has to type in. It's kinda similar to Rails routing; basically, you create an instance of your plugin class, then use the ".map" instance method to connect user-commands to methods. Here's an example:

class SimplePlugin < Plugin
  def say_my_name_please(m, params)
    if params[:name]
      m.reply "The name I'm supposed to say is: #{params[:name]}"
    else
      m.reply "I NEED A NAME TO SAY, IDJIOT!!"
    end
  end
end

# create a new instance
plugin = SimplePlugin.new

# add a new action (user would do: '@say_name Bob')
plugin.map 'say_name :name', :action => 'say_my_name_please'

Pretty straight-forward example there -- :name is the parameter that gets passed to the action.

The .map method has a lot of neat features. Here are some examples.

Default Value for a Parameter

This lets you set the default value(s) for any parameter(s):

plugin.map 'eat :type_of_food', :defaults => {:type_of_food => 'cookie'}

Validate that a Parameter Matches a Regular Expression

This option checks that the :limit parameter is a number and that it can have an optional minus sign before it:

plugin.map 'digg :limit', :defaults => {:limit => 5},
                          :requirements => {:limit => /^-?\d+$/}

Accept any Number of Parameters

This option lets you pass a bunch of words as a single parameter:

plugin.map 'remember *phrase'