]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/commitdiff
Lots of fixes all around, in preparation for the new auth coremodule
authorGiuseppe Bilotta <giuseppe.bilotta@gmail.com>
Thu, 3 Aug 2006 14:06:15 +0000 (14:06 +0000)
committerGiuseppe Bilotta <giuseppe.bilotta@gmail.com>
Thu, 3 Aug 2006 14:06:15 +0000 (14:06 +0000)
lib/rbot/botuser.rb
lib/rbot/core/config.rb
lib/rbot/irc.rb
lib/rbot/ircbot.rb
lib/rbot/messagemapper.rb
lib/rbot/plugins.rb
lib/rbot/registry.rb
lib/rbot/rfc2812.rb

index 6c84a93bca2461d80c56629e8eb8b4c26672aced..ea47fcbd7641b33ab51ae3966bb783fd63c8947e 100644 (file)
@@ -7,164 +7,6 @@
 # Copyright:: Copyright (c) 2006 Giuseppe Bilotta\r
 # License:: GPLv2\r
 \r
-#--\r
-#####\r
-####\r
-### Discussion on IRC on how to implement it\r
-##\r
-#\r
-# <tango_>     a. do we want user groups together with users?\r
-# <markey>     hmm\r
-# <markey>     let me think about it\r
-# <markey>     generally I would say: as simple as possible while keeping it as flexible as need be\r
-# <tango_>     I think we can put user groups in place afterwards if we build the structure right\r
-# <markey>     prolly, yes\r
-# <tango_>     so\r
-# <tango_>     each plugin registers a name\r
-# <tango_>     so rather than auth level we have +name -name\r
-# <markey>     yes\r
-# <markey>     much better\r
-# <tango_>     the default is +name for every plugin, except when the plugin tells otherwise\r
-# <markey>     although.. \r
-# <markey>     if I only want to allow you access to one plugin\r
-# <markey>     I have lots of typing to do\r
-# <tango_>     nope\r
-# <tango_>     we allow things like -*\r
-# <markey>     ok\r
-# <tango_>     and + has precedence\r
-# <tango_>     hm no, not good either\r
-# <tango_>     because we want bot -* +onething and +* -onething to work\r
-# <markey>     but then: one plugin currently can have several levels, no?\r
-# <tango_>     of course\r
-# <markey>     commandedit, commanddel, commandfoo\r
-# <tango_>     name.command ?\r
-# <markey>     yep\r
-# <tango_>     (then you can't have dots in commands\r
-# <tango_>     maybe name:command\r
-# <markey>     or name::comand\r
-# <markey>     like a namespace\r
-# <tango_>     ehehehe yeah I like it :)\r
-# <tango_>     tel\r
-# <tango_>     brb\r
-# <markey>     usermod setcaps eean -*\r
-# <markey>     usermod setcaps eean +quiz::edit\r
-# <markey>     great\r
-# <markey>     or even\r
-# <markey>     auth eean -*, +quiz::edit\r
-# <markey>     awesome\r
-# <markey>     auth eean -*, +quiz::edit, +command, -command::del\r
-# <tango_>     yes\r
-# <markey>     you know, the default should be -*\r
-# <markey>     because\r
-# <markey>     in the time between adding the user and changing auth\r
-# <markey>     it's insecure\r
-# <markey>     user could do havoc\r
-# <markey>     useradd eean, then eean does "~quit", before I change auth\r
-# <tango_>     nope\r
-# <markey>     perhaps we should allow combining useradd with auth\r
-# <tango_>     the default should be +* -important stuff\r
-# <markey>     ok\r
-# <tango_>     how to specify channel stuff?\r
-# <markey>     for one, when you issue the command on the channel itself\r
-# <markey>     then it's channel relative\r
-# <markey>     perhaps\r
-# <markey>     or\r
-# <tango_>     yes but I was thinking more about the syntax\r
-# <markey>     auth eean #rbot -quiz\r
-# <tango_>     hm\r
-# <markey>     or maybe: treat channels like users: auth #rbot -quiz\r
-# <markey>     would shut up quiz in #rbot\r
-# <markey>     hm\r
-# <markey>     heh\r
-# <tango_>     auth * #rbot -quiz\r
-# <markey>     not sure I'm making sense here ;)\r
-# <tango_>     I think syntax should be auth [usermask] [channelmask] [modes]\r
-# <markey>     yes\r
-# <markey>     modes separated by comma?\r
-# <tango_>     where channelmask is implied to be *\r
-# <tango_>     no we can have it spacesplit\r
-# <markey>     great\r
-# <markey>     ok\r
-# <tango_>     modes are detected by +-\r
-# <tango_>     so you can do something like auth markey #rbot -quiz #amarok -chuck\r
-# <markey>     also I like "auth" a lot more than "usermod foo"\r
-# <markey>     yep\r
-# <tango_>     I don't understand why the 'mod'\r
-# <tango_>     we could have all auth commands start with use\r
-# <tango_>     user\r
-# <tango_>     user add\r
-# <tango_>     user list\r
-# <tango_>     user del\r
-# <markey>     yes\r
-# <tango_>     user auth\r
-# <tango_>     hm\r
-# <tango_>     and maybe auth as a synonym for user auth\r
-# <markey>     this is also uncomfortable: usermod wants the full user mask\r
-# <markey>     you have to copy/paste it\r
-# <tango_>     no\r
-# <tango_>     can't you use *?\r
-# <markey>     sorry not sure\r
-# <markey>     but this shows, it's not inuitive\r
-# <markey>     I've read the docs\r
-# <markey>     but didn't know how to use it really\r
-# <tango_>     markey!*@*\r
-# <markey>     that's not very intuitive\r
-# <tango_>     we could use nick as a synonym for nick!*@* if it's too much for you :D\r
-# <markey>     usermod markey foo should suffice\r
-# <markey>     rememember: you're a hacker. when rbot gets many new users, they will often be noobs\r
-# <markey>     gotta make things simple to use\r
-# <tango_>     but the hostmask is only needed for the user creation\r
-# <markey>     really? then forget what I said, sorry\r
-# <tango_>     I think so\r
-# <tango_>     ,help auth\r
-# <testbot>    Auth module (User authentication) topics: setlevel, useradd, userdel, usermod, auth, levels, users, whoami, identify\r
-# <tango_>     ,help usermod\r
-# <testbot>    no help for topic usermod\r
-# <tango_>     ,help auth usermod\r
-# <testbot>    usermod <username> <item> <value> => Modify <username>s settings. Valid <item>s are: hostmask, (+|-)hostmask, password, level (private addressing only)\r
-# <tango_>     see? it's username, not nick :D\r
-# <markey>     btw, help usermod should also work\r
-# <tango_>     ,help auth useradd\r
-# <testbot>    useradd <username> => Add user <mask>, you still need to set him up correctly (private addressing only)\r
-# <markey>     instead of help auth usermode\r
-# <markey>     when it's not ambiguous\r
-# <tango_>     and the help for useradd is wrong\r
-# <markey>     for the website, we could make a logo contest :) the current logo looks like giblet made it in 5 minutes ;)\r
-# <markey>     ah well, for 1.0 maybe\r
-# <tango_>     so a user on rbot is given by\r
-# <tango_>     username, password, hostmasks, permissions\r
-# <markey>     yup\r
-# <tango_>     the default permission is +* -importantstuff\r
-# <markey>     how defines importantstuff?\r
-# <markey>     you mean like core and auth?\r
-# <tango_>     yes\r
-# <markey>     ok\r
-# <tango_>     but we can decide about this :)\r
-# <markey>     some plugins are dangerous by default\r
-# <markey>     like command plugin\r
-# <markey>     you can do all sorts of nasty shit with it\r
-# <tango_>     then command plugin will do something like: command.defaultperm("-command")\r
-# <markey>     yes, good point\r
-# <tango_>     this is then added to the default permissions (user * channel *)\r
-# <tango_>     when checking for auth, we go like this:\r
-# <tango_>     hm\r
-# <tango_>     check user * channel *\r
-# <tango_>     then user name channel *\r
-# <tango_>     then user * channel name\r
-# <tango_>     then user name channel name\r
-# <tango_>     for each of these combinations we match against * first, then against command, and then against command::subcommand\r
-# <markey>     yup\r
-# <tango_>     setting or resetting it depending on wether it's + or -\r
-# <tango_>     the final result gives us the permission\r
-# <tango_>     implementation detail\r
-# <tango_>     username and passwords are strings\r
-# <markey>     (I might rename the command plugin, the name is somewhat confusing)\r
-# <tango_>     yeah\r
-# <tango_>     hostmasks are hostmasks\r
-# <markey>     also I'm pondering to restrict it more: disallow access to @bot\r
-# <tango_>     permissions are in the form [ [channel, {command => bool, ...}] ...]\r
-#++\r
-\r
 require 'singleton'\r
 \r
 module Irc\r
