summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--Rakefile2
-rwxr-xr-xbin/rbot8
-rw-r--r--lib/rbot/dbhash.rb21
-rw-r--r--lib/rbot/ircbot.rb10
-rw-r--r--lib/rbot/messagemapper.rb121
-rw-r--r--lib/rbot/plugins.rb4
-rw-r--r--lib/rbot/registry.rb21
8 files changed, 125 insertions, 67 deletions
diff --git a/ChangeLog b/ChangeLog
index 40ede6d6..92396298 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Thu Aug 04 00:11:52 BST 2005 Tom Gilbert <tom@linuxbrit.co.uk>
+
+ * Tweaked the debug() stuff a bit. Need to do this more cleanly really
+ * Added a fair bit of documentation for some of the new features
+
Wed Aug 03 15:25:07 BST 2005 Tom Gilbert <tom@linuxbrit.co.uk>
* Added french language file (TODO most of the plugins just talk english)
diff --git a/Rakefile b/Rakefile
index 9f9157de..6e796512 100644
--- a/Rakefile
+++ b/Rakefile
@@ -16,7 +16,7 @@ spec = Gem::Specification.new do |s|
s.requirements << 'Ruby, version 1.8.0 (or newer)'
# s.files = Dir.glob("**/*").delete_if { |item| item.include?(".svn") }
- s.files = FileList['lib/**/*.rb', 'bin/*', 'data/**/*', 'AUTHORS', 'COPYING', 'README', 'REQUIREMENTS', 'TODO', 'ChangeLog', 'INSTALL', 'rbot.gemspec', 'Usage_en.txt', 'setup.rb'].to_a
+ s.files = FileList['lib/**/*.rb', 'bin/*', 'data/**/*', 'AUTHORS', 'COPYING', 'README', 'REQUIREMENTS', 'TODO', 'ChangeLog', 'INSTALL', 'Usage_en.txt', 'setup.rb'].to_a
s.executables << 'rbot'
s.autorequire = 'rbot/ircbot'
diff --git a/bin/rbot b/bin/rbot
index a5c71492..d2863756 100755
--- a/bin/rbot
+++ b/bin/rbot
@@ -22,13 +22,6 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
$VERBOSE=true
-$debug = false
-
-# print +message+ if debugging is enabled
-def debug(message=nil)
- print "DEBUG: #{message}\n" if($debug && message)
- #yield
-end
require 'etc'
require 'getoptlong'
@@ -65,7 +58,6 @@ rescue LoadError => e
puts "Error: couldn't find the rbot/ircbot module for loading\n - did you install rbot using install.rb?"
exit 2
end
-
if ($opts["version"])
puts "rbot #{$version}"
diff --git a/lib/rbot/dbhash.rb b/lib/rbot/dbhash.rb
index 5ae2ba87..611ec087 100644
--- a/lib/rbot/dbhash.rb
+++ b/lib/rbot/dbhash.rb
@@ -1,24 +1,3 @@
-# Copyright (C) 2002 Tom Gilbert.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies of the Software and its documentation and acknowledgment shall be
-# given in the documentation and software packages that this Software was
-# used.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
begin
require 'bdb'
rescue Exception => e
diff --git a/lib/rbot/ircbot.rb b/lib/rbot/ircbot.rb
index 11b03f50..f99dd940 100644
--- a/lib/rbot/ircbot.rb
+++ b/lib/rbot/ircbot.rb
@@ -2,6 +2,13 @@ require 'thread'
require 'etc'
require 'fileutils'
+$debug = false unless $debug
+# print +message+ if debugging is enabled
+def debug(message=nil)
+ print "DEBUG: #{message}\n" if($debug && message)
+ #yield
+end
+
# these first
require 'rbot/rbotconfig'
require 'rbot/config'
@@ -369,6 +376,7 @@ class IrcBot
end while(message.length > 0)
end
+ # queue an arbitraty message for the server
def sendq(message="")
# temporary
@socket.queue(message)
@@ -429,6 +437,7 @@ class IrcBot
sendq "TOPIC #{where} :#{topic}"
end
+ # disconnect from the server and cleanup all plugins and modules
def shutdown(message = nil)
trap("SIGTERM", "DEFAULT")
trap("SIGHUP", "DEFAULT")
@@ -539,6 +548,7 @@ class IrcBot
return helpstr
end
+ # returns a string describing the current status of the bot (uptime etc)
def status
secs_up = Time.new - @startup_time
uptime = Utils.secs_to_string secs_up
diff --git a/lib/rbot/messagemapper.rb b/lib/rbot/messagemapper.rb
index c8e2b6ba..b079acd6 100644
--- a/lib/rbot/messagemapper.rb
+++ b/lib/rbot/messagemapper.rb
@@ -1,38 +1,131 @@
module Irc
+
+ # +MessageMapper+ is a class designed to reduce the amount of regexps and
+ # string parsing plugins and bot modules need to do, in order to process
+ # and respond to messages.
+ #
+ # You add templates to the MessageMapper which are examined by the handle
+ # method when handling a message. The templates tell the mapper which
+ # method in its parent class (your class) to invoke for that message. The
+ # string is split, optionally defaulted and validated before being passed
+ # to the matched method.
+ #
+ # A template such as "foo :option :otheroption" will match the string "foo
+ # bar baz" and, by default, result in method +foo+ being called, if
+ # present, in the parent class. It will receive two parameters, the
+ # Message (derived from BasicUserMessage) and a Hash containing
+ # {:option => "bar", :otheroption => "baz"}
+ # See the #map method for more details.
class MessageMapper
+ # used to set the method name used as a fallback for unmatched messages.
+ # The default fallback is a method called "usage".
attr_writer :fallback
+ # parent:: parent class which will receive mapped messages
+ #
+ # create a new MessageMapper with parent class +parent+. This class will
+ # receive messages from the mapper via the handle() method.
def initialize(parent)
@parent = parent
- @routes = Array.new
+ @templates = Array.new
@fallback = 'usage'
end
+ # args:: hash format containing arguments for this template
+ #
+ # map a template string to an action. example:
+ # map 'myplugin :parameter1 :parameter2'
+ # (other examples follow). By default, maps a matched string to an
+ # action with the name of the first word in the template. The action is
+ # a method which takes a message and a parameter hash for arguments.
+ #
+ # The :action => 'method_name' option can be used to override this
+ # default behaviour. Example:
+ # map 'myplugin :parameter1 :parameter2', :action => 'mymethod'
+ #
+ # By default whether a handler is fired depends on an auth check. The
+ # first component of the string is used for the auth check, unless
+ # overridden via the :auth => 'auth_name' option.
+ #
+ # Static parameters (not prefixed with ':' or '*') must match the
+ # respective component of the message exactly. Example:
+ # map 'myplugin :foo is :bar'
+ # will only match messages of the form "myplugin something is
+ # somethingelse"
+ #
+ # Dynamic parameters can be specified by a colon ':' to match a single
+ # component (whitespace seperated), or a * to such up all following
+ # parameters into an array. Example:
+ # map 'myplugin :parameter1 *rest'
+ #
+ # You can provide defaults for dynamic components using the :defaults
+ # parameter. If a component has a default, then it is optional. e.g:
+ # map 'myplugin :foo :bar', :defaults => {:bar => 'qux'}
+ # would match 'myplugin param param2' and also 'myplugin param'. In the
+ # latter case, :bar would be provided from the default.
+ #
+ # Components can be validated before being allowed to match, for
+ # example if you need a component to be a number:
+ # map 'myplugin :param', :requirements => {:param => /^\d+$/}
+ # will only match strings of the form 'myplugin 1234' or some other
+ # number.
+ #
+ # Templates can be set not to match public or private messages using the
+ # :public or :private boolean options.
+ #
+ # Further examples:
+ #
+ # # match 'karmastats' and call my stats() method
+ # map 'karmastats', :action => 'stats'
+ # # match 'karma' with an optional 'key' and call my karma() method
+ # map 'karma :key', :defaults => {:key => false}
+ # # match 'karma for something' and call my karma() method
+ # map 'karma for :key'
+ #
+ # # two matches, one for public messages in a channel, one for
+ # # private messages which therefore require a channel argument
+ # map 'urls search :channel :limit :string', :action => 'search',
+ # :defaults => {:limit => 4},
+ # :requirements => {:limit => /^\d+$/},
+ # :public => false
+ # plugin.map 'urls search :limit :string', :action => 'search',
+ # :defaults => {:limit => 4},
+ # :requirements => {:limit => /^\d+$/},
+ # :private => false
+ #
def map(*args)
- @routes << Template.new(*args)
+ @templates << Template.new(*args)
end
def each
- @routes.each {|route| yield route}
+ @templates.each {|tmpl| yield tmpl}
end
def last
- @routes.last
+ @templates.last
end
+ # m:: derived from BasicUserMessage
+ #
+ # examine the message +m+, comparing it with each map()'d template to
+ # find and process a match. Templates are examined in the order they
+ # were map()'d - first match wins.
+ #
+ # returns +true+ if a match is found including fallbacks, +false+
+ # otherwise.
def handle(m)
- return false if @routes.empty?
+ return false if @templates.empty?
failures = []
- @routes.each do |route|
- options, failure = route.recognize(m)
+ @templates.each do |tmpl|
+ options, failure = tmpl.recognize(m)
if options.nil?
- failures << [route, failure]
+ failures << [tmpl, failure]
else
- action = route.options[:action] ? route.options[:action] : route.items[0]
+ action = tmpl.options[:action] ? tmpl.options[:action] : tmpl.items[0]
next unless @parent.respond_to?(action)
- auth = route.options[:auth] ? route.options[:auth] : route.items[0]
+ auth = tmpl.options[:auth] ? tmpl.options[:auth] : tmpl.items[0]
debug "checking auth for #{auth}"
if m.bot.auth.allow?(auth, m.source, m.replyto)
- debug "route found and auth'd: #{action.inspect} #{options.inspect}"
+ debug "template match found and auth'd: #{action.inspect} #{options.inspect}"
@parent.send(action, m, options)
return true
end
@@ -124,8 +217,8 @@ module Irc
return nil, "Unused components were left: #{components.join '/'}" unless components.empty?
- return nil, "route is not configured for private messages" if @options.has_key?(:private) && !@options[:private] && m.private?
- return nil, "route is not configured for public messages" if @options.has_key?(:public) && !@options[:public] && !m.private?
+ return nil, "template is not configured for private messages" if @options.has_key?(:private) && !@options[:private] && m.private?
+ return nil, "template is not configured for public messages" if @options.has_key?(:public) && !@options[:public] && !m.private?
options.delete_if {|k, v| v.nil?} # Remove nil values.
return options, nil
@@ -137,7 +230,7 @@ module Irc
"<#{self.class.to_s} #{@items.collect{|c| c.kind_of?(String) ? c : c.inspect}.join(' ').inspect}#{default_str}#{when_str}>"
end
- # Verify that the given value passes this route's requirements
+ # Verify that the given value passes this template's requirements
def passes_requirements?(name, value)
return @defaults.key?(name) && @defaults[name].nil? if value.nil? # Make sure it's there if it should be
diff --git a/lib/rbot/plugins.rb b/lib/rbot/plugins.rb
index d98630e1..4e618f61 100644
--- a/lib/rbot/plugins.rb
+++ b/lib/rbot/plugins.rb
@@ -8,8 +8,8 @@ module Plugins
#
# map(template, options)::
# map is the new, cleaner way to respond to specific message formats
- # without littering your plugin code with regexps
- # examples:
+ # without littering your plugin code with regexps. examples:
+ #
# plugin.map 'karmastats', :action => 'karma_stats'
#
# # while in the plugin...
diff --git a/lib/rbot/registry.rb b/lib/rbot/registry.rb
index cd78dcbf..5228d223 100644
--- a/lib/rbot/registry.rb
+++ b/lib/rbot/registry.rb
@@ -1,24 +1,3 @@
-# Copyright (C) 2002 Tom Gilbert.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies of the Software and its documentation and acknowledgment shall be
-# given in the documentation and software packages that this Software was
-# used.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
require 'rbot/dbhash'
module Irc