X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Fplugins.rb;h=4d51cfc5e845d380cef3b1e588755dfe33353ef5;hb=8115edef0169d95f0ebb64d77364e346e9452099;hp=567724f8b19db1e056ae6e834ee5e44dfb89e9dc;hpb=b8802b0ff7eb9695ddf3372c80698405c24ffe97;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/plugins.rb b/lib/rbot/plugins.rb index 567724f8..4d51cfc5 100644 --- a/lib/rbot/plugins.rb +++ b/lib/rbot/plugins.rb @@ -96,6 +96,10 @@ module Plugins unreplied(PrivMessage):: Called for a PRIVMSG which has not been replied to. + notice(NoticeMessage):: + Called for all Notices. Please notice that in general + should not be replied to. + kick(KickMessage):: Called when a user (or the bot) is kicked from a channel the bot is in. @@ -114,10 +118,20 @@ module Plugins nick(NickMessage):: Called when a user (or the bot) changes Nick + modechange(ModeChangeMessage):: + Called when a User or Channel mode is changed topic(TopicMessage):: Called when a user (or the bot) changes a channel topic + welcome(WelcomeMessage):: + Called when the welcome message is received on + joining a server succesfully. + + motd(MotdMessage):: + Called when the Message Of The Day is fully + recevied from the server. + connect:: Called when a server is joined successfully, but before autojoin channels are joined (no params) @@ -134,9 +148,14 @@ module Plugins =end class BotModule - attr_reader :bot # the associated bot - attr_reader :registry # the plugin registry - attr_reader :handler # the message map handler + # the associated bot + attr_reader :bot + + # the plugin registry + attr_reader :registry + + # the message map handler + attr_reader :handler # Initialise your bot module. Always call super if you override this method, # as important variables are set up for you: @@ -160,6 +179,7 @@ module Plugins def initialize @manager = Plugins::manager @bot = @manager.bot + @priority = nil @botmodule_triggers = Array.new @@ -172,6 +192,12 @@ module Plugins end end + # Changing the value of @priority directly will cause problems, + # Please use priority=. + def priority + @priority ||= 1 + end + # Returns the symbol :BotModule def botmodule_class :BotModule @@ -205,7 +231,7 @@ module Plugins # Signal to other BotModules that an even happened. # def call_event(ev, *args) - @bot.plugins.delegate('event_' + ev.to_s.gsub(/[^\w\?!]+/, '_'), *args) + @bot.plugins.delegate('event_' + ev.to_s.gsub(/[^\w\?!]+/, '_'), *(args.push Hash.new)) end # call-seq: map(template, options) @@ -315,6 +341,14 @@ module Plugins m.reply(_("incorrect usage, ask for help using '%{command}'") % {:command => "#{@bot.nick}: help #{m.plugin}"}) end + # Define the priority of the module. During event delegation, lower + # priority modules will be called first. Default priority is 1 + def priority=(prio) + if @priority != prio + @priority = prio + @bot.plugins.mark_priorities_dirty + end + end end # A CoreBotModule is a BotModule that provides core functionality. @@ -368,6 +402,10 @@ module Plugins @names_hash = Hash.new @commandmappers = Hash.new @maps = Hash.new + + # modules will be sorted on first delegate call + @sorted_modules = nil + @delegate_list = Hash.new { |h, k| h[k] = Array.new } @@ -401,6 +439,7 @@ module Plugins @commandmappers.clear @maps.clear @failures_shown = false + mark_priorities_dirty end # Associate with bot _bot_ @@ -452,6 +491,7 @@ module Plugins end @botmodules[kl] << botmodule @names_hash[botmodule.to_sym] = botmodule + mark_priorities_dirty end # Returns an array of the loaded plugins @@ -470,6 +510,12 @@ module Plugins @commandmappers end + # Tells the PluginManager that the next time it delegates an event, it + # should sort the modules by priority + def mark_priorities_dirty + @sorted_modules = nil + end + # Makes a string of error _err_ by adding text _str_ def report_error(str, err) ([str, err.inspect] + err.backtrace).join("\n") @@ -488,11 +534,13 @@ module Plugins # the idea here is to prevent namespace pollution. perhaps there # is another way? plugin_module = Module.new + # each plugin uses its own textdomain, we bind it automatically here + bindtextdomain_to(plugin_module, "rbot-#{File.basename(fname, '.rb')}") desc = desc.to_s + " " if desc begin - plugin_string = IO.readlines(fname).join("") + plugin_string = IO.read(fname) debug "loading #{desc}#{fname}" plugin_module.module_eval(plugin_string, fname) return :loaded @@ -587,6 +635,7 @@ module Plugins @delegate_list[m.intern] << p } } + mark_priorities_dirty end # call the save method for each active plugin @@ -748,9 +797,66 @@ module Plugins return false end - # see if each plugin handles +method+, and if so, call it, passing - # +message+ as a parameter + def sort_modules + @sorted_modules = (core_modules + plugins).sort do |a, b| + a.priority <=> b.priority + end || [] + + @delegate_list.each_value do |list| + list.sort! {|a,b| a.priority <=> b.priority} + end + end + + # call-seq: delegate(method, m, opts={}) + # delegate(method, opts={}) + # + # see if each plugin handles _method_, and if so, call it, passing + # _m_ as a parameter (if present). BotModules are called in order of + # priority from lowest to highest. + # + # If the passed _m_ is a BasicUserMessage and is marked as #ignored?, it + # will only be delegated to plugins with negative priority. Conversely, if + # it's a fake message (see BotModule#fake_message), it will only be + # delegated to plugins with positive priority. + # + # Note that _m_ can also be an exploded Array, but in this case the last + # element of it cannot be a Hash, or it will be interpreted as the options + # Hash for delegate itself. The last element can be a subclass of a Hash, though. + # To be on the safe side, you can add an empty Hash as last parameter for delegate + # when calling it with an exploded Array: + # @bot.plugins.delegate(method, *(args.push Hash.new)) + # + # Currently supported options are the following: + # :above :: + # if specified, the delegation will only consider plugins with a priority + # higher than the specified value + # :below :: + # if specified, the delegation will only consider plugins with a priority + # lower than the specified value + # def delegate(method, *args) + # if the priorities order of the delegate list is dirty, + # meaning some modules have been added or priorities have been + # changed, then the delegate list will need to be sorted before + # delegation. This should always be true for the first delegation. + sort_modules unless @sorted_modules + + opts = {} + opts.merge(args.pop) if args.last.class == Hash + + m = args.first + if BasicUserMessage === m + # ignored messages should not be delegated + # to plugins with positive priority + opts[:below] ||= 0 if m.ignored? + # fake messages should not be delegated + # to plugins with negative priority + opts[:above] ||= 0 if m.recurse_depth > 0 + end + + above = opts[:above] + below = opts[:below] + # debug "Delegating #{method.inspect}" ret = Array.new if method.match(DEFAULT_DELEGATE_PATTERNS) @@ -760,7 +866,10 @@ module Plugins return [] unless @delegate_list.has_key?(m) @delegate_list[m].each { |p| begin - ret.push p.send(method, *args) + prio = p.priority + unless (above and above >= prio) or (below and below <= prio) + ret.push p.send(method, *args) + end rescue Exception => err raise if err.kind_of?(SystemExit) error report_error("#{p.botmodule_class} #{p.name} #{method}() failed:", err) @@ -769,11 +878,14 @@ module Plugins } else debug "slow-delegating #{method}" - (core_modules + plugins).each { |p| + @sorted_modules.each { |p| if(p.respond_to? method) begin # debug "#{p.botmodule_class} #{p.name} responds" - ret.push p.send(method, *args) + prio = p.priority + unless (above and above >= prio) or (below and below <= prio) + ret.push p.send(method, *args) + end rescue Exception => err raise if err.kind_of?(SystemExit) error report_error("#{p.botmodule_class} #{p.name} #{method}() failed:", err) @@ -789,7 +901,7 @@ module Plugins # see if we have a plugin that wants to handle this message, if so, pass # it to the plugin and return true, otherwise false def privmsg(m) - # debug "Delegating privmsg #{m.message.inspect} from #{m.source} to #{m.replyto} with pluginkey #{m.plugin.inspect}" + debug "Delegating privmsg #{m.inspect} with pluginkey #{m.plugin.inspect}" return unless m.plugin k = m.plugin.to_sym if commands.has_key?(k) @@ -797,30 +909,30 @@ module Plugins a = commands[k][:auth] # We check here for things that don't check themselves # (e.g. mapped things) - # debug "Checking auth ..." + debug "Checking auth ..." if a.nil? || @bot.auth.allow?(a, m.source, m.replyto) - # debug "Checking response ..." + debug "Checking response ..." if p.respond_to?("privmsg") begin - # debug "#{p.botmodule_class} #{p.name} responds" + 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}" + debug "Successfully delegated #{m.inspect}" return true else - # debug "#{p.botmodule_class} #{p.name} is registered, but it doesn't respond to privmsg()" + debug "#{p.botmodule_class} #{p.name} is registered, but it doesn't respond to privmsg()" end 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 #{m.source} isn't allowed to call #{m.plugin.inspect} on #{m.replyto}" end + else + debug "Command #{k} isn't handled" 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 # delegate IRC messages, by delegating 'listen' first, and the actual method @@ -831,7 +943,7 @@ module Plugins if method.to_sym == :privmsg delegate('ctcp_listen', m) if m.ctcp delegate('message', m) - privmsg(m) if m.address? + privmsg(m) if m.address? and not m.ignored? delegate('unreplied', m) unless m.replied else delegate(method, m)