@@ -186,6 +28,13 @@ module Irc
   #\r
   module Auth\r
 \r
+    BotConfig.register BotConfigStringValue.new( 'auth.password',\r
+      :default => 'rbotauth', :wizard => true,\r
+      :desc => 'Password for the bot owner' )\r
+    # BotConfig.register BotConfigIntegerValue.new( 'auth.default_level',\r
+    #   :default => 10, :wizard => true,\r
+    #   :desc => 'The default level for new/unknown users' )\r
+\r
     # Generate a random password of length _l_\r
     #\r
     def random_password(l=8)\r
@@ -234,6 +83,7 @@ module Irc
         @command = path.last\r
         debug "Created command #{@command.inspect} with path #{@path.join(', ')}"\r
       end\r
+\r
     end\r
 \r
     # This method raises a TypeError if _user_ is not of class User\r
@@ -291,6 +141,7 @@ module Irc
         }\r
         return allow\r
       end\r
+\r
     end\r
 \r
 \r
@@ -311,6 +162,51 @@ module Irc
         @perm = {}\r
       end\r
 \r
+      # Inspection\r
+      def inspect\r
+        str = "<#{self.class}:#{'0x%08x' % self.object_id}:"\r
+        str << " @username=#{@username.inspect}"\r
+        str << " @netmasks=#{@netmasks.inspect}"\r
+        str << " @perm=#{@perm.inspect}"\r
+        str\r
+      end\r
+\r
+      # Convert into a hash\r
+      def to_hash\r
+        {\r
+          :username => @username,\r
+          :password => @password,\r
+          :netmasks => @netmasks,\r
+          :perm => @perm\r
+        }\r
+      end\r
+\r
+      # Restore from hash\r
+      def from_hash(h)\r
+        @username = h[:username] if h.has_key?(:username)\r
+        @password = h[:password] if h.has_key?(:password)\r
+        @netmasks = h[:netmasks] if h.has_key?(:netmasks)\r
+        @perm = h[:perm] if h.has_key?(:perm)\r
+      end\r
+\r
+      # This method sets the password if the proposed new password\r
+      # is valid\r
+      def password=(pwd=nil)\r
+        if pwd\r
+          begin\r
+            raise InvalidPassword, "#{pwd} contains invalid characters" if pwd !~ /^[A-Za-z0-9]+$/\r
+            raise InvalidPassword, "#{pwd} too short" if pwd.length < 4\r
+            @password = pwd\r
+          rescue InvalidPassword => e\r
+            raise e\r
+          rescue => e\r
+            raise InvalidPassword, "Exception #{e.inspect} while checking #{pwd}"\r
+          end\r
+        else\r
+          reset_password\r
+        end\r
+      end\r
+\r
       # Resets the password by creating a new onw\r
       def reset_password\r
         @password = random_password\r
