### Home Classes Methods

### In Files

- lib/rbot/core/utils/filters.rb
- lib/rbot/core/utils/extends.rb
- lib/rbot/core/utils/wordlist.rb
- lib/rbot/core/remote.rb
- lib/rbot/botuser.rb
- lib/rbot/registry/bdb.rb
- lib/rbot/registry/tc.rb
- lib/rbot/ircbot.rb
- lib/rbot/rbotconfig.rb
- lib/rbot/language.rb
- lib/rbot/config.rb
- lib/rbot/message.rb
- lib/rbot/messagemapper.rb
- lib/rbot/plugins.rb

### Parent

Object

### Namespace

- MODULE Irc::Bot::Auth
- MODULE Irc::Bot::Config
- MODULE Irc::Bot::Plugins
- CLASS Irc::Bot::DataFilter
- CLASS Irc::Bot::DataStream
- CLASS Irc::Bot::Language
- CLASS Irc::Bot::MessageMapper
- CLASS Irc::Bot::MessageParameter
- CLASS Irc::Bot::MessageTemplate
- CLASS Irc::Bot::Registry
- CLASS Irc::Bot::RemoteDispatcher
- CLASS Irc::Bot::RemoteMessage
- CLASS Irc::Bot::RemoteObject
- CLASS Irc::Bot::Wordlist

### Methods

- ::new
- #action
- #channels
- #clear_filters
- #connect
- #ctcp_notice
- #ctcp_say
- #delegate_sent
- #disconnect
- #filter
- #filter_groups
- #filter_names
- #global_filter_name
- #got_sig
- #has_filter?
- #has_filter_group?
- #help
- #inspect
- #join
- #kick
- #mainloop
- #mode
- #myself
- #nick
- #nickchg
- #notice
- #okay
- #part
- #path
- #ping_server
- #quiet_on?
- #quit
- #reconnect
- #register_filter
- #remote_dispatcher
- #remote_object
- #repopulate_botclass_directory
- #rescan
- #reset_quiet
- #restart
- #save
- #say
- #sendmsg
- #sendq
- #server
- #set_default_send_options
- #set_quiet
- #setup_plugins_path
- #shutdown
- #status
- #stop_server_pings
- #topic
- #trap_sigs
- #wanted_nick
- #wanted_nick=
- #whois

### 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

Main bot class, which manages the various components, receives messages, handles them or passes them to plugins, and contains core functionality.

### Constants

COPYRIGHT_NOTICE
SOURCE_URL

### Attributes

auth[R]

the bot's Auth data

botclass[R]

the botclass for this bot (determines configdir among other things)

config[R]

the bot's Config data

httputil[RW]

bot's httputil help object, for fetching resources via http. Sets up proxies etc as defined by the bot configuration/environment

lang[R]

bot's Language data

plugins[R]

bot's plugins. This is an instance of class Plugins

registry[R]

bot's object registry, plugins get an interface to this for persistant storage (hash interface tied to a bdb file, plugins use Accessors to store and restore objects in their own namespaces.)

save_mutex[R]

synchronize with this mutex while touching permanent data files: saving, flushing, cleaning up ...

socket[R]

bot's irc socket TODO multiserver

timer[R]

used to perform actions periodically (saves configuration once per minute by default)

### Public Class Methods

new(botclass, params = {}) click to toggle source

