### Home Classes Methods

### In Files

- lib/rbot/plugins.rb

### Parent

Object

### Methods

- ::new
- #[]
- #add_botmodule
- #add_core_module_dir
- #add_plugin_dir
- #bot_associate
- #cleanup
- #clear_botmodule_dirs
- #commands
- #core_length
- #core_modules
- #delegate
- #help
- #helptopics
- #inspect
- #irc_delegate
- #length
- #load_botmodule_file
- #mark_priorities_dirty
- #plugins
- #privmsg
- #register
- #register_map
- #report_error
- #rescan
- #reset_botmodule_lists
- #save
- #scan
- #scan_botmodules
- #sort_modules
- #status
- #who_handles?

### Included Modules

- Singleton

### Class/Module Index

Quicksearch

- Irc
- Irc::AsciiCasemap
- Irc::BanlistMessage
- Irc::BasicUserMessage
- Irc::Bot
- Irc::Bot::Auth
- Irc::Bot::Auth::BotOwnerClass
- Irc::Bot::Auth::BotUser
- Irc::Bot::Auth::Command
- Irc::Bot::Auth::DefaultBotUserClass
- Irc::Bot::Auth::InvalidPassword
- Irc::Bot::Auth::ManagerClass
- Irc::Bot::Auth::PermissionSet
- Irc::Bot::Config
- Irc::Bot::Config::ArrayValue
- Irc::Bot::Config::BooleanValue
- Irc::Bot::Config::EnumValue
- Irc::Bot::Config::FloatValue
- Irc::Bot::Config::IntegerValue
- Irc::Bot::Config::ManagerClass
- Irc::Bot::Config::StringValue
- Irc::Bot::Config::Value
- Irc::Bot::Config::Wizard
- Irc::Bot::DataFilter
- Irc::Bot::DataStream
- Irc::Bot::Language
- Irc::Bot::MessageMapper
- Irc::Bot::MessageMapper::Failure
- Irc::Bot::MessageMapper::FriendlyFailure
- Irc::Bot::MessageMapper::NoActionFailure
- Irc::Bot::MessageMapper::NoMatchFailure
- Irc::Bot::MessageMapper::NotPrivateFailure
- Irc::Bot::MessageMapper::NotPublicFailure
- Irc::Bot::MessageMapper::PartialMatchFailure
- Irc::Bot::MessageParameter
- Irc::Bot::MessageTemplate
- Irc::Bot::Plugins
- Irc::Bot::Plugins::BotModule
- Irc::Bot::Plugins::CoreBotModule
- Irc::Bot::Plugins::Plugin
- Irc::Bot::Plugins::PluginManagerClass
- Irc::Bot::Plugins::RecurseTooDeep
- Irc::Bot::Plugins::RemoteBotModule
- Irc::Bot::Plugins::RemoteCoreBotModule
- Irc::Bot::Plugins::RemotePlugin
- Irc::Bot::Registry
- Irc::Bot::Registry::Accessor
- Irc::Bot::RemoteDispatcher
- Irc::Bot::RemoteMessage
- Irc::Bot::RemoteObject
- Irc::Bot::Wordlist
- Irc::BotConfig
- Irc::Casemap
- Irc::Channel
- Irc::Channel::Mode
- Irc::Channel::ModeHash
- Irc::Channel::ModeTypeA
- Irc::Channel::ModeTypeB
- Irc::Channel::ModeTypeC
- Irc::Channel::ModeTypeD
- Irc::Channel::Topic
- Irc::Channel::UserMode
- Irc::ChannelList
- Irc::Client
- Irc::DBHash
- Irc::DBTree
- Irc::InviteMessage
- Irc::JoinMessage
- Irc::KickMessage
- Irc::MessageQueue
- Irc::ModeChangeMessage
- Irc::MotdMessage
- Irc::NamesMessage
- Irc::Netmask
- Irc::NetmaskDb
- Irc::NetmaskDb::Tree
- Irc::NetmaskList
- Irc::NickMessage
- Irc::NoSuchTargetMessage
- Irc::NoticeMessage
- Irc::PartMessage
- Irc::PrivMessage
- Irc::QueueRing
- Irc::QuitMessage
- Irc::RfcCasemap
- Irc::Server
- Irc::ServerOrCasemap
- Irc::Socket
- Irc::Socket::IdentityFilter
- Irc::StrictRfcCasemap
- Irc::TopicMessage
- Irc::UnknownMessage
- Irc::User
- Irc::UserList
- Irc::UserMessage
- Irc::Utils
- Irc::Utils::HttpUtil
- Irc::Utils::HttpUtil::CachedObject
- Irc::Utils::ParseTime
- Irc::WelcomeMessage
- Irc::WhoisMessage
- BDB
- BDB::CIBtree
- MonitorMixin
- MonitorMixin::ConditionVariable
- Net
- Net::HTTPResponse
- Regexp
- Regexp::Irc
- Singleton
- Singleton::SingletonClassMethods
- Timer
- Timer::Action
- TokyoCabinet
- TokyoCabinet::CIBDB
- UnicodePlugin
- UnicodePlugin::UnicodeFilter
- Array
- ArrayOf
- AuthModule
- BasicsModule
- ConditionVariable
- ConfigModule
- DottedIndex
- Enumerable
- Exception
- FiltersModule
- GetText
- GetTextVersionError
- HttpUtilPlugin
- IrcLogModule
- Module
- Numeric
- Object
- Range
- RemoteModule
- ServerError
- String
- Symbol
- UserDataModule
- WordlistModule
- YAML