@@ -358,7 +254,7 @@ module Irc
         when Netmask\r
           @netmasks << mask\r
         else\r
-          @netmasks << Netmask(mask)\r
+          @netmasks << Netmask.new(mask)\r
         end\r
       end\r
 \r
@@ -369,7 +265,7 @@ module Irc
         when Netmask\r
           m = mask\r
         else\r
-          m << Netmask(mask)\r
+          m << Netmask.new(mask)\r
         end\r
         @netmasks.delete(m)\r
       end\r
@@ -399,6 +295,7 @@ module Irc
       def login(user, password)\r
         if password == @password\r
           add_netmask(user) unless knows?(user)\r
+          debug "#{user} logged in as #{self.inspect}"\r
           return true\r
         else\r
           return false\r
@@ -417,23 +314,6 @@ module Irc
         return name.to_s.chomp.downcase.gsub(/[^a-z0-9]/,"_")\r
       end\r
 \r
-      # This method sets the password if the proposed new password\r
-      # is valid\r
-      def password=(pwd=nil)\r
-        if pwd\r
-          begin\r
-            raise InvalidPassword, "#{pwd} contains invalid characters" if pwd !~ /^[A-Za-z0-9]+$/\r
-            raise InvalidPassword, "#{pwd} too short" if pwd.length < 4\r
-            @password = pwd\r
-          rescue InvalidPassword => e\r
-            raise e\r
-          rescue => e\r
-            raise InvalidPassword, "Exception #{e.inspect} while checking #{pwd}"\r
-          end\r
-        else\r
-          reset_password\r
-        end\r
-      end\r
     end\r
 \r
 \r
@@ -441,12 +321,15 @@ module Irc
     # identified with the bot\r
     #\r
     class DefaultBotUserClass < BotUser\r
+\r
+      private :login, :add_netmask, :delete_netmask\r
+\r
       include Singleton\r
+\r
       def initialize\r
         super("everyone")\r
         @default_perm = PermissionSet.new\r
       end\r
-      private :login, :add_netmask, :delete_netmask\r
 \r
       # Sets the default permission for the default user (i.e. the ones\r
       # set by the BotModule writers) on all channels\r
