]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/commitdiff
rearrange repo for packaging
authorTom Gilbert <tom@linuxbrit.co.uk>
Wed, 27 Jul 2005 15:59:13 +0000 (15:59 +0000)
committerTom Gilbert <tom@linuxbrit.co.uk>
Wed, 27 Jul 2005 15:59:13 +0000 (15:59 +0000)
85 files changed:
bin/rbot [new file with mode: 0755]
contrib/plugins/figlet.rb [deleted file]
contrib/plugins/ri.rb [deleted file]
contrib/plugins/stats.rb [deleted file]
contrib/plugins/vandale.rb [deleted file]
data/rbot/contrib/plugins/figlet.rb [new file with mode: 0644]
data/rbot/contrib/plugins/ri.rb [new file with mode: 0644]
data/rbot/contrib/plugins/stats.rb [new file with mode: 0644]
data/rbot/contrib/plugins/vandale.rb [new file with mode: 0644]
data/rbot/plugins/autoop.rb [new file with mode: 0644]
data/rbot/plugins/autorejoin.rb [new file with mode: 0644]
data/rbot/plugins/cal.rb [new file with mode: 0644]
data/rbot/plugins/dice.rb [new file with mode: 0644]
data/rbot/plugins/eightball.rb [new file with mode: 0644]
data/rbot/plugins/excuse.rb [new file with mode: 0644]
data/rbot/plugins/fish.rb [new file with mode: 0644]
data/rbot/plugins/fortune.rb [new file with mode: 0644]
data/rbot/plugins/freshmeat.rb [new file with mode: 0644]
data/rbot/plugins/google.rb [new file with mode: 0644]
data/rbot/plugins/host.rb [new file with mode: 0644]
data/rbot/plugins/httpd.rb [new file with mode: 0644]
data/rbot/plugins/insult.rb [new file with mode: 0644]
data/rbot/plugins/karma.rb [new file with mode: 0644]
data/rbot/plugins/lart.rb [new file with mode: 0644]
data/rbot/plugins/math.rb [new file with mode: 0644]
data/rbot/plugins/nickserv.rb [new file with mode: 0644]
data/rbot/plugins/nslookup.rb [new file with mode: 0644]
data/rbot/plugins/opmeh.rb [new file with mode: 0644]
data/rbot/plugins/quotes.rb [new file with mode: 0644]
data/rbot/plugins/remind.rb [new file with mode: 0644]
data/rbot/plugins/roshambo.rb [new file with mode: 0644]
data/rbot/plugins/rot13.rb [new file with mode: 0644]
data/rbot/plugins/roulette.rb [new file with mode: 0644]
data/rbot/plugins/seen.rb [new file with mode: 0644]
data/rbot/plugins/slashdot.rb [new file with mode: 0644]
data/rbot/plugins/spell.rb [new file with mode: 0644]
data/rbot/plugins/tube.rb [new file with mode: 0644]
data/rbot/plugins/url.rb [new file with mode: 0644]
data/rbot/plugins/weather.rb [new file with mode: 0644]
data/rbot/plugins/wserver.rb [new file with mode: 0644]
data/rbot/templates/conf.rbot [new file with mode: 0644]
data/rbot/templates/keywords.rbot [new file with mode: 0644]
data/rbot/templates/lart/larts [new file with mode: 0644]
data/rbot/templates/lart/praises [new file with mode: 0644]
data/rbot/templates/levels.rbot [new file with mode: 0644]
data/rbot/templates/users.rbot [new file with mode: 0644]
rbot.rb [deleted file]
rbot/plugins/autoop.rb [deleted file]
rbot/plugins/autorejoin.rb [deleted file]
rbot/plugins/cal.rb [deleted file]
rbot/plugins/dice.rb [deleted file]
rbot/plugins/eightball.rb [deleted file]
rbot/plugins/excuse.rb [deleted file]
rbot/plugins/fish.rb [deleted file]
rbot/plugins/fortune.rb [deleted file]
rbot/plugins/freshmeat.rb [deleted file]
rbot/plugins/google.rb [deleted file]
rbot/plugins/host.rb [deleted file]
rbot/plugins/httpd.rb [deleted file]
rbot/plugins/insult.rb [deleted file]
rbot/plugins/karma.rb [deleted file]
rbot/plugins/lart.rb [deleted file]
rbot/plugins/math.rb [deleted file]
rbot/plugins/nickserv.rb [deleted file]
rbot/plugins/nslookup.rb [deleted file]
rbot/plugins/opmeh.rb [deleted file]
rbot/plugins/quotes.rb [deleted file]
rbot/plugins/remind.rb [deleted file]
rbot/plugins/roshambo.rb [deleted file]
rbot/plugins/rot13.rb [deleted file]
rbot/plugins/roulette.rb [deleted file]
rbot/plugins/seen.rb [deleted file]
rbot/plugins/slashdot.rb [deleted file]
rbot/plugins/spell.rb [deleted file]
rbot/plugins/tube.rb [deleted file]
rbot/plugins/url.rb [deleted file]
rbot/plugins/weather.rb [deleted file]
rbot/plugins/wserver.rb [deleted file]
rbotconf/conf.rbot [deleted file]
rbotconf/keywords.rbot [deleted file]
rbotconf/lart/larts [deleted file]
rbotconf/lart/praises [deleted file]
rbotconf/levels.rbot [deleted file]
rbotconf/users.rbot [deleted file]
setup.rb [new file with mode: 0755]