create a new Bot with botclass `botclass`

    # File lib/rbot/ircbot.rb, line 271
    def initialize(botclass, params = {})
      # Config for the core bot
      # TODO should we split socket stuff into ircsocket, etc?
      Config.register Config::ArrayValue.new('server.list',
        :default => ['irc://localhost'], :wizard => true,
        :requires_restart => true,
        :desc => "List of irc servers rbot should try to connect to. Use comma to separate values. Servers are in format 'server.doma.in:port'. If port is not specified, default value (6667) is used.")
      Config.register Config::BooleanValue.new('server.ssl',
        :default => false, :requires_restart => true, :wizard => true,
        :desc => "Use SSL to connect to this server?")
      Config.register Config::StringValue.new('server.password',
        :default => false, :requires_restart => true,
        :desc => "Password for connecting to this server (if required)",
        :wizard => true)
      Config.register Config::StringValue.new('server.bindhost',
        :default => false, :requires_restart => true,
        :desc => "Specific local host or IP for the bot to bind to (if required)",
        :wizard => true)
      Config.register Config::IntegerValue.new('server.reconnect_wait',
        :default => 5, :validate => Proc.new{|v| v >= 0},
        :desc => "Seconds to wait before attempting to reconnect, on disconnect")
      Config.register Config::IntegerValue.new('server.ping_timeout',
        :default => 30, :validate => Proc.new{|v| v >= 0},
        :desc => "reconnect if server doesn't respond to PING within this many seconds (set to 0 to disable)")
      Config.register Config::ArrayValue.new('server.nocolor_modes',
        :default => ['c'], :wizard => false,
        :requires_restart => false,
        :desc => "List of channel modes that require messages to be without colors")

      Config.register Config::StringValue.new('irc.nick', :default => "rbot",
        :desc => "IRC nickname the bot should attempt to use", :wizard => true,
        :on_change => Proc.new{|bot, v| bot.sendq "NICK #{v}" })
      Config.register Config::StringValue.new('irc.name',
        :default => "Ruby bot", :requires_restart => true,
        :desc => "IRC realname the bot should use")
      Config.register Config::BooleanValue.new('irc.name_copyright',
        :default => true, :requires_restart => true,
        :desc => "Append copyright notice to bot realname? (please don't disable unless it's really necessary)")
      Config.register Config::StringValue.new('irc.user', :default => "rbot",
        :requires_restart => true,
        :desc => "local user the bot should appear to be", :wizard => true)
      Config.register Config::ArrayValue.new('irc.join_channels',
        :default => [], :wizard => true,
        :desc => "What channels the bot should always join at startup. List multiple channels using commas to separate. If a channel requires a password, use a space after the channel name. e.g: '#chan1, #chan2, #secretchan secritpass, #chan3'")
      Config.register Config::ArrayValue.new('irc.ignore_users',
        :default => [],
        :desc => "Which users to ignore input from. This is mainly to avoid bot-wars triggered by creative people")
      Config.register Config::ArrayValue.new('irc.ignore_channels',
        :default => [],
        :desc => "Which channels to ignore input in. This is mainly to turn the bot into a logbot that doesn't interact with users in any way (in the specified channels)")

      Config.register Config::IntegerValue.new('core.save_every',
        :default => 60, :validate => Proc.new{|v| v >= 0},
        :on_change => Proc.new { |bot, v|
          if @save_timer
            if v > 0
              @timer.reschedule(@save_timer, v)
              @timer.unblock(@save_timer)
            else
              @timer.block(@save_timer)
            end
          else
            if v > 0
              @save_timer = @timer.add(v) { bot.save }
            end
            # Nothing to do when v == 0
          end
        },
        :desc => "How often the bot should persist all configuration to disk (in case of a server crash, for example)")

      Config.register Config::BooleanValue.new('core.run_as_daemon',
        :default => false, :requires_restart => true,
        :desc => "Should the bot run as a daemon?")

      Config.register Config::StringValue.new('log.file',
        :default => false, :requires_restart => true,
        :desc => "Name of the logfile to which console messages will be redirected when the bot is run as a daemon")
      Config.register Config::IntegerValue.new('log.level',
        :default => 1, :requires_restart => false,
        :validate => Proc.new { |v| (0..5).include?(v) },
        :on_change => Proc.new { |bot, v|
          $logger.level = v
        },
        :desc => "The minimum logging level (0=DEBUG,1=INFO,2=WARN,3=ERROR,4=FATAL) for console messages")
      Config.register Config::IntegerValue.new('log.keep',
        :default => 1, :requires_restart => true,
        :validate => Proc.new { |v| v >= 0 },
        :desc => "How many old console messages logfiles to keep")
      Config.register Config::IntegerValue.new('log.max_size',
        :default => 10, :requires_restart => true,
        :validate => Proc.new { |v| v > 0 },
        :desc => "Maximum console messages logfile size (in megabytes)")

      Config.register Config::ArrayValue.new('plugins.path',
        :wizard => true, :default => ['(default)', '(default)/games', '(default)/contrib'],
        :requires_restart => false,
        :on_change => Proc.new { |bot, v| bot.setup_plugins_path },
        :desc => "Where the bot should look for plugins. List multiple directories using commas to separate. Use '(default)' for default prepackaged plugins collection, '(default)/contrib' for prepackaged unsupported plugins collection")

      Config.register Config::EnumValue.new('send.newlines',
        :values => ['split', 'join'], :default => 'split',
        :on_change => Proc.new { |bot, v|
          bot.set_default_send_options :newlines => v.to_sym
        },
        :desc => "When set to split, messages with embedded newlines will be sent as separate lines. When set to join, newlines will be replaced by the value of join_with")
      Config.register Config::StringValue.new('send.join_with',
        :default => ' ',
        :on_change => Proc.new { |bot, v|
          bot.set_default_send_options :join_with => v.dup
        },
        :desc => "String used to replace newlines when send.newlines is set to join")
      Config.register Config::IntegerValue.new('send.max_lines',
        :default => 5,
        :validate => Proc.new { |v| v >= 0 },
        :on_change => Proc.new { |bot, v|
          bot.set_default_send_options :max_lines => v
        },
        :desc => "Maximum number of IRC lines to send for each message (set to 0 for no limit)")
      Config.register Config::EnumValue.new('send.overlong',
        :values => ['split', 'truncate'], :default => 'split',
        :on_change => Proc.new { |bot, v|
          bot.set_default_send_options :overlong => v.to_sym
        },
        :desc => "When set to split, messages which are too long to fit in a single IRC line are split into multiple lines. When set to truncate, long messages are truncated to fit the IRC line length")
      Config.register Config::StringValue.new('send.split_at',
        :default => '\s+',
        :on_change => Proc.new { |bot, v|
          bot.set_default_send_options :split_at => Regexp.new(v)
        },
        :desc => "A regular expression that should match the split points for overlong messages (see send.overlong)")
      Config.register Config::BooleanValue.new('send.purge_split',
        :default => true,
        :on_change => Proc.new { |bot, v|
          bot.set_default_send_options :purge_split => v
        },
        :desc => "Set to true if the splitting boundary (set in send.split_at) should be removed when splitting overlong messages (see send.overlong)")
      Config.register Config::StringValue.new('send.truncate_text',
        :default => "#{Reverse}...#{Reverse}",
        :on_change => Proc.new { |bot, v|
          bot.set_default_send_options :truncate_text => v.dup
        },
        :desc => "When truncating overlong messages (see send.overlong) or when sending too many lines per message (see send.max_lines) replace the end of the last line with this text")
      Config.register Config::IntegerValue.new('send.penalty_pct',
        :default => 100,
        :validate => Proc.new { |v| v >= 0 },
        :on_change => Proc.new { |bot, v|
          bot.socket.penalty_pct = v
        },
        :desc => "Percentage of IRC penalty to consider when sending messages to prevent being disconnected for excess flood. Set to 0 to disable penalty control.")
      Config.register Config::StringValue.new('core.db',
        :default => "bdb",
        :wizard => true, :default => "bdb",
        :validate => Proc.new { |v| ["bdb", "tc"].include? v },
        :requires_restart => true,
        :desc => "DB adaptor to use for storing settings and plugin data. Options are: bdb (Berkeley DB, stable adaptor, but troublesome to install and unmaintained), tc (Tokyo Cabinet, new adaptor, fast and furious, but may be not available and contain bugs)")

      @argv = params[:argv]
      @run_dir = params[:run_dir] || Dir.pwd

      unless FileTest.directory? Config::coredir
        error "core directory '#{Config::coredir}' not found, did you setup.rb?"
        exit 2
      end

      unless FileTest.directory? Config::datadir
        error "data directory '#{Config::datadir}' not found, did you setup.rb?"
        exit 2
      end

      unless botclass and not botclass.empty?
        # We want to find a sensible default.
        # * On POSIX systems we prefer ~/.rbot for the effective uid of the process
        # * On Windows (at least the NT versions) we want to put our stuff in the
        #   Application Data folder.
        # We don't use any particular O/S detection magic, exploiting the fact that
        # Etc.getpwuid is nil on Windows
        if Etc.getpwuid(Process::Sys.geteuid)
          botclass = Etc.getpwuid(Process::Sys.geteuid)[:dir].dup
        else
          if ENV.has_key?('APPDATA')
            botclass = ENV['APPDATA'].dup
            botclass.gsub!("\\","/")
          end
        end
        botclass = File.join(botclass, ".rbot")
      end
      botclass = File.expand_path(botclass)
      @botclass = botclass.gsub(/\/$/, "")

      repopulate_botclass_directory

      registry_dir = File.join(@botclass, 'registry')
      Dir.mkdir(registry_dir) unless File.exist?(registry_dir)
      unless FileTest.directory? registry_dir
        error "registry storage location #{registry_dir} is not a directory"
        exit 2
      end
      save_dir = File.join(@botclass, 'safe_save')
      Dir.mkdir(save_dir) unless File.exist?(save_dir)
      unless FileTest.directory? save_dir
        error "safe save location #{save_dir} is not a directory"
        exit 2
      end

      # Time at which the last PING was sent
      @last_ping = nil
      # Time at which the last line was RECV'd from the server
      @last_rec = nil

      @startup_time = Time.new

      begin
        @config = Config.manager
        @config.bot_associate(self)
      rescue Exception => e
        fatal e
        log_session_end
        exit 2
      end

      if @config['core.run_as_daemon']
        $daemonize = true
      end
      case @config["core.db"]
        when "bdb"
          require 'rbot/registry/bdb'
        when "tc"
           require 'rbot/registry/tc'
        else
          raise _("Unknown DB adaptor: %s") % @config["core.db"]
      end

      @logfile = @config['log.file']
      if @logfile.class!=String || @logfile.empty?
        logfname =  File.basename(botclass).gsub(/^\.+/,'')
        logfname << ".log"
        @logfile = File.join(botclass, logfname)
        debug "Using `#{@logfile}' as debug log"
      end

      # See http://blog.humlab.umu.se/samuel/archives/000107.html
      # for the backgrounding code
      if $daemonize
        begin
          exit if fork
          Process.setsid
          exit if fork
        rescue NotImplementedError
          warning "Could not background, fork not supported"
        rescue SystemExit
          exit 0
        rescue Exception => e
          warning "Could not background. #{e.pretty_inspect}"
        end
        Dir.chdir botclass
        # File.umask 0000                # Ensure sensible umask. Adjust as needed.
      end

      logger = Logger.new(@logfile,
                          @config['log.keep'],
                          @config['log.max_size']*1024*1024)
      logger.datetime_format= $dateformat
      logger.level = @config['log.level']
      logger.level = $cl_loglevel if defined? $cl_loglevel
      logger.level = 0 if $debug

      restart_logger(logger)

      log_session_start

      if $daemonize
        log "Redirecting standard input/output/error"
        [$stdin, $stdout, $stderr].each do |fd|
          begin
            fd.reopen "/dev/null"
          rescue Errno::ENOENT
            # On Windows, there's not such thing as /dev/null
            fd.reopen "NUL"
          end
        end

        def $stdout.write(str=nil)
          log str, 2
          return str.to_s.size
        end
        def $stdout.write(str=nil)
          if str.to_s.match(/:\d+: warning:/)
            warning str, 2
          else
            error str, 2
          end
          return str.to_s.size
        end
      end

      File.open($opts['pidfile'] || File.join(@botclass, 'rbot.pid'), 'w') do |pf|
        pf << "#{$$}\n"
      end

      @registry = Registry.new self

      @timer = Timer.new
      @save_mutex = Mutex.new
      if @config['core.save_every'] > 0
        @save_timer = @timer.add(@config['core.save_every']) { save }
      else
        @save_timer = nil
      end
      @quit_mutex = Mutex.new

      @plugins = nil
      @lang = Language.new(self, @config['core.language'])

      begin
        @auth = Auth::manager
        @auth.bot_associate(self)
        # @auth.load("#{botclass}/botusers.yaml")
      rescue Exception => e
        fatal e
        log_session_end
        exit 2
      end
      @auth.everyone.set_default_permission("*", true)
      @auth.botowner.password= @config['auth.password']

      @plugins = Plugins::manager
      @plugins.bot_associate(self)
      setup_plugins_path()

      if @config['server.name']
          debug "upgrading configuration (server.name => server.list)"
          srv_uri = 'irc://' + @config['server.name']
          srv_uri += ":#{@config['server.port']}" if @config['server.port']
          @config.items['server.list'.to_sym].set_string(srv_uri)
          @config.delete('server.name'.to_sym)
          @config.delete('server.port'.to_sym)
          debug "server.list is now #{@config['server.list'].inspect}"
      end

      @socket = Irc::Socket.new(@config['server.list'], @config['server.bindhost'], :ssl => @config['server.ssl'], :penalty_pct =>@config['send.penalty_pct'])
      @client = Client.new

      @plugins.scan

      # Channels where we are quiet
      # Array of channels names where the bot should be quiet
      # '*' means all channels
      #
      @quiet = Set.new
      # but we always speak here
      @not_quiet = Set.new

      # the nick we want, if it's different from the irc.nick config value
      # (e.g. as set by a !nick command)
      @wanted_nick = nil

      @client[:welcome] = proc {|data|
        m = WelcomeMessage.new(self, server, data[:source], data[:target], data[:message])

        @plugins.delegate("welcome", m)
        @plugins.delegate("connect")
      }

      # TODO the next two @client should go into rfc2812.rb, probably
      # Since capabs are two-steps processes, server.supports[:capab]
      # should be a three-state: nil, [], [....]
      asked_for = { :"identify-msg" => false }
      @client[:isupport] = proc { |data|
        if server.supports[:capab] and !asked_for[:"identify-msg"]
          sendq "CAPAB IDENTIFY-MSG"
          asked_for[:"identify-msg"] = true
        end
      }
      @client[:datastr] = proc { |data|
        if data[:text] == "IDENTIFY-MSG"
          server.capabilities[:"identify-msg"] = 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], :handle_id => true)
        # debug "Message source is #{data[:source].inspect}"
        # debug "Message target is #{data[:target].inspect}"
        # debug "Bot is #{myself.inspect}"

        @config['irc.ignore_channels'].each { |channel|
          if m.target.downcase == channel.downcase
            m.ignored = true
            break
          end
        }
        @config['irc.ignore_users'].each { |mask|
          if m.source.matches?(server.new_netmask(mask))
            m.ignored = true
            break
          end
        } unless m.ignored

        @plugins.irc_delegate('privmsg', m)
      }
      @client[:notice] = proc { |data|
        message = NoticeMessage.new(self, server, data[:source], data[:target], data[:message], :handle_id => true)
        # pass it off to plugins that want to hear everything
        @plugins.irc_delegate "notice", message
      }
      @client[:motd] = proc { |data|
        m = MotdMessage.new(self, server, data[:source], data[:target], data[:motd])
        @plugins.delegate "motd", m
      }
      @client[:nicktaken] = proc { |data|
        new = "#{data[:nick]}_"
        nickchg new
        # If we're setting our nick at connection because our choice was taken,
        # we have to fix our nick manually, because there will be no NICK message
        # to inform us that our nick has been changed.
        if data[:target] == '*'
          debug "setting my connection nick to #{new}"
          nick = new
        end
        @plugins.delegate "nicktaken", data[:nick]
      }
      @client[:badnick] = proc {|data|
        warning "bad nick (#{data[:nick]})"
      }
      @client[:ping] = proc {|data|
        sendq "PONG #{data[:pingid]}"
      }
      @client[:pong] = proc {|data|
        @last_ping = nil
      }
      @client[:nick] = proc {|data|
        # debug "Message source is #{data[:source].inspect}"
        # debug "Bot is #{myself.inspect}"
        source = data[:source]
        old = data[:oldnick]
        new = data[:newnick]
        m = NickMessage.new(self, server, source, old, new)
        m.is_on = data[:is_on]
        if source == myself
          debug "my nick is now #{new}"
        end
        @plugins.irc_delegate("nick", m)
      }
      @client[:quit] = proc {|data|
        source = data[:source]
        message = data[:message]
        m = QuitMessage.new(self, server, source, source, message)
        m.was_on = data[:was_on]
        @plugins.irc_delegate("quit", m)
      }
      @client[:mode] = proc {|data|
        m = ModeChangeMessage.new(self, server, data[:source], data[:target], data[:modestring])
        m.modes = data[:modes]
        @plugins.delegate "modechange", m
      }
      @client[:whois] = proc {|data|
        source = data[:source]
        target = server.get_user(data[:whois][:nick])
        m = WhoisMessage.new(self, server, source, target, data[:whois])
        @plugins.delegate "whois", m
      }
      @client[:join] = proc {|data|
        m = JoinMessage.new(self, server, data[:source], data[:channel], data[:message])
        sendq("MODE #{data[:channel]}", nil, 0) if m.address?
        @plugins.irc_delegate("join", m)
        sendq("WHO #{data[:channel]}", data[:channel], 2) if m.address?
      }
      @client[:part] = proc {|data|
        m = PartMessage.new(self, server, data[:source], data[:channel], data[:message])
        @plugins.irc_delegate("part", m)
      }
      @client[:kick] = proc {|data|
        m = KickMessage.new(self, server, data[:source], data[:target], data[:channel],data[:message])
        @plugins.irc_delegate("kick", m)
      }
      @client[:invite] = proc {|data|
        m = InviteMessage.new(self, server, data[:source], data[:target], data[:channel])
        @plugins.irc_delegate("invite", m)
      }
      @client[:changetopic] = proc {|data|
        m = TopicMessage.new(self, server, data[:source], data[:channel], data[:topic])
        m.info_or_set = :set
        @plugins.irc_delegate("topic", m)
      }
      # @client[:topic] = proc { |data|
      #   irclog "@ Topic is \"#{data[:topic]}\"", data[:channel]
      # }
      @client[:topicinfo] = proc { |data|
        channel = data[:channel]
        topic = channel.topic
        m = TopicMessage.new(self, server, data[:source], channel, topic)
        m.info_or_set = :info
        @plugins.irc_delegate("topic", m)
      }
      @client[:names] = proc { |data|
        m = NamesMessage.new(self, server, server, data[:channel])
        m.users = data[:users]
        @plugins.delegate "names", m
      }
      @client[:banlist] = proc { |data|
        m = BanlistMessage.new(self, server, server, data[:channel])
        m.bans = data[:bans]
        @plugins.delegate "banlist", m
      }
      @client[:nosuchtarget] = proc { |data|
        m = NoSuchTargetMessage.new(self, server, server, data[:target], data[:message])
        @plugins.delegate "nosuchtarget", m
      }
      @client[:error] = proc { |data|
        raise ServerError, data[:message]
      }
      @client[:unknown] = proc { |data|
        #debug "UNKNOWN: #{data[:serverstring]}"
        m = UnknownMessage.new(self, server, server, nil, data[:serverstring])
        @plugins.delegate "unknown_message", m
      }

      set_default_send_options :newlines => @config['send.newlines'].to_sym,
        :join_with => @config['send.join_with'].dup,
        :max_lines => @config['send.max_lines'],
        :overlong => @config['send.overlong'].to_sym,
        :split_at => Regexp.new(@config['send.split_at']),
        :purge_split => @config['send.purge_split'],
        :truncate_text => @config['send.truncate_text'].dup

      trap_sigs
    end