@@ -480,6 +363,7 @@ module Irc
         end\r
         return allow\r
       end\r
+\r
     end\r
 \r
     # Returns the only instance of DefaultBotUserClass\r
@@ -491,7 +375,9 @@ module Irc
     # This is the BotOwner: he can do everything\r
     #\r
     class BotOwnerClass < BotUser\r
+\r
       include Singleton\r
+\r
       def initialize\r
         super("owner")\r
       end\r
@@ -499,6 +385,7 @@ module Irc
       def permit?(cmd, chan=nil)\r
         return true\r
       end\r
+\r
     end\r
 \r
     # Returns the only instance of BotOwnerClass\r
@@ -512,6 +399,7 @@ module Irc
     # everything\r
     #\r
     class AuthManagerClass\r
+\r
       include Singleton\r
 \r
       attr_reader :everyone\r
@@ -539,6 +427,18 @@ module Irc
         @has_changes = false\r
       end\r
 \r
+      def set_changed\r
+        @has_changes = true\r
+      end\r
+\r
+      def reset_changed\r
+        @has_changes = false\r
+      end\r
+\r
+      def changed?\r
+        @has_changes\r
+      end\r
+\r
       # resets the hashes\r
       def reset_hashes\r
         @botusers = Hash.new\r
@@ -548,23 +448,24 @@ module Irc
         }\r
       end\r
 \r
-      # load botlist from userfile\r
-      def load_merge(filename=nil)\r
-        # TODO\r
-        raise NotImplementedError\r
-        @has_changes = true\r
-      end\r
-\r
-      def load(filename=nil)\r
+      def load_array(ary, forced)\r
+        raise "Won't load with unsaved changes" if @has_changes and not forced\r
         reset_hashes\r
-        load_merge(filename)\r
+        ary.each { |x|\r
+          raise TypeError, "#{x} should be a Hash" unless x.class <= Hash\r
+          u = x[:username]\r
+          unless include?(u)\r
+            create_botuser(u)\r
+          end\r
+          get_botuser(u).from_hash(x)\r
+        }\r
+        @has_changes=false\r
       end\r
 \r
-      # save botlist to userfile\r
-      def save(filename=nil)\r
-        return unless @has_changes\r
-        # TODO\r
-        raise NotImplementedError\r
+      def save_array\r
+        @allbotusers.values.map { |x|\r
+          x.to_hash\r
+        }\r
       end\r
 \r
       # checks if we know about a certain BotUser username\r
@@ -589,6 +490,11 @@ module Irc
         @allbotusers[k] = bu\r
       end\r
 \r
+      # returns the botuser with name _name_\r
+      def get_botuser(name)\r
+        @allbotusers.fetch(BotUser.sanitize_username(name).to_sym)\r
+      end\r
+\r
       # Logs Irc::User _ircuser_ in to BotUser _botusername_ with password _pwd_\r
       #\r
       # raises an error if _botusername_ is not a known BotUser username\r
@@ -597,7 +503,7 @@ module Irc
       #\r
       def login(ircuser, botusername, pwd, bymask = false)\r
         Irc::error_if_not_user(ircuser)\r
-        n = BotUser.sanitize_username(name)\r
+        n = BotUser.sanitize_username(botusername)\r
         k = n.to_sym\r
         raise "No such BotUser #{n}" unless include?(k)\r
         if @botusers.has_key?(ircuser)\r
@@ -605,7 +511,7 @@ module Irc
           # @botusers[ircuser].logout(ircuser)\r
         end\r
         bu = @allbotusers[k]\r
-        if bymask && bu.knows?(user)\r
+        if bymask && bu.knows?(ircuser)\r
           @botusers[ircuser] = bu\r
           return true\r
         elsif bu.login(ircuser, pwd)\r
@@ -656,6 +562,7 @@ module Irc
       def allow?(cmdtxt, user, chan=nil)\r
         permit?(user, cmdtxt, chan)\r
       end\r
+\r
     end\r
 \r
     # Returns the only instance of AuthManagerClass\r
@@ -663,5 +570,7 @@ module Irc
     def Auth.authmanager\r
       return AuthManagerClass.instance\r
     end\r
+\r
   end\r
+\r
 end\r
index 7eda780f4536a69d4113ae22570957dc3d3abf19..3099a00a79aa2d5d50375032f2cf96ce56fa17f0 100644 (file)
@@ -4,6 +4,10 @@
 \r
 class ConfigModule < CoreBotModule\r
 \r
+  def save\r
+    @bot.config.save\r
+  end\r
+\r
   def handle_list(m, params)\r
     modules = []\r
     if params[:module]\r
