]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/plugins.rb
New unreplied() method for plugins that want to handle PRIVMSGs unreplied by any...
[user/henk/code/ruby/rbot.git] / lib / rbot / plugins.rb
index 933ea14f24927dfbb3f3211b1cc31fb497d28015..e101e627772d0cf977689f9b5173216abacd84b7 100644 (file)
@@ -61,11 +61,14 @@ module Plugins
                          etc.
 
   privmsg(PrivMessage)::
                          etc.
 
   privmsg(PrivMessage)::
-                         called for a PRIVMSG if the first word matches one
+                         Called for a PRIVMSG if the first word matches one
                          the plugin register()d for. Use m.plugin to get
                          that word and m.params for the rest of the message,
                          if applicable.
 
                          the plugin register()d for. Use m.plugin to get
                          that word and m.params for the rest of the message,
                          if applicable.
 
+  unreplied(PrivMessage)::
+                         Called for a PRIVMSG which has not been replied to.
+
   kick(KickMessage)::
                          Called when a user (or the bot) is kicked from a
                          channel the bot is in.
   kick(KickMessage)::
                          Called when a user (or the bot) is kicked from a
                          channel the bot is in.
@@ -88,6 +91,10 @@ module Plugins
   connect()::            Called when a server is joined successfully, but
                          before autojoin channels are joined (no params)
 
   connect()::            Called when a server is joined successfully, but
                          before autojoin channels are joined (no params)
 
+  set_language(String)::
+                         Called when the user sets a new language
+                         whose name is the given String
+
   save::                 Called when you are required to save your plugin's
                          state, if you maintain data between sessions
 
   save::                 Called when you are required to save your plugin's
                          state, if you maintain data between sessions
 
@@ -98,21 +105,26 @@ module Plugins
 
   class BotModule
     attr_reader :bot   # the associated bot
 
   class BotModule
     attr_reader :bot   # the associated bot
-    attr_reader :botmodule_class # the botmodule class (:coremodule or :plugin)
 
     # initialise your bot module. Always call super if you override this method,
     # as important variables are set up for you
 
     # initialise your bot module. Always call super if you override this method,
     # as important variables are set up for you
-    def initialize(kl)
+    def initialize
       @manager = Plugins::pluginmanager
       @bot = @manager.bot
 
       @manager = Plugins::pluginmanager
       @bot = @manager.bot
 
-      @botmodule_class = kl.to_sym
       @botmodule_triggers = Array.new
 
       @handler = MessageMapper.new(self)
       @registry = BotRegistryAccessor.new(@bot, self.class.to_s.gsub(/^.*::/, ""))
 
       @manager.add_botmodule(self)
       @botmodule_triggers = Array.new
 
       @handler = MessageMapper.new(self)
       @registry = BotRegistryAccessor.new(@bot, self.class.to_s.gsub(/^.*::/, ""))
 
       @manager.add_botmodule(self)
+      if self.respond_to?('set_language')
+        self.set_language(@bot.lang.language)
+      end
+    end
+
+    def botmodule_class
+      :BotModule
     end
 
     def flush_registry
     end
 
     def flush_registry
@@ -183,6 +195,11 @@ module Plugins
       name
     end
 
       name
     end
 
+    # intern the name
+    def to_sym
+      self.name.to_sym
+    end
+
     # return a help string for your module. for complex modules, you may wish
     # to break your help into topics, and return a list of available topics if
     # +topic+ is nil. +plugin+ is passed containing the matching prefix for
     # return a help string for your module. for complex modules, you may wish
     # to break your help into topics, and return a list of available topics if
     # +topic+ is nil. +plugin+ is passed containing the matching prefix for
@@ -197,7 +214,11 @@ module Plugins
     # message prefixes
     def register(cmd, opts={})
       raise ArgumentError, "Second argument must be a hash!" unless opts.kind_of?(Hash)
     # message prefixes
     def register(cmd, opts={})
       raise ArgumentError, "Second argument must be a hash!" unless opts.kind_of?(Hash)