No matching classes.

# Irc::Bot::Plugins::PluginManagerClass

Singleton to manage multiple plugins and delegate messages to them for handling

### Constants

DEFAULT_DELEGATE_PATTERNS

  This is the list of patterns commonly delegated to plugins. A fast delegation lookup is enabled for them.

### Attributes

bot[R]
botmodules[R]
maps[R]

### Public Class Methods

new() click to toggle source

    # File lib/rbot/plugins.rb, line 422
    def initialize
      @botmodules = {
        :CoreBotModule => [],
        :Plugin => []
      }

      @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
      }

      @core_module_dirs = []
      @plugin_dirs = []

      @failed = Array.new
      @ignored = Array.new

      bot_associate(nil)
    end

### Public Instance Methods

[](name) click to toggle source

Returns the botmodule with the given name

    # File lib/rbot/plugins.rb, line 479
    def [](name)
      @names_hash[name.to_sym]
    end

add_botmodule(botmodule) click to toggle source

    # File lib/rbot/plugins.rb, line 508
    def add_botmodule(botmodule)
      raise TypeError, "Argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
      kl = botmodule.botmodule_class
      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
      mark_priorities_dirty
    end

add_core_module_dir(*dirlist) click to toggle source

add one or more directories to the list of directories to load core modules from

    # File lib/rbot/plugins.rb, line 614
    def add_core_module_dir(*dirlist)
      @core_module_dirs += dirlist
      debug "Core module loading paths: #{@core_module_dirs.join(', ')}"
    end

add_plugin_dir(*dirlist) click to toggle source

add one or more directories to the list of directories to load plugins from

    # File lib/rbot/plugins.rb, line 621
    def add_plugin_dir(*dirlist)
      @plugin_dirs += dirlist
      debug "Plugin loading paths: #{@plugin_dirs.join(', ')}"
    end

bot_associate(bot) click to toggle source

Associate with bot bot

    # File lib/rbot/plugins.rb, line 473
    def bot_associate(bot)
      reset_botmodule_lists
      @bot = bot
    end

cleanup() click to toggle source

call the cleanup method for each active plugin

    # File lib/rbot/plugins.rb, line 716
    def cleanup
      delegate 'cleanup'
      reset_botmodule_lists
    end

clear_botmodule_dirs() click to toggle source

    # File lib/rbot/plugins.rb, line 626
    def clear_botmodule_dirs
      @core_module_dirs.clear
      @plugin_dirs.clear
      debug "Core module and plugin loading paths cleared"
    end

commands() click to toggle source

Returns a hash of the registered message prefixes and associated plugins

    # File lib/rbot/plugins.rb, line 536
    def commands
      @commandmappers
    end