@@ -18,7 +22,7 @@ class ConfigModule < CoreBotModule
         m.reply modules.join(", ")\r
       end\r
     else\r
-      @bot.configitems.each_key do |key|\r
+      @bot.config.items.each_key do |key|\r
         name = key.to_s.split('.').first\r
         modules.push name unless modules.include?(name)\r
       end\r
@@ -132,7 +136,7 @@ class ConfigModule < CoreBotModule
     @bot.save\r
     m.reply "rescanning ..."\r
     @bot.rescan\r
-    m.reply "done. #{@plugins.status(true)}"\r
+    m.reply "done. #{@bot.plugins.status(true)}"\r
   end\r
 \r
   def bot_nick(m, param)\r
index ffc2be7182681862bf4dacc80e1c220b40cdce7d..74db8e85fd4d1ff750803760c2cce91fa6298b94 100644 (file)
@@ -373,12 +373,12 @@ module Irc
     # globs is not handled yet.\r
     # \r
     def matches?(arg)\r
-      cmp = Netmask(arg)\r
+      cmp = Netmask.new(arg)\r
       raise TypeError, "#{arg} and #{self} have different casemaps" if @casemap != cmp.casemap\r
       raise TypeError, "#{arg} is not a valid Netmask" unless cmp.class <= Netmask\r
       [:nick, :user, :host].each { |component|\r
-        us = self.send(:component)\r
-        them = cmp.send(:component)\r
+        us = self.send(component)\r
+        them = cmp.send(component)\r
         raise NotImplementedError if us.has_irc_glob? && them.has_irc_glob?\r
         return false if us.has_irc_glob? && !them.has_irc_glob?\r
         return false unless us =~ them.to_irc_regexp\r
@@ -389,7 +389,7 @@ module Irc
     # Case equality. Checks if arg matches self\r
     #\r
     def ===(arg)\r
-      Netmask(arg).matches?(self)\r
+      Netmask.new(arg).matches?(self)\r
     end\r
   end\r
 \r
@@ -951,7 +951,7 @@ module Irc
     # a name of\r
     #\r
     def user_or_channel?(name)\r
-      if supports[:chantypes].include?(name[0].chr)\r
+      if supports[:chantypes].include?(name[0])\r
         return Channel\r
       else\r
         return User\r
@@ -961,7 +961,7 @@ module Irc
     # Returns the actual User or Channel object matching _name_\r
     #\r
     def user_or_channel(name)\r
-      if supports[:chantypes].include?(name[0].chr)\r
+      if supports[:chantypes].include?(name[0])\r
         return channel(name)\r
       else\r
         return user(name)\r
@@ -1132,15 +1132,7 @@ module Irc
     # new_user(_str_, +false+)\r
     #\r
     def user(str)\r
-      # This method can get called before server has been initialized (e.g. on\r
-      # Freenode there is a NOTICE from AUTH on connect). In this case we just\r
-      # return the string\r
-      #\r
-      if defined?(@supports)\r
-        new_user(str, false)\r
-      else\r
-        str\r
-      end\r
+      new_user(str, false)\r
     end\r
 \r
     # Remove User _someuser_ from the list of <code>User</code>s.\r
index f765668d88fa632eb9926c85a07084acdcb0e4c2..df0659b59ad4537e65addbe6faaa1d8acc121d38 100644 (file)
@@ -104,17 +104,13 @@ class IrcBot
   # by default)
   attr_reader :timer
 
+  # synchronize with this mutex while touching permanent data files:
+  # saving, flushing, cleaning up ...
+  attr_reader :save_mutex
+
   # bot's Language data
   attr_reader :lang
 
-  # server the bot is connected to
-  # TODO multiserver
-  attr_reader :server
-
-  # the client personality of the bot
-  # TODO multiserver
-  attr_reader :client
-
   # bot's irc socket
   # TODO multiserver
   attr_reader :socket
@@ -131,8 +127,17 @@ class IrcBot
   # proxies etc as defined by the bot configuration/environment
   attr_reader :httputil
 
+  # server we are connected to
+  # TODO multiserver
+  def server
+    @client.server
+  end
+
   # bot User in the client/server connection
-  attr_reader :myself
+  # TODO multiserver
+  def myself
+    @client.client
+  end
 
   # bot User in the client/server connection
   def nick
@@ -335,6 +340,7 @@ class IrcBot
     @registry = BotRegistry.new self
 
     @timer = Timer::Timer.new(1.0) # only need per-second granularity
+    @save_mutex = Mutex.new
     @timer.add(@config['core.save_every']) { save } if @config['core.save_every']
 
     @logs = Hash.new
@@ -356,6 +362,7 @@ class IrcBot
       exit 2
     end
     @auth.everyone.set_default_permission("*", true)
+    @auth.botowner.password= @config['auth.password']
 
     Dir.mkdir("#{botclass}/plugins") unless File.exist?("#{botclass}/plugins")
     @plugins = Plugins::pluginmanager
@@ -367,9 +374,7 @@ class IrcBot
 
     @socket = IrcSocket.new(@config['server.name'], @config['server.port'], @config['server.bindhost'], @config['server.sendq_delay'], @config['server.sendq_burst'])
     @client = IrcClient.new
-    @server = @client.server
-    @myself = @client.client
-    @myself.nick = @config['irc.nick']
+    myself.nick = @config['irc.nick']
 
     # Channels where we are quiet
     # It's nil when we are not quiet, an empty list when we are quiet
@@ -394,18 +399,20 @@ class IrcBot
       # TODO this needs to go into rfc2812.rb
       # Since capabs are two-steps processes, server.supports[:capab]
       # should be a three-state: nil, [], [....]
-      sendq "CAPAB IDENTIFY-MSG" if @server.supports[:capab]
+      sendq "CAPAB IDENTIFY-MSG" if server.supports[:capab]
     }
     @client[:datastr] = proc { |data|
       # TODO this needs to go into rfc2812.rb
       if data[:text] == "IDENTIFY-MSG"
-        @server.capabilities["identify-msg".to_sym] = true
+        server.capabilities["identify-msg".to_sym] = true
       else
         debug "Not handling RPL_DATASTR #{data[:servermessage]}"
       end
     }
     @client[:privmsg] = proc { |data|
-      m = PrivMessage.new(self, @server, data[:source], data[:target], data[:message])
+      m = PrivMessage.new(self, server, data[:source], data[:target], data[:message])
+      debug "Message target is #{data[:target].inspect}"
+      debug "Bot is #{myself.inspect}"
 
       # TODO use the new Netmask class
       # @config['irc.ignore_users'].each { |mask| return if Irc.netmaskmatch(mask,m.source) }
@@ -416,7 +423,7 @@ class IrcBot
       @plugins.privmsg(m) if m.address?
     }
     @client[:notice] = proc { |data|
-      message = NoticeMessage.new(self, @server, data[:source], data[:target], data[:message])
+      message = NoticeMessage.new(self, server, data[:source], data[:target], data[:message])
       # pass it off to plugins that want to hear everything
       @plugins.delegate "listen", message
     }