### Public Instance Methods

action(where, message, options={}) click to toggle source

perform a CTCP action with message `message` to channel/nick `where`

    # File lib/rbot/ircbot.rb, line 1198
    def action(where, message, options={})
      ctcp_say(where, 'ACTION', message, options)
    end

channels() click to toggle source

bot channels in the client/server connection

    # File lib/rbot/ircbot.rb, line 218
    def channels
      myself.channels
    end

clear_filters() click to toggle source

This method clears the filter list and installs the identity filter

    # File lib/rbot/core/utils/filters.rb, line 142
    def clear_filters
      @filters ||= {}
      @filters.clear

      @filter_group ||= {}
      @filter_group.clear

      register_filter(:identity) { |stream| stream }
    end

connect() click to toggle source

connect the bot to IRC

    # File lib/rbot/ircbot.rb, line 929
    def connect
      # make sure we don't have any spurious ping checks running
      # (and initialize the vars if this is the first time we connect)
      stop_server_pings
      begin
        quit if $interrupted > 0
        @socket.connect
        @last_rec = Time.now
      rescue => e
        raise e.class, "failed to connect to IRC server at #{@socket.server_uri}: #{e}"
      end
      quit if $interrupted > 0

      realname = @config['irc.name'].clone || 'Ruby bot'
      realname << ' ' + COPYRIGHT_NOTICE if @config['irc.name_copyright']

      @socket.emergency_puts "PASS " + @config['server.password'] if @config['server.password']
      @socket.emergency_puts "NICK #{@config['irc.nick']}\nUSER #{@config['irc.user']} 4 #{@socket.server_uri.host} :#{realname}"
      quit if $interrupted > 0
      myself.nick = @config['irc.nick']
      myself.user = @config['irc.user']
    end