-      return if @manager.knows?(cmd, @botmodule_class)
+      who = @manager.who_handles?(cmd)
+      if who
+        raise "Command #{cmd} is already handled by #{who.botmodule_class} #{who}" if who != self
+        return
+      end
       if opts.has_key?(:auth)
         @manager.register(self, cmd, opts[:auth])
       else
       if opts.has_key?(:auth)
         @manager.register(self, cmd, opts[:auth])
       else
@@ -215,14 +236,14 @@ module Plugins
   end
 
   class CoreBotModule < BotModule
   end
 
   class CoreBotModule < BotModule
-    def initialize
-      super(:coremodule)
+    def botmodule_class
+      :CoreBotModule
     end
   end
 
   class Plugin < BotModule
     end
   end
 
   class Plugin < BotModule
-    def initialize
-      super(:plugin)
+    def botmodule_class
+      :Plugin
     end
   end
 
     end
   end
 
@@ -234,23 +255,28 @@ module Plugins
     attr_reader :botmodules
 
     def initialize
     attr_reader :botmodules
 
     def initialize
-      bot_associate(nil)
+      @botmodules = {
+        :CoreBotModule => [],
+        :Plugin => []
+      }
+
+      @names_hash = Hash.new
+      @commandmappers = Hash.new
 
       @dirs = []
 
       @dirs = []
+
+      @failed = Array.new
+      @ignored = Array.new
+
+      bot_associate(nil)
     end
 
     # Reset lists of botmodules
     def reset_botmodule_lists
     end
 
     # Reset lists of botmodules
     def reset_botmodule_lists
-      @botmodules = {
-        :coremodule => [],
-        :plugin => []
-      }
-
-      @commandmappers = {
-        :coremodule => {},
-        :plugin => {}
-      }
-
+      @botmodules[:CoreBotModule].clear
+      @botmodules[:Plugin].clear
+      @names_hash.clear
+      @commandmappers.clear
     end
 
     # Associate with bot _bot_
     end
 
     # Associate with bot _bot_
@@ -259,47 +285,52 @@ module Plugins
       @bot = bot
     end
 
       @bot = bot
     end
 
-    # Returns +true+ if _name_ is a known botmodule of class kl
-    def knows?(name, kl)
-      return @commandmappers[kl.to_sym].has_key?(name.to_sym)
+    # Returns the botmodule with the given _name_
+    def [](name)
+      @names_hash[name.to_sym]
+    end
+
+    # Returns +true+ if _cmd_ has already been registered as a command
+    def who_handles?(cmd)
+      return nil unless @commandmappers.has_key?(cmd.to_sym)
+      return @commandmappers[cmd.to_sym][:botmodule]
     end
 
     # Registers botmodule _botmodule_ with command _cmd_ and command path _auth_path_
     def register(botmodule, cmd, auth_path)
       raise TypeError, "First argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
     end
 
     # Registers botmodule _botmodule_ with command _cmd_ and command path _auth_path_
     def register(botmodule, cmd, auth_path)
       raise TypeError, "First argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
-      kl = botmodule.botmodule_class
-      @commandmappers[kl.to_sym][cmd.to_sym] = {:botmodule => botmodule, :auth => auth_path}
-      h = @commandmappers[kl.to_sym][cmd.to_sym]
-      # debug "Registered command mapper for #{cmd.to_sym} (#{kl.to_sym}): #{h[:botmodule].name} with command path #{h[:auth]}"
+      @commandmappers[cmd.to_sym] = {:botmodule => botmodule, :auth => auth_path}
     end
 
     def add_botmodule(botmodule)
       raise TypeError, "Argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
       kl = botmodule.botmodule_class
     end
 
     def add_botmodule(botmodule)
       raise TypeError, "Argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
       kl = botmodule.botmodule_class
-      raise "#{kl.to_s} #{botmodule.name} already registered!" if @botmodules[kl.to_sym].include?(botmodule)
-      @botmodules[kl.to_sym] << botmodule
+      if @names_hash.has_key?(botmodule.to_sym)
+        case self[botmodule].botmodule_class
+        when kl
+          raise "#{kl} #{botmodule} already registered!"
+        else
+          raise "#{self[botmodule].botmodule_class} #{botmodule} already registered, cannot re-register as #{kl}"
+        end
+      end
+      @botmodules[kl] << botmodule
+      @names_hash[botmodule.to_sym] = botmodule
     end
 
     # Returns an array of the loaded plugins
     def core_modules
     end
 
     # Returns an array of the loaded plugins
     def core_modules