core_length() click to toggle source

    # File lib/rbot/plugins.rb, line 802
    def core_length
      core_modules.length
    end

core_modules() click to toggle source

Returns an array of the loaded plugins

    # File lib/rbot/plugins.rb, line 525
    def core_modules
      @botmodules[:CoreBotModule]
    end

delegate(method, m, opts={}) click to toggle source
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

    # File lib/rbot/plugins.rb, line 905
    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)
        debug "fast-delegating #{method}"
        m = method.to_sym
        debug "no-one to delegate to" unless @delegate_list.has_key?(m)
        return [] unless @delegate_list.has_key?(m)
        @delegate_list[m].each { |p|
          begin
            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)
            raise if err.kind_of?(BDB::Fatal)
          end
        }
      else
        debug "slow-delegating #{method}"
        @sorted_modules.each { |p|
          if(p.respond_to? method)
            begin
              # debug "#{p.botmodule_class} #{p.name} responds"
              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)
              raise if err.kind_of?(BDB::Fatal)
            end
          end
        }
      end
      return ret
      # debug "Finished delegating #{method.inspect}"
    end

help(topic="") click to toggle source

return help for `topic` (call associated plugin's help method)

    # File lib/rbot/plugins.rb, line 807
    def help(topic="")
      case topic
      when /fail(?:ed)?\s*plugins?.*(trace(?:back)?s?)?/
        # debug "Failures: #{@failed.inspect}"
        return _("no plugins failed to load") if @failed.empty?
        return @failed.collect { |p|
          _('%{highlight}%{plugin}%{highlight} in %{dir} failed with error %{exception}: %{reason}') % {
              :highlight => Bold, :plugin => p[:name], :dir => p[:dir],
              :exception => p[:reason].class, :reason => p[:reason],
          } + if $1 && !p[:reason].backtrace.empty?
                _('at %{backtrace}') % {:backtrace => p[:reason].backtrace.join(', ')}
              else
                ''
              end
        }.join("\n")
      when /ignored?\s*plugins?/
        return _('no plugins were ignored') if @ignored.empty?

        tmp = Hash.new
        @ignored.each do |p|
          reason = p[:loaded] ? _('overruled by previous') : _(p[:reason].to_s)
          ((tmp[p[:dir]] ||= Hash.new)[reason] ||= Array.new).push(p[:name])
        end

        return tmp.map do |dir, reasons|
          # FIXME get rid of these string concatenations to make gettext easier
          s = reasons.map { |r, list|
            list.map { |_| _.sub(/\.rb$/, '') }.join(', ') + " (#{r})"
          }.join('; ')
          "in #{dir}: #{s}"
        end.join('; ')
      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
            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
        }

        # Nope, let's see if it's a command, and ask for help at the corresponding botmodule
        k = key.to_sym
        if commands.has_key?(k)
          p = commands[k][:botmodule]
          begin
            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
        end
      end
      return false
    end

helptopics() click to toggle source

return list of help topics (plugin names)

    # File lib/rbot/plugins.rb, line 792
    def helptopics
      rv = status
      @failures_shown = true
      rv
    end

inspect() click to toggle source

    # File lib/rbot/plugins.rb, line 448
    def inspect
      ret = self.to_s[0..-2]
      ret << ' corebotmodules='
      ret << @botmodules[:CoreBotModule].map { |m|
        m.name
      }.inspect
      ret << ' plugins='
      ret << @botmodules[:Plugin].map { |m|
        m.name
      }.inspect
      ret << ">"
    end

irc_delegate(method, m) click to toggle source

delegate IRC messages, by delegating 'listen' first, and the actual method afterwards. Delegating 'privmsg' also delegates ctcp_listen and message as appropriate.

    # File lib/rbot/plugins.rb, line 1009
    def irc_delegate(method, m)
      delegate('listen', m)
      if method.to_sym == :privmsg
        delegate('ctcp_listen', m) if m.ctcp
        delegate('message', m)
        privmsg(m) if m.address? and not m.ignored?
        delegate('unreplied', m) unless m.replied
      else
        delegate(method, m)
      end
    end

length() click to toggle source

    # File lib/rbot/plugins.rb, line 798
    def length
      plugins.length
    end