ctcp_notice(where, command, message, options={}) click to toggle source

    # File lib/rbot/ircbot.rb, line 1187
    def ctcp_notice(where, command, message, options={})
      return if where.kind_of?(Channel) and quiet_on?(where)
      sendmsg "NOTICE", where, "\0001#{command} #{message}\0001", options
    end

ctcp_say(where, command, message, options={}) click to toggle source

    # File lib/rbot/ircbot.rb, line 1192
    def ctcp_say(where, command, message, options={})
      return if where.kind_of?(Channel) and quiet_on?(where)
      sendmsg "PRIVMSG", where, "\0001#{command} #{message}\0001", options
    end

disconnect(message=nil) click to toggle source

    # File lib/rbot/ircbot.rb, line 1218
    def disconnect(message=nil)
      message = @lang.get("quit") if (!message || message.empty?)
      if @socket.connected?
        begin
          debug "Clearing socket"
          @socket.clearq
          debug "Sending quit message"
          @socket.emergency_puts "QUIT :#{message}"
          debug "Logging quits"
          delegate_sent('QUIT', myself, message)
          debug "Flushing socket"
          @socket.flush
        rescue SocketError => e
          error "error while disconnecting socket: #{e.pretty_inspect}"
        end
        debug "Shutting down socket"
        @socket.shutdown
      end
      stop_server_pings
      @client.reset
    end