-      @botmodules[:coremodule]
+      @botmodules[:CoreBotModule]
     end
 
     # Returns an array of the loaded plugins
     def plugins
     end
 
     # Returns an array of the loaded plugins
     def plugins
-      @botmodules[:plugin]
+      @botmodules[:Plugin]
     end
 
     # Returns a hash of the registered message prefixes and associated
     # plugins
     end
 
     # Returns a hash of the registered message prefixes and associated
     # plugins
-    def plugin_commands
-      @commandmappers[:plugin]
-    end
-
-    # Returns a hash of the registered message prefixes and associated
-    # core modules
-    def core_commands
-      @commandmappers[:coremodule]
+    def commands
+      @commandmappers
     end
 
     # Makes a string of error _err_ by adding text _str_
     end
 
     # Makes a string of error _err_ by adding text _str_
@@ -361,8 +392,8 @@ module Plugins
 
     # load plugins from pre-assigned list of directories
     def scan
 
     # load plugins from pre-assigned list of directories
     def scan
-      @failed = Array.new
-      @ignored = Array.new
+      @failed.clear
+      @ignored.clear
       processed = Hash.new
 
       @bot.config['plugins.blacklist'].each { |p|
       processed = Hash.new
 
       @bot.config['plugins.blacklist'].each { |p|
@@ -504,6 +535,8 @@ module Plugins
       when /^(\S+)\s*(.*)$/
         key = $1
         params = $2
       when /^(\S+)\s*(.*)$/
         key = $1
         params = $2
+
+       # Let's see if we can match a plugin by the given name
         (core_modules + plugins).each { |p|
          next unless p.name == key
           begin
         (core_modules + plugins).each { |p|
          next unless p.name == key
           begin
@@ -513,17 +546,18 @@ module Plugins
             error report_error("#{p.botmodule_class} #{p.name} help() failed:", err)
           end
         }
             error report_error("#{p.botmodule_class} #{p.name} help() failed:", err)
           end
         }
+
+       # Nope, let's see if it's a command, and ask for help at the corresponding botmodule
         k = key.to_sym
         k = key.to_sym
-        [core_commands, plugin_commands].each { |pl|
-          next unless pl.has_key?(k)
-          p = pl[k][:botmodule] 
+        if commands.has_key?(k)
+          p = commands[k][:botmodule] 
           begin
           begin
-            return p.help(p.name, params)
+            return p.help(key, params)
           rescue Exception => err
             #rescue TimeoutError, StandardError, NameError, SyntaxError => err
             error report_error("#{p.botmodule_class} #{p.name} help() failed:", err)
           end
           rescue Exception => err
             #rescue TimeoutError, StandardError, NameError, SyntaxError => err
             error report_error("#{p.botmodule_class} #{p.name} help() failed:", err)
           end
-        }
+        end
       end
       return false
     end
       end
       return false
     end
@@ -554,45 +588,34 @@ 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
     def privmsg(m)
       # debug "Delegating privmsg #{m.message.inspect} from #{m.source} to #{m.replyto} with pluginkey #{m.plugin.inspect}"
       return unless m.plugin
-      [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.kind_of?(SystemExit)
-                error report_error("#{p.botmodule_class} #{p.name} privmsg() failed:", err)
-                raise if err.kind_of?(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()"
+      k = m.plugin.to_sym
+      if commands.has_key?(k)
+        p = commands[k][:botmodule]
+        a = commands[k][:auth]
+        # 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.kind_of?(SystemExit)
+              error report_error("#{p.botmodule_class} #{p.name} privmsg() failed:", err)
+              raise if err.kind_of?(BDB::Fatal)
             end
             end
+            # debug "Successfully delegated #{m.message}"
+            return true
           else
           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
           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
         end
-        # debug "Finished delegating privmsg with key #{m.plugin.inspect}" + ( pl.empty? ? "" : " to #{pl.values.first[:botmodule].botmodule_class}s" )
-      }
+      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
       return false
       # debug "Finished delegating privmsg with key #{m.plugin.inspect}"
     end