@@ -442,7 +449,7 @@ class IrcBot
       source = data[:source]
       old = data[:oldnick]
       new = data[:newnick]
-      m = NickMessage.new(self, @server, source, old, new)
+      m = NickMessage.new(self, server, source, old, new)
       if source == myself
         debug "my nick is now #{new}"
       end
@@ -455,7 +462,7 @@ class IrcBot
     @client[:quit] = proc {|data|
       source = data[:source]
       message = data[:message]
-      m = QuitMessage.new(self, @server, source, source, message)
+      m = QuitMessage.new(self, server, source, source, message)
       data[:was_on].each { |ch|
         irclog "@ Quit: #{source}: #{message}", ch
       }
@@ -466,21 +473,21 @@ class IrcBot
       irclog "@ Mode #{data[:modestring]} by #{data[:source]}", data[:channel]
     }
     @client[:join] = proc {|data|
-      m = JoinMessage.new(self, @server, data[:source], data[:channel], data[:message])
+      m = JoinMessage.new(self, server, data[:source], data[:channel], data[:message])
       irclogjoin(m)
 
       @plugins.delegate("listen", m)
       @plugins.delegate("join", m)
     }
     @client[:part] = proc {|data|
-      m = PartMessage.new(self, @server, data[:source], data[:channel], data[:message])
+      m = PartMessage.new(self, server, data[:source], data[:channel], data[:message])
       irclogpart(m)
 
       @plugins.delegate("listen", m)
       @plugins.delegate("part", m)
     }
     @client[:kick] = proc {|data|
-      m = KickMessage.new(self, @server, data[:source], data[:target], data[:channel],data[:message])
+      m = KickMessage.new(self, server, data[:source], data[:target], data[:channel],data[:message])
       irclogkick(m)
 
       @plugins.delegate("listen", m)
@@ -492,7 +499,7 @@ class IrcBot
       end
     }
     @client[:changetopic] = proc {|data|
-      m = TopicMessage.new(self, @server, data[:source], data[:channel], data[:topic])
+      m = TopicMessage.new(self, server, data[:source], data[:channel], data[:topic])
       irclogtopic(m)
 
       @plugins.delegate("listen", m)
@@ -505,7 +512,7 @@ class IrcBot
       channel = data[:channel]
       topic = channel.topic
       irclog "@ Topic set by #{topic.set_by} on #{topic.set_on}", channel
-      m = TopicMessage.new(self, @server, data[:source], channel, topic)
+      m = TopicMessage.new(self, server, data[:source], channel, topic)
 
       @plugins.delegate("listen", m)
       @plugins.delegate("topic", m)
@@ -623,7 +630,7 @@ class IrcBot
       end
 
       stop_server_pings
-      @server.clear
+      server.clear
       if @socket.connected?
         @socket.clearq
         @socket.shutdown
@@ -798,7 +805,7 @@ class IrcBot
       @socket.shutdown
     end
     debug "Logging quits"
-    @server.channels.each { |ch|
+    server.channels.each { |ch|
       irclog "@ quit (#{message})", ch
     }
     debug "Saving"
@@ -834,11 +841,13 @@ class IrcBot
 
   # call the save method for bot's config, keywords, auth and all plugins
   def save
-    @config.save
-    # @keywords.save
-    @auth.save
-    @plugins.save
-    DBTree.cleanup_logs
+    @save_mutex.synchronize do
+      # @config.save
+      # @keywords.save
+      # @auth.save
+      @plugins.save
+      DBTree.cleanup_logs
+    end
   end
 
   # call the rescan method for the bot's lang, keywords and all plugins
@@ -965,8 +974,6 @@ class IrcBot
         case where
         when Channel
           irclog "-=#{myself}=- #{message}", where
-        when User
-             irclog "[-=#{where}=-] #{message}", $1
         else
              irclog "[-=#{where}=-] #{message}", where
         end
@@ -974,8 +981,6 @@ class IrcBot
         case where
         when Channel
           irclog "<#{myself}> #{message}", where
-        when User
-          irclog "[msg(#{where})] #{message}", $1
         else
           irclog "[msg(#{where})] #{message}", where
         end
index 2214780b3c875c8c9ef6595782764d3c7d57db1c..193804fef8af450f2a6bc29385b39a00ecacf23c 100644 (file)
@@ -178,16 +178,23 @@ module Irc
         else
           raise ArgumentError, "Can't find auth base in #{botmodule.inspect}"
         end
-        post = items.reject{ |x|
+        words = items.reject{ |x|
           x == pre || x.kind_of?(Symbol)
         }
-        if post.empty?
+        if words.empty?
           post = nil
         else
-          post = post.first
+          post = words.first
         end
         if hash.has_key?(:auth_path)
           extra = hash[:auth_path]
+          if extra.sub!(/^:/, "")
+            pre += post
+            post = nil
+          end
+          if extra.sub!(/:$/, "")
+            post = [post,words[1]].compact.join("::") if words.length > 1
+          end
           pre = nil if extra.sub!(/^!/, "")
           post = nil if extra.sub!(/!$/, "")
         else
index f8eddd6ed65736c689f1a9ae92db35894757dd80..43793e99760fe95f0d5790cc42b163fd966857e4 100644 (file)
@@ -2,7 +2,7 @@ require 'singleton'
 
 module Irc
     BotConfig.register BotConfigArrayValue.new('plugins.blacklist',
-      :default => [], :wizard => false, :requires_restart => true,
+      :default => [], :wizard => false, :requires_rescan => true,
       :desc => "Plugins that should not be loaded")
 module Plugins
   require 'rbot/messagemapper'
@@ -417,7 +417,9 @@ module Plugins
 
     # call the cleanup method for each active plugin
     def cleanup
-      delegate 'cleanup'
+      @bot.save_mutex.synchronize do
+        delegate 'cleanup'
+      end
       reset_botmodule_lists
     end
 
@@ -540,6 +542,7 @@ module Plugins
               # debug "#{p.botmodule_class} #{p.name} responds"
               p.send method, *args
             rescue Exception => err
+              raise if err.class <= SystemExit
               error report_error("#{p.botmodule_class} #{p.name} #{method}() failed:", err)
               raise if err.class <= BDB::Fatal
             end
@@ -554,49 +557,46 @@ module Plugins
     def privmsg(m)
       # debug "Delegating privmsg #{m.message.inspect} from #{m.source} to #{m.replyto} with pluginkey #{m.plugin.inspect}"
       return unless m.plugin
-      begin
-        [core_commands, plugin_commands].each { |pl|
-          # We do it this way to skip creating spurious keys
-          # FIXME use fetch?
-          k = m.plugin.to_sym
-          if pl.has_key?(k)
-            p = pl[k][:botmodule]
-            a = pl[k][:auth]
-          else
-            p = nil
-            a = nil
-          end
-          if p
-            # We check here for things that don't check themselves
-            # (e.g. mapped things)
-            # debug "Checking auth ..."
-            if a.nil? || @bot.auth.allow?(a, m.source, m.replyto)
-              # debug "Checking response ..."
-              if p.respond_to?("privmsg")
-                begin
-                  # debug "#{p.botmodule_class} #{p.name} responds"
-                  p.privmsg(m)
-                rescue Exception => err
-                  error report_error("#{p.botmodule_class} #{p.name} privmsg() failed:", err)
-                  raise if err.class <= BDB::Fatal
-                end
-                # debug "Successfully delegated #{m.message}"
-                return true
-              else
-                # debug "#{p.botmodule_class} #{p.name} is registered, but it doesn't respond to privmsg()"
+      [core_commands, plugin_commands].each { |pl|
+        # We do it this way to skip creating spurious keys
+        # FIXME use fetch?
+        k = m.plugin.to_sym
+        if pl.has_key?(k)
+          p = pl[k][:botmodule]
+          a = pl[k][:auth]
+        else
+          p = nil
+          a = nil
+        end
+        if p
+          # We check here for things that don't check themselves
+          # (e.g. mapped things)
+          # debug "Checking auth ..."
+          if a.nil? || @bot.auth.allow?(a, m.source, m.replyto)
+            # debug "Checking response ..."
+            if p.respond_to?("privmsg")
+              begin
+                # debug "#{p.botmodule_class} #{p.name} responds"
+                p.privmsg(m)
+              rescue Exception => err
+                raise if err.class <= SystemExit
+                error report_error("#{p.botmodule_class} #{p.name} privmsg() failed:", err)
+                raise if err.class <= BDB::Fatal
               end
+              # debug "Successfully delegated #{m.message}"
+              return true
             else
-              # debug "#{p.botmodule_class} #{p.name} is registered, but #{m.source} isn't allowed to call #{m.plugin.inspect} on #{m.replyto}"
+              # debug "#{p.botmodule_class} #{p.name} is registered, but it doesn't respond to privmsg()"
             end
           else
-            # debug "No #{pl.values.first[:botmodule].botmodule_class} registered #{m.plugin.inspect}" unless pl.empty?
+            # debug "#{p.botmodule_class} #{p.name} is registered, but #{m.source} isn't allowed to call #{m.plugin.inspect} on #{m.replyto}"
           end
-          # debug "Finished delegating privmsg with key #{m.plugin.inspect}" + ( pl.empty? ? "" : " to #{pl.values.first[:botmodule].botmodule_class}s" )
-        }
-        return false
-      rescue Exception => e
-        error report_error("couldn't delegate #{m.message.inspect}", e)
-      end
+        else
+          # debug "No #{pl.values.first[:botmodule].botmodule_class} registered #{m.plugin.inspect}" unless pl.empty?
+        end
+        # debug "Finished delegating privmsg with key #{m.plugin.inspect}" + ( pl.empty? ? "" : " to #{pl.values.first[:botmodule].botmodule_class}s" )
+      }
+      return false
       # debug "Finished delegating privmsg with key #{m.plugin.inspect}"
     end
   end
index d86850d41fc2037b1f4536c4e71ec99c1d124e71..e679722a9694c029e68f421cecc967499a40ce50 100644 (file)
@@ -137,11 +137,13 @@ module Irc
     end
 
     def flush
+      # debug "fushing registry #{@registry}"
       @registry.flush
       @registry.sync
     end
 
     def close
+      # debug "closing registry #{@registry}"
       @registry.close
     end
 
index e2438210ad735ffa957e439ff80d7c01b20affee..697c13e95a9fa15db7f5e55c2bb8c85e4235c88c 100644 (file)
@@ -922,6 +922,7 @@ module Irc
             data[:nick] = $2
             data[:address] = $3
             @client = @server.user(data[:netmask])
+            set = true
           when /Welcome to the Internet Relay Network\s(\S+)/
             data[:nick] = $1
           when /Welcome.*\s+(\S+)$/
@@ -929,7 +930,7 @@ module Irc
           when /^(\S+)$/
             data[:nick] = $1
           end
-          @user ||= @server.user(data[:nick])
+          @client = @server.user(data[:nick]) unless set
           handle(:welcome, data)
         when RPL_YOURHOST
           # "Your host is <servername>, running version <ver>"
@@ -1016,7 +1017,7 @@ module Irc
 
           users.each { |ar|
             u = @server.user(ar[0])
-            chan.users << u
+            chan.users << u unless chan.users.include?(u)
             if ar[1]
               m = @server.supports[:prefix][:prefixes].index(ar[1].to_sym)
               m = @server.supports[:prefix][:modes][m]