X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Fplugins.rb;h=9a41610f6e4961b9fb6de8431e80b8674d8d6cfd;hb=16c921257d38522fd2930b1ee8d52675f0d4f1e0;hp=d1bf073ac0e12132b1c9beeeb46959c56acb7d59;hpb=33c336af346dc08b4f4f4951dd6eae7150cef481;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/plugins.rb b/lib/rbot/plugins.rb index d1bf073a..9a41610f 100644 --- a/lib/rbot/plugins.rb +++ b/lib/rbot/plugins.rb @@ -10,6 +10,9 @@ class Bot Config.register Config::ArrayValue.new('plugins.blacklist', :default => [], :wizard => false, :requires_rescan => true, :desc => "Plugins that should not be loaded") + Config.register Config::ArrayValue.new('plugins.whitelist', + :default => [], :wizard => false, :requires_rescan => true, + :desc => "Only whitelisted plugins will be loaded unless the list is empty") module Plugins require 'rbot/messagemapper' @@ -179,6 +182,7 @@ module Plugins def initialize @manager = Plugins::manager @bot = @manager.bot + @priority = nil @botmodule_triggers = Array.new @@ -197,7 +201,7 @@ module Plugins @priority ||= 1 end - # Returns the symbol :BotModule + # Returns the symbol :BotModule def botmodule_class :BotModule end @@ -317,7 +321,7 @@ module Plugins # # This command is now superceded by the #map() command, which should be used # instead whenever possible. - # + # def register(cmd, opts={}) raise ArgumentError, "Second argument must be a hash!" unless opts.kind_of?(Hash) who = @manager.who_handles?(cmd) @@ -337,10 +341,19 @@ module Plugins # MessageMapper uses 'usage' as its default fallback method. # def usage(m, params = {}) + if params[:failures].respond_to? :find + friendly = params[:failures].find do |f| + f.kind_of? MessageMapper::FriendlyFailure + end + if friendly + m.reply friendly.friendly + return + end + end 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 + # 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 @@ -348,6 +361,20 @@ module Plugins @bot.plugins.mark_priorities_dirty end end + + # Directory name to be joined to the botclass to access data files. By + # default this is the plugin name itself, but may be overridden, for + # example by plugins that share their datafiles or for backwards + # compatibilty + def dirname + name + end + + # Filename for a datafile built joining the botclass, plugin dirname and + # actual file name + def datafile(*fname) + @bot.path dirname, *fname + end end # A CoreBotModule is a BotModule that provides core functionality. @@ -409,7 +436,8 @@ module Plugins h[k] = Array.new } - @dirs = [] + @core_module_dirs = [] + @plugin_dirs = [] @failed = Array.new @ignored = Array.new @@ -539,12 +567,13 @@ module Plugins 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 rescue Exception => err # rescue TimeoutError, StandardError, NameError, LoadError, SyntaxError => err + raise if err.kind_of? DBFatal error report_error("#{desc}#{fname} load failed", err) bt = err.backtrace.select { |line| line.match(/^(\(eval\)|#{fname}):\d+/) @@ -554,10 +583,27 @@ module Plugins "#{fname}#{$1}#{$3}" } } - msg = err.to_str.gsub(/^\(eval\)(:\d+)(:in `.*')?(:.*)?/) { |m| + msg = err.to_s.gsub(/^\(eval\)(:\d+)(:in `.*')?(:.*)?/) { |m| "#{fname}#{$1}#{$3}" } - newerr = err.class.new(msg) + begin + newerr = err.class.new(msg) + rescue ArgumentError => err_in_err + # Somebody should hang the ActiveSupport developers by their balls + # with barbed wire. Their MissingSourceFile extension to LoadError + # _expects_ a second argument, breaking the usual Exception interface + # (instead, the smart thing to do would have been to make the second + # parameter optional and run the code in the from_message method if + # it was missing). + # Anyway, we try to cope with this in the simplest possible way. On + # the upside, this new block can be extended to handle other similar + # idiotic approaches + if err.class.respond_to? :from_message + newerr = err.class.from_message(msg) + else + raise err_in_err + end + end newerr.set_backtrace(bt) return newerr end @@ -565,42 +611,58 @@ module Plugins private :load_botmodule_file # add one or more directories to the list of directories to - # load botmodules from - # - # TODO find a way to specify necessary plugins which _must_ be loaded - # - def add_botmodule_dir(*dirlist) - @dirs += dirlist - debug "Botmodule loading path: #{@dirs.join(', ')}" + # load core modules from + def add_core_module_dir(*dirlist) + @core_module_dirs += dirlist + debug "Core module loading paths: #{@core_module_dirs.join(', ')}" end - def clear_botmodule_dirs - @dirs.clear - debug "Botmodule loading path cleared" + # add one or more directories to the list of directories to + # load plugins from + def add_plugin_dir(*dirlist) + @plugin_dirs += dirlist + debug "Plugin loading paths: #{@plugin_dirs.join(', ')}" end - # load plugins from pre-assigned list of directories - def scan - @failed.clear - @ignored.clear - @delegate_list.clear + def clear_botmodule_dirs + @core_module_dirs.clear + @plugin_dirs.clear + debug "Core module and plugin loading paths cleared" + end + def scan_botmodules(opts={}) + type = opts[:type] processed = Hash.new - @bot.config['plugins.blacklist'].each { |p| - pn = p + ".rb" - processed[pn.intern] = :blacklisted - } + case type + when :core + dirs = @core_module_dirs + when :plugins + dirs = @plugin_dirs - dirs = @dirs - dirs.each {|dir| - if(FileTest.directory?(dir)) - d = Dir.new(dir) - d.sort.each {|file| + @bot.config['plugins.blacklist'].each { |p| + pn = p + ".rb" + processed[pn.intern] = :blacklisted + } - next if(file =~ /^\./) + whitelist = @bot.config['plugins.whitelist'].map { |p| + p + ".rb" + } + end - if processed.has_key?(file.intern) + dirs.each do |dir| + next unless FileTest.directory?(dir) + d = Dir.new(dir) + d.sort.each do |file| + next unless file =~ /\.rb$/ + next if file =~ /^\./ + + case type + when :plugins + if !whitelist.empty? && !whitelist.include?(file) + @ignored << {:name => file, :dir => dir, :reason => :"not whitelisted" } + next + elsif processed.has_key?(file.intern) @ignored << {:name => file, :dir => dir, :reason => processed[file.intern]} next end @@ -614,20 +676,28 @@ module Plugins @ignored << {:name => $1, :dir => dir, :reason => processed[$1.intern]} next end + end + + did_it = load_botmodule_file("#{dir}/#{file}", "plugin") + case did_it + when Symbol + processed[file.intern] = did_it + when Exception + @failed << { :name => file, :dir => dir, :reason => did_it } + end + end + end + end - next unless(file =~ /\.rb$/) + # load plugins from pre-assigned list of directories + def scan + @failed.clear + @ignored.clear + @delegate_list.clear - did_it = load_botmodule_file("#{dir}/#{file}", "plugin") - case did_it - when Symbol - processed[file.intern] = did_it - when Exception - @failed << { :name => file, :dir => dir, :reason => did_it } - end + scan_botmodules(:type => :core) + scan_botmodules(:type => :plugins) - } - end - } debug "finished loading plugins: #{status(true)}" (core_modules + plugins).each { |p| p.methods.grep(DEFAULT_DELEGATE_PATTERNS).each { |m| @@ -797,7 +867,7 @@ module Plugins end def sort_modules - @sorted_modules = (core_modules + plugins).sort do |a, b| + @sorted_modules = (core_modules + plugins).sort do |a, b| a.priority <=> b.priority end || [] @@ -872,7 +942,7 @@ module Plugins rescue Exception => err raise if err.kind_of?(SystemExit) error report_error("#{p.botmodule_class} #{p.name} #{method}() failed:", err) - raise if err.kind_of?(BDB::Fatal) + raise if err.kind_of?(DBFatal) end } else @@ -888,7 +958,7 @@ module Plugins rescue Exception => err raise if err.kind_of?(SystemExit) error report_error("#{p.botmodule_class} #{p.name} #{method}() failed:", err) - raise if err.kind_of?(BDB::Fatal) + raise if err.kind_of?(DBFatal) end end } @@ -918,7 +988,7 @@ module Plugins 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) + raise if err.kind_of?(DBFatal) end debug "Successfully delegated #{m.inspect}" return true @@ -942,7 +1012,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)