filter(filter1, filter2, ..., filterN, stream) → stream click to toggle source
filter(filter1, filter2, ..., filterN, text, hash) → stream
filter(filter1, filter2, ..., filterN, hash) → stream

This method processes the DataStream stream with the filters filter1, filter2, ..., filterN, in sequence (the output of each filter is used as input for the next one. stream can be provided either as a DataStream or as a String and a Hash (see DataStream.new).

    # File lib/rbot/core/utils/filters.rb, line 63
    def filter(*args)
      @filters ||= {}
      if Hash === args.last
        # the stream is a Hash, check if the previous element is not a Symbol
        if Symbol === args[-2]
          ds = DataStream.new(args.pop)
        else
          ds = DataStream.new(*args.slice!(-2, 2))
        end
      else
        # the stream is just whatever else
        ds = DataStream.new(args.pop)
      end
      names = args.dup
      return ds if names.empty?
      # check if filters exist
      missing = names - @filters.keys
      raise "Missing filters: #{missing.join(', ')}" unless missing.empty?
      fs = @filters.values_at(*names)
      fs.inject(ds) { |mid, f| mid = f.call(mid) }
    end

filter_groups() click to toggle source

This method is used to retrieve the filter group names

    # File lib/rbot/core/utils/filters.rb, line 136
    def filter_groups
      return [] unless defined? @filter_group
      return @filter_group.keys
    end

filter_names(group=nil) click to toggle source

This method is used to retrieve the filter names (in a given group)

    # File lib/rbot/core/utils/filters.rb, line 124
    def filter_names(group=nil)
      if group
        gkey = group.to_sym
        return [] unless defined? @filter_group and @filter_group.key?(gkey)
        return @filter_group[gkey].keys
      else
        return [] unless defined? @filters
        return @filters.keys
      end
    end

global_filter_name(name, group=nil) click to toggle source

This method returns the global filter name for filter name in group group

    # File lib/rbot/core/utils/filters.rb, line 87
    def global_filter_name(name, group=nil)
      (group ? "#{group}.#{name}" : name.to_s).intern
    end

got_sig(sig, func=:quit) click to toggle source

things to do when we receive a signal

    # File lib/rbot/ircbot.rb, line 901
    def got_sig(sig, func=:quit)
      debug "received #{sig}, queueing #{func}"
      # this is not an interruption if we just need to reconnect
      $interrupted += 1 unless func == :reconnect
      self.send(func) unless @quit_mutex.locked?
      debug "interrupted #{$interrupted} times"
      if $interrupted >= 3
        debug "drastic!"
        log_session_end
        exit 2
      end
    end

has_filter?(name, group=nil) click to toggle source

This method checks if the bot has a filter named name (in group group)

    # File lib/rbot/core/utils/filters.rb, line 93
    def has_filter?(name, group=nil)
      @filters.key?(global_filter_name(name, group))
    end

has_filter_group?(name) click to toggle source

This method checks if the bot has a filter group named name

    # File lib/rbot/core/utils/filters.rb, line 98
    def has_filter_group?(name)
      @filter_group.key?(name)
    end

help(topic=nil) click to toggle source

m | message asking for help
topic | optional topic help is requested for

respond to online help requests

    # File lib/rbot/ircbot.rb, line 1363
    def help(topic=nil)
      topic = nil if topic == ""
      case topic
      when nil
        helpstr = _("help topics: ")
        helpstr += @plugins.helptopics
        helpstr += _(" (help <topic> for more info)")
      else
        unless(helpstr = @plugins.help(topic))
          helpstr = _("no help for topic %{topic}") % { :topic => topic }
        end
      end
      return helpstr
    end

inspect() click to toggle source

bot inspection TODO multiserver

    # File lib/rbot/ircbot.rb, line 240
    def inspect
      ret = self.to_s[0..-2]
      ret << ' version=' << $version.inspect
      ret << ' botclass=' << botclass.inspect
      ret << ' lang="' << lang.language
      if defined?(GetText)
        ret << '/' << locale
      end
      ret << '"'
      ret << ' nick=' << nick.inspect
      ret << ' server='
      if server
        ret << (server.to_s + (socket ?
          ' [' << socket.server_uri.to_s << ']' : '')).inspect
        unless server.channels.empty?
          ret << " channels="
          ret << server.channels.map { |c|
            "%s%s" % [c.modes_of(nick).map { |m|
              server.prefix_for_mode(m)
            }, c.name]
          }.inspect
        end
      else
        ret << '(none)'
      end
      ret << ' plugins=' << plugins.inspect
      ret << ">"
    end

join(channel, key=nil) click to toggle source

channel | channel to join
key | optional channel key if channel is +s

join a channel

    # File lib/rbot/ircbot.rb, line 1327
    def join(channel, key=nil)
      if(key)
        sendq "JOIN #{channel} :#{key}", channel, 2
      else
        sendq "JOIN #{channel}", channel, 2
      end
    end

kick(channel, user, msg) click to toggle source

kicking a user

    # File lib/rbot/ircbot.rb, line 1356
    def kick(channel, user, msg)
      sendq "KICK #{channel} #{user} :#{msg}", channel, 2
    end

mainloop() click to toggle source

begin event handling loop

    # File lib/rbot/ircbot.rb, line 983
    def mainloop
      while true
        too_fast = false
        begin
          quit_msg = nil
          reconnect(quit_msg, too_fast)
          quit if $interrupted > 0
          while @socket.connected?
            quit if $interrupted > 0

            # Wait for messages and process them as they arrive. If nothing is
            # received, we call the ping_server() method that will PING the
            # server if appropriate, or raise a TimeoutError if no PONG has been
            # received in the user-chosen timeout since the last PING sent.
            if @socket.select(1)
              break unless reply = @socket.gets
              @last_rec = Time.now
              @client.process reply
            else
              ping_server
            end
          end

        # I despair of this. Some of my users get "connection reset by peer"
        # exceptions that ARENT SocketError's. How am I supposed to handle
        # that?
        rescue SystemExit
          log_session_end
          exit 0
        rescue Errno::ETIMEDOUT, Errno::ECONNABORTED, TimeoutError, SocketError => e
          error "network exception: #{e.pretty_inspect}"
          quit_msg = e.to_s
        rescue ServerError => e
          # received an ERROR from the server
          quit_msg = "server ERROR: " + e.message
          too_fast = e.message.index("reconnect too fast")
          retry
        rescue BDB::Fatal => e
          fatal "fatal bdb error: #{e.pretty_inspect}"
          DBTree.stats
          # Why restart? DB problems are serious stuff ...
          # restart("Oops, we seem to have registry problems ...")
          log_session_end
          exit 2
        rescue Exception => e
          error "non-net exception: #{e.pretty_inspect}"
          quit_msg = e.to_s
        rescue => e
          fatal "unexpected exception: #{e.pretty_inspect}"
          log_session_end
          exit 2
        end
      end
    end

mode(channel, mode, target=nil) click to toggle source

changing mode

    # File lib/rbot/ircbot.rb, line 1346
    def mode(channel, mode, target=nil)
      sendq "MODE #{channel} #{mode} #{target}", channel, 2
    end

myself() click to toggle source

bot User in the client/server connection TODO multiserver

    # File lib/rbot/ircbot.rb, line 208
    def myself
      @client.user
    end

nick() click to toggle source

bot nick in the client/server connection

    # File lib/rbot/ircbot.rb, line 213
    def nick
      myself.nick
    end

nickchg(name) click to toggle source

attempt to change bot's nick to `name`

    # File lib/rbot/ircbot.rb, line 1341
    def nickchg(name)
      sendq "NICK #{name}"
    end

notice(where, message, options={}) click to toggle source

send a notice message to channel/nick `where`

    # File lib/rbot/ircbot.rb, line 1176
    def notice(where, message, options={})
      return if where.kind_of?(Channel) and quiet_on?(where)
      sendmsg "NOTICE", where, message, options
    end

okay(where) click to toggle source

quick way to say "okay" (or equivalent) to `where`

    # File lib/rbot/ircbot.rb, line 1203
    def okay(where)
      say where, @lang.get("okay")
    end

part(channel, message="") click to toggle source

part a channel

    # File lib/rbot/ircbot.rb, line 1336
    def part(channel, message="")
      sendq "PART #{channel} :#{message}", channel, 2
    end

path(*components) click to toggle source

Return a path under the current botclass by joining the mentioned components. The components are automatically converted to String

    # File lib/rbot/ircbot.rb, line 828
    def path(*components)
      File.join(@botclass, *(components.map {|c| c.to_s}))
    end

ping_server() click to toggle source

We want to respond to a hung server in a timely manner. If nothing was received in the user-selected timeout and we haven't PINGed the server yet, we PING the server. If the PONG is not received within the user-defined timeout, we assume we're in ping timeout and act accordingly.

    # File lib/rbot/ircbot.rb, line 1394
    def ping_server
      act_timeout = @config['server.ping_timeout']
      return if act_timeout <= 0
      now = Time.now
      if @last_rec && now > @last_rec + act_timeout
        if @last_ping.nil?
          # No previous PING pending, send a new one
          sendq "PING :rbot"
          @last_ping = Time.now
        else
          diff = now - @last_ping
          if diff > act_timeout
            debug "no PONG from server in #{diff} seconds, reconnecting"
            # the actual reconnect is handled in the main loop:
            raise TimeoutError, "no PONG from server in #{diff} seconds"
          end
        end
      end
    end

quiet_on?(channel) click to toggle source

checks if we should be quiet on a channel

    # File lib/rbot/ircbot.rb, line 872
    def quiet_on?(channel)
      ch = channel.downcase
      return (@quiet.include?('*') && !@not_quiet.include?(ch)) || @quiet.include?(ch)
    end

quit(message=nil) click to toggle source

message | optional IRC quit message

quit IRC, shutdown the bot

    # File lib/rbot/ircbot.rb, line 1274
    def quit(message=nil)
      begin
        shutdown(message)
      ensure
        exit 0
      end
    end

reconnect(message=nil, too_fast=false) click to toggle source

disconnect the bot from IRC, if connected, and then connect (again)

    # File lib/rbot/ircbot.rb, line 953
    def reconnect(message=nil, too_fast=false)
      # we will wait only if @last_rec was not nil, i.e. if we were connected or
      # got disconnected by a network error
      # if someone wants to manually call disconnect() _and_ reconnect(), they
      # will have to take care of the waiting themselves
      will_wait = !!@last_rec

      if @socket.connected?
        disconnect(message)
      end

      begin
        if will_wait
          log "\n\nDisconnected\n\n"

          quit if $interrupted > 0

          log "\n\nWaiting to reconnect\n\n"
          sleep @config['server.reconnect_wait']
          sleep 10*@config['server.reconnect_wait'] if too_fast
        end

        connect
      rescue Exception => e
        will_wait = true
        retry
      end
    end

register_filter(name, group=nil, &block) click to toggle source

This method is used to register a new filter

    # File lib/rbot/core/utils/filters.rb, line 103
    def register_filter(name, group=nil, &block)
      raise "No block provided" unless block_given?
      @filters ||= {}
      tlkey = global_filter_name(name, group)
      key = name.to_sym
      if has_filter?(tlkey)
        debug "Overwriting filter #{tlkey}"
      end
      @filters[tlkey] = DataFilter.new(&block)
      if group
        gkey = group.to_sym
        @filter_group ||= {}
        @filter_group[gkey] ||= {}
        if @filter_group[gkey].key?(key)
          debug "Overwriting filter #{key} in group #{gkey}"
        end
        @filter_group[gkey][key] = @filters[tlkey]
      end
    end

remote_dispatcher() click to toggle source

The bot also manages a single (for the moment) remote dispatcher. This method makes it accessible to the outside world, creating it if necessary.

    # File lib/rbot/core/remote.rb, line 241
    def remote_dispatcher
      if defined? @remote_dispatcher
        @remote_dispatcher
      else
        @remote_dispatcher = RemoteDispatcher.new(self)
      end
    end

remote_object() click to toggle source

The bot also manages a single (for the moment) remote object. This method makes it accessible to the outside world, creating it if necessary.

    # File lib/rbot/core/remote.rb, line 252
    def remote_object
      if defined? @remote_object
        @remote_object
      else
        @remote_object = RemoteObject.new(self)
      end
    end

repopulate_botclass_directory() click to toggle source

    # File lib/rbot/ircbot.rb, line 801
    def repopulate_botclass_directory
      template_dir = File.join Config::datadir, 'templates'
      if FileTest.directory? @botclass
        # compare the templates dir with the current botclass dir, filling up the
        # latter with any missing file. Sadly, FileUtils.cp_r doesn't have an
        # :update option, so we have to do it manually.
        # Note that we use the */** pattern because we don't want to match
        # keywords.rbot, which gets deleted on load and would therefore be missing
        # always
        missing = Dir.chdir(template_dir) { Dir.glob('*/**') } - Dir.chdir(@botclass) { Dir.glob('*/**') }
        missing.map do |f|
          dest = File.join(@botclass, f)
          FileUtils.mkdir_p(File.dirname(dest))
          FileUtils.cp File.join(template_dir, f), dest
        end
      else
        log "no #{@botclass} directory found, creating from templates..."
        if FileTest.exist? @botclass
          error "file #{@botclass} exists but isn't a directory"
          exit 2
        end
        FileUtils.cp_r template_dir, @botclass
      end
    end

rescan() click to toggle source

call the rescan method for all of the botmodules

    # File lib/rbot/ircbot.rb, line 1314
    def rescan
      debug "\tstopping timer..."
      @timer.stop
      @save_mutex.synchronize do
        @lang.rescan
        @plugins.rescan
      end
      @timer.start
    end

reset_quiet(channel = nil) click to toggle source

    # File lib/rbot/ircbot.rb, line 889
    def reset_quiet(channel = nil)
      if channel
        ch = channel.downcase.dup
        @quiet.delete(ch)
        @not_quiet << ch
      else
        @quiet.clear
        @not_quiet.clear
      end
    end

restart(message=nil) click to toggle source

totally shutdown and respawn the bot

    # File lib/rbot/ircbot.rb, line 1283
    def restart(message=nil)
      message = _("restarting, back in %{wait}...") % {
        :wait => @config['server.reconnect_wait']
      } if (!message || message.empty?)
      shutdown(message)
      sleep @config['server.reconnect_wait']
      begin
        # now we re-exec
        # Note, this fails on Windows
        debug "going to exec #{$0} #{@argv.inspect} from #{@run_dir}"
        log_session_end
        Dir.chdir(@run_dir)
        exec($0, *@argv)
      rescue Errno::ENOENT
        log_session_end
        exec("ruby", *(@argv.unshift $0))
      rescue Exception => e
        $interrupted += 1
        raise e
      end
    end

save() click to toggle source

call the save method for all of the botmodules

    # File lib/rbot/ircbot.rb, line 1306
    def save
      @save_mutex.synchronize do
        @plugins.save
        DBTree.cleanup_logs
      end
    end

say(where, message, options={}) click to toggle source

say something (PRIVMSG) to channel/nick `where`

    # File lib/rbot/ircbot.rb, line 1182
    def say(where, message, options={})
      return if where.kind_of?(Channel) and quiet_on?(where)
      sendmsg "PRIVMSG", where, message, options
    end

sendmsg(original_type, original_where, original_message, options={}) click to toggle source

type | message type
where | message target
message | message text

send message `message` of type `type` to target `where` Type can be PRIVMSG, NOTICE, etc, but those you should really use the relevant say() or notice() methods. This one should be used for IRCd extensions you want to use in modules.

    # File lib/rbot/ircbot.rb, line 1045
    def sendmsg(original_type, original_where, original_message, options={})

      # filter message with sendmsg filters
      ds = DataStream.new original_message.to_s.dup,
        :type => original_type, :dest => original_where,
        :options => @default_send_options.merge(options)
      filters = filter_names(:sendmsg)
      filters.each do |fname|
        debug "filtering #{ds[:text]} with sendmsg filter #{fname}"
        ds.merge! filter(self.global_filter_name(fname, :sendmsg), ds)
      end

      opts = ds[:options]
      type = ds[:type]
      where = ds[:dest]
      filtered = ds[:text]

      # For starters, set up appropriate queue channels and rings
      mchan = opts[:queue_channel]
      mring = opts[:queue_ring]
      if mchan
        chan = mchan
      else
        chan = where
      end
      if mring
        ring = mring
      else
        case where
        when User
          ring = 1
        else
          ring = 2
        end
      end

      multi_line = filtered.gsub(/[\r\n]+/, "\n")

      # if target is a channel with nocolor modes, strip colours
      if where.kind_of?(Channel) and where.mode.any?(*config['server.nocolor_modes'])
        multi_line.replace BasicUserMessage.strip_formatting(multi_line)
      end

      messages = Array.new
      case opts[:newlines]
      when :join
        messages << [multi_line.gsub("\n", opts[:join_with])]
      when :split
        multi_line.each_line { |line|
          line.chomp!
          next unless(line.size > 0)
          messages << line
        }
      else
        raise "Unknown :newlines option #{opts[:newlines]} while sending #{original_message.inspect}"
      end

      # The IRC protocol requires that each raw message must be not longer
      # than 512 characters. From this length with have to subtract the EOL
      # terminators (CR+LF) and the length of ":botnick!botuser@bothost "
      # that will be prepended by the server to all of our messages.

      # The maximum raw message length we can send is therefore 512 - 2 - 2
      # minus the length of our hostmask.

      max_len = 508 - myself.fullform.size

      # On servers that support IDENTIFY-MSG, we have to subtract 1, because messages
      # will have a + or - prepended
      if server.capabilities[:"identify-msg"]
        max_len -= 1
      end

      # When splitting the message, we'll be prefixing the following string:
      # (e.g. "PRIVMSG #rbot :")
      fixed = "#{type} #{where} :"

      # And this is what's left
      left = max_len - fixed.size

      truncate = opts[:truncate_text]
      truncate = @default_send_options[:truncate_text] if truncate.size > left
      truncate = "" if truncate.size > left

      all_lines = messages.map { |line|
        if line.size < left
          line
        else
          case opts[:overlong]
          when :split
            msg = line.dup
            sub_lines = Array.new
            begin
              sub_lines << msg.slice!(0, left)
              break if msg.empty?
              lastspace = sub_lines.last.rindex(opts[:split_at])
              if lastspace
                msg.replace sub_lines.last.slice!(lastspace, sub_lines.last.size) + msg
                msg.gsub!(/^#{opts[:split_at]}/, "") if opts[:purge_split]
              end
            end until msg.empty?
            sub_lines
          when :truncate
            line.slice(0, left - truncate.size) << truncate
          else
            raise "Unknown :overlong option #{opts[:overlong]} while sending #{original_message.inspect}"
          end
        end
      }.flatten

      if opts[:max_lines] > 0 and all_lines.length > opts[:max_lines]
        lines = all_lines[0...opts[:max_lines]]
        new_last = lines.last.slice(0, left - truncate.size) << truncate
        lines.last.replace(new_last)
      else
        lines = all_lines
      end

      lines.each { |line|
        sendq "#{fixed}#{line}", chan, ring
        delegate_sent(type, where, line)
      }
    end

sendq(message="", chan=nil, ring=0) click to toggle source

queue an arbitraty message for the server

    # File lib/rbot/ircbot.rb, line 1170
    def sendq(message="", chan=nil, ring=0)
      # temporary
      @socket.queue(message, chan, ring)
    end

server() click to toggle source

server we are connected to TODO multiserver

    # File lib/rbot/ircbot.rb, line 202
    def server
      @client.server
    end

set_default_send_options(opts={}) click to toggle source

    # File lib/rbot/ircbot.rb, line 852
    def set_default_send_options(opts={})
      # Default send options for NOTICE and PRIVMSG
      unless defined? @default_send_options
        @default_send_options = {
          :queue_channel => nil,      # use default queue channel
          :queue_ring => nil,         # use default queue ring
          :newlines => :split,        # or :join
          :join_with => ' ',          # by default, use a single space
          :max_lines => 0,          # maximum number of lines to send with a single command
          :overlong => :split,        # or :truncate
          # TODO an array of splitpoints would be preferrable for this option:
          :split_at => /\s+/,         # by default, split overlong lines at whitespace
          :purge_split => true,       # should the split string be removed?
          :truncate_text => "#{Reverse}...#{Reverse}"  # text to be appened when truncating
        }
      end
      @default_send_options.update opts unless opts.empty?
    end

set_quiet(channel = nil) click to toggle source

    # File lib/rbot/ircbot.rb, line 877
    def set_quiet(channel = nil)
      if channel
        ch = channel.downcase.dup
        @not_quiet.delete(ch)
        @quiet << ch
      else
        @quiet.clear
        @not_quiet.clear
        @quiet << '*'
      end
    end

setup_plugins_path() click to toggle source

    # File lib/rbot/ircbot.rb, line 832
    def setup_plugins_path
      plugdir_default = File.join(Config::datadir, 'plugins')
      plugdir_local = File.join(@botclass, 'plugins')
      Dir.mkdir(plugdir_local) unless File.exist?(plugdir_local)

      @plugins.clear_botmodule_dirs
      @plugins.add_core_module_dir(File.join(Config::coredir, 'utils'))
      @plugins.add_core_module_dir(Config::coredir)
      if FileTest.directory? plugdir_local
        @plugins.add_plugin_dir(plugdir_local)
      else
        warning "local plugin location #{plugdir_local} is not a directory"
      end

      @config['plugins.path'].each do |_|
          path = _.sub(/^\(default\)/, plugdir_default)
          @plugins.add_plugin_dir(path)
      end
    end

shutdown(message=nil) click to toggle source

disconnect from the server and cleanup all plugins and modules

    # File lib/rbot/ircbot.rb, line 1241
    def shutdown(message=nil)
      @quit_mutex.synchronize do
        debug "Shutting down: #{message}"
        ## No we don't restore them ... let everything run through
        # begin
        #   trap("SIGINT", "DEFAULT")
        #   trap("SIGTERM", "DEFAULT")
        #   trap("SIGHUP", "DEFAULT")
        # rescue => e
        #   debug "failed to restore signals: #{e.inspect}\nProbably running on windows?"
        # end
        debug "\tdisconnecting..."
        disconnect(message)
        debug "\tstopping timer..."
        @timer.stop
        debug "\tsaving ..."
        save
        debug "\tcleaning up ..."
        @save_mutex.synchronize do
          @plugins.cleanup
        end
        # debug "\tstopping timers ..."
        # @timer.stop
        # debug "Closing registries"
        # @registry.close
        debug "\t\tcleaning up the db environment ..."
        DBTree.cleanup_env
        log "rbot quit (#{message})"
      end
    end

status() click to toggle source

returns a string describing the current status of the bot (uptime etc)

    # File lib/rbot/ircbot.rb, line 1379
    def status
      secs_up = Time.new - @startup_time
      uptime = Utils.secs_to_string secs_up
      # return "Uptime #{uptime}, #{@plugins.length} plugins active, #{@registry.length} items stored in registry, #{@socket.lines_sent} lines sent, #{@socket.lines_received} received."
      return (_("Uptime %{up}, %{plug} plugins active, %{sent} lines sent, %{recv} received.") %
               {
                 :up => uptime, :plug => @plugins.length,
                 :sent => @socket.lines_sent, :recv => @socket.lines_received
               })
    end

stop_server_pings() click to toggle source

    # File lib/rbot/ircbot.rb, line 1414
    def stop_server_pings
      # cancel previous PINGs and reset time of last RECV
      @last_ping = nil
      @last_rec = nil
    end

topic(where, topic=nil) click to toggle source

set topic of channel `where` to `topic` can also be used to retrieve the topic of channel `where` by omitting the last argument

    # File lib/rbot/ircbot.rb, line 1210
    def topic(where, topic=nil)
      if topic.nil?
        sendq "TOPIC #{where}", where, 2
      else
        sendq "TOPIC #{where} :#{topic}", where, 2
      end
    end

trap_sigs() click to toggle source

trap signals

    # File lib/rbot/ircbot.rb, line 915
    def trap_sigs
      begin
        trap("SIGINT") { got_sig("SIGINT") }
        trap("SIGTERM") { got_sig("SIGTERM") }
        trap("SIGHUP") { got_sig("SIGHUP", :restart) }
        trap("SIGUSR1") { got_sig("SIGUSR1", :reconnect) }
      rescue ArgumentError => e
        debug "failed to trap signals (#{e.pretty_inspect}): running on Windows?"
      rescue Exception => e
        debug "failed to trap signals: #{e.pretty_inspect}"
      end
    end

wanted_nick() click to toggle source

nick wanted by the bot. This defaults to the irc.nick config value, but may be overridden by a manual !nick command

    # File lib/rbot/ircbot.rb, line 224
    def wanted_nick
      @wanted_nick || config['irc.nick']
    end

wanted_nick=(wn) click to toggle source

set the nick wanted by the bot

    # File lib/rbot/ircbot.rb, line 229
    def wanted_nick=(wn)
      if wn.nil? or wn.to_s.downcase == config['irc.nick'].downcase
        @wanted_nick = nil
      else
        @wanted_nick = wn.to_s.dup
      end
    end

whois(nick, target=nil) click to toggle source

asking whois

    # File lib/rbot/ircbot.rb, line 1351
    def whois(nick, target=nil)
      sendq "WHOIS #{target} #{nick}", nil, 0
    end

### Private Instance Methods

delegate_sent(type, where, message) click to toggle source

delegate sent messages

    # File lib/rbot/ircbot.rb, line 1423
    def delegate_sent(type, where, message)
      args = [self, server, myself, server.user_or_channel(where.to_s), message]
      case type
        when "NOTICE"
          m = NoticeMessage.new(*args)
        when "PRIVMSG"
          m = PrivMessage.new(*args)
        when "QUIT"
          m = QuitMessage.new(*args)
          m.was_on = myself.channels
      end
      @plugins.delegate('sent', m)
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.