diff --git a/bin/rbot b/bin/rbot
new file mode 100755 (executable)
index 0000000..6f08fe5
--- /dev/null
+++ b/bin/rbot
@@ -0,0 +1,64 @@
+#!/usr/bin/env ruby
+
+# 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.
+
+$VERBOSE=true
+
+require 'getoptlong'
+require 'rbot/ircbot'
+
+$debug = true
+$version="0.9.8"
+$opts = Hash.new
+
+# print +message+ if debugging is enabled
+def debug(message=nil)
+  print "DEBUG: #{message}\n" if($debug && message)
+  #yield
+end
+
+opts = GetoptLong.new(
+  [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
+  [ "--help",  "-h", GetoptLong::OPTIONAL_ARGUMENT ]
+)
+
+opts.each {|opt, arg|
+  $debug = true if(opt == "--debug")
+  $opts[opt.sub(/^-+/, "")] = arg
+}
+
+botclass = ARGV.shift
+botclass = "rbotconf" unless(botclass);
+
+unless FileTest.directory? botclass
+  # TODO copy in samples/templates from install directory
+end
+
+if(bot = Irc::IrcBot.new(botclass))
+  if($opts["help"])
+    puts bot.help($opts["help"])
+  else
+    # run the bot
+    bot.mainloop
+  end
+end
+
diff --git a/contrib/plugins/figlet.rb b/contrib/plugins/figlet.rb
deleted file mode 100644 (file)
index ce17fe7..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-class FigletPlugin < Plugin
-  def help(plugin, topic="")
-    "figlet [<message>] => print using figlet"
-  end
-  def privmsg(m)
-         case m.params
-         when nil
-                 m.reply "incorrect usage: " + help(m.plugin)
-                 return
-         when (/^-/)
-                 m.reply "incorrect usage: " + help(m.plugin)
-                 return
-         else
-                 m.reply Utils.safe_exec("/usr/bin/figlet", "-k", "-f", "mini", m.params)
-                 return
-         end
-  end
-end
-plugin = FigletPlugin.new
-plugin.register("figlet")
diff --git a/contrib/plugins/ri.rb b/contrib/plugins/ri.rb
deleted file mode 100644 (file)
index 99292f1..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-#  Author:  Michael Brailsford  <brailsmt@yahoo.com>
-#           aka  brailsmt
-#  Purpose: To respond to requests for information from the ri command line
-#  utility.
-
-class RiPlugin < Plugin
-
-       @@handlers = {
-               "ri" => "ri_handler",
-               "msgri" => "msgri_handler"
-       }
-
-       #{{{
-       def initialize
-               super
-               @cache = Hash.new
-       end
-       #}}}
-       #{{{
-       def privmsg(m)
-               if not m.params
-                       m.reply "uhmm... whatever"
-                       return
-               end
-
-               meth = self.method(@@handlers[m.plugin])
-               meth.call(m)
-       end
-       #}}}
-       #{{{
-       def cleanup
-               @cache = nil
-       end
-       #}}}
-       #{{{
-       def ri_handler(m)
-               response = ""
-               if @cache[m.params]
-                       response = @cache[m.params]
-               else
-                       IO.popen("-") {|p|
-                               if(p)
-                                       response = p.readlines.join "\n"
-                                       @cache[m.params] = response
-                               else
-                                       $stderr = $stdout
-                                       exec("ri", m.params)
-                               end
-                       }
-                       @cache[m.params] = response
-               end
-
-               @bot.say m.sourcenick, response
-               m.reply "Finished \"ri #{m.params}\"" 
-       end
-       #}}}
-       #{{{
-       def msgri_handler(m)
-               response = ""
-               tell_nick, query = m.params.split()
-               if @cache[query]
-                       response = @cache[query]
-               else
-                       IO.popen("-") {|p|
-                               if(p)
-                                       response = p.readlines.join "\n"
-                                       @cache[m.params] = response
-                               else
-                                       $stderr = $stdout
-                                       exec("ri", query)
-                               end
-                       }
-                       @cache[query] = response
-               end
-
-               @bot.say tell_nick, response
-               m.reply "Finished telling #{tell_nick} about \"ri #{query}\"" 
-       end
-       #}}}
-end
-plugin = RiPlugin.new
-plugin.register("ri")
-plugin.register("msgri")
diff --git a/contrib/plugins/stats.rb b/contrib/plugins/stats.rb
deleted file mode 100644 (file)
index 4cafbbe..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-#  Author:    Michael Brailsford  <brailsmt@yahoo.com>
-#             aka brailsmt
-#  Purpose:   Provides the ability to track various tokens that are spoken in a
-#             channel.
-#  Copyright: 2002 Michael Brailsford.  All rights reserved.
-#  License:   This plugin is licensed under the BSD license.  The terms of
-#             which follow.
-#
-#  Redistribution and use in source and binary forms, with or without
-#  modification, are permitted provided that the following conditions
-#  are met:
-#
-#  1. Redistributions of source code must retain the above copyright notice,
-#     this list of conditions and the following disclaimer.
-#
-#  2. Redistributions in binary form must reproduce the above copyright
-#     notice, this list of conditions and the following disclaimer in the
-#     documentation and/or other materials provided with the distribution.
-
-class StatsPlugin < Plugin
-
-       @@commands = { 
-               "stats" => "handle_stats",
-               "track" => "handle_track",
-               "untrack" => "handle_untrack", 
-               "listtokens" => "handle_listtokens",
-               "rmabuser" => "handle_rmabuser"
-       }
-
-       #{{{
-       def initialize
-               super
-               @listen = true
-               @channels = Hash.new
-               #check to see if a stats token file already exists for this channel...
-               Dir["#{@bot.botclass}/stats/*"].each { |fname|
-                       channel = File.basename fname
-                       tokens = Hash.new
-                       IO.foreach(fname) { |line|
-                               if line =~ /^(\S+)\s*<=>(.*)/
-                                       tokens[$1] = parse_token_stats $2
-                               end
-                       }
-                       @channels[channel] = tokens
-               }
-       end
-       #}}}
-       #{{{
-       def cleanup
-               @channels = nil
-       end
-       #}}}
-       #{{{
-       def help(plugin, topic="")
-               "Stats:  The stats plugin tracks various tokens from users in the channel.  The tokens are only tracked if it is the only thing on a line.\nUsage:  stats <token>  --  lists the stats for <token>\n        [un]track <token>  --  Adds or deletes <token> from the list of tokens\n        listtokens  --  lists the tokens that are currently being tracked"
-       end
-       #}}}
-       #{{{
-       def privmsg(m)
-               if not m.params and not m.plugin =~ /listtokens/
-                       m.reply "What a crazy fool!  Did you mean |help stats?"
-                       return
-               end
-               
-               meth = self.method(@@commands[m.plugin])
-               meth.call(m)
-       end
-       #}}}
-       #{{{
-       def save
-               Dir.mkdir("#{@bot.botclass}/stats") if not FileTest.directory?("#{@bot.botclass}/stats")
-               #save the tokens to a file...
-               @channels.each_pair { |channel, tokens|
-                       if not tokens.empty?
-                               File.open("#{@bot.botclass}/stats/#{channel}", "w") { |f|
-                                       tokens.each { |token, datahash|
-                                               f.puts "#{token} <=> #{datahash_to_s(datahash)}"
-                                       }
-                               }
-                       else
-                               File.delete "#{@bot.botclass}/stats/#{channel}"
-                       end
-               }
-       end
-       #}}}
-       #{{{
-       def listen(m)
-               if not m.private?
-                       tokens = @channels[m.target]
-                       if not @@commands[m.plugin]
-                               tokens.each_pair { |key, hsh|
-                                       if not m.message.scan(/#{Regexp.escape(key)}/).empty?
-                                               if hsh[m.sourcenick]
-                                                       hsh[m.sourcenick] += 1
-                                               else
-                                                       hsh[m.sourcenick] = 1
-                                               end
-                                       end
-                               }
-                       end
-               end
-#This is the old code  {{{
-#              if not m.private?
-#                      tokens = @channels[m.target]
-#                      hsh = tokens[m.message]
-#                      if hsh
-#                              if hsh[m.sourcenick]
-#                                      hsh[m.sourcenick] += 1
-#                              else
-#                                      hsh[m.sourcenick] = 1
-#                              end
-#                      end
-#              end     }}}
-       end
-       #}}}
-       #The following are helper functions for the plugin      {{{
-               def datahash_to_s(dhash)
-                       rv = ""
-                       dhash.each { |key, val|
-                               rv << "#{key}:#{val} "
-                       }
-                       rv.chomp
-               end
-
-               def parse_token_stats(stats)
-                       rv = Hash.new
-                       stats.split(" ").each { |nickstat|
-                               nick, stat = nickstat.split ":"
-                               rv[nick] = stat.to_i
-                       }
-                       rv
-               end
-               #}}}
-       #The following are handler methods for dealing with each command from IRC       {{{
-       #{{{
-       def handle_stats(m)
-               if not m.private?
-                       total = 0
-                       tokens = @channels[m.target]
-                       hsh = tokens[m.params]
-                       msg1 = ""
-                       if not hsh.empty?
-                               sorted = hsh.sort { |i, j| j[1] <=> i[1] }
-                               sorted.each { |a|
-                                       total += a[1]
-                               }
-
-                               msg = "Stats for #{m.params}.  Said #{total} times.  The top sayers are "
-                               if sorted[0..2]
-                                       msg << "#{sorted[0].join ':'}" if sorted[0]
-                                       msg << ", #{sorted[1].join ':'}" if sorted[1]
-                                       msg << ", and #{sorted[2].join ':'}" if sorted[2]
-                                       msg << "."
-
-                                       msg1 << "#{m.sourcenick} has said it "
-                                       if hsh[m.sourcenick]
-                                               msg1 << "#{hsh[m.sourcenick]} times."
-                                       else
-                                               msg1 << "0 times."
-                                       end
-                               else
-                                       msg << "#{m.params} has not been said yet!"
-                               end
-                               @bot.action m.replyto, msg
-                               @bot.action m.replyto, msg1 if msg1
-                       else
-                               m.reply "#{m.params} is not currently being tracked."
-                       end
-               end
-       end
-       #}}}
-       #{{{
-       def handle_track(m)
-               if not m.private?
-                       if @channels[m.target]
-                               tokens = @channels[m.target]
-                       else
-                               tokens = Hash.new
-                               @channels[m.target] = tokens
-                       end
-                       tokens[m.params] = Hash.new
-                       m.reply "now tracking #{m.params}"
-               end
-       end
-       #}}}
-       #{{{
-       def handle_untrack(m)
-               if not m.private?
-                       toks = @channels[m.target]
-                       if toks.has_key? m.params
-                               toks.delete m.params
-                               m.reply "no longer tracking #{m.params}"
-                       else
-                               m.reply "Are your signals crossed?  Since when have I tracked that?"
-                       end
-               end
-
-               toks = nil
-       end
-       #}}}
-       #{{{
-       def handle_listtokens(m)
-               if not m.private? and not @channels.empty?
-                       tokens = @channels[m.target]
-                       unless tokens.empty?
-                               toks = ""
-                               tokens.each_key { |k|
-                                       toks << "#{k} "
-                               }
-                               @bot.action m.replyto, "is currently keeping stats for:  #{toks}"
-                       else
-                               @bot.action m.replyto, "is not currently keeping stats for anything"
-                       end
-               elsif not m.private?
-                       @bot.action m.replyto, "is not currently keeping stats for anything"
-               end
-       end
-       #}}}
-       #{{{
-       def handle_rmabuser(m)
-               m.reply "This feature has not yet been implemented"
-       end
-       #}}}
-       #}}}
-
-end
-plugin = StatsPlugin.new
-plugin.register("stats")
-plugin.register("track")
-plugin.register("untrack")
-plugin.register("listtokens")
-#plugin.register("rmabuser")
diff --git a/contrib/plugins/vandale.rb b/contrib/plugins/vandale.rb
deleted file mode 100644 (file)
index 7b806c8..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#----------------------------------------------------------------#
-# Filename: vandale.rb
-# Description: Rbot plugin. Looks up a word in the Dutch VanDale
-#      dictionary
-# Author: eWoud - ewoud.nuyts<AT>student.kuleuven.ac.be
-# requires GnuVD www.djcbsoftware.nl/projecten/gnuvd/
-#----------------------------------------------------------------#
-
-class VanDalePlugin < Plugin
-  def help(plugin, topic="")
-    "vandale [<word>] => Look up in the VanDale dictionary"
-  end
-  def privmsg(m)
-       case m.params
-       when (/^([\w-]+)$/)
-               ret = Array.new
-               Utils.safe_exec("/usr/local/bin/gnuvd", m.params).each{|line| if line.length > 5 then ret << line end}
-               m.reply ret.delete_at(0)
-               while ret[0] =~ /^[[:alpha:]_]*[0-9]/ 
-                       m.reply ret.delete_at(0)
-               end
-               while ret[0] =~ /^[0-9]/
-                       m.reply ret.delete_at(0)
-               end
-               i = 0
-               while i < ret.length
-                       ret[i] = ret[i].slice(/^[[:graph:]_]*/)
-                       if ret[i].length == 0 or ret[i] =~ /^[0-9]/
-                       then
-                               ret.delete_at(i)
-                       else
-                               i = i+1
-                       end
-               end
-               if ret.length != 0 then
-                       m.reply "zie ook " + ret.join(", ")
-               end
-               return
-       when nil
-               m.reply "incorrect usage: " + help(m.plugin)
-               return
-       else
-               m.reply "incorrect usage: " + help(m.plugin)
-               return
-       end
-  end
-end
-plugin = VanDalePlugin.new
-plugin.register("vandale")
diff --git a/data/rbot/contrib/plugins/figlet.rb b/data/rbot/contrib/plugins/figlet.rb
new file mode 100644 (file)
index 0000000..ce17fe7
--- /dev/null
@@ -0,0 +1,20 @@
+class FigletPlugin < Plugin
+  def help(plugin, topic="")
+    "figlet [<message>] => print using figlet"
+  end
+  def privmsg(m)
+         case m.params
+         when nil
+                 m.reply "incorrect usage: " + help(m.plugin)
+                 return
+         when (/^-/)
+                 m.reply "incorrect usage: " + help(m.plugin)
+                 return
+         else
+                 m.reply Utils.safe_exec("/usr/bin/figlet", "-k", "-f", "mini", m.params)
+                 return
+         end
+  end
+end
+plugin = FigletPlugin.new
+plugin.register("figlet")
diff --git a/data/rbot/contrib/plugins/ri.rb b/data/rbot/contrib/plugins/ri.rb
new file mode 100644 (file)
index 0000000..99292f1
--- /dev/null
@@ -0,0 +1,83 @@
+#  Author:  Michael Brailsford  <brailsmt@yahoo.com>
+#           aka  brailsmt
+#  Purpose: To respond to requests for information from the ri command line
+#  utility.
+
+class RiPlugin < Plugin
+
+       @@handlers = {
+               "ri" => "ri_handler",
+               "msgri" => "msgri_handler"
+       }
+
+       #{{{
+       def initialize
+               super
+               @cache = Hash.new
+       end
+       #}}}
+       #{{{
+       def privmsg(m)
+               if not m.params
+                       m.reply "uhmm... whatever"
+                       return
+               end
+
+               meth = self.method(@@handlers[m.plugin])
+               meth.call(m)
+       end
+       #}}}
+       #{{{
+       def cleanup
+               @cache = nil
+       end
+       #}}}
+       #{{{
+       def ri_handler(m)
+               response = ""
+               if @cache[m.params]
+                       response = @cache[m.params]
+               else
+                       IO.popen("-") {|p|
+                               if(p)
+                                       response = p.readlines.join "\n"
+                                       @cache[m.params] = response
+                               else
+                                       $stderr = $stdout
+                                       exec("ri", m.params)
+                               end
+                       }
+                       @cache[m.params] = response
+               end
+
+               @bot.say m.sourcenick, response
+               m.reply "Finished \"ri #{m.params}\"" 
+       end
+       #}}}
+       #{{{
+       def msgri_handler(m)
+               response = ""
+               tell_nick, query = m.params.split()
+               if @cache[query]
+                       response = @cache[query]
+               else
+                       IO.popen("-") {|p|
+                               if(p)
+                                       response = p.readlines.join "\n"
+                                       @cache[m.params] = response
+                               else
+                                       $stderr = $stdout
+                                       exec("ri", query)
+                               end
+                       }
+                       @cache[query] = response
+               end
+
+               @bot.say tell_nick, response
+               m.reply "Finished telling #{tell_nick} about \"ri #{query}\"" 
+       end
+       #}}}
+end
+plugin = RiPlugin.new
+plugin.register("ri")
+plugin.register("msgri")
diff --git a/data/rbot/contrib/plugins/stats.rb b/data/rbot/contrib/plugins/stats.rb
new file mode 100644 (file)
index 0000000..4cafbbe
--- /dev/null
@@ -0,0 +1,232 @@
+#  Author:    Michael Brailsford  <brailsmt@yahoo.com>
+#             aka brailsmt
+#  Purpose:   Provides the ability to track various tokens that are spoken in a
+#             channel.
+#  Copyright: 2002 Michael Brailsford.  All rights reserved.
+#  License:   This plugin is licensed under the BSD license.  The terms of
+#             which follow.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions
+#  are met:
+#
+#  1. Redistributions of source code must retain the above copyright notice,
+#     this list of conditions and the following disclaimer.
+#
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+
+class StatsPlugin < Plugin
+
+       @@commands = { 
+               "stats" => "handle_stats",
+               "track" => "handle_track",
+               "untrack" => "handle_untrack", 
+               "listtokens" => "handle_listtokens",
+               "rmabuser" => "handle_rmabuser"
+       }
+
+       #{{{
+       def initialize
+               super
+               @listen = true
+               @channels = Hash.new
+               #check to see if a stats token file already exists for this channel...
+               Dir["#{@bot.botclass}/stats/*"].each { |fname|
+                       channel = File.basename fname
+                       tokens = Hash.new
+                       IO.foreach(fname) { |line|
+                               if line =~ /^(\S+)\s*<=>(.*)/
+                                       tokens[$1] = parse_token_stats $2
+                               end
+                       }
+                       @channels[channel] = tokens
+               }
+       end
+       #}}}
+       #{{{
+       def cleanup
+               @channels = nil
+       end
+       #}}}
+       #{{{
+       def help(plugin, topic="")
+               "Stats:  The stats plugin tracks various tokens from users in the channel.  The tokens are only tracked if it is the only thing on a line.\nUsage:  stats <token>  --  lists the stats for <token>\n        [un]track <token>  --  Adds or deletes <token> from the list of tokens\n        listtokens  --  lists the tokens that are currently being tracked"
+       end
+       #}}}
+       #{{{
+       def privmsg(m)
+               if not m.params and not m.plugin =~ /listtokens/
+                       m.reply "What a crazy fool!  Did you mean |help stats?"
+                       return
+               end
+               
+               meth = self.method(@@commands[m.plugin])
+               meth.call(m)
+       end
+       #}}}
+       #{{{
+       def save
+               Dir.mkdir("#{@bot.botclass}/stats") if not FileTest.directory?("#{@bot.botclass}/stats")
+               #save the tokens to a file...
+               @channels.each_pair { |channel, tokens|
+                       if not tokens.empty?
+                               File.open("#{@bot.botclass}/stats/#{channel}", "w") { |f|
+                                       tokens.each { |token, datahash|
+                                               f.puts "#{token} <=> #{datahash_to_s(datahash)}"
+                                       }
+                               }
+                       else
+                               File.delete "#{@bot.botclass}/stats/#{channel}"
+                       end
+               }
+       end
+       #}}}
+       #{{{
+       def listen(m)
+               if not m.private?
+                       tokens = @channels[m.target]
+                       if not @@commands[m.plugin]
+                               tokens.each_pair { |key, hsh|
+                                       if not m.message.scan(/#{Regexp.escape(key)}/).empty?
+                                               if hsh[m.sourcenick]
+                                                       hsh[m.sourcenick] += 1
+                                               else
+                                                       hsh[m.sourcenick] = 1
+                                               end
+                                       end
+                               }
+                       end
+               end
+#This is the old code  {{{
+#              if not m.private?
+#                      tokens = @channels[m.target]
+#                      hsh = tokens[m.message]
+#                      if hsh
+#                              if hsh[m.sourcenick]
+#                                      hsh[m.sourcenick] += 1
+#                              else
+#                                      hsh[m.sourcenick] = 1
+#                              end
+#                      end
+#              end     }}}
+       end
+       #}}}
+       #The following are helper functions for the plugin      {{{
+               def datahash_to_s(dhash)
+                       rv = ""
+                       dhash.each { |key, val|
+                               rv << "#{key}:#{val} "
+                       }
+                       rv.chomp
+               end
+
+               def parse_token_stats(stats)
+                       rv = Hash.new
+                       stats.split(" ").each { |nickstat|
+                               nick, stat = nickstat.split ":"
+                               rv[nick] = stat.to_i
+                       }
+                       rv
+               end
+               #}}}
+       #The following are handler methods for dealing with each command from IRC       {{{
+       #{{{
+       def handle_stats(m)
+               if not m.private?
+                       total = 0
+                       tokens = @channels[m.target]
+                       hsh = tokens[m.params]
+                       msg1 = ""
+                       if not hsh.empty?
+                               sorted = hsh.sort { |i, j| j[1] <=> i[1] }
+                               sorted.each { |a|
+                                       total += a[1]
+                               }
+
+                               msg = "Stats for #{m.params}.  Said #{total} times.  The top sayers are "
+                               if sorted[0..2]
+                                       msg << "#{sorted[0].join ':'}" if sorted[0]
+                                       msg << ", #{sorted[1].join ':'}" if sorted[1]
+                                       msg << ", and #{sorted[2].join ':'}" if sorted[2]
+                                       msg << "."
+
+                                       msg1 << "#{m.sourcenick} has said it "
+                                       if hsh[m.sourcenick]
+                                               msg1 << "#{hsh[m.sourcenick]} times."
+                                       else
+                                               msg1 << "0 times."
+                                       end
+                               else
+                                       msg << "#{m.params} has not been said yet!"
+                               end
+                               @bot.action m.replyto, msg
+                               @bot.action m.replyto, msg1 if msg1
+                       else
+                               m.reply "#{m.params} is not currently being tracked."
+                       end
+               end
+       end
+       #}}}
+       #{{{
+       def handle_track(m)
+               if not m.private?
+                       if @channels[m.target]
+                               tokens = @channels[m.target]
+                       else
+                               tokens = Hash.new
+                               @channels[m.target] = tokens
+                       end
+                       tokens[m.params] = Hash.new
+                       m.reply "now tracking #{m.params}"
+               end
+       end
+       #}}}
+       #{{{
+       def handle_untrack(m)
+               if not m.private?
+                       toks = @channels[m.target]
+                       if toks.has_key? m.params
+                               toks.delete m.params
+                               m.reply "no longer tracking #{m.params}"
+                       else
+                               m.reply "Are your signals crossed?  Since when have I tracked that?"
+                       end
+               end
+
+               toks = nil
+       end
+       #}}}
+       #{{{
+       def handle_listtokens(m)
+               if not m.private? and not @channels.empty?
+                       tokens = @channels[m.target]
+                       unless tokens.empty?
+                               toks = ""
+                               tokens.each_key { |k|
+                                       toks << "#{k} "
+                               }
+                               @bot.action m.replyto, "is currently keeping stats for:  #{toks}"
+                       else
+                               @bot.action m.replyto, "is not currently keeping stats for anything"
+                       end
+               elsif not m.private?
+                       @bot.action m.replyto, "is not currently keeping stats for anything"
+               end
+       end
+       #}}}
+       #{{{
+       def handle_rmabuser(m)
+               m.reply "This feature has not yet been implemented"
+       end
+       #}}}
+       #}}}
+
+end
+plugin = StatsPlugin.new
+plugin.register("stats")
+plugin.register("track")
+plugin.register("untrack")
+plugin.register("listtokens")
+#plugin.register("rmabuser")
diff --git a/data/rbot/contrib/plugins/vandale.rb b/data/rbot/contrib/plugins/vandale.rb
new file mode 100644 (file)
index 0000000..7b806c8
--- /dev/null
@@ -0,0 +1,49 @@
+#----------------------------------------------------------------#
+# Filename: vandale.rb
+# Description: Rbot plugin. Looks up a word in the Dutch VanDale
+#      dictionary
+# Author: eWoud - ewoud.nuyts<AT>student.kuleuven.ac.be
+# requires GnuVD www.djcbsoftware.nl/projecten/gnuvd/
+#----------------------------------------------------------------#
+
+class VanDalePlugin < Plugin
+  def help(plugin, topic="")
+    "vandale [<word>] => Look up in the VanDale dictionary"
+  end
+  def privmsg(m)
+       case m.params
+       when (/^([\w-]+)$/)
+               ret = Array.new
+               Utils.safe_exec("/usr/local/bin/gnuvd", m.params).each{|line| if line.length > 5 then ret << line end}
+               m.reply ret.delete_at(0)
+               while ret[0] =~ /^[[:alpha:]_]*[0-9]/ 
+                       m.reply ret.delete_at(0)
+               end
+               while ret[0] =~ /^[0-9]/
+                       m.reply ret.delete_at(0)
+               end
+               i = 0
+               while i < ret.length
+                       ret[i] = ret[i].slice(/^[[:graph:]_]*/)
+                       if ret[i].length == 0 or ret[i] =~ /^[0-9]/
+                       then
+                               ret.delete_at(i)
+                       else
+                               i = i+1
+                       end
+               end
+               if ret.length != 0 then
+                       m.reply "zie ook " + ret.join(", ")
+               end
+               return
+       when nil
+               m.reply "incorrect usage: " + help(m.plugin)
+               return
+       else
+               m.reply "incorrect usage: " + help(m.plugin)
+               return
+       end
+  end
+end
+plugin = VanDalePlugin.new
+plugin.register("vandale")
diff --git a/data/rbot/plugins/autoop.rb b/data/rbot/plugins/autoop.rb
new file mode 100644 (file)
index 0000000..fdbcf6e
--- /dev/null
@@ -0,0 +1,68 @@
+class AutoOP < Plugin
+    @@handlers = {
+        "addop" => "handle_addop",
+        "rmop" => "handle_rmop",
+        "listop" => "handle_listop"
+    }
+    
+    def help(plugin, topic="")
+        "perform autoop based on hostmask - usage: addop <hostmask>, rmop <hostmask>, listop"
+    end
+    
+    def join(m)
+        if(!m.address?)
+          @registry.each { |mask,channels|
+            if(Irc.netmaskmatch(mask, m.source) && channels.include?(m.channel))
+              @bot.mode(m.channel, "+o", m.sourcenick)
+            end
+          }
+        end
+    end
+    
+    def privmsg(m)
+        if(m.private?)
+          if (!m.params || m.params == "list")
+            handle_listop(m)
+          elsif (m.params =~ /^add\s+(.+)$/)
+            handle_addop(m, $1)
+          elsif (m.params =~ /^rm\s+(.+)$/)
+            handle_rmop(m, $1)
+          end
+        end
+    end
+
+    def handle_addop(m, params)
+        ma = /^(.+?)(\s+(.+))?$/.match(params)
+        channels = ma[2] ? ma[2] : @bot.config['JOIN_CHANNELS']
+        if(ma[1] && channels)
+            @registry[ma[1]] = channels.split(/,\s*/).collect { |x|
+                x.strip
+            }
+            m.okay
+        else
+            m.reply @bot.lang.get('dunno')
+        end
+    end
+
+    def handle_rmop(m, params)
+       if(!@registry.delete(params))
+         m.reply @bot.lang.get('dunno')
+       else
+         m.okay
+       end
+    end
+
+    def handle_listop(m)
+        if(@registry.length)
+            @registry.each { |mask,channels|
+                m.reply "#{mask} in #{channels.join(', ')}"
+            }
+        else
+            m.reply "No entrys"
+        end
+    end
+end
+
+plugin = AutoOP.new
+plugin.register("autoop")
+
diff --git a/data/rbot/plugins/autorejoin.rb b/data/rbot/plugins/autorejoin.rb
new file mode 100644 (file)
index 0000000..aba4650
--- /dev/null
@@ -0,0 +1,16 @@
+class AutoRejoinPlugin < Plugin
+  def help(plugin, topic="")
+    "performs an automatic rejoin if the bot is kicked from a channel"
+  end
+  def kick(m)
+    if m.address?
+      @bot.timer.add_once(10, m) {|m|
+        @bot.join m.channel
+        @bot.say m.channel, @bot.lang.get("insult") % m.sourcenick
+      }
+    end
+  end
+end
+
+plugin = AutoRejoinPlugin.new
+plugin.register("autorejoin")
diff --git a/data/rbot/plugins/cal.rb b/data/rbot/plugins/cal.rb
new file mode 100644 (file)
index 0000000..4f28310
--- /dev/null
@@ -0,0 +1,15 @@
+class CalPlugin < Plugin
+  def help(plugin, topic="")
+    "cal [options] => show current calendar [unix cal options]"
+  end
+  def cal(m, params)
+    if params.has_key?(:month)
+      m.reply Utils.safe_exec("cal", params[:month], params[:year])
+    else
+      m.reply Utils.safe_exec("cal")
+    end
+  end
+end
+plugin = CalPlugin.new
+plugin.map 'cal :month :year', :requirements => {:month => /^\d+$/, :year => /^\d+$/}
+plugin.map 'cal'
diff --git a/data/rbot/plugins/dice.rb b/data/rbot/plugins/dice.rb
new file mode 100644 (file)
index 0000000..928da89
--- /dev/null
@@ -0,0 +1,81 @@
+##################
+# Filename: dice.rb
+# Description: Rbot plugin. Rolls rpg style dice
+# Author: David Dorward (http://david.us-lot.org/ - you might find a more up to date version of this plugin there)
+# Version: 0.3.2
+# Date: Sat 6 Apr 2002
+#
+# You can get rbot from: http://www.linuxbrit.co.uk/rbot/
+#
+# Changelog
+# 0.1 - Initial release
+# 0.1.1 - bug fix, only 1 digit for number of dice sides on first roll
+# 0.3.0 - Spelling correction on changelog 0.1.1
+#       - Return results of each roll
+# 0.3.1 - Minor documentation update
+# 0.3.2 - Bug fix, could not subtract numbers (String can't be coerced into Fixnum)
+#
+# TODO: Test! Test! Test!
+#       Comment!
+#       Fumble/Critical counter (1's and x's where x is sides on dice)
+####################################################
+
+class DiceDisplay
+  attr_reader :total, :view
+  def initialize(view, total)
+    @total = total
+    @view = view
+  end
+end
+
+class DicePlugin < Plugin
+  def help(plugin, topic="")
+    "dice <string> (where <string> is something like: d6 or 2d6 or 2d6+4 or 2d6+1d20 or 2d6+1d5+4d7-3d4-6) => Rolls that set of virtual dice"
+  end
+
+  def rolldice(d)
+    dice = d.split(/d/)
+    r = 0
+    unless dice[0] =~ /^[0-9]+/
+      dice[0] = 1
+    end
+    for i in 0...dice[0].to_i
+      r = r + rand(dice[1].to_i) + 1
+    end
+    return r
+  end
+
+  def iddice(d)
+    porm = d.slice!(0,1)
+    if d =~ /d/
+      r = rolldice(d)
+    else
+      r = d
+    end
+    if porm == "-"
+      r = 0 - r.to_i
+    end
+    viewer = DiceDisplay.new("[" + porm.to_s + d.to_s + "=" + r.to_s + "] ", r)
+    return viewer
+  end
+
+  def privmsg(m)
+    unless(m.params && m.params =~ /^[0-9]*d[0-9]+([+-]([0-9]+|[0-9]*d[0-9])+)*$/)
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+    a = m.params.scan(/^[0-9]*d[0-9]+|[+-][0-9]*d[0-9]+|[+-][0-9]+/)
+    r = rolldice(a[0])
+    t = "[" + a[0].to_s + "=" + r.to_s + "] "
+    for i in 1...a.length
+      tmp = iddice(a[i])
+      r = r + tmp.total.to_i
+      t = t + tmp.view.to_s
+    end
+    m.reply r.to_s + " | " + t
+  end
+end
+plugin = DicePlugin.new
+plugin.register("dice")
+##############################################
+#fin
diff --git a/data/rbot/plugins/eightball.rb b/data/rbot/plugins/eightball.rb
new file mode 100644 (file)
index 0000000..6474849
--- /dev/null
@@ -0,0 +1,19 @@
+# Author: novex, daniel@novex.net.nz based on code from slap.rb by oct
+
+class EightBallPlugin < Plugin
+  def initialize
+    super
+    @answers=['yes', 'no', 'outlook not so good', 'all signs point to yes', 'all signs point to no', 'why the hell are you asking me?', 'the answer is unclear']
+  end
+  def help(plugin, topic="")
+    "magic 8-ball ruby bot module written by novex for nvinfo on #dumber@quakenet, usage:<botname> 8ball will i ever beat this cancer?"
+  end
+  def eightball(m, params)
+    answers = @answers[rand(@answers.length)]
+    action = "shakes the magic 8-ball... #{answers}"
+    @bot.action m.replyto, action
+  end
+end
+plugin = EightBallPlugin.new
+plugin.map '8ball', :action => 'usage'
+plugin.map '8ball *params', :action => 'eightball'
diff --git a/data/rbot/plugins/excuse.rb b/data/rbot/plugins/excuse.rb
new file mode 100644 (file)
index 0000000..38e85ad
--- /dev/null
@@ -0,0 +1,470 @@
+class ExcusePlugin < Plugin
+  # excuses courtesy of http://www.cs.wisc.edu/~ballard/bofh/
+@@excuses = [
+"clock speed",
+"solar flares",
+"electromagnetic radiation from satellite debris",
+"static from nylon underwear",
+"static from plastic slide rules",
+"global warming",
+"poor power conditioning",
+"static buildup",
+"doppler effect",
+"hardware stress fractures",
+"magnetic interference from money/credit cards",
+"dry joints on cable plug",
+"we're waiting for [the phone company] to fix that line",
+"sounds like a Windows problem, try calling Microsoft support",
+"temporary routing anomaly",
+"somebody was calculating pi on the server",
+"fat electrons in the lines",
+"excess surge protection",
+"floating point processor overflow",
+"divide-by-zero error",
+"POSIX compliance problem",
+"monitor resolution too high",
+"improperly oriented keyboard",
+"network packets travelling uphill (use a carrier pigeon)",
+"Decreasing electron flux",
+"first Saturday after first full moon in Winter",
+"radiosity depletion",
+"CPU radiator broken",
+"It works the way the Wang did, what's the problem",
+"positron router malfunction",
+"cellular telephone interference",
+"techtonic stress",
+"piezo-electric interference",
+"(l)user error",
+"working as designed",
+"dynamic software linking table corrupted",
+"heavy gravity fluctuation, move computer to floor rapidly",
+"secretary plugged hairdryer into UPS",
+"terrorist activities",
+"not enough memory, go get system upgrade",
+"interrupt configuration error",
+"spaghetti cable cause packet failure",
+"boss forgot system password",
+"bank holiday - system operating credits  not recharged",
+"virus attack, luser responsible",
+"waste water tank overflowed onto computer",
+"Complete Transient Lockout",
+"bad ether in the cables",
+"Bogon emissions",
+"Change in Earth's rotational speed",
+"Cosmic ray particles crashed through the hard disk platter",
+"Smell from unhygienic janitorial staff wrecked the tape heads",
+"Little hamster in running wheel had coronary; waiting for replacement to be Fedexed from Wyoming",
+"Evil dogs hypnotised the night shift",
+"Plumber mistook routing panel for decorative wall fixture",
+"Electricians made popcorn in the power supply",
+"Groundskeepers stole the root password",
+"high pressure system failure",
+"failed trials, system needs redesigned",
+"system has been recalled",
+"not approved by the FCC",
+"need to wrap system in aluminum foil to fix problem",
+"not properly grounded, please bury computer",
+"CPU needs recalibration",
+"system needs to be rebooted",
+"bit bucket overflow",
+"descramble code needed from software company",
+"only available on a need to know basis",
+"knot in cables caused data stream to become twisted and kinked",
+"nesting roaches shorted out the ether cable",
+"The file system is full of it",
+"Satan did it",
+"Daemons did it",
+"You're out of memory",
+"There isn't any problem",
+"Unoptimized hard drive",
+"Typo in the code",
+"Yes, yes, its called a design limitation",
+"Look, buddy:  Windows 3.1 IS A General Protection Fault.",
+"That's a great computer you have there; have you considered how it would work as a BSD machine?",
+"Please excuse me, I have to circuit an AC line through my head to get this database working.",
+"Yeah, yo mama dresses you funny and you need a mouse to delete files.",
+"Support staff hung over, send aspirin and come back LATER.",
+"Someone is standing on the ethernet cable, causing a kink in the cable",
+"Windows 95 undocumented 'feature'",
+"Runt packets",
+"Password is too complex to decrypt",
+"Boss' kid fucked up the machine",
+"Electromagnetic energy loss",
+"Budget cuts",
+"Mouse chewed through power cable",
+"Stale file handle (next time use Tupperware(tm)!)",
+"Feature not yet implemented",
+"Internet outage",
+"Pentium FDIV bug",
+"Vendor no longer supports the product",
+"Small animal kamikaze attack on power supplies",
+"The vendor put the bug there.",
+"SIMM crosstalk.",
+"IRQ dropout",
+"Collapsed Backbone",
+"Power company testing new voltage spike (creation) equipment",
+"operators on strike due to broken coffee machine",
+"backup tape overwritten with copy of system manager's favourite CD",
+"UPS interrupted the server's power",
+"The electrician didn't know what the yellow cable was so he yanked the ethernet out.",
+"The keyboard isn't plugged in",
+"The air conditioning water supply pipe ruptured over the machine room",
+"The electricity substation in the car park blew up.",
+"The rolling stones concert down the road caused a brown out",
+"The salesman drove over the CPU board.",
+"The monitor is plugged into the serial port",
+"Root nameservers are out of sync",
+"electro-magnetic pulses from French above ground nuke testing.",
+"your keyboard's space bar is generating spurious keycodes.",
+"the real ttys became pseudo ttys and vice-versa.",
+"the printer thinks its a router.",
+"the router thinks its a printer.",
+"evil hackers from Serbia.",
+"we just switched to FDDI.",
+"halon system went off and killed the operators.",
+"because Bill Gates is a Jehovah's witness and so nothing can work on St. Swithin's day.",
+"user to computer ratio too high.",
+"user to computer ration too low.",
+"we just switched to Sprint.",
+"it has Intel Inside",
+"Sticky bits on disk.",
+"Power Company having EMP problems with their reactor",
+"The ring needs another token",
+"new management",
+"telnet: Unable to connect to remote host: Connection refused",
+"SCSI Chain overterminated",
+"It's not plugged in.",
+"because of network lag due to too many people playing deathmatch",
+"You put the disk in upside down.",
+"Daemons loose in system.",
+"User was distributing pornography on server; system seized by FBI.",
+"BNC (brain not connected)",
+"UBNC (user brain not connected)",
+"LBNC (luser brain not connected)",
+"disks spinning backwards - toggle the hemisphere jumper.",
+"new guy cross-connected phone lines with ac power bus.",
+"had to use hammer to free stuck disk drive heads.",
+"Too few computrons available.",
+"Communications satellite used by the military for star wars.",
+"Party-bug in the Aloha protocol.",
+"Insert coin for new game",
+"Dew on the telephone lines.",
+"Arcserve crashed the server again.",
+"Some one needed the powerstrip, so they pulled the switch plug.",
+"My pony-tail hit the on/off switch on the power strip.",
+"Big to little endian conversion error",
+"You can tune a file system, but you can't tune a fish (from most tunefs man pages)",
+"Dumb terminal",
+"Zombie processes haunting the computer",
+"Incorrect time synchronization",
+"Defunct processes",
+"Stubborn processes",
+"non-redundant fan failure ",
+"monitor VLF leakage",
+"bugs in the RAID",
+"no 'any' key on keyboard",
+"root rot",
+"Backbone Scoliosis",
+"/pub/lunch",
+"excessive collisions & not enough packet ambulances",
+"le0: no carrier: transceiver cable problem?",
+"broadcast packets on wrong frequency",
+"popper unable to process jumbo kernel",
+"NOTICE: alloc: /dev/null: filesystem full",
+"pseudo-user on a pseudo-terminal",
+"Recursive traversal of loopback mount points",
+"Backbone adjustment",
+"OS swapped to disk",
+"vapors from evaporating sticky-note adhesives",
+"sticktion",
+"short leg on process table",
+"multicasts on broken packets",
+"ether leak",
+"Atilla the Hub",
+"endothermal recalibration",
+"filesystem not big enough for Jumbo Kernel Patch",
+"loop found in loop in redundant loopback",
+"system consumed all the paper for paging",
+"permission denied",
+"Reformatting Page. Wait...",
+"..disk or the processor is on fire.",
+"SCSI's too wide.",
+"Proprietary Information.",
+"Just type 'mv * /dev/null'.",
+"runaway cat on system.",
+"Did you pay the new Support Fee?",
+"We only support a 1200 bps connection.",
+"We only support a 28000 bps connection.",
+"Me no internet, only janitor, me just wax floors.",
+"I'm sorry a pentium won't do, you need an SGI to connect with us.",
+"Post-it Note Sludge leaked into the monitor.",
+"the curls in your keyboard cord are losing electricity.",
+"The monitor needs another box of pixels.",
+"RPC_PMAP_FAILURE",
+"kernel panic: write-only-memory (/dev/wom0) capacity exceeded.",
+"Write-only-memory subsystem too slow for this machine. Contact your local dealer.",
+"Just pick up the phone and give modem connect sounds. 'Well you said we should get more lines so we don't have voice lines.'",
+"Quantum dynamics are affecting the transistors",
+"Police are examining all internet packets in the search for a narco-net-trafficker",
+"We are currently trying a new concept of using a live mouse.  Unfortunately, one has yet to survive being hooked up to the computer.....please bear with us.",
+"Your mail is being routed through Germany ... and they're censoring us.",
+"Only people with names beginning with 'A' are getting mail this week (a la Microsoft)",
+"We didn't pay the Internet bill and it's been cut off.",
+"Lightning strikes.",
+"Of course it doesn't work. We've performed a software upgrade.",
+"Change your language to Finnish.",
+"Fluorescent lights are generating negative ions. If turning them off doesn't work, take them out and put tin foil on the ends.",
+"High nuclear activity in your area.",
+"What office are you in? Oh, that one.  Did you know that your building was built over the universities first nuclear research site? And wow, aren't you the lucky one, your office is right over where the core is buried!",
+"The MGs ran out of gas.",
+"The UPS doesn't have a battery backup.",
+"Recursivity.  Call back if it happens again.",
+"Someone thought The Big Red Button was a light switch.",
+"The mainframe needs to rest.  It's getting old, you know.",
+"I'm not sure.  Try calling the Internet's head office -- it's in the book.",
+"The lines are all busy (busied out, that is -- why let them in to begin with?).",
+"Jan  9 16:41:27 huber su: 'su root' succeeded for .... on /dev/pts/1",
+"It's those computer people in X {city of world}.  They keep stuffing things up.",
+"A star wars satellite accidently blew up the WAN.",
+"Fatal error right in front of screen",
+"That function is not currently supported, but Bill Gates assures us it will be featured in the next upgrade.",
+"wrong polarity of neutron flow",
+"Lusers learning curve appears to be fractal",
+"We had to turn off that service to comply with the CDA Bill.",
+"Ionization from the air-conditioning",
+"TCP/IP UDP alarm threshold is set too low.",
+"Someone is broadcasting pygmy packets and the router doesn't know how to deal with them.",
+"The new frame relay network hasn't bedded down the software loop transmitter yet. ",
+"Fanout dropping voltage too much, try cutting some of those little traces",
+"Plate voltage too low on demodulator tube",
+"You did wha... oh _dear_....",
+"CPU needs bearings repacked",
+"Too many little pins on CPU confusing it, bend back and forth until 10-20% are neatly removed. Do _not_ leave metal bits visible!",
+"_Rosin_ core solder? But...",
+"Software uses US measurements, but the OS is in metric...",
+"The computer fleetly, mouse and all.",
+"Your cat tried to eat the mouse.",
+"The Borg tried to assimilate your system. Resistance is futile.",
+"It must have been the lightning storm we had (yesterday) (last week) (last month)",
+"Due to Federal Budget problems we have been forced to cut back on the number of users able to access the system at one time. (namely none allowed....)",
+"Too much radiation coming from the soil.",
+"Unfortunately we have run out of bits/bytes/whatever. Don't worry, the next supply will be coming next week.",
+"Program load too heavy for processor to lift.",
+"Processes running slowly due to weak power supply",
+"Our ISP is having {switching,routing,SMDS,frame relay} problems",
+"We've run out of licenses",
+"Interference from lunar radiation",
+"Standing room only on the bus.",
+"You need to install an RTFM interface.",
+"That would be because the software doesn't work.",
+"That's easy to fix, but I can't be bothered.",
+"Someone's tie is caught in the printer, and if anything else gets printed, he'll be in it too.",
+"We're upgrading /dev/null",
+"The Usenet news is out of date",
+"Our POP server was kidnapped by a weasel.",
+"It's stuck in the Web.",
+"Your modem doesn't speak English.",
+"The mouse escaped.",
+"All of the packets are empty.",
+"The UPS is on strike.",
+"Neutrino overload on the nameserver",
+"Melting hard drives",
+"Someone has messed up the kernel pointers",
+"The kernel license has expired",
+"Netscape has crashed",
+"The cord jumped over and hit the power switch.",
+"It was OK before you touched it.",
+"Bit rot",
+"U.S. Postal Service",
+"Your Flux Capacitor has gone bad.",
+"The Dilithium Crystals need to be rotated.",
+"The static electricity routing is acting up...",
+"Traceroute says that there is a routing problem in the backbone.  It's not our problem.",
+"The co-locator cannot verify the frame-relay gateway to the ISDN server.",
+"High altitude condensation from U.S.A.F prototype aircraft has contaminated the primary subnet mask. Turn off your computer for 9 days to avoid damaging it.",
+"Lawn mower blade in your fan need sharpening",
+"Electrons on a bender",
+"Telecommunications is upgrading. ",
+"Telecommunications is downgrading.",
+"Telecommunications is downshifting.",
+"Hard drive sleeping. Let it wake up on it's own...",
+"Interference between the keyboard and the chair.",
+"The CPU has shifted, and become decentralized.",
+"Due to the CDA, we no longer have a root account.",
+"We ran out of dial tone and we're and waiting for the phone company to deliver another bottle.",
+"You must've hit the wrong any key.",
+"PCMCIA slave driver",
+"The Token fell out of the ring. Call us when you find it.",
+"The hardware bus needs a new token.",
+"Too many interrupts",
+"Not enough interrupts",
+"The data on your hard drive is out of balance.",
+"Digital Manipulator exceeding velocity parameters",
+"appears to be a Slow/Narrow SCSI-0 Interface problem",
+"microelectronic Riemannian curved-space fault in write-only file system",
+"fractal radiation jamming the backbone",
+"routing problems on the neural net",
+"IRQ-problems with the Un-Interruptible-Power-Supply",
+"CPU-angle has to be adjusted because of vibrations coming from the nearby road",
+"emissions from GSM-phones",
+"CD-ROM server needs recalibration",
+"firewall needs cooling",
+"asynchronous inode failure",
+"transient bus protocol violation",
+"incompatible bit-registration operators",
+"your process is not ISO 9000 compliant",
+"You need to upgrade your VESA local bus to a MasterCard local bus.",
+"The recent proliferation of Nuclear Testing",
+"Elves on strike. (Why do they call EMAG Elf Magic)",
+"Internet exceeded Luser level, please wait until a luser logs off before attempting to log back on.",
+"Your EMAIL is now being delivered by the USPS.",
+"Your computer hasn't been returning all the bits it gets from the Internet.",
+"You've been infected by the Telescoping Hubble virus.",
+"Scheduled global CPU outage",
+"Your Pentium has a heating problem - try cooling it with ice cold water.(Do not turn off your computer, you do not want to cool down the Pentium Chip while he isn't working, do you?)",
+"Your processor has processed too many instructions.  Turn it off immediately, do not type any commands!!",
+"Your packets were eaten by the terminator",
+"Your processor does not develop enough heat.",
+"We need a licensed electrician to replace the light bulbs in the computer room.",
+"The POP server is out of Coke",
+"Fiber optics caused gas main leak",
+"Server depressed, needs Prozac",
+"quantum decoherence",
+"those damn raccoons!",
+"suboptimal routing experience",
+"A plumber is needed, the network drain is clogged",
+"50% of the manual is in .pdf readme files",
+"the AA battery in the wallclock sends magnetic interference",
+"the xy axis in the trackball is coordinated with the summer solstice",
+"the butane lighter causes the pincushioning",
+"old inkjet cartridges emanate barium-based fumes",
+"manager in the cable duct",
+"We'll fix that in the next (upgrade, update, patch release, service pack).",
+"HTTPD Error 666 : BOFH was here",
+"HTTPD Error 4004 : very old Intel cpu - insufficient processing power",
+"The ATM board has run out of 10 pound notes.  We are having a whip round to refill it, care to contribute ?",
+"Network failure -  call NBC",
+"Having to manually track the satellite.",
+"Your/our computer(s) had suffered a memory leak, and we are waiting for them to be topped up.",
+"The rubber band broke",
+"We're on Token Ring, and it looks like the token got loose.",
+"Stray Alpha Particles from memory packaging caused Hard Memory Error on Server.",
+"paradigm shift...without a clutch",
+"PEBKAC (Problem Exists Between Keyboard And Chair)",
+"The cables are not the same length.",
+"Second-system effect.",
+"Chewing gum on /dev/sd3c",
+"Boredom in the Kernel.",
+"the daemons! the daemons! the terrible daemons!",
+"I'd love to help you -- it's just that the Boss won't let me near the computer. ",
+"struck by the Good Times virus",
+"YOU HAVE AN I/O ERROR -> Incompetent Operator error",
+"Your parity check is overdrawn and you're out of cache.",
+"Communist revolutionaries taking over the server room and demanding all the computers in the building or they shoot the sysadmin. Poor misguided fools.",
+"Plasma conduit breach",
+"Out of cards on drive D:",
+"Sand fleas eating the Internet cables",
+"parallel processors running perpendicular today",
+"ATM cell has no roaming feature turned on, notebooks can't connect",
+"Webmasters kidnapped by evil cult.",
+"Failure to adjust for daylight savings time.",
+"Virus transmitted from computer to sysadmins.",
+"Virus due to computers having unsafe sex.",
+"Incorrectly configured static routes on the corerouters.",
+"Forced to support NT servers; sysadmins quit.",
+"Suspicious pointer corrupted virtual machine",
+"It's the InterNIC's fault.",
+"Root name servers corrupted.",
+"Budget cuts forced us to sell all the power cords for the servers.",
+"Someone hooked the twisted pair wires into the answering machine.",
+"Operators killed by year 2000 bug bite.",
+"We've picked COBOL as the language of choice.",
+"Operators killed when huge stack of backup tapes fell over.",
+"Robotic tape changer mistook operator's tie for a backup tape.",
+"Someone was smoking in the computer room and set off the halon systems.",
+"Your processor has taken a ride to Heaven's Gate on the UFO behind Hale-Bopp's comet.",
+"it's an ID-10-T error",
+"Dyslexics retyping hosts file on servers",
+"The Internet is being scanned for viruses.",
+"Your computer's union contract is set to expire at midnight.",
+"Bad user karma.",
+"/dev/clue was linked to /dev/null",
+"Increased sunspot activity.",
+"We already sent around a notice about that.",
+"It's union rules. There's nothing we can do about it. Sorry.",
+"Interference from the Van Allen Belt.",
+"Jupiter is aligned with Mars.",
+"Redundant ACLs. ",
+"Mail server hit by UniSpammer.",
+"T-1's congested due to porn traffic to the news server.",
+"Data for intranet got routed through the extranet and landed on the internet.",
+"We are a 100% Microsoft Shop.",
+"We are Microsoft.  What you are experiencing is not a problem; it is an undocumented feature.",
+"Sales staff sold a product we don't offer.",
+"Secretary sent chain letter to all 5000 employees.",
+"Sysadmin didn't hear pager go off due to loud music from bar-room speakers.",
+"Sysadmin accidentally destroyed pager with a large hammer.",
+"Sysadmins unavailable because they are in a meeting talking about why they are unavailable so much.",
+"Bad cafeteria food landed all the sysadmins in the hospital.",
+"Route flapping at the NAP.",
+"Computers under water due to SYN flooding.",
+"The vulcan-death-grip ping has been applied.",
+"Electrical conduits in machine room are melting.",
+"Traffic jam on the Information Superhighway.",
+"Radial Telemetry Infiltration",
+"Cow-tippers tipped a cow onto the server.",
+"tachyon emissions overloading the system",
+"Maintenance window broken",
+"We're out of slots on the server",
+"Computer room being moved.  Our systems are down for the weekend.",
+"Sysadmins busy fighting SPAM.",
+"Repeated reboots of the system failed to solve problem",
+"Feature was not beta tested",
+"Domain controller not responding",
+"Someone else stole your IP address, call the Internet detectives!",
+"It's not RFC-822 compliant.",
+"operation failed because: there is no message for this error (#1014)",
+"stop bit received",
+"internet is needed to catch the etherbunny",
+"network down, IP packets delivered via UPS",
+"Firmware update in the coffee machine",
+"Temporal anomaly",
+"Mouse has out-of-cheese-error",
+"Borg implants are failing",
+"Borg nanites have infested the server",
+"error: one bad user found in front of screen",
+"Please state the nature of the technical emergency",
+"Internet shut down due to maintenance",
+"Daemon escaped from pentagram",
+"crop circles in the corn shell",
+"sticky bit has come loose",
+"Hot Java has gone cold",
+"Cache miss - please take better aim next time",
+"Hash table has woodworm",
+"Trojan horse ran out of hay",
+"Zombie processes detected, machine is haunted.",
+"overflow error in /dev/null",
+"Browser's cookie is corrupted -- someone's been nibbling on it.",
+"Mailer-daemon is busy burning your message in hell.",
+"According to Microsoft, it's by design",
+"vi needs to be upgraded to vii",
+"greenpeace free'd the mallocs",
+"Terrorists crashed an airplane into the server room, have to remove /bin/laden. (rm -rf /bin/laden)",
+"astropneumatic oscillations in the water-cooling",
+"Somebody ran the operating system through a spelling checker.",
+"Rhythmic variations in the voltage reaching the power supply.",
+"Keyboard Actuator Failure.  Order and Replace."
+]
+
+  def help(plugin, topic="")
+    "excuse => supply a random excuse"
+  end
+  def privmsg(m)
+    excuse = @@excuses[rand(@@excuses.length)]
+    m.reply excuse
+  end
+end
+
+plugin = ExcusePlugin.new
+plugin.register("excuse")
+
diff --git a/data/rbot/plugins/fish.rb b/data/rbot/plugins/fish.rb
new file mode 100644 (file)
index 0000000..57aaafc
--- /dev/null
@@ -0,0 +1,61 @@
+require 'net/http'
+require 'uri/common'
+Net::HTTP.version_1_2
+
+class BabelPlugin < Plugin
+  def help(plugin, topic="")
+    "translate to <lang> <string> => translate from english to <lang>, translate from <lang> <string> => translate to english from <lang>, translate <fromlang> <tolang> <string> => translate from <fromlang> to <tolang>. Languages: en, fr, de, it, pt, es, nl"
+  end
+  def translate(m, params)
+    langs = ["en", "fr", "de", "it", "pt", "es", "nl"]
+    trans_from = params[:fromlang] ? params[:fromlang] : 'en'
+    trans_to = params[:tolang] ? params[:tolang] : 'en'
+    trans_text = params[:phrase].to_s
+    
+    query = "/babelfish/tr"
+    lang_match = langs.join("|")
+    unless(trans_from =~ /^(#{lang_match})$/ && trans_to =~ /^(#{lang_match})$/)
+      m.reply "invalid language: valid languagess are: #{langs.join(' ')}"
+      return
+    end
+
+    data_text = URI.escape trans_text
+    trans_pair = "#{trans_from}_#{trans_to}"
+    data = "lp=#{trans_pair}&doit=done&intl=1&tt=urltext&urltext=#{data_text}"
+
+    # check cache for previous lookups
+    if @registry.has_key?("#{trans_pair}/#{data_text}")
+      m.reply @registry["#{trans_pair}/#{data_text}"]
+      return
+    end
+
+    http = @bot.httputil.get_proxy(URI.parse("http://babelfish.altavista.com"))
+
+    http.start {|http|
+      resp = http.post(query, data, {"content-type",
+      "application/x-www-form-urlencoded"})
+  
+  if (resp.code == "200")
+    resp.body.each_line do |l|
+      if(l =~ /^\s+<td bgcolor=white class=s><div style=padding:10px;>(.*)<\/div>/)
+        answer = $1
+        # cache the answer
+        if(answer.length > 0)
+          @registry["#{trans_pair}/#{data_text}"] = answer
+        end
+        m.reply answer
+        return
+      end
+    end
+    m.reply "couldn't parse babelfish response html :("
+  else
+    m.reply "couldn't talk to babelfish :("
+  end
+  }
+  end
+end
+plugin = BabelPlugin.new
+plugin.map 'translate to :tolang *phrase'
+plugin.map 'translate from :fromlang *phrase'
+plugin.map 'translate :fromlang :tolang *phrase'
+
diff --git a/data/rbot/plugins/fortune.rb b/data/rbot/plugins/fortune.rb
new file mode 100644 (file)
index 0000000..184b6b1
--- /dev/null
@@ -0,0 +1,22 @@
+class FortunePlugin < Plugin
+  def help(plugin, topic="")
+    "fortune [<module>] => get a (short) fortune, optionally specifying fortune db"
+  end
+  def fortune(m, params)
+    db = params[:db]
+    fortune = nil
+    ["/usr/games/fortune", "/usr/bin/fortune", "/usr/local/bin/fortune"].each {|f|
+      if FileTest.executable? f
+        fortune = f
+        break
+      end
+    }
+    m.reply "fortune binary not found" unless fortune
+    ret = Utils.safe_exec(fortune, "-n", "255", "-s", db)
+    m.reply ret.gsub(/\t/, "  ").split(/\n/).join(" ")
+    return
+  end
+end
+plugin = FortunePlugin.new
+plugin.map 'fortune :db', :defaults => {:db => 'fortunes'},
+                          :requirements => {:db => /^[^-][\w-]+$/}
diff --git a/data/rbot/plugins/freshmeat.rb b/data/rbot/plugins/freshmeat.rb
new file mode 100644 (file)
index 0000000..20fa724
--- /dev/null
@@ -0,0 +1,98 @@
+require 'rexml/document'
+require 'uri/common'
+
+class FreshmeatPlugin < Plugin
+  include REXML
+  def help(plugin, topic="")
+    "freshmeat search [<max>=4] <string> => search freshmeat for <string>, freshmeat [<max>=4] => return up to <max> freshmeat headlines"
+  end
+  
+  def search_freshmeat(m, params)
+    max = params[:limit].to_i
+    search = params[:search].to_s
+    max = 8 if max > 8
+    begin
+      xml = @bot.httputil.get(URI.parse("http://freshmeat.net/search-xml/?orderby=locate_projectname_full_DESC&q=#{URI.escape(search)}"))
+    rescue URI::InvalidURIError, URI::BadURIError => e
+      m.reply "illegal search string #{search}"
+      return
+    end
+    unless xml
+      m.reply "search for #{search} failed"
+      return
+    end
+    doc = Document.new xml
+    unless doc
+      m.reply "search for #{search} failed"
+      return
+    end
+    matches = Array.new
+    max_width = 250
+    title_width = 0
+    url_width = 0
+    done = 0
+    doc.elements.each("*/match") {|e|
+      name = e.elements["projectname_short"].text
+      url = "http://freshmeat.net/projects/#{name}/"
+      desc = e.elements["desc_short"].text
+      title = e.elements["projectname_full"].text
+      #title_width = title.length if title.length > title_width
+      url_width = url.length if url.length > url_width
+      matches << [title, url, desc]
+      done += 1
+      break if done >= max
+    }
+    if matches.length == 0
+      m.reply "not found: #{search}"
+    end
+    matches.each {|mat|
+      title = mat[0]
+      url = mat[1]
+      desc = mat[2]
+      desc.gsub!(/(.{#{max_width - 3 - url_width}}).*/, '\1..')
+      reply = sprintf("%s | %s", url.ljust(url_width), desc)
+      m.reply reply
+    }
+  end
+  
+  def freshmeat(m, params)
+    max = params[:limit].to_i
+    max = 8 if max > 8
+    xml = @bot.httputil.get(URI.parse("http://images.feedstermedia.com/feedcache/ostg/freshmeat/fm-releases-global.xml"))
+    unless xml
+      m.reply "freshmeat news parse failed"
+      return
+    end
+    doc = Document.new xml
+    unless doc
+      m.reply "freshmeat news parse failed"
+      return
+    end
+    matches = Array.new
+    max_width = 60
+    title_width = 0
+    done = 0
+    doc.elements.each("*/channel/item") {|e|
+      desc = e.elements["description"].text
+      title = e.elements["title"].text
+      #title.gsub!(/\s+\(.*\)\s*$/, "")
+      title.strip!
+      title_width = title.length if title.length > title_width
+      matches << [title, desc]
+      done += 1
+      break if done >= max
+    }
+    matches.each {|mat|
+      title = mat[0]
+      #desc = mat[1]
+      #desc.gsub!(/(.{#{max_width - 3 - title_width}}).*/, '\1..')
+      #reply = sprintf("%#{title_width}s | %s", title, desc)
+      m.reply title
+    }
+  end
+end
+plugin = FreshmeatPlugin.new
+plugin.map 'freshmeat search :limit *search', :action => 'search_freshmeat',
+            :defaults => {:limit => 4}, :requirements => {:limit => /^\d+$/}
+plugin.map 'freshmeat :limit', :defaults => {:limit => 4}, 
+                               :requirements => {:limit => /^\d+$/}
diff --git a/data/rbot/plugins/google.rb b/data/rbot/plugins/google.rb
new file mode 100644 (file)
index 0000000..cd96f23
--- /dev/null
@@ -0,0 +1,51 @@
+require 'net/http'
+require 'uri/common'
+
+Net::HTTP.version_1_2
+
+class GooglePlugin < Plugin
+  def help(plugin, topic="")
+    "search <string> => search google for <string>"
+  end
+  def privmsg(m)
+    unless(m.params && m.params.length > 0)
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+    searchfor = URI.escape m.params
+
+    query = "/search?q=#{searchfor}&btnI=I%27m%20feeling%20lucky"
+    result = "not found!"
+
+    proxy_host = nil
+    proxy_port = nil
+
+    if(ENV['http_proxy'])
+      if(ENV['http_proxy'] =~ /^http:\/\/(.+):(\d+)$/)
+        proxy_host = $1
+        proxy_port = $2
+      end
+    end
+
+    http = @bot.httputil.get_proxy(URI.parse("http://www.google.com"))
+
+    begin
+      http.start {|http|
+        resp = http.get(query)
+        if resp.code == "302"
+          result = resp['location']
+        end
+      }
+    rescue => e
+      p e
+      if e.response && e.response['location']
+        result = e.response['location']
+      else
+        result = "error!"
+      end
+    end
+    m.reply "#{m.params}: #{result}"
+  end
+end
+plugin = GooglePlugin.new
+plugin.register("search")
diff --git a/data/rbot/plugins/host.rb b/data/rbot/plugins/host.rb
new file mode 100644 (file)
index 0000000..ef8dc8b
--- /dev/null
@@ -0,0 +1,14 @@
+class HostPlugin < Plugin
+  def help(plugin, topic="")
+    "host <domain> => query nameserver about domain names and zones for <domain>"
+  end
+  def privmsg(m)
+    unless(m.params =~ /^(\w|-|\.)+$/)
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+    m.reply Utils.safe_exec("host", m.params)
+  end
+end
+plugin = HostPlugin.new
+plugin.register("host")
diff --git a/data/rbot/plugins/httpd.rb b/data/rbot/plugins/httpd.rb
new file mode 100644 (file)
index 0000000..92fe3a8
--- /dev/null
@@ -0,0 +1,35 @@
+require 'webrick'
+
+class HttpPlugin < Plugin
+  include WEBrick
+
+
+  def initialize
+    super
+    @http_server = HTTPServer.new(
+      :Port => 5555
+    )
+    @http_server.mount_proc("/") { |req, resp|
+      resp['content-type'] = 'text/html'
+      resp.body = "<html><head><title>rbot httpd plugin</title></head><body>"
+      resp.body += "#{@bot.status} <br />"
+      resp.body += "hello from rbot."
+      resp.body += "</body>"
+      raise HTTPStatus::OK
+    }
+    Thread.new {
+      @http_server.start
+    }
+  end
+  def cleanup
+    @http_server.shutdown
+  end
+  def help(plugin, topic="")
+    "no help yet"
+  end
+  def privmsg(m)
+  end
+end
+
+plugin = HttpPlugin.new
+plugin.register("http")
diff --git a/data/rbot/plugins/insult.rb b/data/rbot/plugins/insult.rb
new file mode 100644 (file)
index 0000000..5f0384e
--- /dev/null
@@ -0,0 +1,258 @@
+class InsultPlugin < Plugin
+
+## insults courtesy of http://insulthost.colorado.edu/
+
+##
+# Adjectives
+##
+@@adj = [
+"acidic",
+"antique",
+"contemptible",
+"culturally-unsound",
+"despicable",
+"evil",
+"fermented",
+"festering",
+"foul",
+"fulminating",
+"humid",
+"impure",
+"inept",
+"inferior",
+"industrial",
+"left-over",
+"low-quality",
+"malodorous",
+"off-color",
+"penguin-molesting",
+"petrified",
+"pointy-nosed",
+"salty",
+"sausage-snorfling",
+"tastless",
+"tempestuous",
+"tepid",
+"tofu-nibbling",
+"unintelligent",
+"unoriginal",
+"uninspiring",
+"weasel-smelling",
+"wretched",
+"spam-sucking",
+"egg-sucking",
+"decayed",
+"halfbaked",
+"infected",
+"squishy",
+"porous",
+"pickled",
+"coughed-up",
+"thick",
+"vapid",
+"hacked-up",
+"unmuzzled",
+"bawdy",
+"vain",
+"lumpish",
+"churlish",
+"fobbing",
+"rank",
+"craven",
+"puking",
+"jarring",
+"fly-bitten",
+"pox-marked",
+"fen-sucked",
+"spongy",
+"droning",
+"gleeking",
+"warped",
+"currish",
+"milk-livered",
+"surly",
+"mammering",
+"ill-borne",
+"beef-witted",
+"tickle-brained",
+"half-faced",
+"headless",
+"wayward",
+"rump-fed",
+"onion-eyed",
+"beslubbering",
+"villainous",
+"lewd-minded",
+"cockered",
+"full-gorged",
+"rude-snouted",
+"crook-pated",
+"pribbling",
+"dread-bolted",
+"fool-born",
+"puny",
+"fawning",
+"sheep-biting",
+"dankish",
+"goatish",
+"weather-bitten",
+"knotty-pated",
+"malt-wormy",
+"saucyspleened",
+"motley-mind",
+"it-fowling",
+"vassal-willed",
+"loggerheaded",
+"clapper-clawed",
+"frothy",
+"ruttish",
+"clouted",
+"common-kissing",
+"pignutted",
+"folly-fallen",
+"plume-plucked",
+"flap-mouthed",
+"swag-bellied",
+"dizzy-eyed",
+"gorbellied",
+"weedy",
+"reeky",
+"measled",
+"spur-galled",
+"mangled",
+"impertinent",
+"bootless",
+"toad-spotted",
+"hasty-witted",
+"horn-beat",
+"yeasty",
+"boil-brained",
+"tottering",
+"hedge-born",
+"hugger-muggered",
+"elf-skinned",
+]
+
+##
+# Amounts 
+##
+@@amt = [
+"accumulation",
+"bucket",
+"coagulation",
+"enema-bucketful",
+"gob",
+"half-mouthful",
+"heap",
+"mass",
+"mound",
+"petrification",
+"pile",
+"puddle",
+"stack",
+"thimbleful",
+"tongueful",
+"ooze",
+"quart",
+"bag",
+"plate",
+"ass-full",
+"assload",
+]
+
+##
+# Objects
+##
+@@noun = [
+"bat toenails",
+"bug spit",
+"cat hair",
+"chicken piss",
+"dog vomit",
+"dung",
+"fat-woman's stomach-bile",
+"fish heads",
+"guano",
+"gunk",
+"pond scum",
+"rat retch",
+"red dye number-9",
+"Sun IPC manuals",
+"waffle-house grits",
+"yoo-hoo",
+"dog balls",
+"seagull puke",
+"cat bladders",
+"pus",
+"urine samples",
+"squirrel guts",
+"snake assholes",
+"snake bait",
+"buzzard gizzards",
+"cat-hair-balls",
+"rat-farts",
+"pods",
+"armadillo snouts",
+"entrails",
+"snake snot",
+"eel ooze",
+"slurpee-backwash",
+"toxic waste",
+"Stimpy-drool",
+"poopy",
+"poop",
+"craptacular carpet droppings",
+"jizzum",
+"cold sores",
+"anal warts",
+]
+  
+  def help(plugin, topic="")
+    if(plugin == "insult")
+      return "insult me|<person> => insult you or <person>"
+    elsif(plugin == "msginsult")
+      return "msginsult <nick> => insult <nick> via /msg"
+    else
+      return "insult module topics: msginsult, insult"
+    end
+  end
+  def privmsg(m)
+    suffix=""
+    unless(m.params)
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+    msgto = m.channel
+    if(m.plugin =~ /^msginsult$/)
+      prefix = "you are "
+      if (m.params =~ /^#/)
+        prefix += "all "
+      end
+      msgto = m.params
+      suffix = " (from #{m.sourcenick})"
+    elsif(m.params =~ /^me$/)
+      prefix = "you are "
+    else
+      prefix = "#{m.params} is "
+    end
+    insult = generate_insult
+    @bot.say msgto, prefix + insult + suffix
+  end
+  def generate_insult
+    adj = @@adj[rand(@@adj.length)]
+    adj2 = ""
+    loop do
+      adj2 = @@adj[rand(@@adj.length)]
+      break if adj2 != adj
+    end
+    amt = @@amt[rand(@@amt.length)]
+    noun = @@noun[rand(@@noun.length)]
+    start = "a "
+    start = "an " if ['a','e','i','o','u'].include?(adj[0].chr)
+    "#{start}#{adj} #{amt} of #{adj2} #{noun}"
+  end
+end
+plugin = InsultPlugin.new
+plugin.register("insult")
+plugin.register("msginsult")
+
diff --git a/data/rbot/plugins/karma.rb b/data/rbot/plugins/karma.rb
new file mode 100644 (file)
index 0000000..148427a
--- /dev/null
@@ -0,0 +1,85 @@
+class KarmaPlugin < Plugin
+  def initialize
+    super
+
+    # this plugin only wants to store ints!
+    class << @registry
+      def store(val)
+        val.to_i
+      end
+      def restore(val)
+        val.to_i
+      end
+    end
+    @registry.set_default(0)
+
+    # import if old file format found
+    if(File.exist?("#{@bot.botclass}/karma.rbot"))
+      puts "importing old karma data"
+      IO.foreach("#{@bot.botclass}/karma.rbot") do |line|
+        if(line =~ /^(\S+)<=>([\d-]+)$/)
+          item = $1
+          karma = $2.to_i
+          @registry[item] = karma
+        end
+      end
+      File.delete("#{@bot.botclass}/karma.rbot")
+    end
+
+  end
+
+  def stats(m, params)
+    if (@registry.length)
+      max = @registry.values.max
+      min = @registry.values.min
+      best = @registry.to_hash.index(max)
+      worst = @registry.to_hash.index(min)
+      m.reply "#{@registry.length} items. Best: #{best} (#{max}); Worst: #{worst} (#{min})"
+    end
+  end
+
+  def karma(m, params)
+    thing = params[:key]
+    thing = m.sourcenick unless thing
+    thing = thing.to_s
+    karma = @registry[thing]
+    if(karma != 0)
+      m.reply "karma for #{thing}: #{@registry[thing]}"
+    else
+      m.reply "#{thing} has neutral karma"
+    end
+  end
+  
+  
+  def help(plugin, topic="")
+    "karma module: <thing>++/<thing>-- => increase/decrease karma for <thing>, karma for <thing>? => show karma for <thing>, karmastats => show stats. Karma is a community rating system - only in-channel messages can affect karma and you cannot adjust your own."
+  end
+  def listen(m)
+    return unless m.kind_of?(PrivMessage) && m.public?
+    # in channel message, the kind we are interested in
+    if(m.message =~ /(\+\+|--)/)
+      string = m.message.sub(/\W(--|\+\+)(\(.*?\)|[^(++)(\-\-)\s]+)/, "\2\1")
+      seen = Hash.new
+      while(string.sub!(/(\(.*?\)|[^(++)(\-\-)\s]+)(\+\+|--)/, ""))
+        key = $1
+        change = $2
+        next if seen[key]
+        seen[key] = true
+
+        key.sub!(/^\((.*)\)$/, "\1")
+        key.gsub!(/\s+/, " ")
+        next unless(key.length > 0)
+        next if(key == m.sourcenick)
+        if(change == "++")
+          @registry[key] += 1
+        elsif(change == "--")
+          @registry[key] -= 1
+        end
+      end
+    end
+  end
+end
+plugin = KarmaPlugin.new
+plugin.map 'karmastats', :action => 'stats'
+plugin.map 'karma :key', :defaults => {:key => false}
+plugin.map 'karma for :key'
diff --git a/data/rbot/plugins/lart.rb b/data/rbot/plugins/lart.rb
new file mode 100644 (file)
index 0000000..de76719
--- /dev/null
@@ -0,0 +1,181 @@
+#  Author:     Michael Brailsford  <brailsmt@yahoo.com>
+#              aka brailsmt
+#  Purpose:       Provide for humorous larts and praises
+#  Copyright:  2002 Michael Brailsford.  All rights reserved.
+#  License:    This plugin is licensed under the BSD license.  The terms of
+#              which follow.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions
+#  are met:
+#
+#  1. Redistributions of source code must retain the above copyright notice,
+#     this list of conditions and the following disclaimer.
+#
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+
+class LartPlugin < Plugin
+
+       # Keep a 1:1 relation between commands and handlers
+       @@handlers = {
+               "lart" => "handle_lart",
+               "praise" => "handle_praise",
+               "addlart" => "handle_addlart",
+               "rmlart" => "handle_rmlart",
+               "addpraise" => "handle_addpraise",
+               "rmpraise" => "handle_rmpraise"
+       }
+
+  def name
+    "lart"
+  end
+
+       #{{{
+       def initialize
+               super
+               @larts = Array.new
+               @praises = Array.new
+               #read in the lart and praise files
+               if File.exists? "#{@bot.botclass}/lart/larts"
+                       IO.foreach("#{@bot.botclass}/lart/larts") { |line|
+                               @larts << line.chomp
+                       }
+               end
+               if File.exists? "#{@bot.botclass}/lart/praises"
+                       IO.foreach("#{@bot.botclass}/lart/praises") { |line|
+                               @praises << line.chomp
+                       }
+               end
+       end
+       #}}}
+       #{{{
+       def cleanup
+       end
+       #}}}
+       #{{{
+       def save
+               Dir.mkdir("#{@bot.botclass}/lart") if not FileTest.directory? "#{@bot.botclass}/lart"
+               File.open("#{@bot.botclass}/lart/larts", "w") { |file|
+                       file.puts @larts
+               }
+               File.open("#{@bot.botclass}/lart/praises", "w") { |file|
+                       file.puts @praises
+               }
+       end
+       #}}}
+       #{{{
+       def privmsg(m)
+               if not m.params
+                       m.reply "What a crazy fool!  Did you mean |help stats?"
+                       return
+               end
+
+               meth = self.method(@@handlers[m.plugin])
+               meth.call(m) if(@bot.auth.allow?(m.plugin, m.source, m.replyto))
+       end
+       #}}}
+       #{{{
+       def help(plugin, topic="")
+               "Lart:  The lart plugin allows you to punish/praise someone in the channel.  You can also add new punishments and new praises as well as delete them.  For the curious, LART is an acronym for Luser Attitude Readjustment Tool.\nUsage:  punish/lart <nick> <reason>  --  punishes <nick> for <reason>.  The reason is optional.\n        praise <nick> <reason>  --  praises <nick> for <reason>.  The reason is optional.\n        mod[lart|punish|praise] [add|remove]  --  Add or remove a lart or praise."
+       end
+       #}}}
+       # The following are command handlers    {{{
+       #{{{
+       def handle_lart(m)
+               for_idx = m.params =~ /\s+\bfor\b/
+               if for_idx
+                       nick = m.params[0, for_idx]
+               else
+                       nick = m.params
+               end
+               lart = @larts[get_msg_idx(@larts.length)]
+               if lart == nil
+                       m.reply "I dunno any larts"
+                       return
+               end
+               if nick == @bot.nick
+                       lart = replace_who lart, m.sourcenick
+                       lart << " for trying to make me lart myself"
+               else
+                       lart = replace_who lart, nick
+                       lart << m.params[for_idx, m.params.length] if for_idx
+               end
+
+               @bot.action m.replyto, lart
+       end
+       #}}}
+       #{{{
+       def handle_praise(m)
+               for_idx = m.params =~ /\s+\bfor\b/
+               if for_idx
+                       nick = m.params[0, for_idx]
+               else
+                       nick = m.params
+               end
+               praise = @praises[get_msg_idx(@praises.length)]
+               if not praise
+                       m.reply "I dunno any praises"
+                       return
+               end
+
+               if nick == m.sourcenick
+                       praise = @larts[get_msg_idx(@larts.length)]
+                       praise = replace_who praise, nick
+               else
+                       praise = replace_who praise, nick
+                       praise << m.params.gsub(/#{nick}/, "")
+               end
+
+               @bot.action m.replyto, praise
+       end
+       #}}}
+       #{{{
+       def handle_addlart(m)
+               @larts << m.params
+               m.okay
+       end
+       #}}}
+       #{{{
+       def handle_rmlart(m)
+               @larts.delete m.params
+               m.okay
+       end
+       #}}}
+       #{{{
+       def handle_addpraise(m)
+               @praises << m.params
+               m.okay
+       end
+       #}}}
+       #{{{
+       def handle_rmpraise(m)
+               @praises.delete m.params
+               m.okay
+       end
+       #}}}
+       #}}}
+
+       #  The following are utils for larts/praises    {{{
+       #{{{
+       def replace_who(msg, nick)
+               msg.gsub(/<who>/i, "#{nick}")
+       end
+       #}}}
+       #{{{
+       def get_msg_idx(max)
+               idx = rand(max)
+       end
+       #}}}
+       #}}}
+end
+plugin = LartPlugin.new
+plugin.register("lart")
+plugin.register("praise")
+
+plugin.register("addlart")
+plugin.register("addpraise")
+
+plugin.register("rmlart")
+plugin.register("rmpraise")
diff --git a/data/rbot/plugins/math.rb b/data/rbot/plugins/math.rb
new file mode 100644 (file)
index 0000000..4a20738
--- /dev/null
@@ -0,0 +1,122 @@
+class MathPlugin < Plugin
+  @@digits = {
+     "first" => "1",
+     "second" => "2",
+     "third" => "3",
+     "fourth" => "4",
+     "fifth" => "5",
+     "sixth" => "6",
+     "seventh" => "7",
+     "eighth" => "8",
+     "ninth" => "9",
+     "tenth" => "10",
+     "one" => "1",
+     "two" => "2",
+     "three" => "3",
+     "four" => "4",
+     "five" => "5",
+     "six" => "6",
+     "seven" => "7",
+     "eight" => "8",
+     "nine" => "9",
+     "ten" => "10"
+  };
+
+  def help(plugin, topic="")
+    "math <expression>, evaluate mathematical expression"
+  end
+  def privmsg(m)
+    unless(m.params)
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+
+    expr = m.params.dup
+    @@digits.each {|k,v|
+      expr.gsub!(/\b#{k}\b/, v)
+    }
+
+    while expr =~ /(exp ([\w\d]+))/
+      exp = $1
+      val = Math.exp($2).to_s
+      expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
+    end
+    
+    while expr =~ /^\s*(dec2hex\s*(\d+))\s*\?*/
+      exp = $1
+      val = sprintf("%x", $2)
+      expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
+    end
+
+    expr.gsub(/\be\b/, Math.exp(1).to_s)
+
+    while expr =~ /(log\s*((\d+\.?\d*)|\d*\.?\d+))\s*/
+      exp = $1
+      res = $2
+      
+      if res == 0
+        val = "Infinity"
+      else
+        val = Math.log(res).to_s
+      end
+      
+      expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
+    end
+
+    while expr =~ /(bin2dec ([01]+))/
+      exp = $1
+      val = join('', unpack('B*', pack('N', $2)))
+      val.gsub!(/^0+/, "")
+      expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
+    end
+
+    expr.gsub!(/ to the power of /, " ** ")
+    expr.gsub!(/ to the /, " ** ")
+    expr.gsub!(/\btimes\b/, "*")
+    expr.gsub!(/\bdiv(ided by)? /, "/ ")
+    expr.gsub!(/\bover /, "/ ")
+    expr.gsub!(/\bsquared/, "**2 ")
+    expr.gsub!(/\bcubed/, "**3 ")
+    expr.gsub!(/\bto\s+(\d+)(r?st|nd|rd|th)?( power)?/, "**\1 ")
+    expr.gsub!(/\bpercent of/, "*0.01*")
+    expr.gsub!(/\bpercent/, "*0.01")
+    expr.gsub!(/\% of\b/, "*0.01*")
+    expr.gsub!(/\%/, "*0.01")
+    expr.gsub!(/\bsquare root of (\d+)/, "\1 ** 0.5 ")
+    expr.gsub!(/\bcubed? root of (\d+)/, "\1 **(1.0/3.0) ")
+    expr.gsub!(/ of /, " * ")
+    expr.gsub!(/(bit(-| )?)?xor(\'?e?d( with))?/, "^")
+    expr.gsub!(/(bit(-| )?)?or(\'?e?d( with))?/, "|")
+    expr.gsub!(/bit(-| )?and(\'?e?d( with))?/, "& ")
+    expr.gsub!(/(plus|and)/, "+")
+
+    if (expr =~ /^\s*[-\d*+\s()\/^\.\|\&\*\!]+\s*$/ &&
+       expr !~ /^\s*\(?\d+\.?\d*\)?\s*$/ &&
+       expr !~ /^\s*$/ &&
+       expr !~ /^\s*[( )]+\s*$/)
+
+       begin
+         debug "evaluating expression \"#{expr}\""
+         answer = eval(expr)
+         if answer =~ /^[-+\de\.]+$/
+           answer = sprintf("%1.12f", answer)
+           answer.gsub!(/\.?0+$/, "")
+           answer.gsub!(/(\.\d+)000\d+/, '\1')
+           if (answer.length > 30)
+             answer = "a number with >30 digits..."
+           end
+         end
+         m.reply answer
+       rescue Exception => e
+         puts "couldn't evaluate expression \"#{m.params}\": #{e}"
+         m.reply "illegal expression \"#{m.params}\""
+         return
+       end
+    else
+      m.reply "illegal expression \"#{m.params}\""
+      return
+    end
+  end
+end
+plugin = MathPlugin.new
+plugin.register("math")
diff --git a/data/rbot/plugins/nickserv.rb b/data/rbot/plugins/nickserv.rb
new file mode 100644 (file)
index 0000000..1ef2baf
--- /dev/null
@@ -0,0 +1,99 @@
+# automatically lookup nicks in @registry and identify when asked
+
+class NickServPlugin < Plugin
+  
+  def help(plugin, topic="")
+    case topic
+    when ""
+      return "nickserv plugin: handles nickserv protected IRC nicks. topics password, register, identify, listnicks"
+    when "password"
+      return "nickserv password <nick> <passwd>: remember the password for nick <nick> and use it to identify in future"
+    when "register"
+      return "nickserv register [<password> [<email>]]: register the current nick, choosing a random password unless <password> is supplied - current nick must not already be registered for this to work. Also specify email if required by your services"
+    when "identify"
+      return "nickserv identify: identify with nickserv - shouldn't be needed - bot should identify with nickserv immediately on request - however this could be useful after splits or service disruptions, or when you just set the password for the current nick"
+    when "listnicks"
+      return "nickserv listnicks: lists nicknames and associated password the bot knows about - you will need config level auth access to do this one and it will reply by privmsg only"
+    end
+  end
+  
+  def initialize
+    super
+    # this plugin only wants to store strings!
+    class << @registry
+      def store(val)
+        val
+      end
+      def restore(val)
+        val
+      end
+    end
+  end
+  
+  def privmsg(m)
+    return unless m.params
+    
+    case m.params
+    when (/^password\s*(\S*)\s*(.*)$/)
+      nick = $1
+      passwd = $2
+      @registry[nick] = passwd
+      m.okay
+    when (/^register$/)
+      passwd = genpasswd
+      @bot.sendmsg "PRIVMSG", "NickServ", "REGISTER " + passwd
+      @registry[@bot.nick] = passwd
+      m.okay
+    when (/^register\s*(\S*)\s*(.*)$/)
+      passwd = $1
+      email = $2
+      @bot.sendmsg "PRIVMSG", "NickServ", "REGISTER " + passwd + " " + email
+      @registry[@bot.nick] = passwd
+      m.okay
+    when (/^register\s*(.*)\s*$/)
+      passwd = $1
+      @bot.sendmsg "PRIVMSG", "NickServ", "REGISTER " + passwd
+      @registry[@bot.nick] = passwd
+      m.okay
+    when (/^listnicks$/)
+      if @bot.auth.allow?("config", m.source, m.replyto)
+        if @registry.length > 0
+          @registry.each {|k,v|
+            @bot.say m.sourcenick, "#{k} => #{v}"
+          }
+        else
+          m.reply "none known"
+        end
+      end
+    when (/^identify$/)
+      if @registry.has_key?(@bot.nick)
+        @bot.sendmsg "PRIVMSG", "NickServ", "IDENTIFY " + @registry[@bot.nick]
+        m.okay
+      else
+        m.reply "I dunno the nickserv password for the nickname #{@bot.nick} :("
+      end
+    end
+  end
+  
+  def listen(m)
+    return unless(m.kind_of? NoticeMessage)
+
+    if (m.sourcenick == "NickServ" && m.message =~ /This nickname is owned by someone else/)
+      puts "nickserv asked us to identify for nick #{@bot.nick}"
+      if @registry.has_key?(@bot.nick)
+        @bot.sendmsg "PRIVMSG", "NickServ", "IDENTIFY " + @registry[@bot.nick]
+      end
+    end
+  end
+
+  def genpasswd
+    # generate a random password
+    passwd = ""
+    8.times do
+      passwd += (rand(26) + (rand(2) == 0 ? 65 : 97) ).chr
+    end
+    return passwd
+  end
+end
+plugin = NickServPlugin.new
+plugin.register("nickserv")
diff --git a/data/rbot/plugins/nslookup.rb b/data/rbot/plugins/nslookup.rb
new file mode 100644 (file)
index 0000000..92da1ba
--- /dev/null
@@ -0,0 +1,56 @@
+class DnsPlugin < Plugin
+  begin
+    require 'resolv-replace'
+    def gethostname(address)
+      Resolv.getname(address)
+    end
+    def getaddresses(name)
+      Resolv.getaddresses(name)
+    end
+  rescue LoadError
+    def gethostname(address)
+      Socket.gethostbyname(address).first
+    end
+    def getaddresses(name)
+      a = Socket.gethostbyname(name)
+      list = Socket.getaddrinfo(a[0], 'http')
+      addresses = Array.new
+      list.each {|line|
+       addresses << line[3]
+      }
+      addresses
+    end
+  end
+
+  def help(plugin, topic="")
+    "nslookup|dns <hostname|ip> => show local resolution results for hostname or ip address"
+  end
+  def privmsg(m)
+    unless(m.params)
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+    Thread.new do
+      if(m.params =~ /^\d+\.\d+\.\d+\.\d+$/)
+       begin
+         a = gethostname(m.params)
+         m.reply m.params + ": " + a if a
+       rescue StandardError => err
+         m.reply "#{m.params}: not found"
+       end
+      elsif(m.params =~ /^\S+$/)
+       begin
+         a = getaddresses(m.params)
+         m.reply m.params + ": " + a.join(", ")
+       rescue StandardError => err
+         m.reply "#{m.params}: not found"
+       end
+      else
+       m.reply "incorrect usage: " + help(m.plugin)
+      end
+    end
+  end
+end
+plugin = DnsPlugin.new
+plugin.register("nslookup")
+plugin.register("dns")
diff --git a/data/rbot/plugins/opmeh.rb b/data/rbot/plugins/opmeh.rb
new file mode 100644 (file)
index 0000000..2776de6
--- /dev/null
@@ -0,0 +1,19 @@
+class OpMehPlugin < Plugin\r
+\r
+  def help(plugin, topic="")\r
+    return "opmeh <channel> => grant user ops in <channel>"\r
+  end\r
+\r
+  def privmsg(m)\r
+    if(m.params)\r
+      channel = m.params\r
+    else\r
+      channel = m.channel\r
+    end\r
+    target = m.sourcenick\r
+    @bot.sendq("MODE #{channel} +o #{target}")\r
+    m.okay\r
+  end\r
+end\r
+plugin = OpMehPlugin.new\r
+plugin.register("opmeh")\r
diff --git a/data/rbot/plugins/quotes.rb b/data/rbot/plugins/quotes.rb
new file mode 100644 (file)
index 0000000..674a9ed
--- /dev/null
@@ -0,0 +1,321 @@
+Quote = Struct.new("Quote", "num", "date", "source", "quote")
+
+class QuotePlugin < Plugin
+  def initialize
+    super
+    @lists = Hash.new
+    Dir["#{@bot.botclass}/quotes/*"].each {|f|
+      channel = File.basename(f)
+      @lists[channel] = Array.new if(!@lists.has_key?(channel))
+      IO.foreach(f) {|line|
+        if(line =~ /^(\d+) \| ([^|]+) \| (\S+) \| (.*)$/)
+          num = $1.to_i
+          @lists[channel][num] = Quote.new(num, $2, $3, $4)
+        end
+      }
+    }
+  end
+  def save
+    Dir.mkdir("#{@bot.botclass}/quotes") if(!FileTest.directory?("#{@bot.botclass}/quotes"))
+    @lists.each {|channel, quotes|
+      File.open("#{@bot.botclass}/quotes/#{channel}", "w") {|file|
+        quotes.compact.each {|q| 
+          file.puts "#{q.num} | #{q.date} | #{q.source} | #{q.quote}"
+        }
+      }
+    }
+  end
+  def addquote(source, channel, quote)
+    @lists[channel] = Array.new if(!@lists.has_key?(channel))
+    num = @lists[channel].length 
+    @lists[channel][num] = Quote.new(num, Time.new, source, quote)
+    return num
+  end
+  def getquote(source, channel, num=nil)
+    return nil unless(@lists.has_key?(channel))
+    return nil unless(@lists[channel].length > 0)
+    if(num)
+      if(@lists[channel][num])
+        return @lists[channel][num], @lists[channel].length - 1
+      end
+    else
+      # random quote
+      return @lists[channel].compact[rand(@lists[channel].nitems)],
+                                       @lists[channel].length - 1
+    end
+  end
+  def delquote(channel, num)
+    return false unless(@lists.has_key?(channel))
+    return false unless(@lists[channel].length > 0)
+    if(@lists[channel][num])
+      @lists[channel][num] = nil
+      return true
+    end
+    return false
+  end
+  def countquote(source, channel=nil, regexp=nil)
+    unless(channel)
+      total=0
+      @lists.each_value {|l|
+        total += l.compact.length
+      }
+      return total
+    end
+    return 0 unless(@lists.has_key?(channel))
+    return 0 unless(@lists[channel].length > 0)
+    if(regexp)
+      matches = @lists[channel].compact.find_all {|a| a.quote =~ /#{regexp}/i }
+    else
+      matches = @lists[channel].compact
+    end
+    return matches.length
+  end
+  def searchquote(source, channel, regexp)
+    return nil unless(@lists.has_key?(channel))
+    return nil unless(@lists[channel].length > 0)
+    matches = @lists[channel].compact.find_all {|a| a.quote =~ /#{regexp}/i }
+    if(matches.length > 0)
+       return matches[rand(matches.length)], @lists[channel].length - 1
+    else
+      return nil
+    end
+  end
+  def help(plugin, topic="")
+    case topic
+      when "addquote"
+        return "addquote [<channel>] <quote> => Add quote <quote> for channel <channel>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !addquote without addressing if so configured"
+      when "delquote"
+        return "delquote [<channel>] <num> => delete quote from <channel> with number <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !delquote without addressing if so configured"
+      when "getquote"
+        return "getquote [<channel>] [<num>] => get quote from <channel> with number <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Without <num>, a random quote will be returned. Responds to !getquote without addressing if so configured"
+      when "searchquote"
+        return "searchquote [<channel>] <regexp> => search for quote from <channel> that matches <regexp>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !searchquote without addressing if so configured"
+      when "topicquote"
+        return "topicquote [<channel>] [<num>] => set topic to quote from <channel> with number <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Without <num>, a random quote will be set. Responds to !topicquote without addressing if so configured"
+      when "countquote"
+        return "countquote [<channel>] <regexp> => count quotes from <channel> that match <regexp>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !countquote without addressing if so configured"
+      when "whoquote"
+        return "whoquote [<channel>] <num> => show who added quote <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately"
+      when "whenquote"
+        return "whenquote [<channel>] <num> => show when quote <num> was added. You only need to supply <channel> if you are addressing #{@bot.nick} privately"
+      else
+        return "Quote module (Quote storage and retrieval) topics: addquote, getquote, searchquote, topicquote, countquote, whoquote, whenquote"
+    end
+  end
+  def listen(m)
+    return unless(m.kind_of? PrivMessage)
+
+    command = m.message.dup
+    if(m.address? && m.private?)
+      case command
+        when (/^addquote\s+(#\S+)\s+(.*)/)
+          channel = $1
+          quote = $2
+          if(@bot.auth.allow?("addquote", m.source, m.replyto))
+            if(channel =~ /^#/)
+              num = addquote(m.source, channel, quote)
+              m.reply "added the quote (##{num})"
+            end
+          end
+        when (/^getquote\s+(#\S+)$/)
+          channel = $1
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = getquote(m.source, channel)
+            if(quote)
+              m.reply "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^getquote\s+(#\S+)\s+(\d+)$/)
+          channel = $1
+          num = $2.to_i
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = getquote(m.source, channel, num)
+            if(quote)
+              m.reply "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^whoquote\s+(#\S+)\s+(\d+)$/)
+          channel = $1
+          num = $2.to_i
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = getquote(m.source, channel, num)
+            if(quote)
+              m.reply "quote #{quote.num} added by #{quote.source}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^whenquote\s+(#\S+)\s+(\d+)$/)
+          channel = $1
+          num = $2.to_i
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = getquote(m.source, channel, num)
+            if(quote)
+              m.reply "quote #{quote.num} added on #{quote.date}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^topicquote\s+(#\S+)$/)
+          channel = $1
+          if(@bot.auth.allow?("topicquote", m.source, m.replyto))
+            quote, total = getquote(m.source, channel)
+            if(quote)
+              @bot.topic channel, "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^topicquote\s+(#\S+)\s+(\d+)$/)
+          channel = $1
+          num = $2.to_i
+          if(@bot.auth.allow?("topicquote", m.source, m.replyto))
+            quote, total = getquote(m.source, channel, num)
+            if(quote)
+              @bot.topic channel, "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^delquote\s+(#\S+)\s+(\d+)$/)
+          channel = $1
+          num = $2.to_i
+          if(@bot.auth.allow?("delquote", m.source, m.replyto))
+            if(delquote(channel, num))
+              m.okay
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^searchquote\s+(#\S+)\s+(.*)$/)
+          channel = $1
+          reg = $2
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = searchquote(m.source, channel, reg)
+            if(quote)
+              m.reply "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^countquote$/)
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            total = countquote(m.source)
+            m.reply "#{total} quotes"
+          end
+        when (/^countquote\s+(#\S+)\s*(.*)$/)
+          channel = $1
+          reg = $2
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            total = countquote(m.source, channel, reg)
+            if(reg.length > 0)
+              m.reply "#{total} quotes match: #{reg}"
+            else
+              m.reply "#{total} quotes"
+            end
+          end
+      end
+    elsif (m.address? || (@bot.config["QUOTE_LISTEN"] && command.gsub!(/^!/, "")))
+      case command
+        when (/^addquote\s+(.+)/)
+          if(@bot.auth.allow?("addquote", m.source, m.replyto))
+            num = addquote(m.source, m.target, $1)
+            m.reply "added the quote (##{num})"
+          end
+        when (/^getquote$/)
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = getquote(m.source, m.target)
+            if(quote)
+              m.reply "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "no quotes found!"
+            end
+          end
+        when (/^getquote\s+(\d+)$/)
+          num = $1.to_i
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = getquote(m.source, m.target, num)
+            if(quote)
+              m.reply "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^whenquote\s+(\d+)$/)
+          num = $1.to_i
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = getquote(m.source, m.target, num)
+            if(quote)
+              m.reply "quote #{quote.num} added on #{quote.date}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^whoquote\s+(\d+)$/)
+          num = $1.to_i
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = getquote(m.source, m.target, num)
+            if(quote)
+              m.reply "quote #{quote.num} added by #{quote.source}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^topicquote$/)
+          if(@bot.auth.allow?("topicquote", m.source, m.replyto))
+            quote, total = getquote(m.source, m.target)
+            if(quote)
+              @bot.topic m.target, "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "no quotes found!"
+            end
+          end
+        when (/^topicquote\s+(\d+)$/)
+          num = $1.to_i
+          if(@bot.auth.allow?("topicquote", m.source, m.replyto))
+            quote, total = getquote(m.source, m.target, num)
+            if(quote)
+              @bot.topic m.target, "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^delquote\s+(\d+)$/)
+          num = $1.to_i
+          if(@bot.auth.allow?("delquote", m.source, m.replyto))
+            if(delquote(m.target, num))
+              m.okay
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^searchquote\s+(.*)$/)
+          reg = $1
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            quote, total = searchquote(m.source, m.target, reg)
+            if(quote)
+              m.reply "[#{quote.num}] #{quote.quote}"
+            else
+              m.reply "quote not found!"
+            end
+          end
+        when (/^countquote(?:\s+(.*))?$/)
+          reg = $1
+          if(@bot.auth.allow?("getquote", m.source, m.replyto))
+            total = countquote(m.source, m.target, reg)
+            if(reg && reg.length > 0)
+              m.reply "#{total} quotes match: #{reg}"
+            else
+              m.reply "#{total} quotes"
+            end
+          end
+      end
+    end
+  end
+end
+plugin = QuotePlugin.new
+plugin.register("quotes")
diff --git a/data/rbot/plugins/remind.rb b/data/rbot/plugins/remind.rb
new file mode 100644 (file)
index 0000000..5ad980a
--- /dev/null
@@ -0,0 +1,154 @@
+require 'rbot/utils'
+
+class RemindPlugin < Plugin
+  def initialize
+    super
+    @reminders = Hash.new
+  end
+  def cleanup
+    @reminders.each_value {|v|
+      v.each_value {|vv|
+        @bot.timer.remove(vv)
+      }
+    }
+    @reminders.clear
+  end
+  def help(plugin, topic="")
+    if(plugin =~ /^remind\+$/)
+      "see remind. remind+ can be used to remind someone else of something, using <nick> instead of 'me'. However this will generally require a higher auth level than remind."
+    else
+      "remind me [about] <message> in <time>, remind me [about] <message> every <time>, remind me [about] <message> at <time>, remind me no more [about] <message>, remind me no more"
+    end
+  end
+  def add_reminder(who, subject, timestr, repeat=false)
+    begin
+      period = Irc::Utils.timestr_offset(timestr)
+    rescue RuntimeError
+      return "couldn't parse that time string (#{timestr}) :("
+    end
+    if(period <= 0)
+      return "that time is in the past! (#{timestr})"
+    end
+    if(period < 30 && repeat)
+      return "repeats of less than 30 seconds are forbidden"
+    end
+    if(!@reminders.has_key?(who))
+      @reminders[who] = Hash.new
+    elsif(@reminders[who].has_key?(subject))
+      del_reminder(who, subject)
+    end
+
+    if(repeat)
+      @reminders[who][subject] = @bot.timer.add(period) {
+        time = Time.now + period
+        tstr = time.strftime("%H:%M:%S")
+        @bot.say who, "repeat reminder (next at #{tstr}): #{subject}"
+      }
+    else
+      @reminders[who][subject] = @bot.timer.add_once(period) {
+        time = Time.now + period
+        tstr = time.strftime("%H:%M:%S")
+        @bot.say who, "reminder (#{tstr}): #{subject}"
+      }
+    end
+    return false
+  end
+  def del_reminder(who, subject=nil)
+    if(subject)
+      if(@reminders.has_key?(who) && @reminders[who].has_key?(subject))
+        @bot.timer.remove(@reminders[who][subject])
+        @reminders[who].delete(subject)
+      end
+    else
+      if(@reminders.has_key?(who))
+        @reminders[who].each_value {|v|
+          @bot.timer.remove(v)
+        }
+        @reminders.delete(who)
+      end
+    end
+  end
+  def privmsg(m)
+
+    if(m.params =~ /^(\S+)\s+(?:about\s+)?(.*)\s+in\s+(.*)$/)
+      who = $1
+      subject = $2
+      period = $3
+      if(who =~ /^me$/)
+        who = m.sourcenick
+      else
+        unless(m.plugin =~ /^remind\+$/)
+          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
+          return
+        end
+      end
+      if(err = add_reminder(who, subject, period))
+        m.reply "incorrect usage: " + err
+        return
+      end
+    elsif(m.params =~ /^(\S+)\s+(?:about\s+)?(.*)\s+every\s+(.*)$/)
+      who = $1
+      subject = $2
+      period = $3
+      if(who =~ /^me$/)
+        who = m.sourcenick
+      else
+        unless(m.plugin =~ /^remind\+$/)
+          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
+          return
+        end
+      end
+      if(err = add_reminder(who, subject, period, true))
+        m.reply "incorrect usage: " + err
+        return
+      end
+    elsif(m.params =~ /^(\S+)\s+(?:about\s+)?(.*)\s+at\s+(.*)$/)
+      who = $1
+      subject = $2
+      time = $3
+      if(who =~ /^me$/)
+        who = m.sourcenick
+      else
+        unless(m.plugin =~ /^remind\+$/)
+          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
+          return
+        end
+      end
+      if(err = add_reminder(who, subject, time))
+        m.reply "incorrect usage: " + err
+        return
+      end
+    elsif(m.params =~ /^(\S+)\s+no\s+more\s+(?:about\s+)?(.*)$/)
+      who = $1
+      subject = $2
+      if(who =~ /^me$/)
+        who = m.sourcenick
+      else
+        unless(m.plugin =~ /^remind\+$/)
+          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
+          return
+        end
+      end
+      del_reminder(who, subject)
+    elsif(m.params =~ /^(\S+)\s+no\s+more$/)
+      who = $1
+      if(who =~ /^me$/)
+        who = m.sourcenick
+      else
+        unless(m.plugin =~ /^remind\+$/)
+          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
+          return
+        end
+      end
+      del_reminder(who)
+    else
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+    m.okay
+  end
+end
+plugin = RemindPlugin.new
+plugin.register("remind")
+plugin.register("remind+")
+
diff --git a/data/rbot/plugins/roshambo.rb b/data/rbot/plugins/roshambo.rb
new file mode 100644 (file)
index 0000000..4f20fb1
--- /dev/null
@@ -0,0 +1,54 @@
+# Play the game of roshambo (rock-paper-scissors)
+# Copyright (C) 2004 Hans Fugal
+# Distributed under the same license as rbot itself
+require 'time'
+class RoshamboPlugin < Plugin
+  def initialize
+    super 
+    @scoreboard = {}
+  end
+  def help(plugin, topic="")
+    "roshambo <rock|paper|scissors> => play roshambo"
+  end
+  def privmsg(m)
+    # simultaneity
+    choice = choose
+
+    # init scoreboard
+    if (not @scoreboard.has_key?(m.sourcenick) or (Time.now - @scoreboard[m.sourcenick]['timestamp']) > 3600)
+      @scoreboard[m.sourcenick] = {'me'=>0,'you'=>0,'timestamp'=>Time.now}
+    end
+    case m.params
+    when 'rock','paper','scissors'
+      s = score(choice,m.params)
+      @scoreboard[m.sourcenick]['timestamp'] = Time.now
+      myscore=@scoreboard[m.sourcenick]['me']
+      yourscore=@scoreboard[m.sourcenick]['you']
+      case s
+      when 1
+       yourscore=@scoreboard[m.sourcenick]['you'] += 1
+       m.reply "#{choice}. You win. Score: me #{myscore} you #{yourscore}"
+      when 0
+       m.reply "#{choice}. We tie. Score: me #{myscore} you #{yourscore}"
+      when -1
+       myscore=@scoreboard[m.sourcenick]['me'] += 1
+       m.reply "#{choice}! I win! Score: me #{myscore} you #{yourscore}"
+      end
+    else
+      m.reply "incorrect usage: " + help(m.plugin)
+    end
+  end
+      
+  def choose
+    ['rock','paper','scissors'][rand(3)]
+  end
+  def score(a,b)
+    beats = {'rock'=>'scissors', 'paper'=>'rock', 'scissors'=>'paper'}
+    return -1 if beats[a] == b
+    return 1 if beats[b] == a
+    return 0
+  end
+end
+plugin = RoshamboPlugin.new
+plugin.register("roshambo")
+plugin.register("rps")
diff --git a/data/rbot/plugins/rot13.rb b/data/rbot/plugins/rot13.rb
new file mode 100644 (file)
index 0000000..1f367db
--- /dev/null
@@ -0,0 +1,14 @@
+class RotPlugin < Plugin
+  def help(plugin, topic="")
+    "rot13 <string> => encode <string> to rot13 or back"
+  end
+  def privmsg(m)
+    unless(m.params && m.params =~ /^.+$/)
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+    m.reply m.params.tr("A-Za-z", "N-ZA-Mn-za-m");
+  end
+end
+plugin = RotPlugin.new
+plugin.register("rot13")
diff --git a/data/rbot/plugins/roulette.rb b/data/rbot/plugins/roulette.rb
new file mode 100644 (file)
index 0000000..c9d585e
--- /dev/null
@@ -0,0 +1,147 @@
+RouletteHistory = Struct.new("RouletteHistory", :games, :shots, :deaths, :misses, :wins)
+
+class RoulettePlugin < Plugin
+  def initialize
+    super
+    reload
+  end
+  def help(plugin, topic="")
+    "roulette => play russian roulette - starts a new game if one isn't already running. One round in a six chambered gun. Take turns to say roulette to the bot, until somebody dies. roulette reload => force the gun to reload, roulette stats => show stats from all games, roulette stats <player> => show stats for <player>, roulette clearstats => clear stats (config level auth required)"
+  end
+  def privmsg(m)
+    if m.params == "reload"
+      @bot.action m.replyto, "reloads"
+      reload
+      # all players win on a reload
+      # (allows you to play 3-shot matches etc)
+      @players.each {|plyr|
+        pdata = @registry[plyr]
+        next if pdata == nil
+        pdata.wins += 1
+        @registry[plyr] = pdata
+      }
+      return
+    elsif m.params == "stats"
+      m.reply stats
+      return
+    elsif m.params =~ /^stats\s+(.+)$/
+      m.reply(playerstats($1))
+      return
+    elsif m.params == "clearstats"
+      if @bot.auth.allow?("config", m.source, m.replyto)
+        @registry.clear
+        m.okay
+      end
+      return
+    elsif m.params
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+    if m.private?
+      m.reply "you gotta play roulette in channel dude"
+      return
+    end
+
+    playerdata = nil
+    if @registry.has_key?(m.sourcenick)
+      playerdata = @registry[m.sourcenick]
+    else
+      playerdata = RouletteHistory.new(0,0,0,0,0)
+    end
+    
+    unless @players.include?(m.sourcenick)
+      @players << m.sourcenick
+      playerdata.games += 1
+    end
+    playerdata.shots += 1
+    
+    shot = @chambers.pop
+    if shot
+      m.reply "#{m.sourcenick}: chamber #{6 - @chambers.length} of 6 => *BANG*"
+      playerdata.deaths += 1
+      @players.each {|plyr|
+        next if plyr == m.sourcenick
+        pdata = @registry[plyr]
+        next if pdata == nil
+        pdata.wins += 1
+        @registry[plyr] = pdata
+      }
+    else
+      m.reply "#{m.sourcenick}: chamber #{6 - @chambers.length} of 6 => +click+"
+      playerdata.misses += 1
+    end
+
+    @registry[m.sourcenick] = playerdata
+    
+    if shot || @chambers.empty?
+      @bot.action m.replyto, "reloads"
+      reload
+    end
+  end
+  def reload
+    @chambers = [false, false, false, false, false, false]
+    @chambers[rand(@chambers.length)] = true
+    @players = Array.new
+  end
+  def playerstats(player)
+    pstats = @registry[player]
+    return "#{player} hasn't played enough games yet" if pstats.nil?
+    return "#{player} has played #{pstats.games} games, won #{pstats.wins} and lost #{pstats.deaths}. #{player} pulled the trigger #{pstats.shots} times and found the chamber empty on #{pstats.misses} occasions."
+  end
+  def stats
+    total_players = 0
+    total_games = 0
+    total_shots = 0
+    
+    died_most = [nil,0]
+    won_most = [nil,0]
+    h_win_percent = [nil,0]
+    l_win_percent = [nil,0]
+    h_luck_percent = [nil,0]
+    l_luck_percent = [nil,0]
+    @registry.each {|k,v|
+      total_players += 1
+      total_games += v.deaths
+      total_shots += v.shots
+      
+      win_rate = v.wins.to_f / v.games * 100
+      if h_win_percent[0].nil? || win_rate > h_win_percent[1] && v.games > 2
+        h_win_percent = [[k], win_rate]
+      elsif win_rate == h_win_percent[1] && v.games > 2
+        h_win_percent[0] << k
+      end
+      if l_win_percent[0].nil? || win_rate < l_win_percent[1] && v.games > 2
+        l_win_percent = [[k], win_rate]
+      elsif win_rate == l_win_percent[1] && v.games > 2
+        l_win_percent[0] << k
+      end
+
+      luck = v.misses.to_f / v.shots * 100
+      if h_luck_percent[0].nil? || luck > h_luck_percent[1] && v.games > 2
+        h_luck_percent = [[k], luck]
+      elsif luck == h_luck_percent[1] && v.games > 2
+        h_luck_percent[0] << k
+      end
+      if l_luck_percent[0].nil? || luck < l_luck_percent[1] && v.games > 2
+        l_luck_percent = [[k], luck]
+      elsif luck == l_luck_percent[1] && v.games > 2
+        l_luck_percent[0] << k
+      end
+
+      if died_most[0].nil? || v.deaths > died_most[1]
+        died_most = [[k], v.deaths]
+      elsif v.deaths == died_most[1]
+        died_most[0] << k
+      end
+      if won_most[0].nil? || v.wins > won_most[1]
+        won_most = [[k], v.wins]
+      elsif v.wins == won_most[1]
+        won_most[0] << k
+      end
+    }
+    return "roulette stats: no games played yet" if total_games < 1
+    return "roulette stats: #{total_games} games completed, #{total_shots} shots fired at #{total_players} players. Luckiest: #{h_luck_percent[0].join(',')} (#{sprintf '%.1f', h_luck_percent[1]}% clicks). Unluckiest: #{l_luck_percent[0].join(',')} (#{sprintf '%.1f', l_luck_percent[1]}% clicks). Highest survival rate: #{h_win_percent[0].join(',')} (#{sprintf '%.1f', h_win_percent[1]}%). Lowest survival rate: #{l_win_percent[0].join(',')} (#{sprintf '%.1f', l_win_percent[1]}%). Most wins: #{won_most[0].join(',')} (#{won_most[1]}). Most deaths: #{died_most[0].join(',')} (#{died_most[1]})."
+  end
+end
+plugin = RoulettePlugin.new
+plugin.register("roulette")
diff --git a/data/rbot/plugins/seen.rb b/data/rbot/plugins/seen.rb
new file mode 100644 (file)
index 0000000..6bd86a7
--- /dev/null
@@ -0,0 +1,89 @@
+Saw = Struct.new("Saw", :nick, :time, :type, :where, :message)
+
+class SeenPlugin < Plugin
+  def help(plugin, topic="")
+    "seen <nick> => have you seen, or when did you last see <nick>"
+  end
+  
+  def privmsg(m)
+    unless(m.params =~ /^(\S)+$/)
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+
+    m.params.gsub!(/\?$/, "")
+
+    if @registry.has_key?(m.params)
+      m.reply seen(@registry[m.params])
+    else
+      m.reply "nope!"
+    end
+  end
+
+  def listen(m)
+    # keep database up to date with who last said what
+    if m.kind_of?(PrivMessage)
+      return if m.private? || m.address?
+      if m.action?
+        @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "ACTION", 
+                                          m.target, m.message.dup)
+      else
+        @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "PUBLIC",
+                                          m.target, m.message.dup)
+      end
+    elsif m.kind_of?(QuitMessage)
+      return if m.address?
+      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "QUIT", 
+                                        nil, m.message.dup)
+    elsif m.kind_of?(NickMessage)
+      return if m.address?
+      @registry[m.message] = Saw.new(m.sourcenick.dup, Time.new, "NICK", 
+                                        nil, m.message.dup)
+      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "NICK", 
+                                        nil, m.message.dup)
+    elsif m.kind_of?(PartMessage)
+      return if m.address?
+      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "PART", 
+                                        m.target, m.message.dup)
+    elsif m.kind_of?(JoinMessage)
+      return if m.address?
+      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "JOIN", 
+                                        m.target, m.message.dup)
+    elsif m.kind_of?(TopicMessage)
+      return if m.address?
+      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "TOPIC", 
+                                        m.target, m.message.dup)
+    end
+  end
+  
+  def seen(saw)
+    ret = "#{saw.nick} was last seen "
+    ago = Time.new - saw.time
+    
+    if (ago.to_i == 0)
+      ret += "just now, "
+    else
+      ret += Utils.secs_to_string(ago) + " ago, "
+    end
+
+    case saw.type
+    when "PUBLIC"
+      ret += "saying #{saw.message}"
+    when "ACTION"
+      ret += "doing #{saw.nick} #{saw.message}"
+    when "NICK"
+      ret += "changing nick from #{saw.nick} to #{saw.message}"
+    when "PART"
+      ret += "leaving #{saw.where}"
+    when "JOIN"
+      ret += "joining #{saw.where}"
+    when "QUIT"
+      ret += "quiting IRC (#{saw.message})"
+    when "TOPIC"
+      ret += "changing the topic of #{saw.where} to #{saw.message}"
+    end
+  end
+  
+end
+plugin = SeenPlugin.new
+plugin.register("seen")
diff --git a/data/rbot/plugins/slashdot.rb b/data/rbot/plugins/slashdot.rb
new file mode 100644 (file)
index 0000000..b09ac7a
--- /dev/null
@@ -0,0 +1,95 @@
+require 'rexml/document'
+require 'uri/common'
+
+class SlashdotPlugin < Plugin
+  include REXML
+  def help(plugin, topic="")
+    "slashdot search <string> [<max>=4] => search slashdot for <string>, slashdot [<max>=4] => return up to <max> slashdot headlines (use negative max to return that many headlines, but all on one line.)"
+  end
+  def privmsg(m)
+    if m.params && m.params =~ /^search\s+(.*)\s+(\d+)$/
+      search = $1
+      limit = $2.to_i
+      search_slashdot m, search, limit
+    elsif m.params && m.params =~ /^search\s+(.*)$/
+      search = $1
+      search_slashdot m, search
+    elsif m.params && m.params =~ /^([-\d]+)$/
+      limit = $1.to_i
+      slashdot m, limit
+    else
+      slashdot m
+    end
+  end
+  
+  def search_slashdot(m, search, max=4)
+    begin
+      xml = @bot.httputil.get(URI.parse("http://slashdot.org/search.pl?content_type=rss&query=#{URI.escape(search)}"))
+    rescue URI::InvalidURIError, URI::BadURIError => e
+      m.reply "illegal search string #{search}"
+      return
+    end
+    unless xml
+      m.reply "search for #{search} failed"
+      return
+    end
+    begin
+      doc = Document.new xml
+    rescue REXML::ParseException => e
+      puts e
+      m.reply "couldn't parse output XML: #{e.class}"
+      return
+    end
+    unless doc
+      m.reply "search for #{search} failed"
+      return
+    end
+    max = 8 if max > 8
+    done = 0
+    doc.elements.each("*/item") {|e|
+      desc = e.elements["title"].text
+      desc.gsub!(/(.{150}).*/, '\1..')
+      reply = sprintf("%s | %s", e.elements["link"].text, desc)
+      m.reply reply
+      done += 1
+      break if done >= max
+    }
+  end
+  
+  def slashdot(m, max=4)
+    xml = @bot.httputil.get(URI.parse("http://slashdot.org/slashdot.xml"))
+    unless xml
+      m.reply "slashdot news parse failed"
+      return
+    end
+    doc = Document.new xml
+    unless doc
+      m.reply "slashdot news parse failed (invalid xml)"
+      return
+    end
+    done = 0
+    oneline = false
+    if max < 0
+      max = (0 - max)
+      oneline = true
+    end
+    max = 8 if max > 8
+    matches = Array.new
+    doc.elements.each("*/story") {|e|
+      matches << [ e.elements["title"].text, 
+                   e.elements["author"].text, 
+                   e.elements["time"].text.gsub(/\d{4}-(\d{2})-(\d{2})/, "\\2/\\1").gsub(/:\d\d$/, "") ]
+      done += 1
+      break if done >= max
+    } 
+    if oneline
+      m.reply matches.collect{|mat| mat[0]}.join(" | ")
+    else
+      matches.each {|mat|
+        m.reply sprintf("%36s | %8s | %8s", mat[0][0,36], mat[1][0,8], mat[2])
+      }
+    end
+  end
+end
+plugin = SlashdotPlugin.new
+plugin.register("slashdot")
diff --git a/data/rbot/plugins/spell.rb b/data/rbot/plugins/spell.rb
new file mode 100644 (file)
index 0000000..81ee1ac
--- /dev/null
@@ -0,0 +1,36 @@
+class SpellPlugin < Plugin
+  def help(plugin, topic="")
+    "spell <word> => check spelling of <word>, suggest alternatives"
+  end
+  def privmsg(m)
+    unless(m.params && m.params =~ /^\S+$/)
+      m.reply "incorrect usage: " + help(m.plugin)
+      return
+    end
+    p = IO.popen("ispell -a -S", "w+")
+    if(p)
+      p.puts m.params
+      p.close_write
+      p.each_line {|l|
+        if(l =~ /^\*/)
+          m.reply "#{m.params} may be spelled correctly"
+          return
+        elsif(l =~ /^\s*&.*: (.*)$/)
+          m.reply "#{m.params}: #$1"
+          return
+        elsif(l =~ /^\s*\+ (.*)$/)
+          m.reply "#{m.params} is presumably derived from " + $1.downcase
+          return
+        elsif(l =~ /^\s*#/)
+          m.reply "#{m.params}: no suggestions"
+          return
+        end
+      }
+    else
+      m.reply "couldn't exec ispell :("
+      return
+    end
+  end
+end
+plugin = SpellPlugin.new
+plugin.register("spell")
diff --git a/data/rbot/plugins/tube.rb b/data/rbot/plugins/tube.rb
new file mode 100644 (file)
index 0000000..77ca522
--- /dev/null
@@ -0,0 +1,77 @@
+#Tube Status Enquiry plugin for rbot
+#Plugin by Colm Linehan
+
+require 'rexml/document'
+require 'uri/common'
+
+class TubePlugin < Plugin
+  include REXML
+  def help(plugin, topic="")
+  "tube [district|circle|metropolitan|central|jubilee|bakerloo|waterloo_city|hammersmith_city|victoria|eastlondon|northern|piccadilly] => display tube service status for the specified line(Docklands Light Railway is not currently supported), tube stations => list tube stations (not lines) with problems"
+  end
+  def privmsg(m)
+  if m.params && m.params =~ /^stations$/
+    check_stations m
+  elsif m.params && m.params =~ /^(.*)$/
+    line = $1.downcase.capitalize
+    check_tube m, line
+  end
+  end
+  
+  def check_tube(m, line)
+  begin
+    tube_page = @bot.httputil.get(URI.parse("http://www.tfl.gov.uk/tfl/service_rt_tube.shtml"), 1, 1)
+  rescue URI::InvalidURIError, URI::BadURIError => e
+    m.reply "Cannot contact Tube Service Status page"
+    return
+  end
+  unless tube_page
+    m.reply "Cannot contact Tube Service Status page"
+    return
+  end
+  next_line = false
+  tube_page.each_line {|l|
+    next if l == "\r\n"
+    next if l == "\n"
+    if (next_line)
+      if (l =~ /^<tr valign=top> <td>\s*(.*)<\/td><\/tr>/i)
+        m.reply $1.split(/<[^>]+>|&nbsp;/i).join(" ")
+        return
+      else
+        m.reply "There are problems on the #{line} line, but I didn't understand the page format. You should check out http://www.tfl.gov.uk/tfl/service_rt_tube.shtml for more details."
+        return
+      end
+    end
+    next_line = true if (l =~ /class="#{line}"/i)
+    }
+  m.reply "No Problems on the #{line} line."
+  end
+
+  def check_stations(m)
+    begin
+      tube_page = @bot.httputil.get(URI.parse("http://www.tfl.gov.uk/tfl/service_rt_tube.shtml"))
+    rescue URI::InvalidURIError, URI::BadURIError => e
+      m.reply "Cannot contact Tube Service Status page"
+      return
+    end
+    unless tube_page
+      m.reply "Cannot contact Tube Service Status page"
+      return
+    end
+    stations_array = Array.new
+    tube_page.each_line {|l|
+        if (l =~ /<tr valign=top> <td valign="middle" class="Station"><b>(.*)<\/b><\/td><\/tr>\s*/i)
+          stations_array.push $1
+        end
+        }
+    if stations_array.empty? 
+      m.reply "There are no station-specific announcements"
+      return
+    else
+      m.reply stations_array.join(", ")
+      return
+    end
+  end
+end
+plugin = TubePlugin.new
+plugin.register("tube")
diff --git a/data/rbot/plugins/url.rb b/data/rbot/plugins/url.rb
new file mode 100644 (file)
index 0000000..ed82d1c
--- /dev/null
@@ -0,0 +1,98 @@
+Url = Struct.new("Url", :channel, :nick, :time, :url)
+
+class UrlPlugin < Plugin
+  def initialize
+    super
+    @registry.set_default(Array.new)
+  end
+  def help(plugin, topic="")
+    "urls [<max>=4] => list <max> last urls mentioned in current channel, urls <channel> [<max>=4] => list <max> last urls mentioned in <channel>, urls search <regexp> => search for matching urls, urls search <channel> <regexp>, search for matching urls in channel <channel>"
+  end
+  def listen(m)
+    return unless m.kind_of?(PrivMessage)
+    return if m.address?
+    # TODO support multiple urls in one line
+    if m.message =~ /(f|ht)tps?:\/\//
+      if m.message =~ /((f|ht)tps?:\/\/.*?)(?:\s+|$)/
+        url = Url.new(m.target, m.sourcenick, Time.new, $1)
+        list = @registry[m.target]
+        debug "#{list.length} urls so far"
+        if list.length > 50
+          list.pop
+        end
+        debug "storing url #{url.url}"
+        list.unshift url
+        debug "#{list.length} urls now"
+        @registry[m.target] = list
+      end
+    end
+  end
+  def privmsg(m)
+    case m.params
+    when nil
+      if m.public?
+        urls m, m.target
+      else
+        m.reply "in a private message, you need to specify a channel name for urls"
+      end
+    when (/^(\d+)$/)
+      max = $1.to_i
+      if m.public?
+        urls m, m.target, max
+      else
+        m.reply "in a private message, you need to specify a channel name for urls"
+      end
+    when (/^(#.*?)\s+(\d+)$/)
+      channel = $1
+      max = $2.to_i
+      urls m, channel, max
+    when (/^(#.*?)$/)
+      channel = $1
+      urls m, channel
+    when (/^search\s+(#.*?)\s+(.*)$/)
+      channel = $1
+      string = $2
+      search m, channel, string
+    when (/^search\s+(.*)$/)
+      string = $1
+      if m.public?
+        search m, m.target, string
+      else
+        m.reply "in a private message, you need to specify a channel name for urls"
+      end
+    else
+      m.reply "incorrect usage: " + help(m.plugin)
+    end
+  end
+
+  def urls(m, channel, max=4)
+    max = 10 if max > 10
+    max = 1 if max < 1
+    list = @registry[channel]
+    if list.empty?
+      m.reply "no urls seen yet for channel #{channel}"
+    else
+      list[0..(max-1)].each do |url|
+        m.reply "[#{url.time.strftime('%Y/%m/%d %H:%M:%S')}] <#{url.nick}> #{url.url}"
+      end
+    end
+  end
+
+  def search(m, channel, string, max=4)
+    max = 10 if max > 10
+    max = 1 if max < 1
+    regex = Regexp.new(string)
+    list = @registry[channel].find_all {|url|
+      regex.match(url.url) || regex.match(url.nick)
+    }
+    if list.empty?
+      m.reply "no matches for channel #{channel}"
+    else
+      list[0..(max-1)].each do |url|
+        m.reply "[#{url.time.strftime('%Y/%m/%d %H:%M:%S')}] <#{url.nick}> #{url.url}"
+      end
+    end
+  end
+end
+plugin = UrlPlugin.new
+plugin.register("urls")
diff --git a/data/rbot/plugins/weather.rb b/data/rbot/plugins/weather.rb
new file mode 100644 (file)
index 0000000..3e4134e
--- /dev/null
@@ -0,0 +1,55 @@
+class WeatherPlugin < Plugin
+  
+  def help(plugin, topic="")
+    "weather <ICAO> => display the current weather at the location specified by the ICAO code [Lookup your ICAO code at http://www.nws.noaa.gov/oso/siteloc.shtml] - this will also store the ICAO against your nick, so you can later just say \"weather\", weather => display the current weather at the location you last asked for"
+  end
+  
+  def initialize
+    super
+    # this plugin only wants to store strings
+    class << @registry
+      def store(val)
+        val
+      end
+      def restore(val)
+        val
+      end
+    end
+    @metar_cache = Hash.new
+  end
+  
+  def describe(m, where)
+    if @metar_cache.has_key?(where) &&
+       Time.now - @metar_cache[where].date < 3600
+      met = @metar_cache[where]
+    else
+      met = Utils.get_metar(where)
+    end
+    
+    if met
+      m.reply met.pretty_print
+      @metar_cache[where] = met
+    else
+      m.reply "couldn't find weather data for #{where}"
+    end
+  end
+  
+  def privmsg(m)
+    case m.params
+    when nil
+      if @registry.has_key?(m.sourcenick)
+        where = @registry[m.sourcenick]
+        describe(m,where)
+      else
+        m.reply "I don't know where #{m.sourcenick} is!"
+      end
+    when (/^(\S{4})$/)
+      where = $1
+      @registry[m.sourcenick] = where
+      describe(m,where)
+    end
+  end
+  
+end
+plugin = WeatherPlugin.new
+plugin.register("weather")
diff --git a/data/rbot/plugins/wserver.rb b/data/rbot/plugins/wserver.rb
new file mode 100644 (file)
index 0000000..e1fe10b
--- /dev/null
@@ -0,0 +1,75 @@
+require 'net/http'
+require 'uri'
+Net::HTTP.version_1_2
+
+class WserverPlugin < Plugin
+  def help(plugin, topic="")
+    "wserver <uri> => try and determine what webserver <uri> is using"
+  end
+  def privmsg(m)
+    unless(m.params && m.params =~ /^\S+$/)
+      m.reply "incorrect usage: " + help(m.plugins)
+      return
+    end
+
+    redirect_count = 0
+    hostname = m.params.dup
+    hostname = "http://#{hostname}" unless hostname =~ /:\/\//
+    begin
+      if(redirect_count > 3)
+        m.reply "cowardly refusing to follow more than 3 redirects"
+        return
+      end
+      
+      begin
+        uri = URI.parse(hostname)
+      rescue URI::InvalidURIError => err
+        m.reply "#{m.params} is not a valid URI"
+        return
+      end
+      
+      unless(uri)
+        m.reply "incorrect usage: " + help(m.plugin)
+        return
+      end
+        
+      http = @bot.httputil.get_proxy(uri)
+      http.open_timeout = 5
+      
+      http.start {|http|
+        resp = http.head('/')
+        server = resp['Server']
+        if(server && server.length > 0)
+          m.reply "#{uri.host} is running #{server}"
+        else
+          m.reply "couldn't tell what #{uri.host} is running"
+        end
+        
+        if(resp.code == "302" || resp.code == "301") 
+          newloc = resp['location']
+          newuri = URI.parse(newloc)
+          # detect and ignore incorrect redirects (to relative paths etc)
+          if (newuri.host != nil)
+            if(uri.host != newuri.host)
+              m.reply "#{uri.host} redirects to #{newuri.scheme}://#{newuri.host}"
+              raise resp['location']
+            end
+          end
+        end
+      }
+    rescue TimeoutError => err
+      m.reply "timed out connecting to #{uri.host}:#{uri.port} :("
+      return
+    rescue RuntimeError => err
+      redirect_count += 1
+      hostname = err.message
+      retry
+    rescue StandardError => err
+      puts err
+      m.reply "couldn't connect to #{uri.host}:#{uri.port} :("
+      return
+    end
+  end
+end
+plugin = WserverPlugin.new
+plugin.register("wserver")
diff --git a/data/rbot/templates/conf.rbot b/data/rbot/templates/conf.rbot
new file mode 100644 (file)
index 0000000..d1a152f
--- /dev/null
@@ -0,0 +1,13 @@
+ADDRESS_PREFIX = |
+SENDQ_DELAY = 2.0
+SENDQ_BURST = 4
+NICK = rbot
+USER = rbot
+JOIN_CHANNELS = #giblet
+PORT = 6667
+LANGUAGE = english
+KEYWORD_LISTEN = false
+SAVE_EVERY = 60
+SERVER = irc.openprojects.net
+PASSWD = masterpwd
+NO_KEYWORD_ADDRESS = false
diff --git a/data/rbot/templates/keywords.rbot b/data/rbot/templates/keywords.rbot
new file mode 100644 (file)
index 0000000..d985fa3
--- /dev/null
@@ -0,0 +1,4 @@
+lb<=is=>http://linuxbrit.co.uk
+offended<=is=><reply><who> is offended!
+giblet<=is=>My master!
+rbot<=is=><reply>That's me! :-))
diff --git a/data/rbot/templates/lart/larts b/data/rbot/templates/lart/larts
new file mode 100644 (file)
index 0000000..c3a2588
--- /dev/null
@@ -0,0 +1,98 @@
+swaps <who>'s shampoo with nair
+installs windows on <who>'s machine
+forces <who> to use perl for 3 weeks
+registers <who>'s name with 50 known spammers
+resizes <who>'s terminal to 40x24
+takes <who>'s tea
+dispenses <who>'s email address to a few hundred 'bulk mailing services'
+pokes <who> in the eye
+beats <who> senseless with a 50lb Unix manual
+cats /dev/urandom into <who>'s ear
+signs <who> up for AOL
+enrolls <who> in Visual Basic 101
+sporks <who>
+drops a truckload of VAXen on <who>
+judo chops <who>
+resizes <who>'s terminal to 40x24
+formats <who>'s harddrive to fat12
+rm -rf's <who>
+stabs <who>
+steals <who>'s mojo
+strangles <who> with a doohicky mouse cord
+whacks <who> with the cluebat
+sells <who> on E-Bay
+uses <who> as a biological warfare study
+uses the "Customer Appreciation Bat" on <who>
+reads <who> some vogon poetry
+puts <who> in the Total Perspective Vortex
+casts <who> into the fires of Mt. Doom
+gives <who> a melvin
+turns over <who> to Agent Smith to be "bugged"
+takes away <who>'s internet connection
+pushes <who> past the Shoe Event Horizon
+counts "1, 2, 5... er... 3!" and hurls the Holy Handgrenade Of Antioch at <who>
+puts <who> in a nest of camel spiders
+makes <who> read slashdot at -1
+puts "alias vim=emacs" in <who>'s /etc/profile
+uninstalls ld from <who>'s system
+locks <who> in the Chateau d'If
+signs <who> up for getting hit on the head lessons
+makes <who> try to set up a Lexmark printer
+fills <who>'s Visene eyedrop bottle with lime juice
+casts <who> into the fires of Mt. Doom.
+gives <who> a Chicago Steamer
+rips off <who>'s arm, and uses it to beat them to death
+pierces <who>'s nose with a rusty paper hole puncher
+pokes <who> with a rusty nail
+puts sugar between <who>'s bedsheets
+pours sand into <who>'s oatmeal
+mixes epoxy into <who>'s toothpaste
+puts Icy-Hot in <who>'s lip balm
+straps <who> to a chair, and plays a endless low bitrate MP3 loop of "the world's most annoying sound" from "Dumb and Dumber"
+tells Dr. Dre that <who> was talking smack
+forces <who> to use a Commodore 64 for all their word processing
+smacks <who> in the face with a burlap sack full of broken glass
+puts <who> in a room with several heavily armed manic depressives
+makes <who> watch reruns of "Blue's Clues"
+puts lye in <who>'s coffee
+tattoos the Windows symbol on <who>'s ass
+lets Borg have his way with <who>
+signs <who> up for line dancing classes at the local senior center
+wakes <who> out of a sound sleep with some brand new nipple piercings
+gives <who> a 2 guage Prince Albert
+forces <who> to eat all their veggies
+covers <who>'s toilet paper with lemon-pepper
+fills <who>'s ketchup bottle with Dave's Insanity sauce
+forces <who> to stare at an incredibly frustrating and seemingly neverending IRC political debate
+knocks two of <who>'s teeth out with a 2x4
+removes debian from <who>'s system
+uses <who>'s debian cds for skeet shooting practice
+gives <who>'s phone number to Borg
+posts <who>'s IP and root password on alt.2600
+forces <who> to use words like "irregardless" and "administrate" (thereby sounding like a real dumbass)"
+tickles <who> until they wet their pants and pass out
+replaces <who>'s KY with elmer's clear wood glue
+replaces <who>'s TUMS with alka-seltzer tablets
+squeezes habanero pepper juice into <who>'s tub of vaseline for <who>
+submits <who>'s photo to the people at SA for photoshopping
+Forces <who> to learn the Win32 API
+gives <who> an atomic wedgie
+ties <who> to a chair and forces them to listen to 'N Sync at full blast
+forces <who> to use emacs for text editing
+frowns at <who> really really hard
+jabs a hot car lighter into <who>'s eye sockets
+forces <who> to browse the web with IE
+takes <who> out at the knees with a broken pool cue
+forces <who> to listen to only emo music
+signs <who> up for the Iowa State Ferret Legging Championship
+attempts to hotswap <who>'s RAM
+donkey punches <who>
+puts track spikes into <who>'s side
+replaces <who>'s Astroglide with JB Weld
+replaces <who>'s hypertension pills with rat poison pellets
+replaces <who>s jock itch cream with Nair
+does the Australian Death Grip on <who>
+dances upon the grave of <who>'s ancestors.
+farts in <who>'s general direction
+flogs <who> with stinging neddle
+hands <who> a poison ivy joint
diff --git a/data/rbot/templates/lart/praises b/data/rbot/templates/lart/praises
new file mode 100644 (file)
index 0000000..ac923dd
--- /dev/null
@@ -0,0 +1,2 @@
+hugs <who>
+gives <who> some love
diff --git a/data/rbot/templates/levels.rbot b/data/rbot/templates/levels.rbot
new file mode 100644 (file)
index 0000000..2d11c2d
--- /dev/null
@@ -0,0 +1,21 @@
+70 say
+100 auth
+50 part
+85 config
+80 nick
+50 join
+12 msginsult
+5 keycmd
+5 lart
+5 addlart
+10 rmlart
+5 addpraise
+10 rmpraise
+5 addquote
+12 remind+
+5 getquote
+90 quit
+5 remind
+5 keyword
+15 delquote
+70 opmeh
diff --git a/data/rbot/templates/users.rbot b/data/rbot/templates/users.rbot
new file mode 100644 (file)
index 0000000..61f55fb
--- /dev/null
@@ -0,0 +1 @@
+10 *!*@*
diff --git a/rbot.rb b/rbot.rb
deleted file mode 100755 (executable)
index 6f08fe5..0000000
--- a/rbot.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env ruby
-
-# 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.
-
-$VERBOSE=true
-
-require 'getoptlong'
-require 'rbot/ircbot'
-
-$debug = true
-$version="0.9.8"
-$opts = Hash.new
-
-# print +message+ if debugging is enabled
-def debug(message=nil)
-  print "DEBUG: #{message}\n" if($debug && message)
-  #yield
-end
-
-opts = GetoptLong.new(
-  [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
-  [ "--help",  "-h", GetoptLong::OPTIONAL_ARGUMENT ]
-)
-
-opts.each {|opt, arg|
-  $debug = true if(opt == "--debug")
-  $opts[opt.sub(/^-+/, "")] = arg
-}
-
-botclass = ARGV.shift
-botclass = "rbotconf" unless(botclass);
-
-unless FileTest.directory? botclass
-  # TODO copy in samples/templates from install directory
-end
-
-if(bot = Irc::IrcBot.new(botclass))
-  if($opts["help"])
-    puts bot.help($opts["help"])
-  else
-    # run the bot
-    bot.mainloop
-  end
-end
-
diff --git a/rbot/plugins/autoop.rb b/rbot/plugins/autoop.rb
deleted file mode 100644 (file)
index fdbcf6e..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-class AutoOP < Plugin
-    @@handlers = {
-        "addop" => "handle_addop",
-        "rmop" => "handle_rmop",
-        "listop" => "handle_listop"
-    }
-    
-    def help(plugin, topic="")
-        "perform autoop based on hostmask - usage: addop <hostmask>, rmop <hostmask>, listop"
-    end
-    
-    def join(m)
-        if(!m.address?)
-          @registry.each { |mask,channels|
-            if(Irc.netmaskmatch(mask, m.source) && channels.include?(m.channel))
-              @bot.mode(m.channel, "+o", m.sourcenick)
-            end
-          }
-        end
-    end
-    
-    def privmsg(m)
-        if(m.private?)
-          if (!m.params || m.params == "list")
-            handle_listop(m)
-          elsif (m.params =~ /^add\s+(.+)$/)
-            handle_addop(m, $1)
-          elsif (m.params =~ /^rm\s+(.+)$/)
-            handle_rmop(m, $1)
-          end
-        end
-    end
-
-    def handle_addop(m, params)
-        ma = /^(.+?)(\s+(.+))?$/.match(params)
-        channels = ma[2] ? ma[2] : @bot.config['JOIN_CHANNELS']
-        if(ma[1] && channels)
-            @registry[ma[1]] = channels.split(/,\s*/).collect { |x|
-                x.strip
-            }
-            m.okay
-        else
-            m.reply @bot.lang.get('dunno')
-        end
-    end
-
-    def handle_rmop(m, params)
-       if(!@registry.delete(params))
-         m.reply @bot.lang.get('dunno')
-       else
-         m.okay
-       end
-    end
-
-    def handle_listop(m)
-        if(@registry.length)
-            @registry.each { |mask,channels|
-                m.reply "#{mask} in #{channels.join(', ')}"
-            }
-        else
-            m.reply "No entrys"
-        end
-    end
-end
-
-plugin = AutoOP.new
-plugin.register("autoop")
-
diff --git a/rbot/plugins/autorejoin.rb b/rbot/plugins/autorejoin.rb
deleted file mode 100644 (file)
index aba4650..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-class AutoRejoinPlugin < Plugin
-  def help(plugin, topic="")
-    "performs an automatic rejoin if the bot is kicked from a channel"
-  end
-  def kick(m)
-    if m.address?
-      @bot.timer.add_once(10, m) {|m|
-        @bot.join m.channel
-        @bot.say m.channel, @bot.lang.get("insult") % m.sourcenick
-      }
-    end
-  end
-end
-
-plugin = AutoRejoinPlugin.new
-plugin.register("autorejoin")
diff --git a/rbot/plugins/cal.rb b/rbot/plugins/cal.rb
deleted file mode 100644 (file)
index 4f28310..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-class CalPlugin < Plugin
-  def help(plugin, topic="")
-    "cal [options] => show current calendar [unix cal options]"
-  end
-  def cal(m, params)
-    if params.has_key?(:month)
-      m.reply Utils.safe_exec("cal", params[:month], params[:year])
-    else
-      m.reply Utils.safe_exec("cal")
-    end
-  end
-end
-plugin = CalPlugin.new
-plugin.map 'cal :month :year', :requirements => {:month => /^\d+$/, :year => /^\d+$/}
-plugin.map 'cal'
diff --git a/rbot/plugins/dice.rb b/rbot/plugins/dice.rb
deleted file mode 100644 (file)
index 928da89..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-##################
-# Filename: dice.rb
-# Description: Rbot plugin. Rolls rpg style dice
-# Author: David Dorward (http://david.us-lot.org/ - you might find a more up to date version of this plugin there)
-# Version: 0.3.2
-# Date: Sat 6 Apr 2002
-#
-# You can get rbot from: http://www.linuxbrit.co.uk/rbot/
-#
-# Changelog
-# 0.1 - Initial release
-# 0.1.1 - bug fix, only 1 digit for number of dice sides on first roll
-# 0.3.0 - Spelling correction on changelog 0.1.1
-#       - Return results of each roll
-# 0.3.1 - Minor documentation update
-# 0.3.2 - Bug fix, could not subtract numbers (String can't be coerced into Fixnum)
-#
-# TODO: Test! Test! Test!
-#       Comment!
-#       Fumble/Critical counter (1's and x's where x is sides on dice)
-####################################################
-
-class DiceDisplay
-  attr_reader :total, :view
-  def initialize(view, total)
-    @total = total
-    @view = view
-  end
-end
-
-class DicePlugin < Plugin
-  def help(plugin, topic="")
-    "dice <string> (where <string> is something like: d6 or 2d6 or 2d6+4 or 2d6+1d20 or 2d6+1d5+4d7-3d4-6) => Rolls that set of virtual dice"
-  end
-
-  def rolldice(d)
-    dice = d.split(/d/)
-    r = 0
-    unless dice[0] =~ /^[0-9]+/
-      dice[0] = 1
-    end
-    for i in 0...dice[0].to_i
-      r = r + rand(dice[1].to_i) + 1
-    end
-    return r
-  end
-
-  def iddice(d)
-    porm = d.slice!(0,1)
-    if d =~ /d/
-      r = rolldice(d)
-    else
-      r = d
-    end
-    if porm == "-"
-      r = 0 - r.to_i
-    end
-    viewer = DiceDisplay.new("[" + porm.to_s + d.to_s + "=" + r.to_s + "] ", r)
-    return viewer
-  end
-
-  def privmsg(m)
-    unless(m.params && m.params =~ /^[0-9]*d[0-9]+([+-]([0-9]+|[0-9]*d[0-9])+)*$/)
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-    a = m.params.scan(/^[0-9]*d[0-9]+|[+-][0-9]*d[0-9]+|[+-][0-9]+/)
-    r = rolldice(a[0])
-    t = "[" + a[0].to_s + "=" + r.to_s + "] "
-    for i in 1...a.length
-      tmp = iddice(a[i])
-      r = r + tmp.total.to_i
-      t = t + tmp.view.to_s
-    end
-    m.reply r.to_s + " | " + t
-  end
-end
-plugin = DicePlugin.new
-plugin.register("dice")
-##############################################
-#fin
diff --git a/rbot/plugins/eightball.rb b/rbot/plugins/eightball.rb
deleted file mode 100644 (file)
index 6474849..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-# Author: novex, daniel@novex.net.nz based on code from slap.rb by oct
-
-class EightBallPlugin < Plugin
-  def initialize
-    super
-    @answers=['yes', 'no', 'outlook not so good', 'all signs point to yes', 'all signs point to no', 'why the hell are you asking me?', 'the answer is unclear']
-  end
-  def help(plugin, topic="")
-    "magic 8-ball ruby bot module written by novex for nvinfo on #dumber@quakenet, usage:<botname> 8ball will i ever beat this cancer?"
-  end
-  def eightball(m, params)
-    answers = @answers[rand(@answers.length)]
-    action = "shakes the magic 8-ball... #{answers}"
-    @bot.action m.replyto, action
-  end
-end
-plugin = EightBallPlugin.new
-plugin.map '8ball', :action => 'usage'
-plugin.map '8ball *params', :action => 'eightball'
diff --git a/rbot/plugins/excuse.rb b/rbot/plugins/excuse.rb
deleted file mode 100644 (file)
index 38e85ad..0000000
+++ /dev/null
@@ -1,470 +0,0 @@
-class ExcusePlugin < Plugin
-  # excuses courtesy of http://www.cs.wisc.edu/~ballard/bofh/
-@@excuses = [
-"clock speed",
-"solar flares",
-"electromagnetic radiation from satellite debris",
-"static from nylon underwear",
-"static from plastic slide rules",
-"global warming",
-"poor power conditioning",
-"static buildup",
-"doppler effect",
-"hardware stress fractures",
-"magnetic interference from money/credit cards",
-"dry joints on cable plug",
-"we're waiting for [the phone company] to fix that line",
-"sounds like a Windows problem, try calling Microsoft support",
-"temporary routing anomaly",
-"somebody was calculating pi on the server",
-"fat electrons in the lines",
-"excess surge protection",
-"floating point processor overflow",
-"divide-by-zero error",
-"POSIX compliance problem",
-"monitor resolution too high",
-"improperly oriented keyboard",
-"network packets travelling uphill (use a carrier pigeon)",
-"Decreasing electron flux",
-"first Saturday after first full moon in Winter",
-"radiosity depletion",
-"CPU radiator broken",
-"It works the way the Wang did, what's the problem",
-"positron router malfunction",
-"cellular telephone interference",
-"techtonic stress",
-"piezo-electric interference",
-"(l)user error",
-"working as designed",
-"dynamic software linking table corrupted",
-"heavy gravity fluctuation, move computer to floor rapidly",
-"secretary plugged hairdryer into UPS",
-"terrorist activities",
-"not enough memory, go get system upgrade",
-"interrupt configuration error",
-"spaghetti cable cause packet failure",
-"boss forgot system password",
-"bank holiday - system operating credits  not recharged",
-"virus attack, luser responsible",
-"waste water tank overflowed onto computer",
-"Complete Transient Lockout",
-"bad ether in the cables",
-"Bogon emissions",
-"Change in Earth's rotational speed",
-"Cosmic ray particles crashed through the hard disk platter",
-"Smell from unhygienic janitorial staff wrecked the tape heads",
-"Little hamster in running wheel had coronary; waiting for replacement to be Fedexed from Wyoming",
-"Evil dogs hypnotised the night shift",
-"Plumber mistook routing panel for decorative wall fixture",
-"Electricians made popcorn in the power supply",
-"Groundskeepers stole the root password",
-"high pressure system failure",
-"failed trials, system needs redesigned",
-"system has been recalled",
-"not approved by the FCC",
-"need to wrap system in aluminum foil to fix problem",
-"not properly grounded, please bury computer",
-"CPU needs recalibration",
-"system needs to be rebooted",
-"bit bucket overflow",
-"descramble code needed from software company",
-"only available on a need to know basis",
-"knot in cables caused data stream to become twisted and kinked",
-"nesting roaches shorted out the ether cable",
-"The file system is full of it",
-"Satan did it",
-"Daemons did it",
-"You're out of memory",
-"There isn't any problem",
-"Unoptimized hard drive",
-"Typo in the code",
-"Yes, yes, its called a design limitation",
-"Look, buddy:  Windows 3.1 IS A General Protection Fault.",
-"That's a great computer you have there; have you considered how it would work as a BSD machine?",
-"Please excuse me, I have to circuit an AC line through my head to get this database working.",
-"Yeah, yo mama dresses you funny and you need a mouse to delete files.",
-"Support staff hung over, send aspirin and come back LATER.",
-"Someone is standing on the ethernet cable, causing a kink in the cable",
-"Windows 95 undocumented 'feature'",
-"Runt packets",
-"Password is too complex to decrypt",
-"Boss' kid fucked up the machine",
-"Electromagnetic energy loss",
-"Budget cuts",
-"Mouse chewed through power cable",
-"Stale file handle (next time use Tupperware(tm)!)",
-"Feature not yet implemented",
-"Internet outage",
-"Pentium FDIV bug",
-"Vendor no longer supports the product",
-"Small animal kamikaze attack on power supplies",
-"The vendor put the bug there.",
-"SIMM crosstalk.",
-"IRQ dropout",
-"Collapsed Backbone",
-"Power company testing new voltage spike (creation) equipment",
-"operators on strike due to broken coffee machine",
-"backup tape overwritten with copy of system manager's favourite CD",
-"UPS interrupted the server's power",
-"The electrician didn't know what the yellow cable was so he yanked the ethernet out.",
-"The keyboard isn't plugged in",
-"The air conditioning water supply pipe ruptured over the machine room",
-"The electricity substation in the car park blew up.",
-"The rolling stones concert down the road caused a brown out",
-"The salesman drove over the CPU board.",
-"The monitor is plugged into the serial port",
-"Root nameservers are out of sync",
-"electro-magnetic pulses from French above ground nuke testing.",
-"your keyboard's space bar is generating spurious keycodes.",
-"the real ttys became pseudo ttys and vice-versa.",
-"the printer thinks its a router.",
-"the router thinks its a printer.",
-"evil hackers from Serbia.",
-"we just switched to FDDI.",
-"halon system went off and killed the operators.",
-"because Bill Gates is a Jehovah's witness and so nothing can work on St. Swithin's day.",
-"user to computer ratio too high.",
-"user to computer ration too low.",
-"we just switched to Sprint.",
-"it has Intel Inside",
-"Sticky bits on disk.",
-"Power Company having EMP problems with their reactor",
-"The ring needs another token",
-"new management",
-"telnet: Unable to connect to remote host: Connection refused",
-"SCSI Chain overterminated",
-"It's not plugged in.",
-"because of network lag due to too many people playing deathmatch",
-"You put the disk in upside down.",
-"Daemons loose in system.",
-"User was distributing pornography on server; system seized by FBI.",
-"BNC (brain not connected)",
-"UBNC (user brain not connected)",
-"LBNC (luser brain not connected)",
-"disks spinning backwards - toggle the hemisphere jumper.",
-"new guy cross-connected phone lines with ac power bus.",
-"had to use hammer to free stuck disk drive heads.",
-"Too few computrons available.",
-"Communications satellite used by the military for star wars.",
-"Party-bug in the Aloha protocol.",
-"Insert coin for new game",
-"Dew on the telephone lines.",
-"Arcserve crashed the server again.",
-"Some one needed the powerstrip, so they pulled the switch plug.",
-"My pony-tail hit the on/off switch on the power strip.",
-"Big to little endian conversion error",
-"You can tune a file system, but you can't tune a fish (from most tunefs man pages)",
-"Dumb terminal",
-"Zombie processes haunting the computer",
-"Incorrect time synchronization",
-"Defunct processes",
-"Stubborn processes",
-"non-redundant fan failure ",
-"monitor VLF leakage",
-"bugs in the RAID",
-"no 'any' key on keyboard",
-"root rot",
-"Backbone Scoliosis",
-"/pub/lunch",
-"excessive collisions & not enough packet ambulances",
-"le0: no carrier: transceiver cable problem?",
-"broadcast packets on wrong frequency",
-"popper unable to process jumbo kernel",
-"NOTICE: alloc: /dev/null: filesystem full",
-"pseudo-user on a pseudo-terminal",
-"Recursive traversal of loopback mount points",
-"Backbone adjustment",
-"OS swapped to disk",
-"vapors from evaporating sticky-note adhesives",
-"sticktion",
-"short leg on process table",
-"multicasts on broken packets",
-"ether leak",
-"Atilla the Hub",
-"endothermal recalibration",
-"filesystem not big enough for Jumbo Kernel Patch",
-"loop found in loop in redundant loopback",
-"system consumed all the paper for paging",
-"permission denied",
-"Reformatting Page. Wait...",
-"..disk or the processor is on fire.",
-"SCSI's too wide.",
-"Proprietary Information.",
-"Just type 'mv * /dev/null'.",
-"runaway cat on system.",
-"Did you pay the new Support Fee?",
-"We only support a 1200 bps connection.",
-"We only support a 28000 bps connection.",
-"Me no internet, only janitor, me just wax floors.",
-"I'm sorry a pentium won't do, you need an SGI to connect with us.",
-"Post-it Note Sludge leaked into the monitor.",
-"the curls in your keyboard cord are losing electricity.",
-"The monitor needs another box of pixels.",
-"RPC_PMAP_FAILURE",
-"kernel panic: write-only-memory (/dev/wom0) capacity exceeded.",
-"Write-only-memory subsystem too slow for this machine. Contact your local dealer.",
-"Just pick up the phone and give modem connect sounds. 'Well you said we should get more lines so we don't have voice lines.'",
-"Quantum dynamics are affecting the transistors",
-"Police are examining all internet packets in the search for a narco-net-trafficker",
-"We are currently trying a new concept of using a live mouse.  Unfortunately, one has yet to survive being hooked up to the computer.....please bear with us.",
-"Your mail is being routed through Germany ... and they're censoring us.",
-"Only people with names beginning with 'A' are getting mail this week (a la Microsoft)",
-"We didn't pay the Internet bill and it's been cut off.",
-"Lightning strikes.",
-"Of course it doesn't work. We've performed a software upgrade.",
-"Change your language to Finnish.",
-"Fluorescent lights are generating negative ions. If turning them off doesn't work, take them out and put tin foil on the ends.",
-"High nuclear activity in your area.",
-"What office are you in? Oh, that one.  Did you know that your building was built over the universities first nuclear research site? And wow, aren't you the lucky one, your office is right over where the core is buried!",
-"The MGs ran out of gas.",
-"The UPS doesn't have a battery backup.",
-"Recursivity.  Call back if it happens again.",
-"Someone thought The Big Red Button was a light switch.",
-"The mainframe needs to rest.  It's getting old, you know.",
-"I'm not sure.  Try calling the Internet's head office -- it's in the book.",
-"The lines are all busy (busied out, that is -- why let them in to begin with?).",
-"Jan  9 16:41:27 huber su: 'su root' succeeded for .... on /dev/pts/1",
-"It's those computer people in X {city of world}.  They keep stuffing things up.",
-"A star wars satellite accidently blew up the WAN.",
-"Fatal error right in front of screen",
-"That function is not currently supported, but Bill Gates assures us it will be featured in the next upgrade.",
-"wrong polarity of neutron flow",
-"Lusers learning curve appears to be fractal",
-"We had to turn off that service to comply with the CDA Bill.",
-"Ionization from the air-conditioning",
-"TCP/IP UDP alarm threshold is set too low.",
-"Someone is broadcasting pygmy packets and the router doesn't know how to deal with them.",
-"The new frame relay network hasn't bedded down the software loop transmitter yet. ",
-"Fanout dropping voltage too much, try cutting some of those little traces",
-"Plate voltage too low on demodulator tube",
-"You did wha... oh _dear_....",
-"CPU needs bearings repacked",
-"Too many little pins on CPU confusing it, bend back and forth until 10-20% are neatly removed. Do _not_ leave metal bits visible!",
-"_Rosin_ core solder? But...",
-"Software uses US measurements, but the OS is in metric...",
-"The computer fleetly, mouse and all.",
-"Your cat tried to eat the mouse.",
-"The Borg tried to assimilate your system. Resistance is futile.",
-"It must have been the lightning storm we had (yesterday) (last week) (last month)",
-"Due to Federal Budget problems we have been forced to cut back on the number of users able to access the system at one time. (namely none allowed....)",
-"Too much radiation coming from the soil.",
-"Unfortunately we have run out of bits/bytes/whatever. Don't worry, the next supply will be coming next week.",
-"Program load too heavy for processor to lift.",
-"Processes running slowly due to weak power supply",
-"Our ISP is having {switching,routing,SMDS,frame relay} problems",
-"We've run out of licenses",
-"Interference from lunar radiation",
-"Standing room only on the bus.",
-"You need to install an RTFM interface.",
-"That would be because the software doesn't work.",
-"That's easy to fix, but I can't be bothered.",
-"Someone's tie is caught in the printer, and if anything else gets printed, he'll be in it too.",
-"We're upgrading /dev/null",
-"The Usenet news is out of date",
-"Our POP server was kidnapped by a weasel.",
-"It's stuck in the Web.",
-"Your modem doesn't speak English.",
-"The mouse escaped.",
-"All of the packets are empty.",
-"The UPS is on strike.",
-"Neutrino overload on the nameserver",
-"Melting hard drives",
-"Someone has messed up the kernel pointers",
-"The kernel license has expired",
-"Netscape has crashed",
-"The cord jumped over and hit the power switch.",
-"It was OK before you touched it.",
-"Bit rot",
-"U.S. Postal Service",
-"Your Flux Capacitor has gone bad.",
-"The Dilithium Crystals need to be rotated.",
-"The static electricity routing is acting up...",
-"Traceroute says that there is a routing problem in the backbone.  It's not our problem.",
-"The co-locator cannot verify the frame-relay gateway to the ISDN server.",
-"High altitude condensation from U.S.A.F prototype aircraft has contaminated the primary subnet mask. Turn off your computer for 9 days to avoid damaging it.",
-"Lawn mower blade in your fan need sharpening",
-"Electrons on a bender",
-"Telecommunications is upgrading. ",
-"Telecommunications is downgrading.",
-"Telecommunications is downshifting.",
-"Hard drive sleeping. Let it wake up on it's own...",
-"Interference between the keyboard and the chair.",
-"The CPU has shifted, and become decentralized.",
-"Due to the CDA, we no longer have a root account.",
-"We ran out of dial tone and we're and waiting for the phone company to deliver another bottle.",
-"You must've hit the wrong any key.",
-"PCMCIA slave driver",
-"The Token fell out of the ring. Call us when you find it.",
-"The hardware bus needs a new token.",
-"Too many interrupts",
-"Not enough interrupts",
-"The data on your hard drive is out of balance.",
-"Digital Manipulator exceeding velocity parameters",
-"appears to be a Slow/Narrow SCSI-0 Interface problem",
-"microelectronic Riemannian curved-space fault in write-only file system",
-"fractal radiation jamming the backbone",
-"routing problems on the neural net",
-"IRQ-problems with the Un-Interruptible-Power-Supply",
-"CPU-angle has to be adjusted because of vibrations coming from the nearby road",
-"emissions from GSM-phones",
-"CD-ROM server needs recalibration",
-"firewall needs cooling",
-"asynchronous inode failure",
-"transient bus protocol violation",
-"incompatible bit-registration operators",
-"your process is not ISO 9000 compliant",
-"You need to upgrade your VESA local bus to a MasterCard local bus.",
-"The recent proliferation of Nuclear Testing",
-"Elves on strike. (Why do they call EMAG Elf Magic)",
-"Internet exceeded Luser level, please wait until a luser logs off before attempting to log back on.",
-"Your EMAIL is now being delivered by the USPS.",
-"Your computer hasn't been returning all the bits it gets from the Internet.",
-"You've been infected by the Telescoping Hubble virus.",
-"Scheduled global CPU outage",
-"Your Pentium has a heating problem - try cooling it with ice cold water.(Do not turn off your computer, you do not want to cool down the Pentium Chip while he isn't working, do you?)",
-"Your processor has processed too many instructions.  Turn it off immediately, do not type any commands!!",
-"Your packets were eaten by the terminator",
-"Your processor does not develop enough heat.",
-"We need a licensed electrician to replace the light bulbs in the computer room.",
-"The POP server is out of Coke",
-"Fiber optics caused gas main leak",
-"Server depressed, needs Prozac",
-"quantum decoherence",
-"those damn raccoons!",
-"suboptimal routing experience",
-"A plumber is needed, the network drain is clogged",
-"50% of the manual is in .pdf readme files",
-"the AA battery in the wallclock sends magnetic interference",
-"the xy axis in the trackball is coordinated with the summer solstice",
-"the butane lighter causes the pincushioning",
-"old inkjet cartridges emanate barium-based fumes",
-"manager in the cable duct",
-"We'll fix that in the next (upgrade, update, patch release, service pack).",
-"HTTPD Error 666 : BOFH was here",
-"HTTPD Error 4004 : very old Intel cpu - insufficient processing power",
-"The ATM board has run out of 10 pound notes.  We are having a whip round to refill it, care to contribute ?",
-"Network failure -  call NBC",
-"Having to manually track the satellite.",
-"Your/our computer(s) had suffered a memory leak, and we are waiting for them to be topped up.",
-"The rubber band broke",
-"We're on Token Ring, and it looks like the token got loose.",
-"Stray Alpha Particles from memory packaging caused Hard Memory Error on Server.",
-"paradigm shift...without a clutch",
-"PEBKAC (Problem Exists Between Keyboard And Chair)",
-"The cables are not the same length.",
-"Second-system effect.",
-"Chewing gum on /dev/sd3c",
-"Boredom in the Kernel.",
-"the daemons! the daemons! the terrible daemons!",
-"I'd love to help you -- it's just that the Boss won't let me near the computer. ",
-"struck by the Good Times virus",
-"YOU HAVE AN I/O ERROR -> Incompetent Operator error",
-"Your parity check is overdrawn and you're out of cache.",
-"Communist revolutionaries taking over the server room and demanding all the computers in the building or they shoot the sysadmin. Poor misguided fools.",
-"Plasma conduit breach",
-"Out of cards on drive D:",
-"Sand fleas eating the Internet cables",
-"parallel processors running perpendicular today",
-"ATM cell has no roaming feature turned on, notebooks can't connect",
-"Webmasters kidnapped by evil cult.",
-"Failure to adjust for daylight savings time.",
-"Virus transmitted from computer to sysadmins.",
-"Virus due to computers having unsafe sex.",
-"Incorrectly configured static routes on the corerouters.",
-"Forced to support NT servers; sysadmins quit.",
-"Suspicious pointer corrupted virtual machine",
-"It's the InterNIC's fault.",
-"Root name servers corrupted.",
-"Budget cuts forced us to sell all the power cords for the servers.",
-"Someone hooked the twisted pair wires into the answering machine.",
-"Operators killed by year 2000 bug bite.",
-"We've picked COBOL as the language of choice.",
-"Operators killed when huge stack of backup tapes fell over.",
-"Robotic tape changer mistook operator's tie for a backup tape.",
-"Someone was smoking in the computer room and set off the halon systems.",
-"Your processor has taken a ride to Heaven's Gate on the UFO behind Hale-Bopp's comet.",
-"it's an ID-10-T error",
-"Dyslexics retyping hosts file on servers",
-"The Internet is being scanned for viruses.",
-"Your computer's union contract is set to expire at midnight.",
-"Bad user karma.",
-"/dev/clue was linked to /dev/null",
-"Increased sunspot activity.",
-"We already sent around a notice about that.",
-"It's union rules. There's nothing we can do about it. Sorry.",
-"Interference from the Van Allen Belt.",
-"Jupiter is aligned with Mars.",
-"Redundant ACLs. ",
-"Mail server hit by UniSpammer.",
-"T-1's congested due to porn traffic to the news server.",
-"Data for intranet got routed through the extranet and landed on the internet.",
-"We are a 100% Microsoft Shop.",
-"We are Microsoft.  What you are experiencing is not a problem; it is an undocumented feature.",
-"Sales staff sold a product we don't offer.",
-"Secretary sent chain letter to all 5000 employees.",
-"Sysadmin didn't hear pager go off due to loud music from bar-room speakers.",
-"Sysadmin accidentally destroyed pager with a large hammer.",
-"Sysadmins unavailable because they are in a meeting talking about why they are unavailable so much.",
-"Bad cafeteria food landed all the sysadmins in the hospital.",
-"Route flapping at the NAP.",
-"Computers under water due to SYN flooding.",
-"The vulcan-death-grip ping has been applied.",
-"Electrical conduits in machine room are melting.",
-"Traffic jam on the Information Superhighway.",
-"Radial Telemetry Infiltration",
-"Cow-tippers tipped a cow onto the server.",
-"tachyon emissions overloading the system",
-"Maintenance window broken",
-"We're out of slots on the server",
-"Computer room being moved.  Our systems are down for the weekend.",
-"Sysadmins busy fighting SPAM.",
-"Repeated reboots of the system failed to solve problem",
-"Feature was not beta tested",
-"Domain controller not responding",
-"Someone else stole your IP address, call the Internet detectives!",
-"It's not RFC-822 compliant.",
-"operation failed because: there is no message for this error (#1014)",
-"stop bit received",
-"internet is needed to catch the etherbunny",
-"network down, IP packets delivered via UPS",
-"Firmware update in the coffee machine",
-"Temporal anomaly",
-"Mouse has out-of-cheese-error",
-"Borg implants are failing",
-"Borg nanites have infested the server",
-"error: one bad user found in front of screen",
-"Please state the nature of the technical emergency",
-"Internet shut down due to maintenance",
-"Daemon escaped from pentagram",
-"crop circles in the corn shell",
-"sticky bit has come loose",
-"Hot Java has gone cold",
-"Cache miss - please take better aim next time",
-"Hash table has woodworm",
-"Trojan horse ran out of hay",
-"Zombie processes detected, machine is haunted.",
-"overflow error in /dev/null",
-"Browser's cookie is corrupted -- someone's been nibbling on it.",
-"Mailer-daemon is busy burning your message in hell.",
-"According to Microsoft, it's by design",
-"vi needs to be upgraded to vii",
-"greenpeace free'd the mallocs",
-"Terrorists crashed an airplane into the server room, have to remove /bin/laden. (rm -rf /bin/laden)",
-"astropneumatic oscillations in the water-cooling",
-"Somebody ran the operating system through a spelling checker.",
-"Rhythmic variations in the voltage reaching the power supply.",
-"Keyboard Actuator Failure.  Order and Replace."
-]
-
-  def help(plugin, topic="")
-    "excuse => supply a random excuse"
-  end
-  def privmsg(m)
-    excuse = @@excuses[rand(@@excuses.length)]
-    m.reply excuse
-  end
-end
-
-plugin = ExcusePlugin.new
-plugin.register("excuse")
-
diff --git a/rbot/plugins/fish.rb b/rbot/plugins/fish.rb
deleted file mode 100644 (file)
index 57aaafc..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-require 'net/http'
-require 'uri/common'
-Net::HTTP.version_1_2
-
-class BabelPlugin < Plugin
-  def help(plugin, topic="")
-    "translate to <lang> <string> => translate from english to <lang>, translate from <lang> <string> => translate to english from <lang>, translate <fromlang> <tolang> <string> => translate from <fromlang> to <tolang>. Languages: en, fr, de, it, pt, es, nl"
-  end
-  def translate(m, params)
-    langs = ["en", "fr", "de", "it", "pt", "es", "nl"]
-    trans_from = params[:fromlang] ? params[:fromlang] : 'en'
-    trans_to = params[:tolang] ? params[:tolang] : 'en'
-    trans_text = params[:phrase].to_s
-    
-    query = "/babelfish/tr"
-    lang_match = langs.join("|")
-    unless(trans_from =~ /^(#{lang_match})$/ && trans_to =~ /^(#{lang_match})$/)
-      m.reply "invalid language: valid languagess are: #{langs.join(' ')}"
-      return
-    end
-
-    data_text = URI.escape trans_text
-    trans_pair = "#{trans_from}_#{trans_to}"
-    data = "lp=#{trans_pair}&doit=done&intl=1&tt=urltext&urltext=#{data_text}"
-
-    # check cache for previous lookups
-    if @registry.has_key?("#{trans_pair}/#{data_text}")
-      m.reply @registry["#{trans_pair}/#{data_text}"]
-      return
-    end
-
-    http = @bot.httputil.get_proxy(URI.parse("http://babelfish.altavista.com"))
-
-    http.start {|http|
-      resp = http.post(query, data, {"content-type",
-      "application/x-www-form-urlencoded"})
-  
-  if (resp.code == "200")
-    resp.body.each_line do |l|
-      if(l =~ /^\s+<td bgcolor=white class=s><div style=padding:10px;>(.*)<\/div>/)
-        answer = $1
-        # cache the answer
-        if(answer.length > 0)
-          @registry["#{trans_pair}/#{data_text}"] = answer
-        end
-        m.reply answer
-        return
-      end
-    end
-    m.reply "couldn't parse babelfish response html :("
-  else
-    m.reply "couldn't talk to babelfish :("
-  end
-  }
-  end
-end
-plugin = BabelPlugin.new
-plugin.map 'translate to :tolang *phrase'
-plugin.map 'translate from :fromlang *phrase'
-plugin.map 'translate :fromlang :tolang *phrase'
-
diff --git a/rbot/plugins/fortune.rb b/rbot/plugins/fortune.rb
deleted file mode 100644 (file)
index 184b6b1..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-class FortunePlugin < Plugin
-  def help(plugin, topic="")
-    "fortune [<module>] => get a (short) fortune, optionally specifying fortune db"
-  end
-  def fortune(m, params)
-    db = params[:db]
-    fortune = nil
-    ["/usr/games/fortune", "/usr/bin/fortune", "/usr/local/bin/fortune"].each {|f|
-      if FileTest.executable? f
-        fortune = f
-        break
-      end
-    }
-    m.reply "fortune binary not found" unless fortune
-    ret = Utils.safe_exec(fortune, "-n", "255", "-s", db)
-    m.reply ret.gsub(/\t/, "  ").split(/\n/).join(" ")
-    return
-  end
-end
-plugin = FortunePlugin.new
-plugin.map 'fortune :db', :defaults => {:db => 'fortunes'},
-                          :requirements => {:db => /^[^-][\w-]+$/}
diff --git a/rbot/plugins/freshmeat.rb b/rbot/plugins/freshmeat.rb
deleted file mode 100644 (file)
index 20fa724..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-require 'rexml/document'
-require 'uri/common'
-
-class FreshmeatPlugin < Plugin
-  include REXML
-  def help(plugin, topic="")
-    "freshmeat search [<max>=4] <string> => search freshmeat for <string>, freshmeat [<max>=4] => return up to <max> freshmeat headlines"
-  end
-  
-  def search_freshmeat(m, params)
-    max = params[:limit].to_i
-    search = params[:search].to_s
-    max = 8 if max > 8
-    begin
-      xml = @bot.httputil.get(URI.parse("http://freshmeat.net/search-xml/?orderby=locate_projectname_full_DESC&q=#{URI.escape(search)}"))
-    rescue URI::InvalidURIError, URI::BadURIError => e
-      m.reply "illegal search string #{search}"
-      return
-    end
-    unless xml
-      m.reply "search for #{search} failed"
-      return
-    end
-    doc = Document.new xml
-    unless doc
-      m.reply "search for #{search} failed"
-      return
-    end
-    matches = Array.new
-    max_width = 250
-    title_width = 0
-    url_width = 0
-    done = 0
-    doc.elements.each("*/match") {|e|
-      name = e.elements["projectname_short"].text
-      url = "http://freshmeat.net/projects/#{name}/"
-      desc = e.elements["desc_short"].text
-      title = e.elements["projectname_full"].text
-      #title_width = title.length if title.length > title_width
-      url_width = url.length if url.length > url_width
-      matches << [title, url, desc]
-      done += 1
-      break if done >= max
-    }
-    if matches.length == 0
-      m.reply "not found: #{search}"
-    end
-    matches.each {|mat|
-      title = mat[0]
-      url = mat[1]
-      desc = mat[2]
-      desc.gsub!(/(.{#{max_width - 3 - url_width}}).*/, '\1..')
-      reply = sprintf("%s | %s", url.ljust(url_width), desc)
-      m.reply reply
-    }
-  end
-  
-  def freshmeat(m, params)
-    max = params[:limit].to_i
-    max = 8 if max > 8
-    xml = @bot.httputil.get(URI.parse("http://images.feedstermedia.com/feedcache/ostg/freshmeat/fm-releases-global.xml"))
-    unless xml
-      m.reply "freshmeat news parse failed"
-      return
-    end
-    doc = Document.new xml
-    unless doc
-      m.reply "freshmeat news parse failed"
-      return
-    end
-    matches = Array.new
-    max_width = 60
-    title_width = 0
-    done = 0
-    doc.elements.each("*/channel/item") {|e|
-      desc = e.elements["description"].text
-      title = e.elements["title"].text
-      #title.gsub!(/\s+\(.*\)\s*$/, "")
-      title.strip!
-      title_width = title.length if title.length > title_width
-      matches << [title, desc]
-      done += 1
-      break if done >= max
-    }
-    matches.each {|mat|
-      title = mat[0]
-      #desc = mat[1]
-      #desc.gsub!(/(.{#{max_width - 3 - title_width}}).*/, '\1..')
-      #reply = sprintf("%#{title_width}s | %s", title, desc)
-      m.reply title
-    }
-  end
-end
-plugin = FreshmeatPlugin.new
-plugin.map 'freshmeat search :limit *search', :action => 'search_freshmeat',
-            :defaults => {:limit => 4}, :requirements => {:limit => /^\d+$/}
-plugin.map 'freshmeat :limit', :defaults => {:limit => 4}, 
-                               :requirements => {:limit => /^\d+$/}
diff --git a/rbot/plugins/google.rb b/rbot/plugins/google.rb
deleted file mode 100644 (file)
index cd96f23..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-require 'net/http'
-require 'uri/common'
-
-Net::HTTP.version_1_2
-
-class GooglePlugin < Plugin
-  def help(plugin, topic="")
-    "search <string> => search google for <string>"
-  end
-  def privmsg(m)
-    unless(m.params && m.params.length > 0)
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-    searchfor = URI.escape m.params
-
-    query = "/search?q=#{searchfor}&btnI=I%27m%20feeling%20lucky"
-    result = "not found!"
-
-    proxy_host = nil
-    proxy_port = nil
-
-    if(ENV['http_proxy'])
-      if(ENV['http_proxy'] =~ /^http:\/\/(.+):(\d+)$/)
-        proxy_host = $1
-        proxy_port = $2
-      end
-    end
-
-    http = @bot.httputil.get_proxy(URI.parse("http://www.google.com"))
-
-    begin
-      http.start {|http|
-        resp = http.get(query)
-        if resp.code == "302"
-          result = resp['location']
-        end
-      }
-    rescue => e
-      p e
-      if e.response && e.response['location']
-        result = e.response['location']
-      else
-        result = "error!"
-      end
-    end
-    m.reply "#{m.params}: #{result}"
-  end
-end
-plugin = GooglePlugin.new
-plugin.register("search")
diff --git a/rbot/plugins/host.rb b/rbot/plugins/host.rb
deleted file mode 100644 (file)
index ef8dc8b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-class HostPlugin < Plugin
-  def help(plugin, topic="")
-    "host <domain> => query nameserver about domain names and zones for <domain>"
-  end
-  def privmsg(m)
-    unless(m.params =~ /^(\w|-|\.)+$/)
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-    m.reply Utils.safe_exec("host", m.params)
-  end
-end
-plugin = HostPlugin.new
-plugin.register("host")
diff --git a/rbot/plugins/httpd.rb b/rbot/plugins/httpd.rb
deleted file mode 100644 (file)
index 92fe3a8..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'webrick'
-
-class HttpPlugin < Plugin
-  include WEBrick
-
-
-  def initialize
-    super
-    @http_server = HTTPServer.new(
-      :Port => 5555
-    )
-    @http_server.mount_proc("/") { |req, resp|
-      resp['content-type'] = 'text/html'
-      resp.body = "<html><head><title>rbot httpd plugin</title></head><body>"
-      resp.body += "#{@bot.status} <br />"
-      resp.body += "hello from rbot."
-      resp.body += "</body>"
-      raise HTTPStatus::OK
-    }
-    Thread.new {
-      @http_server.start
-    }
-  end
-  def cleanup
-    @http_server.shutdown
-  end
-  def help(plugin, topic="")
-    "no help yet"
-  end
-  def privmsg(m)
-  end
-end
-
-plugin = HttpPlugin.new
-plugin.register("http")
diff --git a/rbot/plugins/insult.rb b/rbot/plugins/insult.rb
deleted file mode 100644 (file)
index 5f0384e..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-class InsultPlugin < Plugin
-
-## insults courtesy of http://insulthost.colorado.edu/
-
-##
-# Adjectives
-##
-@@adj = [
-"acidic",
-"antique",
-"contemptible",
-"culturally-unsound",
-"despicable",
-"evil",
-"fermented",
-"festering",
-"foul",
-"fulminating",
-"humid",
-"impure",
-"inept",
-"inferior",
-"industrial",
-"left-over",
-"low-quality",
-"malodorous",
-"off-color",
-"penguin-molesting",
-"petrified",
-"pointy-nosed",
-"salty",
-"sausage-snorfling",
-"tastless",
-"tempestuous",
-"tepid",
-"tofu-nibbling",
-"unintelligent",
-"unoriginal",
-"uninspiring",
-"weasel-smelling",
-"wretched",
-"spam-sucking",
-"egg-sucking",
-"decayed",
-"halfbaked",
-"infected",
-"squishy",
-"porous",
-"pickled",
-"coughed-up",
-"thick",
-"vapid",
-"hacked-up",
-"unmuzzled",
-"bawdy",
-"vain",
-"lumpish",
-"churlish",
-"fobbing",
-"rank",
-"craven",
-"puking",
-"jarring",
-"fly-bitten",
-"pox-marked",
-"fen-sucked",
-"spongy",
-"droning",
-"gleeking",
-"warped",
-"currish",
-"milk-livered",
-"surly",
-"mammering",
-"ill-borne",
-"beef-witted",
-"tickle-brained",
-"half-faced",
-"headless",
-"wayward",
-"rump-fed",
-"onion-eyed",
-"beslubbering",
-"villainous",
-"lewd-minded",
-"cockered",
-"full-gorged",
-"rude-snouted",
-"crook-pated",
-"pribbling",
-"dread-bolted",
-"fool-born",
-"puny",
-"fawning",
-"sheep-biting",
-"dankish",
-"goatish",
-"weather-bitten",
-"knotty-pated",
-"malt-wormy",
-"saucyspleened",
-"motley-mind",
-"it-fowling",
-"vassal-willed",
-"loggerheaded",
-"clapper-clawed",
-"frothy",
-"ruttish",
-"clouted",
-"common-kissing",
-"pignutted",
-"folly-fallen",
-"plume-plucked",
-"flap-mouthed",
-"swag-bellied",
-"dizzy-eyed",
-"gorbellied",
-"weedy",
-"reeky",
-"measled",
-"spur-galled",
-"mangled",
-"impertinent",
-"bootless",
-"toad-spotted",
-"hasty-witted",
-"horn-beat",
-"yeasty",
-"boil-brained",
-"tottering",
-"hedge-born",
-"hugger-muggered",
-"elf-skinned",
-]
-
-##
-# Amounts 
-##
-@@amt = [
-"accumulation",
-"bucket",
-"coagulation",
-"enema-bucketful",
-"gob",
-"half-mouthful",
-"heap",
-"mass",
-"mound",
-"petrification",
-"pile",
-"puddle",
-"stack",
-"thimbleful",
-"tongueful",
-"ooze",
-"quart",
-"bag",
-"plate",
-"ass-full",
-"assload",
-]
-
-##
-# Objects
-##
-@@noun = [
-"bat toenails",
-"bug spit",
-"cat hair",
-"chicken piss",
-"dog vomit",
-"dung",
-"fat-woman's stomach-bile",
-"fish heads",
-"guano",
-"gunk",
-"pond scum",
-"rat retch",
-"red dye number-9",
-"Sun IPC manuals",
-"waffle-house grits",
-"yoo-hoo",
-"dog balls",
-"seagull puke",
-"cat bladders",
-"pus",
-"urine samples",
-"squirrel guts",
-"snake assholes",
-"snake bait",
-"buzzard gizzards",
-"cat-hair-balls",
-"rat-farts",
-"pods",
-"armadillo snouts",
-"entrails",
-"snake snot",
-"eel ooze",
-"slurpee-backwash",
-"toxic waste",
-"Stimpy-drool",
-"poopy",
-"poop",
-"craptacular carpet droppings",
-"jizzum",
-"cold sores",
-"anal warts",
-]
-  
-  def help(plugin, topic="")
-    if(plugin == "insult")
-      return "insult me|<person> => insult you or <person>"
-    elsif(plugin == "msginsult")
-      return "msginsult <nick> => insult <nick> via /msg"
-    else
-      return "insult module topics: msginsult, insult"
-    end
-  end
-  def privmsg(m)
-    suffix=""
-    unless(m.params)
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-    msgto = m.channel
-    if(m.plugin =~ /^msginsult$/)
-      prefix = "you are "
-      if (m.params =~ /^#/)
-        prefix += "all "
-      end
-      msgto = m.params
-      suffix = " (from #{m.sourcenick})"
-    elsif(m.params =~ /^me$/)
-      prefix = "you are "
-    else
-      prefix = "#{m.params} is "
-    end
-    insult = generate_insult
-    @bot.say msgto, prefix + insult + suffix
-  end
-  def generate_insult
-    adj = @@adj[rand(@@adj.length)]
-    adj2 = ""
-    loop do
-      adj2 = @@adj[rand(@@adj.length)]
-      break if adj2 != adj
-    end
-    amt = @@amt[rand(@@amt.length)]
-    noun = @@noun[rand(@@noun.length)]
-    start = "a "
-    start = "an " if ['a','e','i','o','u'].include?(adj[0].chr)
-    "#{start}#{adj} #{amt} of #{adj2} #{noun}"
-  end
-end
-plugin = InsultPlugin.new
-plugin.register("insult")
-plugin.register("msginsult")
-
diff --git a/rbot/plugins/karma.rb b/rbot/plugins/karma.rb
deleted file mode 100644 (file)
index 148427a..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-class KarmaPlugin < Plugin
-  def initialize
-    super
-
-    # this plugin only wants to store ints!
-    class << @registry
-      def store(val)
-        val.to_i
-      end
-      def restore(val)
-        val.to_i
-      end
-    end
-    @registry.set_default(0)
-
-    # import if old file format found
-    if(File.exist?("#{@bot.botclass}/karma.rbot"))
-      puts "importing old karma data"
-      IO.foreach("#{@bot.botclass}/karma.rbot") do |line|
-        if(line =~ /^(\S+)<=>([\d-]+)$/)
-          item = $1
-          karma = $2.to_i
-          @registry[item] = karma
-        end
-      end
-      File.delete("#{@bot.botclass}/karma.rbot")
-    end
-
-  end
-
-  def stats(m, params)
-    if (@registry.length)
-      max = @registry.values.max
-      min = @registry.values.min
-      best = @registry.to_hash.index(max)
-      worst = @registry.to_hash.index(min)
-      m.reply "#{@registry.length} items. Best: #{best} (#{max}); Worst: #{worst} (#{min})"
-    end
-  end
-
-  def karma(m, params)
-    thing = params[:key]
-    thing = m.sourcenick unless thing
-    thing = thing.to_s
-    karma = @registry[thing]
-    if(karma != 0)
-      m.reply "karma for #{thing}: #{@registry[thing]}"
-    else
-      m.reply "#{thing} has neutral karma"
-    end
-  end
-  
-  
-  def help(plugin, topic="")
-    "karma module: <thing>++/<thing>-- => increase/decrease karma for <thing>, karma for <thing>? => show karma for <thing>, karmastats => show stats. Karma is a community rating system - only in-channel messages can affect karma and you cannot adjust your own."
-  end
-  def listen(m)
-    return unless m.kind_of?(PrivMessage) && m.public?
-    # in channel message, the kind we are interested in
-    if(m.message =~ /(\+\+|--)/)
-      string = m.message.sub(/\W(--|\+\+)(\(.*?\)|[^(++)(\-\-)\s]+)/, "\2\1")
-      seen = Hash.new
-      while(string.sub!(/(\(.*?\)|[^(++)(\-\-)\s]+)(\+\+|--)/, ""))
-        key = $1
-        change = $2
-        next if seen[key]
-        seen[key] = true
-
-        key.sub!(/^\((.*)\)$/, "\1")
-        key.gsub!(/\s+/, " ")
-        next unless(key.length > 0)
-        next if(key == m.sourcenick)
-        if(change == "++")
-          @registry[key] += 1
-        elsif(change == "--")
-          @registry[key] -= 1
-        end
-      end
-    end
-  end
-end
-plugin = KarmaPlugin.new
-plugin.map 'karmastats', :action => 'stats'
-plugin.map 'karma :key', :defaults => {:key => false}
-plugin.map 'karma for :key'
diff --git a/rbot/plugins/lart.rb b/rbot/plugins/lart.rb
deleted file mode 100644 (file)
index de76719..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-#  Author:     Michael Brailsford  <brailsmt@yahoo.com>
-#              aka brailsmt
-#  Purpose:       Provide for humorous larts and praises
-#  Copyright:  2002 Michael Brailsford.  All rights reserved.
-#  License:    This plugin is licensed under the BSD license.  The terms of
-#              which follow.
-#
-#  Redistribution and use in source and binary forms, with or without
-#  modification, are permitted provided that the following conditions
-#  are met:
-#
-#  1. Redistributions of source code must retain the above copyright notice,
-#     this list of conditions and the following disclaimer.
-#
-#  2. Redistributions in binary form must reproduce the above copyright
-#     notice, this list of conditions and the following disclaimer in the
-#     documentation and/or other materials provided with the distribution.
-
-class LartPlugin < Plugin
-
-       # Keep a 1:1 relation between commands and handlers
-       @@handlers = {
-               "lart" => "handle_lart",
-               "praise" => "handle_praise",
-               "addlart" => "handle_addlart",
-               "rmlart" => "handle_rmlart",
-               "addpraise" => "handle_addpraise",
-               "rmpraise" => "handle_rmpraise"
-       }
-
-  def name
-    "lart"
-  end
-
-       #{{{
-       def initialize
-               super
-               @larts = Array.new
-               @praises = Array.new
-               #read in the lart and praise files
-               if File.exists? "#{@bot.botclass}/lart/larts"
-                       IO.foreach("#{@bot.botclass}/lart/larts") { |line|
-                               @larts << line.chomp
-                       }
-               end
-               if File.exists? "#{@bot.botclass}/lart/praises"
-                       IO.foreach("#{@bot.botclass}/lart/praises") { |line|
-                               @praises << line.chomp
-                       }
-               end
-       end
-       #}}}
-       #{{{
-       def cleanup
-       end
-       #}}}
-       #{{{
-       def save
-               Dir.mkdir("#{@bot.botclass}/lart") if not FileTest.directory? "#{@bot.botclass}/lart"
-               File.open("#{@bot.botclass}/lart/larts", "w") { |file|
-                       file.puts @larts
-               }
-               File.open("#{@bot.botclass}/lart/praises", "w") { |file|
-                       file.puts @praises
-               }
-       end
-       #}}}
-       #{{{
-       def privmsg(m)
-               if not m.params
-                       m.reply "What a crazy fool!  Did you mean |help stats?"
-                       return
-               end
-
-               meth = self.method(@@handlers[m.plugin])
-               meth.call(m) if(@bot.auth.allow?(m.plugin, m.source, m.replyto))
-       end
-       #}}}
-       #{{{
-       def help(plugin, topic="")
-               "Lart:  The lart plugin allows you to punish/praise someone in the channel.  You can also add new punishments and new praises as well as delete them.  For the curious, LART is an acronym for Luser Attitude Readjustment Tool.\nUsage:  punish/lart <nick> <reason>  --  punishes <nick> for <reason>.  The reason is optional.\n        praise <nick> <reason>  --  praises <nick> for <reason>.  The reason is optional.\n        mod[lart|punish|praise] [add|remove]  --  Add or remove a lart or praise."
-       end
-       #}}}
-       # The following are command handlers    {{{
-       #{{{
-       def handle_lart(m)
-               for_idx = m.params =~ /\s+\bfor\b/
-               if for_idx
-                       nick = m.params[0, for_idx]
-               else
-                       nick = m.params
-               end
-               lart = @larts[get_msg_idx(@larts.length)]
-               if lart == nil
-                       m.reply "I dunno any larts"
-                       return
-               end
-               if nick == @bot.nick
-                       lart = replace_who lart, m.sourcenick
-                       lart << " for trying to make me lart myself"
-               else
-                       lart = replace_who lart, nick
-                       lart << m.params[for_idx, m.params.length] if for_idx
-               end
-
-               @bot.action m.replyto, lart
-       end
-       #}}}
-       #{{{
-       def handle_praise(m)
-               for_idx = m.params =~ /\s+\bfor\b/
-               if for_idx
-                       nick = m.params[0, for_idx]
-               else
-                       nick = m.params
-               end
-               praise = @praises[get_msg_idx(@praises.length)]
-               if not praise
-                       m.reply "I dunno any praises"
-                       return
-               end
-
-               if nick == m.sourcenick
-                       praise = @larts[get_msg_idx(@larts.length)]
-                       praise = replace_who praise, nick
-               else
-                       praise = replace_who praise, nick
-                       praise << m.params.gsub(/#{nick}/, "")
-               end
-
-               @bot.action m.replyto, praise
-       end
-       #}}}
-       #{{{
-       def handle_addlart(m)
-               @larts << m.params
-               m.okay
-       end
-       #}}}
-       #{{{
-       def handle_rmlart(m)
-               @larts.delete m.params
-               m.okay
-       end
-       #}}}
-       #{{{
-       def handle_addpraise(m)
-               @praises << m.params
-               m.okay
-       end
-       #}}}
-       #{{{
-       def handle_rmpraise(m)
-               @praises.delete m.params
-               m.okay
-       end
-       #}}}
-       #}}}
-
-       #  The following are utils for larts/praises    {{{
-       #{{{
-       def replace_who(msg, nick)
-               msg.gsub(/<who>/i, "#{nick}")
-       end
-       #}}}
-       #{{{
-       def get_msg_idx(max)
-               idx = rand(max)
-       end
-       #}}}
-       #}}}
-end
-plugin = LartPlugin.new
-plugin.register("lart")
-plugin.register("praise")
-
-plugin.register("addlart")
-plugin.register("addpraise")
-
-plugin.register("rmlart")
-plugin.register("rmpraise")
diff --git a/rbot/plugins/math.rb b/rbot/plugins/math.rb
deleted file mode 100644 (file)
index 4a20738..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-class MathPlugin < Plugin
-  @@digits = {
-     "first" => "1",
-     "second" => "2",
-     "third" => "3",
-     "fourth" => "4",
-     "fifth" => "5",
-     "sixth" => "6",
-     "seventh" => "7",
-     "eighth" => "8",
-     "ninth" => "9",
-     "tenth" => "10",
-     "one" => "1",
-     "two" => "2",
-     "three" => "3",
-     "four" => "4",
-     "five" => "5",
-     "six" => "6",
-     "seven" => "7",
-     "eight" => "8",
-     "nine" => "9",
-     "ten" => "10"
-  };
-
-  def help(plugin, topic="")
-    "math <expression>, evaluate mathematical expression"
-  end
-  def privmsg(m)
-    unless(m.params)
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-
-    expr = m.params.dup
-    @@digits.each {|k,v|
-      expr.gsub!(/\b#{k}\b/, v)
-    }
-
-    while expr =~ /(exp ([\w\d]+))/
-      exp = $1
-      val = Math.exp($2).to_s
-      expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
-    end
-    
-    while expr =~ /^\s*(dec2hex\s*(\d+))\s*\?*/
-      exp = $1
-      val = sprintf("%x", $2)
-      expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
-    end
-
-    expr.gsub(/\be\b/, Math.exp(1).to_s)
-
-    while expr =~ /(log\s*((\d+\.?\d*)|\d*\.?\d+))\s*/
-      exp = $1
-      res = $2
-      
-      if res == 0
-        val = "Infinity"
-      else
-        val = Math.log(res).to_s
-      end
-      
-      expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
-    end
-
-    while expr =~ /(bin2dec ([01]+))/
-      exp = $1
-      val = join('', unpack('B*', pack('N', $2)))
-      val.gsub!(/^0+/, "")
-      expr.gsub!(/#{Regexp.escape exp}/, "+#{val}")
-    end
-
-    expr.gsub!(/ to the power of /, " ** ")
-    expr.gsub!(/ to the /, " ** ")
-    expr.gsub!(/\btimes\b/, "*")
-    expr.gsub!(/\bdiv(ided by)? /, "/ ")
-    expr.gsub!(/\bover /, "/ ")
-    expr.gsub!(/\bsquared/, "**2 ")
-    expr.gsub!(/\bcubed/, "**3 ")
-    expr.gsub!(/\bto\s+(\d+)(r?st|nd|rd|th)?( power)?/, "**\1 ")
-    expr.gsub!(/\bpercent of/, "*0.01*")
-    expr.gsub!(/\bpercent/, "*0.01")
-    expr.gsub!(/\% of\b/, "*0.01*")
-    expr.gsub!(/\%/, "*0.01")
-    expr.gsub!(/\bsquare root of (\d+)/, "\1 ** 0.5 ")
-    expr.gsub!(/\bcubed? root of (\d+)/, "\1 **(1.0/3.0) ")
-    expr.gsub!(/ of /, " * ")
-    expr.gsub!(/(bit(-| )?)?xor(\'?e?d( with))?/, "^")
-    expr.gsub!(/(bit(-| )?)?or(\'?e?d( with))?/, "|")
-    expr.gsub!(/bit(-| )?and(\'?e?d( with))?/, "& ")
-    expr.gsub!(/(plus|and)/, "+")
-
-    if (expr =~ /^\s*[-\d*+\s()\/^\.\|\&\*\!]+\s*$/ &&
-       expr !~ /^\s*\(?\d+\.?\d*\)?\s*$/ &&
-       expr !~ /^\s*$/ &&
-       expr !~ /^\s*[( )]+\s*$/)
-
-       begin
-         debug "evaluating expression \"#{expr}\""
-         answer = eval(expr)
-         if answer =~ /^[-+\de\.]+$/
-           answer = sprintf("%1.12f", answer)
-           answer.gsub!(/\.?0+$/, "")
-           answer.gsub!(/(\.\d+)000\d+/, '\1')
-           if (answer.length > 30)
-             answer = "a number with >30 digits..."
-           end
-         end
-         m.reply answer
-       rescue Exception => e
-         puts "couldn't evaluate expression \"#{m.params}\": #{e}"
-         m.reply "illegal expression \"#{m.params}\""
-         return
-       end
-    else
-      m.reply "illegal expression \"#{m.params}\""
-      return
-    end
-  end
-end
-plugin = MathPlugin.new
-plugin.register("math")
diff --git a/rbot/plugins/nickserv.rb b/rbot/plugins/nickserv.rb
deleted file mode 100644 (file)
index 1ef2baf..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-# automatically lookup nicks in @registry and identify when asked
-
-class NickServPlugin < Plugin
-  
-  def help(plugin, topic="")
-    case topic
-    when ""
-      return "nickserv plugin: handles nickserv protected IRC nicks. topics password, register, identify, listnicks"
-    when "password"
-      return "nickserv password <nick> <passwd>: remember the password for nick <nick> and use it to identify in future"
-    when "register"
-      return "nickserv register [<password> [<email>]]: register the current nick, choosing a random password unless <password> is supplied - current nick must not already be registered for this to work. Also specify email if required by your services"
-    when "identify"
-      return "nickserv identify: identify with nickserv - shouldn't be needed - bot should identify with nickserv immediately on request - however this could be useful after splits or service disruptions, or when you just set the password for the current nick"
-    when "listnicks"
-      return "nickserv listnicks: lists nicknames and associated password the bot knows about - you will need config level auth access to do this one and it will reply by privmsg only"
-    end
-  end
-  
-  def initialize
-    super
-    # this plugin only wants to store strings!
-    class << @registry
-      def store(val)
-        val
-      end
-      def restore(val)
-        val
-      end
-    end
-  end
-  
-  def privmsg(m)
-    return unless m.params
-    
-    case m.params
-    when (/^password\s*(\S*)\s*(.*)$/)
-      nick = $1
-      passwd = $2
-      @registry[nick] = passwd
-      m.okay
-    when (/^register$/)
-      passwd = genpasswd
-      @bot.sendmsg "PRIVMSG", "NickServ", "REGISTER " + passwd
-      @registry[@bot.nick] = passwd
-      m.okay
-    when (/^register\s*(\S*)\s*(.*)$/)
-      passwd = $1
-      email = $2
-      @bot.sendmsg "PRIVMSG", "NickServ", "REGISTER " + passwd + " " + email
-      @registry[@bot.nick] = passwd
-      m.okay
-    when (/^register\s*(.*)\s*$/)
-      passwd = $1
-      @bot.sendmsg "PRIVMSG", "NickServ", "REGISTER " + passwd
-      @registry[@bot.nick] = passwd
-      m.okay
-    when (/^listnicks$/)
-      if @bot.auth.allow?("config", m.source, m.replyto)
-        if @registry.length > 0
-          @registry.each {|k,v|
-            @bot.say m.sourcenick, "#{k} => #{v}"
-          }
-        else
-          m.reply "none known"
-        end
-      end
-    when (/^identify$/)
-      if @registry.has_key?(@bot.nick)
-        @bot.sendmsg "PRIVMSG", "NickServ", "IDENTIFY " + @registry[@bot.nick]
-        m.okay
-      else
-        m.reply "I dunno the nickserv password for the nickname #{@bot.nick} :("
-      end
-    end
-  end
-  
-  def listen(m)
-    return unless(m.kind_of? NoticeMessage)
-
-    if (m.sourcenick == "NickServ" && m.message =~ /This nickname is owned by someone else/)
-      puts "nickserv asked us to identify for nick #{@bot.nick}"
-      if @registry.has_key?(@bot.nick)
-        @bot.sendmsg "PRIVMSG", "NickServ", "IDENTIFY " + @registry[@bot.nick]
-      end
-    end
-  end
-
-  def genpasswd
-    # generate a random password
-    passwd = ""
-    8.times do
-      passwd += (rand(26) + (rand(2) == 0 ? 65 : 97) ).chr
-    end
-    return passwd
-  end
-end
-plugin = NickServPlugin.new
-plugin.register("nickserv")
diff --git a/rbot/plugins/nslookup.rb b/rbot/plugins/nslookup.rb
deleted file mode 100644 (file)
index 92da1ba..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-class DnsPlugin < Plugin
-  begin
-    require 'resolv-replace'
-    def gethostname(address)
-      Resolv.getname(address)
-    end
-    def getaddresses(name)
-      Resolv.getaddresses(name)
-    end
-  rescue LoadError
-    def gethostname(address)
-      Socket.gethostbyname(address).first
-    end
-    def getaddresses(name)
-      a = Socket.gethostbyname(name)
-      list = Socket.getaddrinfo(a[0], 'http')
-      addresses = Array.new
-      list.each {|line|
-       addresses << line[3]
-      }
-      addresses
-    end
-  end
-
-  def help(plugin, topic="")
-    "nslookup|dns <hostname|ip> => show local resolution results for hostname or ip address"
-  end
-  def privmsg(m)
-    unless(m.params)
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-    Thread.new do
-      if(m.params =~ /^\d+\.\d+\.\d+\.\d+$/)
-       begin
-         a = gethostname(m.params)
-         m.reply m.params + ": " + a if a
-       rescue StandardError => err
-         m.reply "#{m.params}: not found"
-       end
-      elsif(m.params =~ /^\S+$/)
-       begin
-         a = getaddresses(m.params)
-         m.reply m.params + ": " + a.join(", ")
-       rescue StandardError => err
-         m.reply "#{m.params}: not found"
-       end
-      else
-       m.reply "incorrect usage: " + help(m.plugin)
-      end
-    end
-  end
-end
-plugin = DnsPlugin.new
-plugin.register("nslookup")
-plugin.register("dns")
diff --git a/rbot/plugins/opmeh.rb b/rbot/plugins/opmeh.rb
deleted file mode 100644 (file)
index 2776de6..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-class OpMehPlugin < Plugin\r
-\r
-  def help(plugin, topic="")\r
-    return "opmeh <channel> => grant user ops in <channel>"\r
-  end\r
-\r
-  def privmsg(m)\r
-    if(m.params)\r
-      channel = m.params\r
-    else\r
-      channel = m.channel\r
-    end\r
-    target = m.sourcenick\r
-    @bot.sendq("MODE #{channel} +o #{target}")\r
-    m.okay\r
-  end\r
-end\r
-plugin = OpMehPlugin.new\r
-plugin.register("opmeh")\r
diff --git a/rbot/plugins/quotes.rb b/rbot/plugins/quotes.rb
deleted file mode 100644 (file)
index 674a9ed..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-Quote = Struct.new("Quote", "num", "date", "source", "quote")
-
-class QuotePlugin < Plugin
-  def initialize
-    super
-    @lists = Hash.new
-    Dir["#{@bot.botclass}/quotes/*"].each {|f|
-      channel = File.basename(f)
-      @lists[channel] = Array.new if(!@lists.has_key?(channel))
-      IO.foreach(f) {|line|
-        if(line =~ /^(\d+) \| ([^|]+) \| (\S+) \| (.*)$/)
-          num = $1.to_i
-          @lists[channel][num] = Quote.new(num, $2, $3, $4)
-        end
-      }
-    }
-  end
-  def save
-    Dir.mkdir("#{@bot.botclass}/quotes") if(!FileTest.directory?("#{@bot.botclass}/quotes"))
-    @lists.each {|channel, quotes|
-      File.open("#{@bot.botclass}/quotes/#{channel}", "w") {|file|
-        quotes.compact.each {|q| 
-          file.puts "#{q.num} | #{q.date} | #{q.source} | #{q.quote}"
-        }
-      }
-    }
-  end
-  def addquote(source, channel, quote)
-    @lists[channel] = Array.new if(!@lists.has_key?(channel))
-    num = @lists[channel].length 
-    @lists[channel][num] = Quote.new(num, Time.new, source, quote)
-    return num
-  end
-  def getquote(source, channel, num=nil)
-    return nil unless(@lists.has_key?(channel))
-    return nil unless(@lists[channel].length > 0)
-    if(num)
-      if(@lists[channel][num])
-        return @lists[channel][num], @lists[channel].length - 1
-      end
-    else
-      # random quote
-      return @lists[channel].compact[rand(@lists[channel].nitems)],
-                                       @lists[channel].length - 1
-    end
-  end
-  def delquote(channel, num)
-    return false unless(@lists.has_key?(channel))
-    return false unless(@lists[channel].length > 0)
-    if(@lists[channel][num])
-      @lists[channel][num] = nil
-      return true
-    end
-    return false
-  end
-  def countquote(source, channel=nil, regexp=nil)
-    unless(channel)
-      total=0
-      @lists.each_value {|l|
-        total += l.compact.length
-      }
-      return total
-    end
-    return 0 unless(@lists.has_key?(channel))
-    return 0 unless(@lists[channel].length > 0)
-    if(regexp)
-      matches = @lists[channel].compact.find_all {|a| a.quote =~ /#{regexp}/i }
-    else
-      matches = @lists[channel].compact
-    end
-    return matches.length
-  end
-  def searchquote(source, channel, regexp)
-    return nil unless(@lists.has_key?(channel))
-    return nil unless(@lists[channel].length > 0)
-    matches = @lists[channel].compact.find_all {|a| a.quote =~ /#{regexp}/i }
-    if(matches.length > 0)
-       return matches[rand(matches.length)], @lists[channel].length - 1
-    else
-      return nil
-    end
-  end
-  def help(plugin, topic="")
-    case topic
-      when "addquote"
-        return "addquote [<channel>] <quote> => Add quote <quote> for channel <channel>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !addquote without addressing if so configured"
-      when "delquote"
-        return "delquote [<channel>] <num> => delete quote from <channel> with number <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !delquote without addressing if so configured"
-      when "getquote"
-        return "getquote [<channel>] [<num>] => get quote from <channel> with number <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Without <num>, a random quote will be returned. Responds to !getquote without addressing if so configured"
-      when "searchquote"
-        return "searchquote [<channel>] <regexp> => search for quote from <channel> that matches <regexp>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !searchquote without addressing if so configured"
-      when "topicquote"
-        return "topicquote [<channel>] [<num>] => set topic to quote from <channel> with number <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Without <num>, a random quote will be set. Responds to !topicquote without addressing if so configured"
-      when "countquote"
-        return "countquote [<channel>] <regexp> => count quotes from <channel> that match <regexp>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !countquote without addressing if so configured"
-      when "whoquote"
-        return "whoquote [<channel>] <num> => show who added quote <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately"
-      when "whenquote"
-        return "whenquote [<channel>] <num> => show when quote <num> was added. You only need to supply <channel> if you are addressing #{@bot.nick} privately"
-      else
-        return "Quote module (Quote storage and retrieval) topics: addquote, getquote, searchquote, topicquote, countquote, whoquote, whenquote"
-    end
-  end
-  def listen(m)
-    return unless(m.kind_of? PrivMessage)
-
-    command = m.message.dup
-    if(m.address? && m.private?)
-      case command
-        when (/^addquote\s+(#\S+)\s+(.*)/)
-          channel = $1
-          quote = $2
-          if(@bot.auth.allow?("addquote", m.source, m.replyto))
-            if(channel =~ /^#/)
-              num = addquote(m.source, channel, quote)
-              m.reply "added the quote (##{num})"
-            end
-          end
-        when (/^getquote\s+(#\S+)$/)
-          channel = $1
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = getquote(m.source, channel)
-            if(quote)
-              m.reply "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^getquote\s+(#\S+)\s+(\d+)$/)
-          channel = $1
-          num = $2.to_i
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = getquote(m.source, channel, num)
-            if(quote)
-              m.reply "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^whoquote\s+(#\S+)\s+(\d+)$/)
-          channel = $1
-          num = $2.to_i
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = getquote(m.source, channel, num)
-            if(quote)
-              m.reply "quote #{quote.num} added by #{quote.source}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^whenquote\s+(#\S+)\s+(\d+)$/)
-          channel = $1
-          num = $2.to_i
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = getquote(m.source, channel, num)
-            if(quote)
-              m.reply "quote #{quote.num} added on #{quote.date}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^topicquote\s+(#\S+)$/)
-          channel = $1
-          if(@bot.auth.allow?("topicquote", m.source, m.replyto))
-            quote, total = getquote(m.source, channel)
-            if(quote)
-              @bot.topic channel, "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^topicquote\s+(#\S+)\s+(\d+)$/)
-          channel = $1
-          num = $2.to_i
-          if(@bot.auth.allow?("topicquote", m.source, m.replyto))
-            quote, total = getquote(m.source, channel, num)
-            if(quote)
-              @bot.topic channel, "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^delquote\s+(#\S+)\s+(\d+)$/)
-          channel = $1
-          num = $2.to_i
-          if(@bot.auth.allow?("delquote", m.source, m.replyto))
-            if(delquote(channel, num))
-              m.okay
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^searchquote\s+(#\S+)\s+(.*)$/)
-          channel = $1
-          reg = $2
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = searchquote(m.source, channel, reg)
-            if(quote)
-              m.reply "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^countquote$/)
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            total = countquote(m.source)
-            m.reply "#{total} quotes"
-          end
-        when (/^countquote\s+(#\S+)\s*(.*)$/)
-          channel = $1
-          reg = $2
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            total = countquote(m.source, channel, reg)
-            if(reg.length > 0)
-              m.reply "#{total} quotes match: #{reg}"
-            else
-              m.reply "#{total} quotes"
-            end
-          end
-      end
-    elsif (m.address? || (@bot.config["QUOTE_LISTEN"] && command.gsub!(/^!/, "")))
-      case command
-        when (/^addquote\s+(.+)/)
-          if(@bot.auth.allow?("addquote", m.source, m.replyto))
-            num = addquote(m.source, m.target, $1)
-            m.reply "added the quote (##{num})"
-          end
-        when (/^getquote$/)
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = getquote(m.source, m.target)
-            if(quote)
-              m.reply "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "no quotes found!"
-            end
-          end
-        when (/^getquote\s+(\d+)$/)
-          num = $1.to_i
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = getquote(m.source, m.target, num)
-            if(quote)
-              m.reply "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^whenquote\s+(\d+)$/)
-          num = $1.to_i
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = getquote(m.source, m.target, num)
-            if(quote)
-              m.reply "quote #{quote.num} added on #{quote.date}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^whoquote\s+(\d+)$/)
-          num = $1.to_i
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = getquote(m.source, m.target, num)
-            if(quote)
-              m.reply "quote #{quote.num} added by #{quote.source}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^topicquote$/)
-          if(@bot.auth.allow?("topicquote", m.source, m.replyto))
-            quote, total = getquote(m.source, m.target)
-            if(quote)
-              @bot.topic m.target, "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "no quotes found!"
-            end
-          end
-        when (/^topicquote\s+(\d+)$/)
-          num = $1.to_i
-          if(@bot.auth.allow?("topicquote", m.source, m.replyto))
-            quote, total = getquote(m.source, m.target, num)
-            if(quote)
-              @bot.topic m.target, "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^delquote\s+(\d+)$/)
-          num = $1.to_i
-          if(@bot.auth.allow?("delquote", m.source, m.replyto))
-            if(delquote(m.target, num))
-              m.okay
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^searchquote\s+(.*)$/)
-          reg = $1
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            quote, total = searchquote(m.source, m.target, reg)
-            if(quote)
-              m.reply "[#{quote.num}] #{quote.quote}"
-            else
-              m.reply "quote not found!"
-            end
-          end
-        when (/^countquote(?:\s+(.*))?$/)
-          reg = $1
-          if(@bot.auth.allow?("getquote", m.source, m.replyto))
-            total = countquote(m.source, m.target, reg)
-            if(reg && reg.length > 0)
-              m.reply "#{total} quotes match: #{reg}"
-            else
-              m.reply "#{total} quotes"
-            end
-          end
-      end
-    end
-  end
-end
-plugin = QuotePlugin.new
-plugin.register("quotes")
diff --git a/rbot/plugins/remind.rb b/rbot/plugins/remind.rb
deleted file mode 100644 (file)
index 5ad980a..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-require 'rbot/utils'
-
-class RemindPlugin < Plugin
-  def initialize
-    super
-    @reminders = Hash.new
-  end
-  def cleanup
-    @reminders.each_value {|v|
-      v.each_value {|vv|
-        @bot.timer.remove(vv)
-      }
-    }
-    @reminders.clear
-  end
-  def help(plugin, topic="")
-    if(plugin =~ /^remind\+$/)
-      "see remind. remind+ can be used to remind someone else of something, using <nick> instead of 'me'. However this will generally require a higher auth level than remind."
-    else
-      "remind me [about] <message> in <time>, remind me [about] <message> every <time>, remind me [about] <message> at <time>, remind me no more [about] <message>, remind me no more"
-    end
-  end
-  def add_reminder(who, subject, timestr, repeat=false)
-    begin
-      period = Irc::Utils.timestr_offset(timestr)
-    rescue RuntimeError
-      return "couldn't parse that time string (#{timestr}) :("
-    end
-    if(period <= 0)
-      return "that time is in the past! (#{timestr})"
-    end
-    if(period < 30 && repeat)
-      return "repeats of less than 30 seconds are forbidden"
-    end
-    if(!@reminders.has_key?(who))
-      @reminders[who] = Hash.new
-    elsif(@reminders[who].has_key?(subject))
-      del_reminder(who, subject)
-    end
-
-    if(repeat)
-      @reminders[who][subject] = @bot.timer.add(period) {
-        time = Time.now + period
-        tstr = time.strftime("%H:%M:%S")
-        @bot.say who, "repeat reminder (next at #{tstr}): #{subject}"
-      }
-    else
-      @reminders[who][subject] = @bot.timer.add_once(period) {
-        time = Time.now + period
-        tstr = time.strftime("%H:%M:%S")
-        @bot.say who, "reminder (#{tstr}): #{subject}"
-      }
-    end
-    return false
-  end
-  def del_reminder(who, subject=nil)
-    if(subject)
-      if(@reminders.has_key?(who) && @reminders[who].has_key?(subject))
-        @bot.timer.remove(@reminders[who][subject])
-        @reminders[who].delete(subject)
-      end
-    else
-      if(@reminders.has_key?(who))
-        @reminders[who].each_value {|v|
-          @bot.timer.remove(v)
-        }
-        @reminders.delete(who)
-      end
-    end
-  end
-  def privmsg(m)
-
-    if(m.params =~ /^(\S+)\s+(?:about\s+)?(.*)\s+in\s+(.*)$/)
-      who = $1
-      subject = $2
-      period = $3
-      if(who =~ /^me$/)
-        who = m.sourcenick
-      else
-        unless(m.plugin =~ /^remind\+$/)
-          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
-          return
-        end
-      end
-      if(err = add_reminder(who, subject, period))
-        m.reply "incorrect usage: " + err
-        return
-      end
-    elsif(m.params =~ /^(\S+)\s+(?:about\s+)?(.*)\s+every\s+(.*)$/)
-      who = $1
-      subject = $2
-      period = $3
-      if(who =~ /^me$/)
-        who = m.sourcenick
-      else
-        unless(m.plugin =~ /^remind\+$/)
-          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
-          return
-        end
-      end
-      if(err = add_reminder(who, subject, period, true))
-        m.reply "incorrect usage: " + err
-        return
-      end
-    elsif(m.params =~ /^(\S+)\s+(?:about\s+)?(.*)\s+at\s+(.*)$/)
-      who = $1
-      subject = $2
-      time = $3
-      if(who =~ /^me$/)
-        who = m.sourcenick
-      else
-        unless(m.plugin =~ /^remind\+$/)
-          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
-          return
-        end
-      end
-      if(err = add_reminder(who, subject, time))
-        m.reply "incorrect usage: " + err
-        return
-      end
-    elsif(m.params =~ /^(\S+)\s+no\s+more\s+(?:about\s+)?(.*)$/)
-      who = $1
-      subject = $2
-      if(who =~ /^me$/)
-        who = m.sourcenick
-      else
-        unless(m.plugin =~ /^remind\+$/)
-          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
-          return
-        end
-      end
-      del_reminder(who, subject)
-    elsif(m.params =~ /^(\S+)\s+no\s+more$/)
-      who = $1
-      if(who =~ /^me$/)
-        who = m.sourcenick
-      else
-        unless(m.plugin =~ /^remind\+$/)
-          m.reply "incorrect usage: use remind+ to remind persons other than yourself"
-          return
-        end
-      end
-      del_reminder(who)
-    else
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-    m.okay
-  end
-end
-plugin = RemindPlugin.new
-plugin.register("remind")
-plugin.register("remind+")
-
diff --git a/rbot/plugins/roshambo.rb b/rbot/plugins/roshambo.rb
deleted file mode 100644 (file)
index 4f20fb1..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-# Play the game of roshambo (rock-paper-scissors)
-# Copyright (C) 2004 Hans Fugal
-# Distributed under the same license as rbot itself
-require 'time'
-class RoshamboPlugin < Plugin
-  def initialize
-    super 
-    @scoreboard = {}
-  end
-  def help(plugin, topic="")
-    "roshambo <rock|paper|scissors> => play roshambo"
-  end
-  def privmsg(m)
-    # simultaneity
-    choice = choose
-
-    # init scoreboard
-    if (not @scoreboard.has_key?(m.sourcenick) or (Time.now - @scoreboard[m.sourcenick]['timestamp']) > 3600)
-      @scoreboard[m.sourcenick] = {'me'=>0,'you'=>0,'timestamp'=>Time.now}
-    end
-    case m.params
-    when 'rock','paper','scissors'
-      s = score(choice,m.params)
-      @scoreboard[m.sourcenick]['timestamp'] = Time.now
-      myscore=@scoreboard[m.sourcenick]['me']
-      yourscore=@scoreboard[m.sourcenick]['you']
-      case s
-      when 1
-       yourscore=@scoreboard[m.sourcenick]['you'] += 1
-       m.reply "#{choice}. You win. Score: me #{myscore} you #{yourscore}"
-      when 0
-       m.reply "#{choice}. We tie. Score: me #{myscore} you #{yourscore}"
-      when -1
-       myscore=@scoreboard[m.sourcenick]['me'] += 1
-       m.reply "#{choice}! I win! Score: me #{myscore} you #{yourscore}"
-      end
-    else
-      m.reply "incorrect usage: " + help(m.plugin)
-    end
-  end
-      
-  def choose
-    ['rock','paper','scissors'][rand(3)]
-  end
-  def score(a,b)
-    beats = {'rock'=>'scissors', 'paper'=>'rock', 'scissors'=>'paper'}
-    return -1 if beats[a] == b
-    return 1 if beats[b] == a
-    return 0
-  end
-end
-plugin = RoshamboPlugin.new
-plugin.register("roshambo")
-plugin.register("rps")
diff --git a/rbot/plugins/rot13.rb b/rbot/plugins/rot13.rb
deleted file mode 100644 (file)
index 1f367db..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-class RotPlugin < Plugin
-  def help(plugin, topic="")
-    "rot13 <string> => encode <string> to rot13 or back"
-  end
-  def privmsg(m)
-    unless(m.params && m.params =~ /^.+$/)
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-    m.reply m.params.tr("A-Za-z", "N-ZA-Mn-za-m");
-  end
-end
-plugin = RotPlugin.new
-plugin.register("rot13")
diff --git a/rbot/plugins/roulette.rb b/rbot/plugins/roulette.rb
deleted file mode 100644 (file)
index c9d585e..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-RouletteHistory = Struct.new("RouletteHistory", :games, :shots, :deaths, :misses, :wins)
-
-class RoulettePlugin < Plugin
-  def initialize
-    super
-    reload
-  end
-  def help(plugin, topic="")
-    "roulette => play russian roulette - starts a new game if one isn't already running. One round in a six chambered gun. Take turns to say roulette to the bot, until somebody dies. roulette reload => force the gun to reload, roulette stats => show stats from all games, roulette stats <player> => show stats for <player>, roulette clearstats => clear stats (config level auth required)"
-  end
-  def privmsg(m)
-    if m.params == "reload"
-      @bot.action m.replyto, "reloads"
-      reload
-      # all players win on a reload
-      # (allows you to play 3-shot matches etc)
-      @players.each {|plyr|
-        pdata = @registry[plyr]
-        next if pdata == nil
-        pdata.wins += 1
-        @registry[plyr] = pdata
-      }
-      return
-    elsif m.params == "stats"
-      m.reply stats
-      return
-    elsif m.params =~ /^stats\s+(.+)$/
-      m.reply(playerstats($1))
-      return
-    elsif m.params == "clearstats"
-      if @bot.auth.allow?("config", m.source, m.replyto)
-        @registry.clear
-        m.okay
-      end
-      return
-    elsif m.params
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-    if m.private?
-      m.reply "you gotta play roulette in channel dude"
-      return
-    end
-
-    playerdata = nil
-    if @registry.has_key?(m.sourcenick)
-      playerdata = @registry[m.sourcenick]
-    else
-      playerdata = RouletteHistory.new(0,0,0,0,0)
-    end
-    
-    unless @players.include?(m.sourcenick)
-      @players << m.sourcenick
-      playerdata.games += 1
-    end
-    playerdata.shots += 1
-    
-    shot = @chambers.pop
-    if shot
-      m.reply "#{m.sourcenick}: chamber #{6 - @chambers.length} of 6 => *BANG*"
-      playerdata.deaths += 1
-      @players.each {|plyr|
-        next if plyr == m.sourcenick
-        pdata = @registry[plyr]
-        next if pdata == nil
-        pdata.wins += 1
-        @registry[plyr] = pdata
-      }
-    else
-      m.reply "#{m.sourcenick}: chamber #{6 - @chambers.length} of 6 => +click+"
-      playerdata.misses += 1
-    end
-
-    @registry[m.sourcenick] = playerdata
-    
-    if shot || @chambers.empty?
-      @bot.action m.replyto, "reloads"
-      reload
-    end
-  end
-  def reload
-    @chambers = [false, false, false, false, false, false]
-    @chambers[rand(@chambers.length)] = true
-    @players = Array.new
-  end
-  def playerstats(player)
-    pstats = @registry[player]
-    return "#{player} hasn't played enough games yet" if pstats.nil?
-    return "#{player} has played #{pstats.games} games, won #{pstats.wins} and lost #{pstats.deaths}. #{player} pulled the trigger #{pstats.shots} times and found the chamber empty on #{pstats.misses} occasions."
-  end
-  def stats
-    total_players = 0
-    total_games = 0
-    total_shots = 0
-    
-    died_most = [nil,0]
-    won_most = [nil,0]
-    h_win_percent = [nil,0]
-    l_win_percent = [nil,0]
-    h_luck_percent = [nil,0]
-    l_luck_percent = [nil,0]
-    @registry.each {|k,v|
-      total_players += 1
-      total_games += v.deaths
-      total_shots += v.shots
-      
-      win_rate = v.wins.to_f / v.games * 100
-      if h_win_percent[0].nil? || win_rate > h_win_percent[1] && v.games > 2
-        h_win_percent = [[k], win_rate]
-      elsif win_rate == h_win_percent[1] && v.games > 2
-        h_win_percent[0] << k
-      end
-      if l_win_percent[0].nil? || win_rate < l_win_percent[1] && v.games > 2
-        l_win_percent = [[k], win_rate]
-      elsif win_rate == l_win_percent[1] && v.games > 2
-        l_win_percent[0] << k
-      end
-
-      luck = v.misses.to_f / v.shots * 100
-      if h_luck_percent[0].nil? || luck > h_luck_percent[1] && v.games > 2
-        h_luck_percent = [[k], luck]
-      elsif luck == h_luck_percent[1] && v.games > 2
-        h_luck_percent[0] << k
-      end
-      if l_luck_percent[0].nil? || luck < l_luck_percent[1] && v.games > 2
-        l_luck_percent = [[k], luck]
-      elsif luck == l_luck_percent[1] && v.games > 2
-        l_luck_percent[0] << k
-      end
-
-      if died_most[0].nil? || v.deaths > died_most[1]
-        died_most = [[k], v.deaths]
-      elsif v.deaths == died_most[1]
-        died_most[0] << k
-      end
-      if won_most[0].nil? || v.wins > won_most[1]
-        won_most = [[k], v.wins]
-      elsif v.wins == won_most[1]
-        won_most[0] << k
-      end
-    }
-    return "roulette stats: no games played yet" if total_games < 1
-    return "roulette stats: #{total_games} games completed, #{total_shots} shots fired at #{total_players} players. Luckiest: #{h_luck_percent[0].join(',')} (#{sprintf '%.1f', h_luck_percent[1]}% clicks). Unluckiest: #{l_luck_percent[0].join(',')} (#{sprintf '%.1f', l_luck_percent[1]}% clicks). Highest survival rate: #{h_win_percent[0].join(',')} (#{sprintf '%.1f', h_win_percent[1]}%). Lowest survival rate: #{l_win_percent[0].join(',')} (#{sprintf '%.1f', l_win_percent[1]}%). Most wins: #{won_most[0].join(',')} (#{won_most[1]}). Most deaths: #{died_most[0].join(',')} (#{died_most[1]})."
-  end
-end
-plugin = RoulettePlugin.new
-plugin.register("roulette")
diff --git a/rbot/plugins/seen.rb b/rbot/plugins/seen.rb
deleted file mode 100644 (file)
index 6bd86a7..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-Saw = Struct.new("Saw", :nick, :time, :type, :where, :message)
-
-class SeenPlugin < Plugin
-  def help(plugin, topic="")
-    "seen <nick> => have you seen, or when did you last see <nick>"
-  end
-  
-  def privmsg(m)
-    unless(m.params =~ /^(\S)+$/)
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-
-    m.params.gsub!(/\?$/, "")
-
-    if @registry.has_key?(m.params)
-      m.reply seen(@registry[m.params])
-    else
-      m.reply "nope!"
-    end
-  end
-
-  def listen(m)
-    # keep database up to date with who last said what
-    if m.kind_of?(PrivMessage)
-      return if m.private? || m.address?
-      if m.action?
-        @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "ACTION", 
-                                          m.target, m.message.dup)
-      else
-        @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "PUBLIC",
-                                          m.target, m.message.dup)
-      end
-    elsif m.kind_of?(QuitMessage)
-      return if m.address?
-      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "QUIT", 
-                                        nil, m.message.dup)
-    elsif m.kind_of?(NickMessage)
-      return if m.address?
-      @registry[m.message] = Saw.new(m.sourcenick.dup, Time.new, "NICK", 
-                                        nil, m.message.dup)
-      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "NICK", 
-                                        nil, m.message.dup)
-    elsif m.kind_of?(PartMessage)
-      return if m.address?
-      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "PART", 
-                                        m.target, m.message.dup)
-    elsif m.kind_of?(JoinMessage)
-      return if m.address?
-      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "JOIN", 
-                                        m.target, m.message.dup)
-    elsif m.kind_of?(TopicMessage)
-      return if m.address?
-      @registry[m.sourcenick] = Saw.new(m.sourcenick.dup, Time.new, "TOPIC", 
-                                        m.target, m.message.dup)
-    end
-  end
-  
-  def seen(saw)
-    ret = "#{saw.nick} was last seen "
-    ago = Time.new - saw.time
-    
-    if (ago.to_i == 0)
-      ret += "just now, "
-    else
-      ret += Utils.secs_to_string(ago) + " ago, "
-    end
-
-    case saw.type
-    when "PUBLIC"
-      ret += "saying #{saw.message}"
-    when "ACTION"
-      ret += "doing #{saw.nick} #{saw.message}"
-    when "NICK"
-      ret += "changing nick from #{saw.nick} to #{saw.message}"
-    when "PART"
-      ret += "leaving #{saw.where}"
-    when "JOIN"
-      ret += "joining #{saw.where}"
-    when "QUIT"
-      ret += "quiting IRC (#{saw.message})"
-    when "TOPIC"
-      ret += "changing the topic of #{saw.where} to #{saw.message}"
-    end
-  end
-  
-end
-plugin = SeenPlugin.new
-plugin.register("seen")
diff --git a/rbot/plugins/slashdot.rb b/rbot/plugins/slashdot.rb
deleted file mode 100644 (file)
index b09ac7a..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-require 'rexml/document'
-require 'uri/common'
-
-class SlashdotPlugin < Plugin
-  include REXML
-  def help(plugin, topic="")
-    "slashdot search <string> [<max>=4] => search slashdot for <string>, slashdot [<max>=4] => return up to <max> slashdot headlines (use negative max to return that many headlines, but all on one line.)"
-  end
-  def privmsg(m)
-    if m.params && m.params =~ /^search\s+(.*)\s+(\d+)$/
-      search = $1
-      limit = $2.to_i
-      search_slashdot m, search, limit
-    elsif m.params && m.params =~ /^search\s+(.*)$/
-      search = $1
-      search_slashdot m, search
-    elsif m.params && m.params =~ /^([-\d]+)$/
-      limit = $1.to_i
-      slashdot m, limit
-    else
-      slashdot m
-    end
-  end
-  
-  def search_slashdot(m, search, max=4)
-    begin
-      xml = @bot.httputil.get(URI.parse("http://slashdot.org/search.pl?content_type=rss&query=#{URI.escape(search)}"))
-    rescue URI::InvalidURIError, URI::BadURIError => e
-      m.reply "illegal search string #{search}"
-      return
-    end
-    unless xml
-      m.reply "search for #{search} failed"
-      return
-    end
-    begin
-      doc = Document.new xml
-    rescue REXML::ParseException => e
-      puts e
-      m.reply "couldn't parse output XML: #{e.class}"
-      return
-    end
-    unless doc
-      m.reply "search for #{search} failed"
-      return
-    end
-    max = 8 if max > 8
-    done = 0
-    doc.elements.each("*/item") {|e|
-      desc = e.elements["title"].text
-      desc.gsub!(/(.{150}).*/, '\1..')
-      reply = sprintf("%s | %s", e.elements["link"].text, desc)
-      m.reply reply
-      done += 1
-      break if done >= max
-    }
-  end
-  
-  def slashdot(m, max=4)
-    xml = @bot.httputil.get(URI.parse("http://slashdot.org/slashdot.xml"))
-    unless xml
-      m.reply "slashdot news parse failed"
-      return
-    end
-    doc = Document.new xml
-    unless doc
-      m.reply "slashdot news parse failed (invalid xml)"
-      return
-    end
-    done = 0
-    oneline = false
-    if max < 0
-      max = (0 - max)
-      oneline = true
-    end
-    max = 8 if max > 8
-    matches = Array.new
-    doc.elements.each("*/story") {|e|
-      matches << [ e.elements["title"].text, 
-                   e.elements["author"].text, 
-                   e.elements["time"].text.gsub(/\d{4}-(\d{2})-(\d{2})/, "\\2/\\1").gsub(/:\d\d$/, "") ]
-      done += 1
-      break if done >= max
-    } 
-    if oneline
-      m.reply matches.collect{|mat| mat[0]}.join(" | ")
-    else
-      matches.each {|mat|
-        m.reply sprintf("%36s | %8s | %8s", mat[0][0,36], mat[1][0,8], mat[2])
-      }
-    end
-  end
-end
-plugin = SlashdotPlugin.new
-plugin.register("slashdot")
diff --git a/rbot/plugins/spell.rb b/rbot/plugins/spell.rb
deleted file mode 100644 (file)
index 81ee1ac..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-class SpellPlugin < Plugin
-  def help(plugin, topic="")
-    "spell <word> => check spelling of <word>, suggest alternatives"
-  end
-  def privmsg(m)
-    unless(m.params && m.params =~ /^\S+$/)
-      m.reply "incorrect usage: " + help(m.plugin)
-      return
-    end
-    p = IO.popen("ispell -a -S", "w+")
-    if(p)
-      p.puts m.params
-      p.close_write
-      p.each_line {|l|
-        if(l =~ /^\*/)
-          m.reply "#{m.params} may be spelled correctly"
-          return
-        elsif(l =~ /^\s*&.*: (.*)$/)
-          m.reply "#{m.params}: #$1"
-          return
-        elsif(l =~ /^\s*\+ (.*)$/)
-          m.reply "#{m.params} is presumably derived from " + $1.downcase
-          return
-        elsif(l =~ /^\s*#/)
-          m.reply "#{m.params}: no suggestions"
-          return
-        end
-      }
-    else
-      m.reply "couldn't exec ispell :("
-      return
-    end
-  end
-end
-plugin = SpellPlugin.new
-plugin.register("spell")
diff --git a/rbot/plugins/tube.rb b/rbot/plugins/tube.rb
deleted file mode 100644 (file)
index 77ca522..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#Tube Status Enquiry plugin for rbot
-#Plugin by Colm Linehan
-
-require 'rexml/document'
-require 'uri/common'
-
-class TubePlugin < Plugin
-  include REXML
-  def help(plugin, topic="")
-  "tube [district|circle|metropolitan|central|jubilee|bakerloo|waterloo_city|hammersmith_city|victoria|eastlondon|northern|piccadilly] => display tube service status for the specified line(Docklands Light Railway is not currently supported), tube stations => list tube stations (not lines) with problems"
-  end
-  def privmsg(m)
-  if m.params && m.params =~ /^stations$/
-    check_stations m
-  elsif m.params && m.params =~ /^(.*)$/
-    line = $1.downcase.capitalize
-    check_tube m, line
-  end
-  end
-  
-  def check_tube(m, line)
-  begin
-    tube_page = @bot.httputil.get(URI.parse("http://www.tfl.gov.uk/tfl/service_rt_tube.shtml"), 1, 1)
-  rescue URI::InvalidURIError, URI::BadURIError => e
-    m.reply "Cannot contact Tube Service Status page"
-    return
-  end
-  unless tube_page
-    m.reply "Cannot contact Tube Service Status page"
-    return
-  end
-  next_line = false
-  tube_page.each_line {|l|
-    next if l == "\r\n"
-    next if l == "\n"
-    if (next_line)
-      if (l =~ /^<tr valign=top> <td>\s*(.*)<\/td><\/tr>/i)
-        m.reply $1.split(/<[^>]+>|&nbsp;/i).join(" ")
-        return
-      else
-        m.reply "There are problems on the #{line} line, but I didn't understand the page format. You should check out http://www.tfl.gov.uk/tfl/service_rt_tube.shtml for more details."
-        return
-      end
-    end
-    next_line = true if (l =~ /class="#{line}"/i)
-    }
-  m.reply "No Problems on the #{line} line."
-  end
-
-  def check_stations(m)
-    begin
-      tube_page = @bot.httputil.get(URI.parse("http://www.tfl.gov.uk/tfl/service_rt_tube.shtml"))
-    rescue URI::InvalidURIError, URI::BadURIError => e
-      m.reply "Cannot contact Tube Service Status page"
-      return
-    end
-    unless tube_page
-      m.reply "Cannot contact Tube Service Status page"
-      return
-    end
-    stations_array = Array.new
-    tube_page.each_line {|l|
-        if (l =~ /<tr valign=top> <td valign="middle" class="Station"><b>(.*)<\/b><\/td><\/tr>\s*/i)
-          stations_array.push $1
-        end
-        }
-    if stations_array.empty? 
-      m.reply "There are no station-specific announcements"
-      return
-    else
-      m.reply stations_array.join(", ")
-      return
-    end
-  end
-end
-plugin = TubePlugin.new
-plugin.register("tube")
diff --git a/rbot/plugins/url.rb b/rbot/plugins/url.rb
deleted file mode 100644 (file)
index ed82d1c..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-Url = Struct.new("Url", :channel, :nick, :time, :url)
-
-class UrlPlugin < Plugin
-  def initialize
-    super
-    @registry.set_default(Array.new)
-  end
-  def help(plugin, topic="")
-    "urls [<max>=4] => list <max> last urls mentioned in current channel, urls <channel> [<max>=4] => list <max> last urls mentioned in <channel>, urls search <regexp> => search for matching urls, urls search <channel> <regexp>, search for matching urls in channel <channel>"
-  end
-  def listen(m)
-    return unless m.kind_of?(PrivMessage)
-    return if m.address?
-    # TODO support multiple urls in one line
-    if m.message =~ /(f|ht)tps?:\/\//
-      if m.message =~ /((f|ht)tps?:\/\/.*?)(?:\s+|$)/
-        url = Url.new(m.target, m.sourcenick, Time.new, $1)
-        list = @registry[m.target]
-        debug "#{list.length} urls so far"
-        if list.length > 50
-          list.pop
-        end
-        debug "storing url #{url.url}"
-        list.unshift url
-        debug "#{list.length} urls now"
-        @registry[m.target] = list
-      end
-    end
-  end
-  def privmsg(m)
-    case m.params
-    when nil
-      if m.public?
-        urls m, m.target
-      else
-        m.reply "in a private message, you need to specify a channel name for urls"
-      end
-    when (/^(\d+)$/)
-      max = $1.to_i
-      if m.public?
-        urls m, m.target, max
-      else
-        m.reply "in a private message, you need to specify a channel name for urls"
-      end
-    when (/^(#.*?)\s+(\d+)$/)
-      channel = $1
-      max = $2.to_i
-      urls m, channel, max
-    when (/^(#.*?)$/)
-      channel = $1
-      urls m, channel
-    when (/^search\s+(#.*?)\s+(.*)$/)
-      channel = $1
-      string = $2
-      search m, channel, string
-    when (/^search\s+(.*)$/)
-      string = $1
-      if m.public?
-        search m, m.target, string
-      else
-        m.reply "in a private message, you need to specify a channel name for urls"
-      end
-    else
-      m.reply "incorrect usage: " + help(m.plugin)
-    end
-  end
-
-  def urls(m, channel, max=4)
-    max = 10 if max > 10
-    max = 1 if max < 1
-    list = @registry[channel]
-    if list.empty?
-      m.reply "no urls seen yet for channel #{channel}"
-    else
-      list[0..(max-1)].each do |url|
-        m.reply "[#{url.time.strftime('%Y/%m/%d %H:%M:%S')}] <#{url.nick}> #{url.url}"
-      end
-    end
-  end
-
-  def search(m, channel, string, max=4)
-    max = 10 if max > 10
-    max = 1 if max < 1
-    regex = Regexp.new(string)
-    list = @registry[channel].find_all {|url|
-      regex.match(url.url) || regex.match(url.nick)
-    }
-    if list.empty?
-      m.reply "no matches for channel #{channel}"
-    else
-      list[0..(max-1)].each do |url|
-        m.reply "[#{url.time.strftime('%Y/%m/%d %H:%M:%S')}] <#{url.nick}> #{url.url}"
-      end
-    end
-  end
-end
-plugin = UrlPlugin.new
-plugin.register("urls")
diff --git a/rbot/plugins/weather.rb b/rbot/plugins/weather.rb
deleted file mode 100644 (file)
index 3e4134e..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-class WeatherPlugin < Plugin
-  
-  def help(plugin, topic="")
-    "weather <ICAO> => display the current weather at the location specified by the ICAO code [Lookup your ICAO code at http://www.nws.noaa.gov/oso/siteloc.shtml] - this will also store the ICAO against your nick, so you can later just say \"weather\", weather => display the current weather at the location you last asked for"
-  end
-  
-  def initialize
-    super
-    # this plugin only wants to store strings
-    class << @registry
-      def store(val)
-        val
-      end
-      def restore(val)
-        val
-      end
-    end
-    @metar_cache = Hash.new
-  end
-  
-  def describe(m, where)
-    if @metar_cache.has_key?(where) &&
-       Time.now - @metar_cache[where].date < 3600
-      met = @metar_cache[where]
-    else
-      met = Utils.get_metar(where)
-    end
-    
-    if met
-      m.reply met.pretty_print
-      @metar_cache[where] = met
-    else
-      m.reply "couldn't find weather data for #{where}"
-    end
-  end
-  
-  def privmsg(m)
-    case m.params
-    when nil
-      if @registry.has_key?(m.sourcenick)
-        where = @registry[m.sourcenick]
-        describe(m,where)
-      else
-        m.reply "I don't know where #{m.sourcenick} is!"
-      end
-    when (/^(\S{4})$/)
-      where = $1
-      @registry[m.sourcenick] = where
-      describe(m,where)
-    end
-  end
-  
-end
-plugin = WeatherPlugin.new
-plugin.register("weather")
diff --git a/rbot/plugins/wserver.rb b/rbot/plugins/wserver.rb
deleted file mode 100644 (file)
index e1fe10b..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-require 'net/http'
-require 'uri'
-Net::HTTP.version_1_2
-
-class WserverPlugin < Plugin
-  def help(plugin, topic="")
-    "wserver <uri> => try and determine what webserver <uri> is using"
-  end
-  def privmsg(m)
-    unless(m.params && m.params =~ /^\S+$/)
-      m.reply "incorrect usage: " + help(m.plugins)
-      return
-    end
-
-    redirect_count = 0
-    hostname = m.params.dup
-    hostname = "http://#{hostname}" unless hostname =~ /:\/\//
-    begin
-      if(redirect_count > 3)
-        m.reply "cowardly refusing to follow more than 3 redirects"
-        return
-      end
-      
-      begin
-        uri = URI.parse(hostname)
-      rescue URI::InvalidURIError => err
-        m.reply "#{m.params} is not a valid URI"
-        return
-      end
-      
-      unless(uri)
-        m.reply "incorrect usage: " + help(m.plugin)
-        return
-      end
-        
-      http = @bot.httputil.get_proxy(uri)
-      http.open_timeout = 5
-      
-      http.start {|http|
-        resp = http.head('/')
-        server = resp['Server']
-        if(server && server.length > 0)
-          m.reply "#{uri.host} is running #{server}"
-        else
-          m.reply "couldn't tell what #{uri.host} is running"
-        end
-        
-        if(resp.code == "302" || resp.code == "301") 
-          newloc = resp['location']
-          newuri = URI.parse(newloc)
-          # detect and ignore incorrect redirects (to relative paths etc)
-          if (newuri.host != nil)
-            if(uri.host != newuri.host)
-              m.reply "#{uri.host} redirects to #{newuri.scheme}://#{newuri.host}"
-              raise resp['location']
-            end
-          end
-        end
-      }
-    rescue TimeoutError => err
-      m.reply "timed out connecting to #{uri.host}:#{uri.port} :("
-      return
-    rescue RuntimeError => err
-      redirect_count += 1
-      hostname = err.message
-      retry
-    rescue StandardError => err
-      puts err
-      m.reply "couldn't connect to #{uri.host}:#{uri.port} :("
-      return
-    end
-  end
-end
-plugin = WserverPlugin.new
-plugin.register("wserver")
diff --git a/rbotconf/conf.rbot b/rbotconf/conf.rbot
deleted file mode 100644 (file)
index 8c6020d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-ADDRESS_PREFIX = |
-SENDQ_DELAY = 2.0
-SENDQ_BURST = 4
-NICK = rbot
-USER = rbot
-JOIN_CHANNELS = #rbot
-PORT = 6667
-LANGUAGE = english
-KEYWORD_LISTEN = false
-SAVE_EVERY = 60
-SERVER = irc.openprojects.net
-PASSWD = masterpwd
-NO_KEYWORD_ADDRESS = false
diff --git a/rbotconf/keywords.rbot b/rbotconf/keywords.rbot
deleted file mode 100644 (file)
index d985fa3..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-lb<=is=>http://linuxbrit.co.uk
-offended<=is=><reply><who> is offended!
-giblet<=is=>My master!
-rbot<=is=><reply>That's me! :-))
diff --git a/rbotconf/lart/larts b/rbotconf/lart/larts
deleted file mode 100644 (file)
index c3a2588..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-swaps <who>'s shampoo with nair
-installs windows on <who>'s machine
-forces <who> to use perl for 3 weeks
-registers <who>'s name with 50 known spammers
-resizes <who>'s terminal to 40x24
-takes <who>'s tea
-dispenses <who>'s email address to a few hundred 'bulk mailing services'
-pokes <who> in the eye
-beats <who> senseless with a 50lb Unix manual
-cats /dev/urandom into <who>'s ear
-signs <who> up for AOL
-enrolls <who> in Visual Basic 101
-sporks <who>
-drops a truckload of VAXen on <who>
-judo chops <who>
-resizes <who>'s terminal to 40x24
-formats <who>'s harddrive to fat12
-rm -rf's <who>
-stabs <who>
-steals <who>'s mojo
-strangles <who> with a doohicky mouse cord
-whacks <who> with the cluebat
-sells <who> on E-Bay
-uses <who> as a biological warfare study
-uses the "Customer Appreciation Bat" on <who>
-reads <who> some vogon poetry
-puts <who> in the Total Perspective Vortex
-casts <who> into the fires of Mt. Doom
-gives <who> a melvin
-turns over <who> to Agent Smith to be "bugged"
-takes away <who>'s internet connection
-pushes <who> past the Shoe Event Horizon
-counts "1, 2, 5... er... 3!" and hurls the Holy Handgrenade Of Antioch at <who>
-puts <who> in a nest of camel spiders
-makes <who> read slashdot at -1
-puts "alias vim=emacs" in <who>'s /etc/profile
-uninstalls ld from <who>'s system
-locks <who> in the Chateau d'If
-signs <who> up for getting hit on the head lessons
-makes <who> try to set up a Lexmark printer
-fills <who>'s Visene eyedrop bottle with lime juice
-casts <who> into the fires of Mt. Doom.
-gives <who> a Chicago Steamer
-rips off <who>'s arm, and uses it to beat them to death
-pierces <who>'s nose with a rusty paper hole puncher
-pokes <who> with a rusty nail
-puts sugar between <who>'s bedsheets
-pours sand into <who>'s oatmeal
-mixes epoxy into <who>'s toothpaste
-puts Icy-Hot in <who>'s lip balm
-straps <who> to a chair, and plays a endless low bitrate MP3 loop of "the world's most annoying sound" from "Dumb and Dumber"
-tells Dr. Dre that <who> was talking smack
-forces <who> to use a Commodore 64 for all their word processing
-smacks <who> in the face with a burlap sack full of broken glass
-puts <who> in a room with several heavily armed manic depressives
-makes <who> watch reruns of "Blue's Clues"
-puts lye in <who>'s coffee
-tattoos the Windows symbol on <who>'s ass
-lets Borg have his way with <who>
-signs <who> up for line dancing classes at the local senior center
-wakes <who> out of a sound sleep with some brand new nipple piercings
-gives <who> a 2 guage Prince Albert
-forces <who> to eat all their veggies
-covers <who>'s toilet paper with lemon-pepper
-fills <who>'s ketchup bottle with Dave's Insanity sauce
-forces <who> to stare at an incredibly frustrating and seemingly neverending IRC political debate
-knocks two of <who>'s teeth out with a 2x4
-removes debian from <who>'s system
-uses <who>'s debian cds for skeet shooting practice
-gives <who>'s phone number to Borg
-posts <who>'s IP and root password on alt.2600
-forces <who> to use words like "irregardless" and "administrate" (thereby sounding like a real dumbass)"
-tickles <who> until they wet their pants and pass out
-replaces <who>'s KY with elmer's clear wood glue
-replaces <who>'s TUMS with alka-seltzer tablets
-squeezes habanero pepper juice into <who>'s tub of vaseline for <who>
-submits <who>'s photo to the people at SA for photoshopping
-Forces <who> to learn the Win32 API
-gives <who> an atomic wedgie
-ties <who> to a chair and forces them to listen to 'N Sync at full blast
-forces <who> to use emacs for text editing
-frowns at <who> really really hard
-jabs a hot car lighter into <who>'s eye sockets
-forces <who> to browse the web with IE
-takes <who> out at the knees with a broken pool cue
-forces <who> to listen to only emo music
-signs <who> up for the Iowa State Ferret Legging Championship
-attempts to hotswap <who>'s RAM
-donkey punches <who>
-puts track spikes into <who>'s side
-replaces <who>'s Astroglide with JB Weld
-replaces <who>'s hypertension pills with rat poison pellets
-replaces <who>s jock itch cream with Nair
-does the Australian Death Grip on <who>
-dances upon the grave of <who>'s ancestors.
-farts in <who>'s general direction
-flogs <who> with stinging neddle
-hands <who> a poison ivy joint
diff --git a/rbotconf/lart/praises b/rbotconf/lart/praises
deleted file mode 100644 (file)
index ac923dd..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-hugs <who>
-gives <who> some love
diff --git a/rbotconf/levels.rbot b/rbotconf/levels.rbot
deleted file mode 100644 (file)
index 2d11c2d..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-70 say
-100 auth
-50 part
-85 config
-80 nick
-50 join
-12 msginsult
-5 keycmd
-5 lart
-5 addlart
-10 rmlart
-5 addpraise
-10 rmpraise
-5 addquote
-12 remind+
-5 getquote
-90 quit
-5 remind
-5 keyword
-15 delquote
-70 opmeh
diff --git a/rbotconf/users.rbot b/rbotconf/users.rbot
deleted file mode 100644 (file)
index 61f55fb..0000000
+++ /dev/null
@@ -1 +0,0 @@
-10 *!*@*
diff --git a/setup.rb b/setup.rb
new file mode 100755 (executable)
index 0000000..0807023
--- /dev/null
+++ b/setup.rb
@@ -0,0 +1,1360 @@
+#
+# setup.rb
+#
+# Copyright (c) 2000-2004 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the terms of
+# the GNU LGPL, Lesser General Public License version 2.1.
+#
+
+unless Enumerable.method_defined?(:map)   # Ruby 1.4.6
+  module Enumerable
+    alias map collect
+  end
+end
+
+unless File.respond_to?(:read)   # Ruby 1.6
+  def File.read(fname)
+    open(fname) {|f|
+      return f.read
+    }
+  end
+end
+
+def File.binread(fname)
+  open(fname, 'rb') {|f|
+    return f.read
+  }
+end
+
+# for corrupted windows stat(2)
+def File.dir?(path)
+  File.directory?((path[-1,1] == '/') ? path : path + '/')
+end
+
+
+class SetupError < StandardError; end
+
+def setup_rb_error(msg)
+  raise SetupError, msg
+end
+
+#
+# Config
+#
+
+if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
+  ARGV.delete(arg)
+  require arg.split(/=/, 2)[1]
+  $".push 'rbconfig.rb'
+else
+  require 'rbconfig'
+end
+
+def multipackage_install?
+  FileTest.directory?(File.dirname($0) + '/packages')
+end
+
+
+class ConfigItem
+  def initialize(name, template, default, desc)
+    @name = name.freeze
+    @template = template
+    @value = default
+    @default = default.dup.freeze
+    @description = desc
+  end
+
+  attr_reader :name
+  attr_reader :description
+
+  attr_accessor :default
+  alias help_default default
+
+  def help_opt
+    "--#{@name}=#{@template}"
+  end
+
+  def value
+    @value
+  end
+
+  def eval(table)
+    @value.gsub(%r<\$([^/]+)>) { table[$1] }
+  end
+
+  def set(val)
+    @value = check(val)
+  end
+
+  private
+
+  def check(val)
+    setup_rb_error "config: --#{name} requires argument" unless val
+    val
+  end
+end
+
+class BoolItem < ConfigItem
+  def config_type
+    'bool'
+  end
+
+  def help_opt
+    "--#{@name}"
+  end
+
+  private
+
+  def check(val)
+    return 'yes' unless val
+    unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
+      setup_rb_error "config: --#{@name} accepts only yes/no for argument"
+    end
+    (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
+  end
+end
+
+class PathItem < ConfigItem
+  def config_type
+    'path'
+  end
+
+  private
+
+  def check(path)
+    setup_rb_error "config: --#{@name} requires argument"  unless path
+    path[0,1] == '$' ? path : File.expand_path(path)
+  end
+end
+
+class ProgramItem < ConfigItem
+  def config_type
+    'program'
+  end
+end
+
+class SelectItem < ConfigItem
+  def initialize(name, template, default, desc)
+    super
+    @ok = template.split('/')
+  end
+
+  def config_type
+    'select'
+  end
+
+  private
+
+  def check(val)
+    unless @ok.include?(val.strip)
+      setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
+    end
+    val.strip
+  end
+end
+
+class PackageSelectionItem < ConfigItem
+  def initialize(name, template, default, help_default, desc)
+    super name, template, default, desc
+    @help_default = help_default
+  end
+
+  attr_reader :help_default
+
+  def config_type
+    'package'
+  end
+
+  private
+
+  def check(val)
+    unless File.dir?("packages/#{val}")
+      setup_rb_error "config: no such package: #{val}"
+    end
+    val
+  end
+end
+
+class ConfigTable_class
+
+  def initialize(items)
+    @items = items
+    @table = {}
+    items.each do |i|
+      @table[i.name] = i
+    end
+    ALIASES.each do |ali, name|
+      @table[ali] = @table[name]
+    end
+  end
+
+  include Enumerable
+
+  def each(&block)
+    @items.each(&block)
+  end
+
+  def key?(name)
+    @table.key?(name)
+  end
+
+  def lookup(name)
+    @table[name] or raise ArgumentError, "no such config item: #{name}"
+  end
+
+  def add(item)
+    @items.push item
+    @table[item.name] = item
+  end
+
+  def remove(name)
+    item = lookup(name)
+    @items.delete_if {|i| i.name == name }
+    @table.delete_if {|name, i| i.name == name }
+    item
+  end
+
+  def new
+    dup()
+  end
+
+  def savefile
+    '.config'
+  end
+
+  def load
+    begin
+      t = dup()
+      File.foreach(savefile()) do |line|
+        k, v = *line.split(/=/, 2)
+        t[k] = v.strip
+      end
+      t
+    rescue Errno::ENOENT
+      setup_rb_error $!.message + "#{File.basename($0)} config first"
+    end
+  end
+
+  def save
+    @items.each {|i| i.value }
+    File.open(savefile(), 'w') {|f|
+      @items.each do |i|
+        f.printf "%s=%s\n", i.name, i.value if i.value
+      end
+    }
+  end
+
+  def [](key)
+    lookup(key).eval(self)
+  end
+
+  def []=(key, val)
+    lookup(key).set val
+  end
+
+end
+
+c = ::Config::CONFIG
+
+rubypath = c['bindir'] + '/' + c['ruby_install_name']
+
+major = c['MAJOR'].to_i
+minor = c['MINOR'].to_i
+teeny = c['TEENY'].to_i
+version = "#{major}.#{minor}"
+
+# ruby ver. >= 1.4.4?
+newpath_p = ((major >= 2) or
+             ((major == 1) and
+              ((minor >= 5) or
+               ((minor == 4) and (teeny >= 4)))))
+
+if c['rubylibdir']
+  # V < 1.6.3
+  _stdruby         = c['rubylibdir']
+  _siteruby        = c['sitedir']
+  _siterubyver     = c['sitelibdir']
+  _siterubyverarch = c['sitearchdir']
+elsif newpath_p
+  # 1.4.4 <= V <= 1.6.3
+  _stdruby         = "$prefix/lib/ruby/#{version}"
+  _siteruby        = c['sitedir']
+  _siterubyver     = "$siteruby/#{version}"
+  _siterubyverarch = "$siterubyver/#{c['arch']}"
+else
+  # V < 1.4.4
+  _stdruby         = "$prefix/lib/ruby/#{version}"
+  _siteruby        = "$prefix/lib/ruby/#{version}/site_ruby"
+  _siterubyver     = _siteruby
+  _siterubyverarch = "$siterubyver/#{c['arch']}"
+end
+libdir = '-* dummy libdir *-'
+stdruby = '-* dummy rubylibdir *-'
+siteruby = '-* dummy site_ruby *-'
+siterubyver = '-* dummy site_ruby version *-'
+parameterize = lambda {|path|
+  path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\
+      .sub(/\A#{Regexp.quote(libdir)}/,      '$libdir')\
+      .sub(/\A#{Regexp.quote(stdruby)}/,     '$stdruby')\
+      .sub(/\A#{Regexp.quote(siteruby)}/,    '$siteruby')\
+      .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver')
+}
+libdir          = parameterize.call(c['libdir'])
+stdruby         = parameterize.call(_stdruby)
+siteruby        = parameterize.call(_siteruby)
+siterubyver     = parameterize.call(_siterubyver)
+siterubyverarch = parameterize.call(_siterubyverarch)
+
+if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
+  makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
+else
+  makeprog = 'make'
+end
+
+common_conf = [
+  PathItem.new('prefix', 'path', c['prefix'],
+               'path prefix of target environment'),
+  PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
+               'the directory for commands'),
+  PathItem.new('libdir', 'path', libdir,
+               'the directory for libraries'),
+  PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
+               'the directory for shared data'),
+  PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
+               'the directory for man pages'),
+  PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
+               'the directory for man pages'),
+  PathItem.new('stdruby', 'path', stdruby,
+               'the directory for standard ruby libraries'),
+  PathItem.new('siteruby', 'path', siteruby,
+      'the directory for version-independent aux ruby libraries'),
+  PathItem.new('siterubyver', 'path', siterubyver,
+               'the directory for aux ruby libraries'),
+  PathItem.new('siterubyverarch', 'path', siterubyverarch,
+               'the directory for aux ruby binaries'),
+  PathItem.new('rbdir', 'path', '$siterubyver',
+               'the directory for ruby scripts'),
+  PathItem.new('sodir', 'path', '$siterubyverarch',
+               'the directory for ruby extentions'),
+  PathItem.new('rubypath', 'path', rubypath,
+               'the path to set to #! line'),
+  ProgramItem.new('rubyprog', 'name', rubypath,
+                  'the ruby program using for installation'),
+  ProgramItem.new('makeprog', 'name', makeprog,
+                  'the make program to compile ruby extentions'),
+  SelectItem.new('shebang', 'all/ruby/never', 'ruby',
+                 'shebang line (#!) editing mode'),
+  BoolItem.new('without-ext', 'yes/no', 'no',
+               'does not compile/install ruby extentions')
+]
+class ConfigTable_class   # open again
+  ALIASES = {
+    'std-ruby'         => 'stdruby',
+    'site-ruby-common' => 'siteruby',     # For backward compatibility
+    'site-ruby'        => 'siterubyver',  # For backward compatibility
+    'bin-dir'          => 'bindir',
+    'bin-dir'          => 'bindir',
+    'rb-dir'           => 'rbdir',
+    'so-dir'           => 'sodir',
+    'data-dir'         => 'datadir',
+    'ruby-path'        => 'rubypath',
+    'ruby-prog'        => 'rubyprog',
+    'ruby'             => 'rubyprog',
+    'make-prog'        => 'makeprog',
+    'make'             => 'makeprog'
+  }
+end
+multipackage_conf = [
+  PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
+                           'package names that you want to install'),
+  PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
+                           'package names that you do not want to install')
+]
+if multipackage_install?
+  ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf)
+else
+  ConfigTable = ConfigTable_class.new(common_conf)
+end
+
+
+module MetaConfigAPI
+
+  def eval_file_ifexist(fname)
+    instance_eval File.read(fname), fname, 1 if File.file?(fname)
+  end
+
+  def config_names
+    ConfigTable.map {|i| i.name }
+  end
+
+  def config?(name)
+    ConfigTable.key?(name)
+  end
+
+  def bool_config?(name)
+    ConfigTable.lookup(name).config_type == 'bool'
+  end
+
+  def path_config?(name)
+    ConfigTable.lookup(name).config_type == 'path'
+  end
+
+  def value_config?(name)
+    case ConfigTable.lookup(name).config_type
+    when 'bool', 'path'
+      true
+    else
+      false
+    end
+  end
+
+  def add_config(item)
+    ConfigTable.add item
+  end
+
+  def add_bool_config(name, default, desc)
+    ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
+  end
+
+  def add_path_config(name, default, desc)
+    ConfigTable.add PathItem.new(name, 'path', default, desc)
+  end
+
+  def set_config_default(name, default)
+    ConfigTable.lookup(name).default = default
+  end
+
+  def remove_config(name)
+    ConfigTable.remove(name)
+  end
+
+end
+
+
+#
+# File Operations
+#
+
+module FileOperations
+
+  def mkdir_p(dirname, prefix = nil)
+    dirname = prefix + File.expand_path(dirname) if prefix
+    $stderr.puts "mkdir -p #{dirname}" if verbose?
+    return if no_harm?
+
+    # does not check '/'... it's too abnormal case
+    dirs = File.expand_path(dirname).split(%r<(?=/)>)
+    if /\A[a-z]:\z/i =~ dirs[0]
+      disk = dirs.shift
+      dirs[0] = disk + dirs[0]
+    end
+    dirs.each_index do |idx|
+      path = dirs[0..idx].join('')
+      Dir.mkdir path unless File.dir?(path)
+    end
+  end
+
+  def rm_f(fname)
+    $stderr.puts "rm -f #{fname}" if verbose?
+    return if no_harm?
+
+    if File.exist?(fname) or File.symlink?(fname)
+      File.chmod 0777, fname
+      File.unlink fname
+    end
+  end
+
+  def rm_rf(dn)
+    $stderr.puts "rm -rf #{dn}" if verbose?
+    return if no_harm?
+
+    Dir.chdir dn
+    Dir.foreach('.') do |fn|
+      next if fn == '.'
+      next if fn == '..'
+      if File.dir?(fn)
+        verbose_off {
+          rm_rf fn
+        }
+      else
+        verbose_off {
+          rm_f fn
+        }
+      end
+    end
+    Dir.chdir '..'
+    Dir.rmdir dn
+  end
+
+  def move_file(src, dest)
+    File.unlink dest if File.exist?(dest)
+    begin
+      File.rename src, dest
+    rescue
+      File.open(dest, 'wb') {|f| f.write File.binread(src) }
+      File.chmod File.stat(src).mode, dest
+      File.unlink src
+    end
+  end
+
+  def install(from, dest, mode, prefix = nil)
+    $stderr.puts "install #{from} #{dest}" if verbose?
+    return if no_harm?
+
+    realdest = prefix ? prefix + File.expand_path(dest) : dest
+    realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
+    str = File.binread(from)
+    if diff?(str, realdest)
+      verbose_off {
+        rm_f realdest if File.exist?(realdest)
+      }
+      File.open(realdest, 'wb') {|f|
+        f.write str
+      }
+      File.chmod mode, realdest
+
+      File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
+        if prefix
+          f.puts realdest.sub(prefix, '')
+        else
+          f.puts realdest
+        end
+      }
+    end
+  end
+
+  def diff?(new_content, path)
+    return true unless File.exist?(path)
+    new_content != File.binread(path)
+  end
+
+  def command(str)
+    $stderr.puts str if verbose?
+    system str or raise RuntimeError, "'system #{str}' failed"
+  end
+
+  def ruby(str)
+    command config('rubyprog') + ' ' + str
+  end
+  
+  def make(task = '')
+    command config('makeprog') + ' ' + task
+  end
+
+  def extdir?(dir)
+    File.exist?(dir + '/MANIFEST')
+  end
+
+  def all_files_in(dirname)
+    Dir.open(dirname) {|d|
+      return d.select {|ent| File.file?("#{dirname}/#{ent}") }
+    }
+  end
+
+  REJECT_DIRS = %w(
+    CVS SCCS RCS CVS.adm .svn
+  )
+
+  def all_dirs_in(dirname)
+    Dir.open(dirname) {|d|
+      return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
+    }
+  end
+
+end
+
+
+#
+# Main Installer
+#
+
+module HookUtils
+
+  def run_hook(name)
+    try_run_hook "#{curr_srcdir()}/#{name}" or
+    try_run_hook "#{curr_srcdir()}/#{name}.rb"
+  end
+
+  def try_run_hook(fname)
+    return false unless File.file?(fname)
+    begin
+      instance_eval File.read(fname), fname, 1
+    rescue
+      setup_rb_error "hook #{fname} failed:\n" + $!.message
+    end
+    true
+  end
+
+end
+
+
+module HookScriptAPI
+
+  def get_config(key)
+    @config[key]
+  end
+
+  alias config get_config
+
+  def set_config(key, val)
+    @config[key] = val
+  end
+
+  #
+  # srcdir/objdir (works only in the package directory)
+  #
+
+  #abstract srcdir_root
+  #abstract objdir_root
+  #abstract relpath
+
+  def curr_srcdir
+    "#{srcdir_root()}/#{relpath()}"
+  end
+
+  def curr_objdir
+    "#{objdir_root()}/#{relpath()}"
+  end
+
+  def srcfile(path)
+    "#{curr_srcdir()}/#{path}"
+  end
+
+  def srcexist?(path)
+    File.exist?(srcfile(path))
+  end
+
+  def srcdirectory?(path)
+    File.dir?(srcfile(path))
+  end
+  
+  def srcfile?(path)
+    File.file? srcfile(path)
+  end
+
+  def srcentries(path = '.')
+    Dir.open("#{curr_srcdir()}/#{path}") {|d|
+      return d.to_a - %w(. ..)
+    }
+  end
+
+  def srcfiles(path = '.')
+    srcentries(path).select {|fname|
+      File.file?(File.join(curr_srcdir(), path, fname))
+    }
+  end
+
+  def srcdirectories(path = '.')
+    srcentries(path).select {|fname|
+      File.dir?(File.join(curr_srcdir(), path, fname))
+    }
+  end
+
+end
+
+
+class ToplevelInstaller
+
+  Version   = '3.3.1'
+  Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
+
+  TASKS = [
+    [ 'all',      'do config, setup, then install' ],
+    [ 'config',   'saves your configurations' ],
+    [ 'show',     'shows current configuration' ],
+    [ 'setup',    'compiles ruby extentions and others' ],
+    [ 'install',  'installs files' ],
+    [ 'clean',    "does `make clean' for each extention" ],
+    [ 'distclean',"does `make distclean' for each extention" ]
+  ]
+
+  def ToplevelInstaller.invoke
+    instance().invoke
+  end
+
+  @singleton = nil
+
+  def ToplevelInstaller.instance
+    @singleton ||= new(File.dirname($0))
+    @singleton
+  end
+
+  include MetaConfigAPI
+
+  def initialize(ardir_root)
+    @config = nil
+    @options = { 'verbose' => true }
+    @ardir = File.expand_path(ardir_root)
+  end
+
+  def inspect
+    "#<#{self.class} #{__id__()}>"
+  end
+
+  def invoke
+    run_metaconfigs
+    case task = parsearg_global()
+    when nil, 'all'
+      @config = load_config('config')
+      parsearg_config
+      init_installers
+      exec_config
+      exec_setup
+      exec_install
+    else
+      @config = load_config(task)
+      __send__ "parsearg_#{task}"
+      init_installers
+      __send__ "exec_#{task}"
+    end
+  end
+  
+  def run_metaconfigs
+    eval_file_ifexist "#{@ardir}/metaconfig"
+  end
+
+  def load_config(task)
+    case task
+    when 'config'
+      ConfigTable.new
+    when 'clean', 'distclean'
+      if File.exist?(ConfigTable.savefile)
+      then ConfigTable.load
+      else ConfigTable.new
+      end
+    else
+      ConfigTable.load
+    end
+  end
+
+  def init_installers
+    @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
+  end
+
+  #
+  # Hook Script API bases
+  #
+
+  def srcdir_root
+    @ardir
+  end
+
+  def objdir_root
+    '.'
+  end
+
+  def relpath
+    '.'
+  end
+
+  #
+  # Option Parsing
+  #
+
+  def parsearg_global
+    valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
+
+    while arg = ARGV.shift
+      case arg
+      when /\A\w+\z/
+        setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg
+        return arg
+
+      when '-q', '--quiet'
+        @options['verbose'] = false
+
+      when       '--verbose'
+        @options['verbose'] = true
+
+      when '-h', '--help'
+        print_usage $stdout
+        exit 0
+
+      when '-v', '--version'
+        puts "#{File.basename($0)} version #{Version}"
+        exit 0
+      
+      when '--copyright'
+        puts Copyright
+        exit 0
+
+      else
+        setup_rb_error "unknown global option '#{arg}'"
+      end
+    end
+
+    nil
+  end
+
+
+  def parsearg_no_options
+    unless ARGV.empty?
+      setup_rb_error "#{task}:  unknown options: #{ARGV.join ' '}"
+    end
+  end
+
+  alias parsearg_show       parsearg_no_options
+  alias parsearg_setup      parsearg_no_options
+  alias parsearg_clean      parsearg_no_options
+  alias parsearg_distclean  parsearg_no_options
+
+  def parsearg_config
+    re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
+    @options['config-opt'] = []
+
+    while i = ARGV.shift
+      if /\A--?\z/ =~ i
+        @options['config-opt'] = ARGV.dup
+        break
+      end
+      m = re.match(i)  or setup_rb_error "config: unknown option #{i}"
+      name, value = *m.to_a[1,2]
+      @config[name] = value
+    end
+  end
+
+  def parsearg_install
+    @options['no-harm'] = false
+    @options['install-prefix'] = ''
+    while a = ARGV.shift
+      case a
+      when /\A--no-harm\z/
+        @options['no-harm'] = true
+      when /\A--prefix=(.*)\z/
+        path = $1
+        path = File.expand_path(path) unless path[0,1] == '/'
+        @options['install-prefix'] = path
+      else
+        setup_rb_error "install: unknown option #{a}"
+      end
+    end
+  end
+
+  def print_usage(out)
+    out.puts 'Typical Installation Procedure:'
+    out.puts "  $ ruby #{File.basename $0} config"
+    out.puts "  $ ruby #{File.basename $0} setup"
+    out.puts "  # ruby #{File.basename $0} install (may require root privilege)"
+    out.puts
+    out.puts 'Detailed Usage:'
+    out.puts "  ruby #{File.basename $0} <global option>"
+    out.puts "  ruby #{File.basename $0} [<global options>] <task> [<task options>]"
+
+    fmt = "  %-24s %s\n"
+    out.puts
+    out.puts 'Global options:'
+    out.printf fmt, '-q,--quiet',   'suppress message outputs'
+    out.printf fmt, '   --verbose', 'output messages verbosely'
+    out.printf fmt, '-h,--help',    'print this message'
+    out.printf fmt, '-v,--version', 'print version and quit'
+    out.printf fmt, '   --copyright',  'print copyright and quit'
+    out.puts
+    out.puts 'Tasks:'
+    TASKS.each do |name, desc|
+      out.printf fmt, name, desc
+    end
+
+    fmt = "  %-24s %s [%s]\n"
+    out.puts
+    out.puts 'Options for CONFIG or ALL:'
+    ConfigTable.each do |item|
+      out.printf fmt, item.help_opt, item.description, item.help_default
+    end
+    out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
+    out.puts
+    out.puts 'Options for INSTALL:'
+    out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
+    out.printf fmt, '--prefix=path',  'install path prefix', '$prefix'
+    out.puts
+  end
+
+  #
+  # Task Handlers
+  #
+
+  def exec_config
+    @installer.exec_config
+    @config.save   # must be final
+  end
+
+  def exec_setup
+    @installer.exec_setup
+  end
+
+  def exec_install
+    @installer.exec_install
+  end
+
+  def exec_show
+    ConfigTable.each do |i|
+      printf "%-20s %s\n", i.name, i.value
+    end
+  end
+
+  def exec_clean
+    @installer.exec_clean
+  end
+
+  def exec_distclean
+    @installer.exec_distclean
+  end
+
+end
+
+
+class ToplevelInstallerMulti < ToplevelInstaller
+
+  include HookUtils
+  include HookScriptAPI
+  include FileOperations
+
+  def initialize(ardir)
+    super
+    @packages = all_dirs_in("#{@ardir}/packages")
+    raise 'no package exists' if @packages.empty?
+  end
+
+  def run_metaconfigs
+    eval_file_ifexist "#{@ardir}/metaconfig"
+    @packages.each do |name|
+      eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
+    end
+  end
+
+  def init_installers
+    @installers = {}
+    @packages.each do |pack|
+      @installers[pack] = Installer.new(@config, @options,
+                                       "#{@ardir}/packages/#{pack}",
+                                       "packages/#{pack}")
+    end
+
+    with    = extract_selection(config('with'))
+    without = extract_selection(config('without'))
+    @selected = @installers.keys.select {|name|
+                  (with.empty? or with.include?(name)) \
+                      and not without.include?(name)
+                }
+  end
+
+  def extract_selection(list)
+    a = list.split(/,/)
+    a.each do |name|
+      setup_rb_error "no such package: #{name}"  unless @installers.key?(name)
+    end
+    a
+  end
+
+  def print_usage(f)
+    super
+    f.puts 'Inluded packages:'
+    f.puts '  ' + @packages.sort.join(' ')
+    f.puts
+  end
+
+  #
+  # multi-package metaconfig API
+  #
+
+  attr_reader :packages
+
+  def declare_packages(list)
+    raise 'package list is empty' if list.empty?
+    list.each do |name|
+      raise "directory packages/#{name} does not exist"\
+              unless File.dir?("#{@ardir}/packages/#{name}")
+    end
+    @packages = list
+  end
+
+  #
+  # Task Handlers
+  #
+
+  def exec_config
+    run_hook 'pre-config'
+    each_selected_installers {|inst| inst.exec_config }
+    run_hook 'post-config'
+    @config.save   # must be final
+  end
+
+  def exec_setup
+    run_hook 'pre-setup'
+    each_selected_installers {|inst| inst.exec_setup }
+    run_hook 'post-setup'
+  end
+
+  def exec_install
+    run_hook 'pre-install'
+    each_selected_installers {|inst| inst.exec_install }
+    run_hook 'post-install'
+  end
+
+  def exec_clean
+    rm_f ConfigTable.savefile
+    run_hook 'pre-clean'
+    each_selected_installers {|inst| inst.exec_clean }
+    run_hook 'post-clean'
+  end
+
+  def exec_distclean
+    rm_f ConfigTable.savefile
+    run_hook 'pre-distclean'
+    each_selected_installers {|inst| inst.exec_distclean }
+    run_hook 'post-distclean'
+  end
+
+  #
+  # lib
+  #
+
+  def each_selected_installers
+    Dir.mkdir 'packages' unless File.dir?('packages')
+    @selected.each do |pack|
+      $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
+      Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
+      Dir.chdir "packages/#{pack}"
+      yield @installers[pack]
+      Dir.chdir '../..'
+    end
+  end
+
+  def verbose?
+    @options['verbose']
+  end
+
+  def no_harm?
+    @options['no-harm']
+  end
+
+end
+
+
+class Installer
+
+  FILETYPES = %w( bin lib ext data )
+
+  include HookScriptAPI
+  include HookUtils
+  include FileOperations
+
+  def initialize(config, opt, srcroot, objroot)
+    @config = config
+    @options = opt
+    @srcdir = File.expand_path(srcroot)
+    @objdir = File.expand_path(objroot)
+    @currdir = '.'
+  end
+
+  def inspect
+    "#<#{self.class} #{File.basename(@srcdir)}>"
+  end
+
+  #
+  # Hook Script API base methods
+  #
+
+  def srcdir_root
+    @srcdir
+  end
+
+  def objdir_root
+    @objdir
+  end
+
+  def relpath
+    @currdir
+  end
+
+  #
+  # configs/options
+  #
+
+  def no_harm?
+    @options['no-harm']
+  end
+
+  def verbose?
+    @options['verbose']
+  end
+
+  def verbose_off
+    begin
+      save, @options['verbose'] = @options['verbose'], false
+      yield
+    ensure
+      @options['verbose'] = save
+    end
+  end
+
+  #
+  # TASK config
+  #
+
+  def exec_config
+    exec_task_traverse 'config'
+  end
+
+  def config_dir_bin(rel)
+  end
+
+  def config_dir_lib(rel)
+  end
+
+  def config_dir_ext(rel)
+    extconf if extdir?(curr_srcdir())
+  end
+
+  def extconf
+    opt = @options['config-opt'].join(' ')
+    command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}"
+  end
+
+  def config_dir_data(rel)
+  end
+
+  #
+  # TASK setup
+  #
+
+  def exec_setup
+    exec_task_traverse 'setup'
+  end
+
+  def setup_dir_bin(rel)
+    all_files_in(curr_srcdir()).each do |fname|
+      adjust_shebang "#{curr_srcdir()}/#{fname}"
+    end
+  end
+
+  def adjust_shebang(path)
+    return if no_harm?
+    tmpfile = File.basename(path) + '.tmp'
+    begin
+      File.open(path, 'rb') {|r|
+        first = r.gets
+        return unless File.basename(config('rubypath')) == 'ruby'
+        return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby'
+        $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
+        File.open(tmpfile, 'wb') {|w|
+          w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
+          w.write r.read
+        }
+        move_file tmpfile, File.basename(path)
+      }
+    ensure
+      File.unlink tmpfile if File.exist?(tmpfile)
+    end
+  end
+
+  def setup_dir_lib(rel)
+  end
+
+  def setup_dir_ext(rel)
+    make if extdir?(curr_srcdir())
+  end
+
+  def setup_dir_data(rel)
+  end
+
+  #
+  # TASK install
+  #
+
+  def exec_install
+    rm_f 'InstalledFiles'
+    exec_task_traverse 'install'
+  end
+
+  def install_dir_bin(rel)
+    install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755
+  end
+
+  def install_dir_lib(rel)
+    install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644
+  end
+
+  def install_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    install_files ruby_extentions('.'),
+                  "#{config('sodir')}/#{File.dirname(rel)}",
+                  0555
+  end
+
+  def install_dir_data(rel)
+    install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644
+  end
+
+  def install_files(list, dest, mode)
+    mkdir_p dest, @options['install-prefix']
+    list.each do |fname|
+      install fname, dest, mode, @options['install-prefix']
+    end
+  end
+
+  def ruby_scripts
+    collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
+  end
+  
+  # picked up many entries from cvs-1.11.1/src/ignore.c
+  reject_patterns = %w( 
+    core RCSLOG tags TAGS .make.state
+    .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
+    *~ *.old *.bak *.BAK *.orig *.rej _$* *$
+
+    *.org *.in .*
+  )
+  mapping = {
+    '.' => '\.',
+    '$' => '\$',
+    '#' => '\#',
+    '*' => '.*'
+  }
+  REJECT_PATTERNS = Regexp.new('\A(?:' +
+                               reject_patterns.map {|pat|
+                                 pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
+                               }.join('|') +
+                               ')\z')
+
+  def collect_filenames_auto
+    mapdir((existfiles() - hookfiles()).reject {|fname|
+             REJECT_PATTERNS =~ fname
+           })
+  end
+
+  def existfiles
+    all_files_in(curr_srcdir()) | all_files_in('.')
+  end
+
+  def hookfiles
+    %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
+      %w( config setup install clean ).map {|t| sprintf(fmt, t) }
+    }.flatten
+  end
+
+  def mapdir(filelist)
+    filelist.map {|fname|
+      if File.exist?(fname)   # objdir
+        fname
+      else                    # srcdir
+        File.join(curr_srcdir(), fname)
+      end
+    }
+  end
+
+  def ruby_extentions(dir)
+    Dir.open(dir) {|d|
+      ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname }
+      if ents.empty?
+        setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
+      end
+      return ents
+    }
+  end
+
+  #
+  # TASK clean
+  #
+
+  def exec_clean
+    exec_task_traverse 'clean'
+    rm_f ConfigTable.savefile
+    rm_f 'InstalledFiles'
+  end
+
+  def clean_dir_bin(rel)
+  end
+
+  def clean_dir_lib(rel)
+  end
+
+  def clean_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    make 'clean' if File.file?('Makefile')
+  end
+
+  def clean_dir_data(rel)
+  end
+
+  #
+  # TASK distclean
+  #
+
+  def exec_distclean
+    exec_task_traverse 'distclean'
+    rm_f ConfigTable.savefile
+    rm_f 'InstalledFiles'
+  end
+
+  def distclean_dir_bin(rel)
+  end
+
+  def distclean_dir_lib(rel)
+  end
+
+  def distclean_dir_ext(rel)
+    return unless extdir?(curr_srcdir())
+    make 'distclean' if File.file?('Makefile')
+  end
+
+  #
+  # lib
+  #
+
+  def exec_task_traverse(task)
+    run_hook "pre-#{task}"
+    FILETYPES.each do |type|
+      if config('without-ext') == 'yes' and type == 'ext'
+        $stderr.puts 'skipping ext/* by user option' if verbose?
+        next
+      end
+      traverse task, type, "#{task}_dir_#{type}"
+    end
+    run_hook "post-#{task}"
+  end
+
+  def traverse(task, rel, mid)
+    dive_into(rel) {
+      run_hook "pre-#{task}"
+      __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
+      all_dirs_in(curr_srcdir()).each do |d|
+        traverse task, "#{rel}/#{d}", mid
+      end
+      run_hook "post-#{task}"
+    }
+  end
+
+  def dive_into(rel)
+    return unless File.dir?("#{@srcdir}/#{rel}")
+
+    dir = File.basename(rel)
+    Dir.mkdir dir unless File.dir?(dir)
+    prevdir = Dir.pwd
+    Dir.chdir dir
+    $stderr.puts '---> ' + rel if verbose?
+    @currdir = rel
+    yield
+    Dir.chdir prevdir
+    $stderr.puts '<--- ' + rel if verbose?
+    @currdir = File.dirname(rel)
+  end
+
+end
+
+
+if $0 == __FILE__
+  begin
+    if multipackage_install?
+      ToplevelInstallerMulti.invoke
+    else
+      ToplevelInstaller.invoke
+    end
+  rescue SetupError
+    raise if $DEBUG
+    $stderr.puts $!.message
+    $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
+    exit 1
+  end
+end