mark_priorities_dirty() click to toggle source

Tells the PluginManager that the next time it delegates an event, it should sort the modules by priority

    # File lib/rbot/plugins.rb, line 542
    def mark_priorities_dirty
      @sorted_modules = nil
    end

plugins() click to toggle source

Returns an array of the loaded plugins

    # File lib/rbot/plugins.rb, line 530
    def plugins
      @botmodules[:Plugin]
    end

privmsg(m) click to toggle source

see if we have a plugin that wants to handle this message, if so, pass it to the plugin and return true, otherwise false

    # File lib/rbot/plugins.rb, line 971
    def privmsg(m)
      debug "Delegating privmsg #{m.inspect} with pluginkey #{m.plugin.inspect}"
      return unless m.plugin
      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
            debug "Successfully delegated #{m.inspect}"
            return true
          else
            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}"
        end
      else
        debug "Command #{k} isn't handled"
      end
      return false
    end

register(botmodule, cmd, auth_path) click to toggle source

Registers botmodule botmodule with command cmd and command path auth_path

    # File lib/rbot/plugins.rb, line 490
    def register(botmodule, cmd, auth_path)
      raise TypeError, "First argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
      @commandmappers[cmd.to_sym] = {:botmodule => botmodule, :auth => auth_path}
    end

register_map(botmodule, map) click to toggle source

Registers botmodule botmodule with map map. This adds the map to the maps hash which has three keys:

botmodule | the associated botmodule
auth | an array of auth keys checked by the map; the first is the full_auth_path of the map
map | the actual MessageTemplate object

    # File lib/rbot/plugins.rb, line 503
    def register_map(botmodule, map)
      raise TypeError, "First argument #{botmodule.inspect} is not of class BotModule" unless botmodule.kind_of?(BotModule)
      @maps[map.template] = { :botmodule => botmodule, :auth => [map.options[:full_auth_path]], :map => map }
    end

report_error(str, err) click to toggle source

Makes a string of error err by adding text str

    # File lib/rbot/plugins.rb, line 547
    def report_error(str, err)
      ([str, err.inspect] + err.backtrace).join("\n")
    end

rescan() click to toggle source

drop all plugins and rescan plugins on disk calls save and cleanup for each plugin before dropping them

    # File lib/rbot/plugins.rb, line 723
    def rescan
      save
      cleanup
      scan
    end

reset_botmodule_lists() click to toggle source

Reset lists of botmodules

    # File lib/rbot/plugins.rb, line 462
    def reset_botmodule_lists
      @botmodules[:CoreBotModule].clear
      @botmodules[:Plugin].clear
      @names_hash.clear
      @commandmappers.clear
      @maps.clear
      @failures_shown = false
      mark_priorities_dirty
    end

save() click to toggle source

call the save method for each active plugin

    # File lib/rbot/plugins.rb, line 710
    def save
      delegate 'flush_registry'
      delegate 'save'
    end

scan() click to toggle source

load plugins from pre-assigned list of directories

    # File lib/rbot/plugins.rb, line 692
    def scan
      @failed.clear
      @ignored.clear
      @delegate_list.clear

      scan_botmodules(:type => :core)
      scan_botmodules(:type => :plugins)

      debug "finished loading plugins: #{status(true)}"
      (core_modules + plugins).each { |p|
       p.methods.grep(DEFAULT_DELEGATE_PATTERNS).each { |m|
         @delegate_list[m.intern] << p
       }
      }
      mark_priorities_dirty
    end

scan_botmodules(opts={}) click to toggle source

    # File lib/rbot/plugins.rb, line 632
    def scan_botmodules(opts={})
      type = opts[:type]
      processed = Hash.new

      case type
      when :core
        dirs = @core_module_dirs
      when :plugins
        dirs = @plugin_dirs

        @bot.config['plugins.blacklist'].each { |p|
          pn = p + ".rb"
          processed[pn.intern] = :blacklisted
        }

        whitelist = @bot.config['plugins.whitelist'].map { |p|
          p + ".rb"
        }
      end

      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

            if(file =~ /^(.+\.rb)\.disabled$/)
              # GB: Do we want to do this? This means that a disabled plugin in a directory
              #     will disable in all subsequent directories. This was probably meant
              #     to be used before plugins.blacklist was implemented, so I think
              #     we don't need this anymore
              processed[$1.intern] = :disabled
              @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

sort_modules() click to toggle source

    # File lib/rbot/plugins.rb, line 868
    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

status(short=false) click to toggle source

    # File lib/rbot/plugins.rb, line 729
    def status(short=false)
      output = []
      if self.core_length > 0
        if short
          output << n_("%{count} core module loaded", "%{count} core modules loaded",
                    self.core_length) % {:count => self.core_length}
        else
          output <<  n_("%{count} core module: %{list}",
                     "%{count} core modules: %{list}", self.core_length) %
                     { :count => self.core_length,
                       :list => core_modules.collect{ |p| p.name}.sort.join(", ") }
        end
      else
        output << _("no core botmodules loaded")
      end
      # Active plugins first
      if(self.length > 0)
        if short
          output << n_("%{count} plugin loaded", "%{count} plugins loaded",
                       self.length) % {:count => self.length}
        else
          output << n_("%{count} plugin: %{list}",
                       "%{count} plugins: %{list}", self.length) %
                   { :count => self.length,
                     :list => plugins.collect{ |p| p.name}.sort.join(", ") }
        end
      else
        output << "no plugins active"
      end
      # Ignored plugins next
      unless @ignored.empty? or @failures_shown
        if short
          output << n_("%{highlight}%{count} plugin ignored%{highlight}",
                       "%{highlight}%{count} plugins ignored%{highlight}",
                       @ignored.length) %
                    { :count => @ignored.length, :highlight => Underline }
        else
          output << n_("%{highlight}%{count} plugin ignored%{highlight}: use %{bold}%{command}%{bold} to see why",
                       "%{highlight}%{count} plugins ignored%{highlight}: use %{bold}%{command}%{bold} to see why",
                       @ignored.length) %
                    { :count => @ignored.length, :highlight => Underline,
                      :bold => Bold, :command => "help ignored plugins"}
        end
      end
      # Failed plugins next
      unless @failed.empty? or @failures_shown
        if short
          output << n_("%{highlight}%{count} plugin failed to load%{highlight}",
                       "%{highlight}%{count} plugins failed to load%{highlight}",
                       @failed.length) %
                    { :count => @failed.length, :highlight => Reverse }
        else
          output << n_("%{highlight}%{count} plugin failed to load%{highlight}: use %{bold}%{command}%{bold} to see why",
                       "%{highlight}%{count} plugins failed to load%{highlight}: use %{bold}%{command}%{bold} to see why",
                       @failed.length) %
                    { :count => @failed.length, :highlight => Reverse,
                      :bold => Bold, :command => "help failed plugins"}
        end
      end
      output.join '; '
    end

who_handles?(cmd) click to toggle source

Returns `true` if cmd has already been registered as a command

    # File lib/rbot/plugins.rb, line 484
    def who_handles?(cmd)
      return nil unless @commandmappers.has_key?(cmd.to_sym)
      return @commandmappers[cmd.to_sym][:botmodule]
    end

### Private Instance Methods

load_botmodule_file(fname, desc=nil) click to toggle source

This method is the one that actually loads a module from the file fname

desc is a simple description of what we are loading (plugin/botmodule/whatever)

It returns the Symbol :loaded on success, and an Exception on failure

    # File lib/rbot/plugins.rb, line 559
    def load_botmodule_file(fname, desc=nil)
      # create a new, anonymous module to "house" the plugin
      # 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.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
        error report_error("#{desc}#{fname} load failed", err)
        bt = err.backtrace.select { |line|
          line.match(/^(\(eval\)|#{fname}):\d+/)
        }
        bt.map! { |el|
          el.gsub(/^\(eval\)(:\d+)(:in `.*')?(:.*)?/) { |m|
            "#{fname}#{$1}#{$3}"
          }
        }
        msg = err.to_s.gsub(/^\(eval\)(:\d+)(:in `.*')?(:.*)?/) { |m|
          "#{fname}#{$1}#{$3}"
        }
        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
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.