Class Irc::Bot
In: lib/rbot/plugins.rb
lib/rbot/config.rb
lib/rbot/language.rb
lib/rbot/registry/bdb.rb
lib/rbot/registry/tc.rb
lib/rbot/ircbot.rb
lib/rbot/botuser.rb
lib/rbot/message.rb
lib/rbot/rbotconfig.rb
lib/rbot/messagemapper.rb
Parent: Object
BasicUserMessage JoinMessage NamesMessage WhoisMessage ModeChangeMessage KickMessage MotdMessage QuitMessage BanlistMessage UserMessage NoSuchTargetMessage TopicMessage NickMessage WelcomeMessage UnknownMessage InviteMessage PartMessage NetmaskList UserList ArrayOf ChannelList Netmask User\n[lib/rbot/botuser.rb\nlib/rbot/irc.rb] Channel Singleton RfcCasemap StrictRfcCasemap AsciiCasemap Casemap PrivMessage NoticeMessage TokyoCabinet::BDB CIBDB Btree CIBtree Socket MessageQueue QueueRing Client DBHash\n[lib/rbot/registry/bdb.rb\nlib/rbot/registry/tc.rb] DBTree\n[lib/rbot/registry/bdb.rb\nlib/rbot/registry/tc.rb] Server NetmaskDb Bot\n[lib/rbot/botuser.rb\nlib/rbot/config.rb\nlib/rbot/ircbot.rb\nlib/rbot/language.rb\nlib/rbot/message.rb\nlib/rbot/messagemapper.rb\nlib/rbot/plugins.rb\nlib/rbot/rbotconfig.rb\nlib/rbot/registry/bdb.rb\nlib/rbot/registry/tc.rb] lib/rbot/ircsocket.rb lib/rbot/rfc2812.rb lib/rbot/registry/tc.rb lib/rbot/irc.rb lib/rbot/maskdb.rb lib/rbot/message.rb lib/rbot/messagemapper.rb lib/rbot/botuser.rb lib/rbot/registry/tc.rb (null) BotConfig PKGConfig ServerOrCasemap Irc dot/m_35_0.png

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

Methods

Classes and Modules

Module Irc::Bot::Auth
Module Irc::Bot::Config
Module Irc::Bot::Plugins
Class Irc::Bot::Language
Class Irc::Bot::MessageMapper
Class Irc::Bot::MessageParameter
Class Irc::Bot::MessageTemplate
Class Irc::Bot::Registry

Constants

COPYRIGHT_NOTICE = "(c) Tom Gilbert and the rbot development team"
SOURCE_URL = "http://ruby-rbot.org"

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

create a new Bot with botclass botclass

[Source]

     # File lib/rbot/ircbot.rb, line 271
271:   def initialize(botclass, params = {})
272:     # Config for the core bot
273:     # TODO should we split socket stuff into ircsocket, etc?
274:     Config.register Config::ArrayValue.new('server.list',
275:       :default => ['irc://localhost'], :wizard => true,
276:       :requires_restart => true,
277:       :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.")
278:     Config.register Config::BooleanValue.new('server.ssl',
279:       :default => false, :requires_restart => true, :wizard => true,
280:       :desc => "Use SSL to connect to this server?")
281:     Config.register Config::StringValue.new('server.password',
282:       :default => false, :requires_restart => true,
283:       :desc => "Password for connecting to this server (if required)",
284:       :wizard => true)
285:     Config.register Config::StringValue.new('server.bindhost',
286:       :default => false, :requires_restart => true,
287:       :desc => "Specific local host or IP for the bot to bind to (if required)",
288:       :wizard => true)
289:     Config.register Config::IntegerValue.new('server.reconnect_wait',
290:       :default => 5, :validate => Proc.new{|v| v >= 0},
291:       :desc => "Seconds to wait before attempting to reconnect, on disconnect")
292:     Config.register Config::IntegerValue.new('server.ping_timeout',
293:       :default => 30, :validate => Proc.new{|v| v >= 0},
294:       :desc => "reconnect if server doesn't respond to PING within this many seconds (set to 0 to disable)")
295:     Config.register Config::ArrayValue.new('server.nocolor_modes',
296:       :default => ['c'], :wizard => false,
297:       :requires_restart => false,
298:       :desc => "List of channel modes that require messages to be without colors")
299: 
300:     Config.register Config::StringValue.new('irc.nick', :default => "rbot",
301:       :desc => "IRC nickname the bot should attempt to use", :wizard => true,
302:       :on_change => Proc.new{|bot, v| bot.sendq "NICK #{v}" })
303:     Config.register Config::StringValue.new('irc.name',
304:       :default => "Ruby bot", :requires_restart => true,
305:       :desc => "IRC realname the bot should use")
306:     Config.register Config::BooleanValue.new('irc.name_copyright',
307:       :default => true, :requires_restart => true,
308:       :desc => "Append copyright notice to bot realname? (please don't disable unless it's really necessary)")
309:     Config.register Config::StringValue.new('irc.user', :default => "rbot",
310:       :requires_restart => true,
311:       :desc => "local user the bot should appear to be", :wizard => true)
312:     Config.register Config::ArrayValue.new('irc.join_channels',
313:       :default => [], :wizard => true,
314:       :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'")
315:     Config.register Config::ArrayValue.new('irc.ignore_users',
316:       :default => [],
317:       :desc => "Which users to ignore input from. This is mainly to avoid bot-wars triggered by creative people")
318:     Config.register Config::ArrayValue.new('irc.ignore_channels',
319:       :default => [],
320:       :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)")
321: 
322:     Config.register Config::IntegerValue.new('core.save_every',
323:       :default => 60, :validate => Proc.new{|v| v >= 0},
324:       :on_change => Proc.new { |bot, v|
325:         if @save_timer
326:           if v > 0
327:             @timer.reschedule(@save_timer, v)
328:             @timer.unblock(@save_timer)
329:           else
330:             @timer.block(@save_timer)
331:           end
332:         else
333:           if v > 0
334:             @save_timer = @timer.add(v) { bot.save }
335:           end
336:           # Nothing to do when v == 0
337:         end
338:       },
339:       :desc => "How often the bot should persist all configuration to disk (in case of a server crash, for example)")
340: 
341:     Config.register Config::BooleanValue.new('core.run_as_daemon',
342:       :default => false, :requires_restart => true,
343:       :desc => "Should the bot run as a daemon?")
344: 
345:     Config.register Config::StringValue.new('log.file',
346:       :default => false, :requires_restart => true,
347:       :desc => "Name of the logfile to which console messages will be redirected when the bot is run as a daemon")
348:     Config.register Config::IntegerValue.new('log.level',
349:       :default => 1, :requires_restart => false,
350:       :validate => Proc.new { |v| (0..5).include?(v) },
351:       :on_change => Proc.new { |bot, v|
352:         $logger.level = v
353:       },
354:       :desc => "The minimum logging level (0=DEBUG,1=INFO,2=WARN,3=ERROR,4=FATAL) for console messages")
355:     Config.register Config::IntegerValue.new('log.keep',
356:       :default => 1, :requires_restart => true,
357:       :validate => Proc.new { |v| v >= 0 },
358:       :desc => "How many old console messages logfiles to keep")
359:     Config.register Config::IntegerValue.new('log.max_size',
360:       :default => 10, :requires_restart => true,
361:       :validate => Proc.new { |v| v > 0 },
362:       :desc => "Maximum console messages logfile size (in megabytes)")
363: 
364:     Config.register Config::ArrayValue.new('plugins.path',
365:       :wizard => true, :default => ['(default)', '(default)/games', '(default)/contrib'],
366:       :requires_restart => false,
367:       :on_change => Proc.new { |bot, v| bot.setup_plugins_path },
368:       :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")
369: 
370:     Config.register Config::EnumValue.new('send.newlines',
371:       :values => ['split', 'join'], :default => 'split',
372:       :on_change => Proc.new { |bot, v|
373:         bot.set_default_send_options :newlines => v.to_sym
374:       },
375:       :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")
376:     Config.register Config::StringValue.new('send.join_with',
377:       :default => ' ',
378:       :on_change => Proc.new { |bot, v|
379:         bot.set_default_send_options :join_with => v.dup
380:       },
381:       :desc => "String used to replace newlines when send.newlines is set to join")
382:     Config.register Config::IntegerValue.new('send.max_lines',
383:       :default => 5,
384:       :validate => Proc.new { |v| v >= 0 },
385:       :on_change => Proc.new { |bot, v|
386:         bot.set_default_send_options :max_lines => v
387:       },
388:       :desc => "Maximum number of IRC lines to send for each message (set to 0 for no limit)")
389:     Config.register Config::EnumValue.new('send.overlong',
390:       :values => ['split', 'truncate'], :default => 'split',
391:       :on_change => Proc.new { |bot, v|
392:         bot.set_default_send_options :overlong => v.to_sym
393:       },
394:       :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")
395:     Config.register Config::StringValue.new('send.split_at',
396:       :default => '\s+',
397:       :on_change => Proc.new { |bot, v|
398:         bot.set_default_send_options :split_at => Regexp.new(v)
399:       },
400:       :desc => "A regular expression that should match the split points for overlong messages (see send.overlong)")
401:     Config.register Config::BooleanValue.new('send.purge_split',
402:       :default => true,
403:       :on_change => Proc.new { |bot, v|
404:         bot.set_default_send_options :purge_split => v
405:       },
406:       :desc => "Set to true if the splitting boundary (set in send.split_at) should be removed when splitting overlong messages (see send.overlong)")
407:     Config.register Config::StringValue.new('send.truncate_text',
408:       :default => "#{Reverse}...#{Reverse}",
409:       :on_change => Proc.new { |bot, v|
410:         bot.set_default_send_options :truncate_text => v.dup
411:       },
412:       :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")
413:     Config.register Config::IntegerValue.new('send.penalty_pct',
414:       :default => 100,
415:       :validate => Proc.new { |v| v >= 0 },
416:       :on_change => Proc.new { |bot, v|
417:         bot.socket.penalty_pct = v
418:       },
419:       :desc => "Percentage of IRC penalty to consider when sending messages to prevent being disconnected for excess flood. Set to 0 to disable penalty control.")
420:     Config.register Config::StringValue.new('core.db',
421:       :default => "bdb",
422:       :wizard => true, :default => "bdb",
423:       :validate => Proc.new { |v| ["bdb", "tc"].include? v },
424:       :requires_restart => true,
425:       :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)")
426: 
427:     @argv = params[:argv]
428:     @run_dir = params[:run_dir] || Dir.pwd
429: 
430:     unless FileTest.directory? Config::coredir
431:       error "core directory '#{Config::coredir}' not found, did you setup.rb?"
432:       exit 2
433:     end
434: 
435:     unless FileTest.directory? Config::datadir
436:       error "data directory '#{Config::datadir}' not found, did you setup.rb?"
437:       exit 2
438:     end
439: 
440:     unless botclass and not botclass.empty?
441:       # We want to find a sensible default.
442:       # * On POSIX systems we prefer ~/.rbot for the effective uid of the process
443:       # * On Windows (at least the NT versions) we want to put our stuff in the
444:       #   Application Data folder.
445:       # We don't use any particular O/S detection magic, exploiting the fact that
446:       # Etc.getpwuid is nil on Windows
447:       if Etc.getpwuid(Process::Sys.geteuid)
448:         botclass = Etc.getpwuid(Process::Sys.geteuid)[:dir].dup
449:       else
450:         if ENV.has_key?('APPDATA')
451:           botclass = ENV['APPDATA'].dup
452:           botclass.gsub!("\\","/")
453:         end
454:       end
455:       botclass = File.join(botclass, ".rbot")
456:     end
457:     botclass = File.expand_path(botclass)
458:     @botclass = botclass.gsub(/\/$/, "")
459: 
460:     repopulate_botclass_directory
461: 
462:     registry_dir = File.join(@botclass, 'registry')
463:     Dir.mkdir(registry_dir) unless File.exist?(registry_dir)
464:     unless FileTest.directory? registry_dir
465:       error "registry storage location #{registry_dir} is not a directory"
466:       exit 2
467:     end
468:     save_dir = File.join(@botclass, 'safe_save')
469:     Dir.mkdir(save_dir) unless File.exist?(save_dir)
470:     unless FileTest.directory? save_dir
471:       error "safe save location #{save_dir} is not a directory"
472:       exit 2
473:     end
474: 
475:     # Time at which the last PING was sent
476:     @last_ping = nil
477:     # Time at which the last line was RECV'd from the server
478:     @last_rec = nil
479: 
480:     @startup_time = Time.new
481: 
482:     begin
483:       @config = Config.manager
484:       @config.bot_associate(self)
485:     rescue Exception => e
486:       fatal e
487:       log_session_end
488:       exit 2
489:     end
490: 
491:     if @config['core.run_as_daemon']
492:       $daemonize = true
493:     end
494:     case @config["core.db"]
495:       when "bdb"
496:         require 'rbot/registry/bdb'
497:       when "tc"
498:          require 'rbot/registry/tc'
499:       else
500:         raise _("Unknown DB adaptor: %s") % @config["core.db"]
501:     end
502: 
503:     @logfile = @config['log.file']
504:     if @logfile.class!=String || @logfile.empty?
505:       logfname =  File.basename(botclass).gsub(/^\.+/,'')
506:       logfname << ".log"
507:       @logfile = File.join(botclass, logfname)
508:       debug "Using `#{@logfile}' as debug log"
509:     end
510: 
511:     # See http://blog.humlab.umu.se/samuel/archives/000107.html
512:     # for the backgrounding code
513:     if $daemonize
514:       begin
515:         exit if fork
516:         Process.setsid
517:         exit if fork
518:       rescue NotImplementedError
519:         warning "Could not background, fork not supported"
520:       rescue SystemExit
521:         exit 0
522:       rescue Exception => e
523:         warning "Could not background. #{e.pretty_inspect}"
524:       end
525:       Dir.chdir botclass
526:       # File.umask 0000                # Ensure sensible umask. Adjust as needed.
527:     end
528: 
529:     logger = Logger.new(@logfile,
530:                         @config['log.keep'],
531:                         @config['log.max_size']*1024*1024)
532:     logger.datetime_format= $dateformat
533:     logger.level = @config['log.level']
534:     logger.level = $cl_loglevel if defined? $cl_loglevel
535:     logger.level = 0 if $debug
536: 
537:     restart_logger(logger)
538: 
539:     log_session_start
540: 
541:     if $daemonize
542:       log "Redirecting standard input/output/error"
543:       [$stdin, $stdout, $stderr].each do |fd|
544:         begin
545:           fd.reopen "/dev/null"
546:         rescue Errno::ENOENT
547:           # On Windows, there's not such thing as /dev/null
548:           fd.reopen "NUL"
549:         end
550:       end
551: 
552:       def $stdout.write(str=nil)
553:         log str, 2
554:         return str.to_s.size
555:       end
556:       def $stdout.write(str=nil)
557:         if str.to_s.match(/:\d+: warning:/)
558:           warning str, 2
559:         else
560:           error str, 2
561:         end
562:         return str.to_s.size
563:       end
564:     end
565: 
566:     File.open($opts['pidfile'] || File.join(@botclass, 'rbot.pid'), 'w') do |pf|
567:       pf << "#{$$}\n"
568:     end
569: 
570:     @registry = Registry.new self
571: 
572:     @timer = Timer.new
573:     @save_mutex = Mutex.new
574:     if @config['core.save_every'] > 0
575:       @save_timer = @timer.add(@config['core.save_every']) { save }
576:     else
577:       @save_timer = nil
578:     end
579:     @quit_mutex = Mutex.new
580: 
581:     @plugins = nil
582:     @lang = Language.new(self, @config['core.language'])
583: 
584:     begin
585:       @auth = Auth::manager
586:       @auth.bot_associate(self)
587:       # @auth.load("#{botclass}/botusers.yaml")
588:     rescue Exception => e
589:       fatal e
590:       log_session_end
591:       exit 2
592:     end
593:     @auth.everyone.set_default_permission("*", true)
594:     @auth.botowner.password= @config['auth.password']
595: 
596:     @plugins = Plugins::manager
597:     @plugins.bot_associate(self)
598:     setup_plugins_path()
599: 
600:     if @config['server.name']
601:         debug "upgrading configuration (server.name => server.list)"
602:         srv_uri = 'irc://' + @config['server.name']
603:         srv_uri += ":#{@config['server.port']}" if @config['server.port']
604:         @config.items['server.list'.to_sym].set_string(srv_uri)
605:         @config.delete('server.name'.to_sym)
606:         @config.delete('server.port'.to_sym)
607:         debug "server.list is now #{@config['server.list'].inspect}"
608:     end
609: 
610:     @socket = Irc::Socket.new(@config['server.list'], @config['server.bindhost'], :ssl => @config['server.ssl'], :penalty_pct =>@config['send.penalty_pct'])
611:     @client = Client.new
612: 
613:     @plugins.scan
614: 
615:     # Channels where we are quiet
616:     # Array of channels names where the bot should be quiet
617:     # '*' means all channels
618:     #
619:     @quiet = Set.new
620:     # but we always speak here
621:     @not_quiet = Set.new
622: 
623:     # the nick we want, if it's different from the irc.nick config value
624:     # (e.g. as set by a !nick command)
625:     @wanted_nick = nil
626: 
627:     @client[:welcome] = proc {|data|
628:       m = WelcomeMessage.new(self, server, data[:source], data[:target], data[:message])
629: 
630:       @plugins.delegate("welcome", m)
631:       @plugins.delegate("connect")
632:     }
633: 
634:     # TODO the next two @client should go into rfc2812.rb, probably
635:     # Since capabs are two-steps processes, server.supports[:capab]
636:     # should be a three-state: nil, [], [....]
637:     asked_for = { "identify-msg""identify-msg" => false }
638:     @client[:isupport] = proc { |data|
639:       if server.supports[:capab] and !asked_for["identify-msg""identify-msg"]
640:         sendq "CAPAB IDENTIFY-MSG"
641:         asked_for["identify-msg""identify-msg"] = true
642:       end
643:     }
644:     @client[:datastr] = proc { |data|
645:       if data[:text] == "IDENTIFY-MSG"
646:         server.capabilities["identify-msg""identify-msg"] = true
647:       else
648:         debug "Not handling RPL_DATASTR #{data[:servermessage]}"
649:       end
650:     }
651: 
652:     @client[:privmsg] = proc { |data|
653:       m = PrivMessage.new(self, server, data[:source], data[:target], data[:message], :handle_id => true)
654:       # debug "Message source is #{data[:source].inspect}"
655:       # debug "Message target is #{data[:target].inspect}"
656:       # debug "Bot is #{myself.inspect}"
657: 
658:       @config['irc.ignore_channels'].each { |channel|
659:         if m.target.downcase == channel.downcase
660:           m.ignored = true
661:           break
662:         end
663:       }
664:       @config['irc.ignore_users'].each { |mask|
665:         if m.source.matches?(server.new_netmask(mask))
666:           m.ignored = true
667:           break
668:         end
669:       } unless m.ignored
670: 
671:       @plugins.irc_delegate('privmsg', m)
672:     }
673:     @client[:notice] = proc { |data|
674:       message = NoticeMessage.new(self, server, data[:source], data[:target], data[:message], :handle_id => true)
675:       # pass it off to plugins that want to hear everything
676:       @plugins.irc_delegate "notice", message
677:     }
678:     @client[:motd] = proc { |data|
679:       m = MotdMessage.new(self, server, data[:source], data[:target], data[:motd])
680:       @plugins.delegate "motd", m
681:     }
682:     @client[:nicktaken] = proc { |data|
683:       new = "#{data[:nick]}_"
684:       nickchg new
685:       # If we're setting our nick at connection because our choice was taken,
686:       # we have to fix our nick manually, because there will be no NICK message
687:       # to inform us that our nick has been changed.
688:       if data[:target] == '*'
689:         debug "setting my connection nick to #{new}"
690:         nick = new
691:       end
692:       @plugins.delegate "nicktaken", data[:nick]
693:     }
694:     @client[:badnick] = proc {|data|
695:       warning "bad nick (#{data[:nick]})"
696:     }
697:     @client[:ping] = proc {|data|
698:       sendq "PONG #{data[:pingid]}"
699:     }
700:     @client[:pong] = proc {|data|
701:       @last_ping = nil
702:     }
703:     @client[:nick] = proc {|data|
704:       # debug "Message source is #{data[:source].inspect}"
705:       # debug "Bot is #{myself.inspect}"
706:       source = data[:source]
707:       old = data[:oldnick]
708:       new = data[:newnick]
709:       m = NickMessage.new(self, server, source, old, new)
710:       m.is_on = data[:is_on]
711:       if source == myself
712:         debug "my nick is now #{new}"
713:       end
714:       @plugins.irc_delegate("nick", m)
715:     }
716:     @client[:quit] = proc {|data|
717:       source = data[:source]
718:       message = data[:message]
719:       m = QuitMessage.new(self, server, source, source, message)
720:       m.was_on = data[:was_on]
721:       @plugins.irc_delegate("quit", m)
722:     }
723:     @client[:mode] = proc {|data|
724:       m = ModeChangeMessage.new(self, server, data[:source], data[:target], data[:modestring])
725:       m.modes = data[:modes]
726:       @plugins.delegate "modechange", m
727:     }
728:     @client[:whois] = proc {|data|
729:       source = data[:source]
730:       target = server.get_user(data[:whois][:nick])
731:       m = WhoisMessage.new(self, server, source, target, data[:whois])
732:       @plugins.delegate "whois", m
733:     }
734:     @client[:join] = proc {|data|
735:       m = JoinMessage.new(self, server, data[:source], data[:channel], data[:message])
736:       sendq("MODE #{data[:channel]}", nil, 0) if m.address?
737:       @plugins.irc_delegate("join", m)
738:       sendq("WHO #{data[:channel]}", data[:channel], 2) if m.address?
739:     }
740:     @client[:part] = proc {|data|
741:       m = PartMessage.new(self, server, data[:source], data[:channel], data[:message])
742:       @plugins.irc_delegate("part", m)
743:     }
744:     @client[:kick] = proc {|data|
745:       m = KickMessage.new(self, server, data[:source], data[:target], data[:channel],data[:message])
746:       @plugins.irc_delegate("kick", m)
747:     }
748:     @client[:invite] = proc {|data|
749:       m = InviteMessage.new(self, server, data[:source], data[:target], data[:channel])
750:       @plugins.irc_delegate("invite", m)
751:     }
752:     @client[:changetopic] = proc {|data|
753:       m = TopicMessage.new(self, server, data[:source], data[:channel], data[:topic])
754:       m.info_or_set = :set
755:       @plugins.irc_delegate("topic", m)
756:     }
757:     # @client[:topic] = proc { |data|
758:     #   irclog "@ Topic is \"#{data[:topic]}\"", data[:channel]
759:     # }
760:     @client[:topicinfo] = proc { |data|
761:       channel = data[:channel]
762:       topic = channel.topic
763:       m = TopicMessage.new(self, server, data[:source], channel, topic)
764:       m.info_or_set = :info
765:       @plugins.irc_delegate("topic", m)
766:     }
767:     @client[:names] = proc { |data|
768:       m = NamesMessage.new(self, server, server, data[:channel])
769:       m.users = data[:users]
770:       @plugins.delegate "names", m
771:     }
772:     @client[:banlist] = proc { |data|
773:       m = BanlistMessage.new(self, server, server, data[:channel])
774:       m.bans = data[:bans]
775:       @plugins.delegate "banlist", m
776:     }
777:     @client[:nosuchtarget] = proc { |data|
778:       m = NoSuchTargetMessage.new(self, server, server, data[:target], data[:message])
779:       @plugins.delegate "nosuchtarget", m
780:     }
781:     @client[:error] = proc { |data|
782:       raise ServerError, data[:message]
783:     }
784:     @client[:unknown] = proc { |data|
785:       #debug "UNKNOWN: #{data[:serverstring]}"
786:       m = UnknownMessage.new(self, server, server, nil, data[:serverstring])
787:       @plugins.delegate "unknown_message", m
788:     }
789: 
790:     set_default_send_options :newlines => @config['send.newlines'].to_sym,
791:       :join_with => @config['send.join_with'].dup,
792:       :max_lines => @config['send.max_lines'],
793:       :overlong => @config['send.overlong'].to_sym,
794:       :split_at => Regexp.new(@config['send.split_at']),
795:       :purge_split => @config['send.purge_split'],
796:       :truncate_text => @config['send.truncate_text'].dup
797: 
798:     trap_sigs
799:   end

Public Instance methods

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

[Source]

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

bot channels in the client/server connection

[Source]

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

connect the bot to IRC

[Source]

     # File lib/rbot/ircbot.rb, line 929
929:   def connect
930:     # make sure we don't have any spurious ping checks running
931:     # (and initialize the vars if this is the first time we connect)
932:     stop_server_pings
933:     begin
934:       quit if $interrupted > 0
935:       @socket.connect
936:       @last_rec = Time.now
937:     rescue => e
938:       raise e.class, "failed to connect to IRC server at #{@socket.server_uri}: #{e}"
939:     end
940:     quit if $interrupted > 0
941: 
942:     realname = @config['irc.name'].clone || 'Ruby bot'
943:     realname << ' ' + COPYRIGHT_NOTICE if @config['irc.name_copyright']
944: 
945:     @socket.emergency_puts "PASS " + @config['server.password'] if @config['server.password']
946:     @socket.emergency_puts "NICK #{@config['irc.nick']}\nUSER #{@config['irc.user']} 4 #{@socket.server_uri.host} :#{realname}"
947:     quit if $interrupted > 0
948:     myself.nick = @config['irc.nick']
949:     myself.user = @config['irc.user']
950:   end

[Source]

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

[Source]

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

[Source]

      # File lib/rbot/ircbot.rb, line 1218
1218:   def disconnect(message=nil)
1219:     message = @lang.get("quit") if (!message || message.empty?)
1220:     if @socket.connected?
1221:       begin
1222:         debug "Clearing socket"
1223:         @socket.clearq
1224:         debug "Sending quit message"
1225:         @socket.emergency_puts "QUIT :#{message}"
1226:         debug "Logging quits"
1227:         delegate_sent('QUIT', myself, message)
1228:         debug "Flushing socket"
1229:         @socket.flush
1230:       rescue SocketError => e
1231:         error "error while disconnecting socket: #{e.pretty_inspect}"
1232:       end
1233:       debug "Shutting down socket"
1234:       @socket.shutdown
1235:     end
1236:     stop_server_pings
1237:     @client.reset
1238:   end

things to do when we receive a signal

[Source]

     # File lib/rbot/ircbot.rb, line 901
901:   def got_sig(sig, func=:quit)
902:     debug "received #{sig}, queueing #{func}"
903:     # this is not an interruption if we just need to reconnect
904:     $interrupted += 1 unless func == :reconnect
905:     self.send(func) unless @quit_mutex.locked?
906:     debug "interrupted #{$interrupted} times"
907:     if $interrupted >= 3
908:       debug "drastic!"
909:       log_session_end
910:       exit 2
911:     end
912:   end
m:message asking for help
topic:optional topic help is requested for

respond to online help requests

[Source]

      # File lib/rbot/ircbot.rb, line 1363
1363:   def help(topic=nil)
1364:     topic = nil if topic == ""
1365:     case topic
1366:     when nil
1367:       helpstr = _("help topics: ")
1368:       helpstr += @plugins.helptopics
1369:       helpstr += _(" (help <topic> for more info)")
1370:     else
1371:       unless(helpstr = @plugins.help(topic))
1372:         helpstr = _("no help for topic %{topic}") % { :topic => topic }
1373:       end
1374:     end
1375:     return helpstr
1376:   end

bot inspection TODO multiserver

[Source]

     # File lib/rbot/ircbot.rb, line 240
240:   def inspect
241:     ret = self.to_s[0..-2]
242:     ret << ' version=' << $version.inspect
243:     ret << ' botclass=' << botclass.inspect
244:     ret << ' lang="' << lang.language
245:     if defined?(GetText)
246:       ret << '/' << locale
247:     end
248:     ret << '"'
249:     ret << ' nick=' << nick.inspect
250:     ret << ' server='
251:     if server
252:       ret << (server.to_s + (socket ?
253:         ' [' << socket.server_uri.to_s << ']' : '')).inspect
254:       unless server.channels.empty?
255:         ret << " channels="
256:         ret << server.channels.map { |c|
257:           "%s%s" % [c.modes_of(nick).map { |m|
258:             server.prefix_for_mode(m)
259:           }, c.name]
260:         }.inspect
261:       end
262:     else
263:       ret << '(none)'
264:     end
265:     ret << ' plugins=' << plugins.inspect
266:     ret << ">"
267:   end
channel:channel to join
key:optional channel key if channel is +s

join a channel

[Source]

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

kicking a user

[Source]

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

begin event handling loop

[Source]

      # File lib/rbot/ircbot.rb, line 983
 983:   def mainloop
 984:     while true
 985:       too_fast = false
 986:       begin
 987:         quit_msg = nil
 988:         reconnect(quit_msg, too_fast)
 989:         quit if $interrupted > 0
 990:         while @socket.connected?
 991:           quit if $interrupted > 0
 992: 
 993:           # Wait for messages and process them as they arrive. If nothing is
 994:           # received, we call the ping_server() method that will PING the
 995:           # server if appropriate, or raise a TimeoutError if no PONG has been
 996:           # received in the user-chosen timeout since the last PING sent.
 997:           if @socket.select(1)
 998:             break unless reply = @socket.gets
 999:             @last_rec = Time.now
1000:             @client.process reply
1001:           else
1002:             ping_server
1003:           end
1004:         end
1005: 
1006:       # I despair of this. Some of my users get "connection reset by peer"
1007:       # exceptions that ARENT SocketError's. How am I supposed to handle
1008:       # that?
1009:       rescue SystemExit
1010:         log_session_end
1011:         exit 0
1012:       rescue Errno::ETIMEDOUT, Errno::ECONNABORTED, TimeoutError, SocketError => e
1013:         error "network exception: #{e.pretty_inspect}"
1014:         quit_msg = e.to_s
1015:       rescue ServerError => e
1016:         # received an ERROR from the server
1017:         quit_msg = "server ERROR: " + e.message
1018:         too_fast = e.message.index("reconnect too fast")
1019:         retry
1020:       rescue BDB::Fatal => e
1021:         fatal "fatal bdb error: #{e.pretty_inspect}"
1022:         DBTree.stats
1023:         # Why restart? DB problems are serious stuff ...
1024:         # restart("Oops, we seem to have registry problems ...")
1025:         log_session_end
1026:         exit 2
1027:       rescue Exception => e
1028:         error "non-net exception: #{e.pretty_inspect}"
1029:         quit_msg = e.to_s
1030:       rescue => e
1031:         fatal "unexpected exception: #{e.pretty_inspect}"
1032:         log_session_end
1033:         exit 2
1034:       end
1035:     end
1036:   end

changing mode

[Source]

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

bot User in the client/server connection TODO multiserver

[Source]

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

bot nick in the client/server connection

[Source]

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

attempt to change bot‘s nick to name

[Source]

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

send a notice message to channel/nick where

[Source]

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

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

[Source]

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

part a channel

[Source]

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

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

[Source]

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

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.

[Source]

      # File lib/rbot/ircbot.rb, line 1394
1394:   def ping_server
1395:     act_timeout = @config['server.ping_timeout']
1396:     return if act_timeout <= 0
1397:     now = Time.now
1398:     if @last_rec && now > @last_rec + act_timeout
1399:       if @last_ping.nil?
1400:         # No previous PING pending, send a new one
1401:         sendq "PING :rbot"
1402:         @last_ping = Time.now
1403:       else
1404:         diff = now - @last_ping
1405:         if diff > act_timeout
1406:           debug "no PONG from server in #{diff} seconds, reconnecting"
1407:           # the actual reconnect is handled in the main loop:
1408:           raise TimeoutError, "no PONG from server in #{diff} seconds"
1409:         end
1410:       end
1411:     end
1412:   end

checks if we should be quiet on a channel

[Source]

     # File lib/rbot/ircbot.rb, line 872
872:   def quiet_on?(channel)
873:     ch = channel.downcase
874:     return (@quiet.include?('*') && !@not_quiet.include?(ch)) || @quiet.include?(ch)
875:   end
message:optional IRC quit message

quit IRC, shutdown the bot

[Source]

      # File lib/rbot/ircbot.rb, line 1274
1274:   def quit(message=nil)
1275:     begin
1276:       shutdown(message)
1277:     ensure
1278:       exit 0
1279:     end
1280:   end

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

[Source]

     # File lib/rbot/ircbot.rb, line 953
953:   def reconnect(message=nil, too_fast=false)
954:     # we will wait only if @last_rec was not nil, i.e. if we were connected or
955:     # got disconnected by a network error
956:     # if someone wants to manually call disconnect() _and_ reconnect(), they
957:     # will have to take care of the waiting themselves
958:     will_wait = !!@last_rec
959: 
960:     if @socket.connected?
961:       disconnect(message)
962:     end
963: 
964:     begin
965:       if will_wait
966:         log "\n\nDisconnected\n\n"
967: 
968:         quit if $interrupted > 0
969: 
970:         log "\n\nWaiting to reconnect\n\n"
971:         sleep @config['server.reconnect_wait']
972:         sleep 10*@config['server.reconnect_wait'] if too_fast
973:       end
974: 
975:       connect
976:     rescue Exception => e
977:       will_wait = true
978:       retry
979:     end
980:   end

[Source]

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

call the rescan method for all of the botmodules

[Source]

      # File lib/rbot/ircbot.rb, line 1314
1314:   def rescan
1315:     debug "\tstopping timer..."
1316:     @timer.stop
1317:     @save_mutex.synchronize do
1318:       @lang.rescan
1319:       @plugins.rescan
1320:     end
1321:     @timer.start
1322:   end

[Source]

     # File lib/rbot/ircbot.rb, line 889
889:   def reset_quiet(channel = nil)
890:     if channel
891:       ch = channel.downcase.dup
892:       @quiet.delete(ch)
893:       @not_quiet << ch
894:     else
895:       @quiet.clear
896:       @not_quiet.clear
897:     end
898:   end

totally shutdown and respawn the bot

[Source]

      # File lib/rbot/ircbot.rb, line 1283
1283:   def restart(message=nil)
1284:     message = _("restarting, back in %{wait}...") % {
1285:       :wait => @config['server.reconnect_wait']
1286:     } if (!message || message.empty?)
1287:     shutdown(message)
1288:     sleep @config['server.reconnect_wait']
1289:     begin
1290:       # now we re-exec
1291:       # Note, this fails on Windows
1292:       debug "going to exec #{$0} #{@argv.inspect} from #{@run_dir}"
1293:       log_session_end
1294:       Dir.chdir(@run_dir)
1295:       exec($0, *@argv)
1296:     rescue Errno::ENOENT
1297:       log_session_end
1298:       exec("ruby", *(@argv.unshift $0))
1299:     rescue Exception => e
1300:       $interrupted += 1
1301:       raise e
1302:     end
1303:   end

call the save method for all of the botmodules

[Source]

      # File lib/rbot/ircbot.rb, line 1306
1306:   def save
1307:     @save_mutex.synchronize do
1308:       @plugins.save
1309:       DBTree.cleanup_logs
1310:     end
1311:   end

say something (PRIVMSG) to channel/nick where

[Source]

      # File lib/rbot/ircbot.rb, line 1182
1182:   def say(where, message, options={})
1183:     return if where.kind_of?(Channel) and quiet_on?(where)
1184:     sendmsg "PRIVMSG", where, message, options
1185:   end
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.

[Source]

      # File lib/rbot/ircbot.rb, line 1045
1045:   def sendmsg(original_type, original_where, original_message, options={})
1046: 
1047:     # filter message with sendmsg filters
1048:     ds = DataStream.new original_message.to_s.dup,
1049:       :type => original_type, :dest => original_where,
1050:       :options => @default_send_options.merge(options)
1051:     filters = filter_names(:sendmsg)
1052:     filters.each do |fname|
1053:       debug "filtering #{ds[:text]} with sendmsg filter #{fname}"
1054:       ds.merge! filter(self.global_filter_name(fname, :sendmsg), ds)
1055:     end
1056: 
1057:     opts = ds[:options]
1058:     type = ds[:type]
1059:     where = ds[:dest]
1060:     filtered = ds[:text]
1061: 
1062:     # For starters, set up appropriate queue channels and rings
1063:     mchan = opts[:queue_channel]
1064:     mring = opts[:queue_ring]
1065:     if mchan
1066:       chan = mchan
1067:     else
1068:       chan = where
1069:     end
1070:     if mring
1071:       ring = mring
1072:     else
1073:       case where
1074:       when User
1075:         ring = 1
1076:       else
1077:         ring = 2
1078:       end
1079:     end
1080: 
1081:     multi_line = filtered.gsub(/[\r\n]+/, "\n")
1082: 
1083:     # if target is a channel with nocolor modes, strip colours
1084:     if where.kind_of?(Channel) and where.mode.any?(*config['server.nocolor_modes'])
1085:       multi_line.replace BasicUserMessage.strip_formatting(multi_line)
1086:     end
1087: 
1088:     messages = Array.new
1089:     case opts[:newlines]
1090:     when :join
1091:       messages << [multi_line.gsub("\n", opts[:join_with])]
1092:     when :split
1093:       multi_line.each_line { |line|
1094:         line.chomp!
1095:         next unless(line.size > 0)
1096:         messages << line
1097:       }
1098:     else
1099:       raise "Unknown :newlines option #{opts[:newlines]} while sending #{original_message.inspect}"
1100:     end
1101: 
1102:     # The IRC protocol requires that each raw message must be not longer
1103:     # than 512 characters. From this length with have to subtract the EOL
1104:     # terminators (CR+LF) and the length of ":botnick!botuser@bothost "
1105:     # that will be prepended by the server to all of our messages.
1106: 
1107:     # The maximum raw message length we can send is therefore 512 - 2 - 2
1108:     # minus the length of our hostmask.
1109: 
1110:     max_len = 508 - myself.fullform.size
1111: 
1112:     # On servers that support IDENTIFY-MSG, we have to subtract 1, because messages
1113:     # will have a + or - prepended
1114:     if server.capabilities["identify-msg""identify-msg"]
1115:       max_len -= 1
1116:     end
1117: 
1118:     # When splitting the message, we'll be prefixing the following string:
1119:     # (e.g. "PRIVMSG #rbot :")
1120:     fixed = "#{type} #{where} :"
1121: 
1122:     # And this is what's left
1123:     left = max_len - fixed.size
1124: 
1125:     truncate = opts[:truncate_text]
1126:     truncate = @default_send_options[:truncate_text] if truncate.size > left
1127:     truncate = "" if truncate.size > left
1128: 
1129:     all_lines = messages.map { |line|
1130:       if line.size < left
1131:         line
1132:       else
1133:         case opts[:overlong]
1134:         when :split
1135:           msg = line.dup
1136:           sub_lines = Array.new
1137:           begin
1138:             sub_lines << msg.slice!(0, left)
1139:             break if msg.empty?
1140:             lastspace = sub_lines.last.rindex(opts[:split_at])
1141:             if lastspace
1142:               msg.replace sub_lines.last.slice!(lastspace, sub_lines.last.size) + msg
1143:               msg.gsub!(/^#{opts[:split_at]}/, "") if opts[:purge_split]
1144:             end
1145:           end until msg.empty?
1146:           sub_lines
1147:         when :truncate
1148:           line.slice(0, left - truncate.size) << truncate
1149:         else
1150:           raise "Unknown :overlong option #{opts[:overlong]} while sending #{original_message.inspect}"
1151:         end
1152:       end
1153:     }.flatten
1154: 
1155:     if opts[:max_lines] > 0 and all_lines.length > opts[:max_lines]
1156:       lines = all_lines[0...opts[:max_lines]]
1157:       new_last = lines.last.slice(0, left - truncate.size) << truncate
1158:       lines.last.replace(new_last)
1159:     else
1160:       lines = all_lines
1161:     end
1162: 
1163:     lines.each { |line|
1164:       sendq "#{fixed}#{line}", chan, ring
1165:       delegate_sent(type, where, line)
1166:     }
1167:   end

queue an arbitraty message for the server

[Source]

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

server we are connected to TODO multiserver

[Source]

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

[Source]

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

[Source]

     # File lib/rbot/ircbot.rb, line 877
877:   def set_quiet(channel = nil)
878:     if channel
879:       ch = channel.downcase.dup
880:       @not_quiet.delete(ch)
881:       @quiet << ch
882:     else
883:       @quiet.clear
884:       @not_quiet.clear
885:       @quiet << '*'
886:     end
887:   end

[Source]

     # File lib/rbot/ircbot.rb, line 832
832:   def setup_plugins_path
833:     plugdir_default = File.join(Config::datadir, 'plugins')
834:     plugdir_local = File.join(@botclass, 'plugins')
835:     Dir.mkdir(plugdir_local) unless File.exist?(plugdir_local)
836: 
837:     @plugins.clear_botmodule_dirs
838:     @plugins.add_core_module_dir(File.join(Config::coredir, 'utils'))
839:     @plugins.add_core_module_dir(Config::coredir)
840:     if FileTest.directory? plugdir_local
841:       @plugins.add_plugin_dir(plugdir_local)
842:     else
843:       warning "local plugin location #{plugdir_local} is not a directory"
844:     end
845: 
846:     @config['plugins.path'].each do |_|
847:         path = _.sub(/^\(default\)/, plugdir_default)
848:         @plugins.add_plugin_dir(path)
849:     end
850:   end

disconnect from the server and cleanup all plugins and modules

[Source]

      # File lib/rbot/ircbot.rb, line 1241
1241:   def shutdown(message=nil)
1242:     @quit_mutex.synchronize do
1243:       debug "Shutting down: #{message}"
1244:       ## No we don't restore them ... let everything run through
1245:       # begin
1246:       #   trap("SIGINT", "DEFAULT")
1247:       #   trap("SIGTERM", "DEFAULT")
1248:       #   trap("SIGHUP", "DEFAULT")
1249:       # rescue => e
1250:       #   debug "failed to restore signals: #{e.inspect}\nProbably running on windows?"
1251:       # end
1252:       debug "\tdisconnecting..."
1253:       disconnect(message)
1254:       debug "\tstopping timer..."
1255:       @timer.stop
1256:       debug "\tsaving ..."
1257:       save
1258:       debug "\tcleaning up ..."
1259:       @save_mutex.synchronize do
1260:         @plugins.cleanup
1261:       end
1262:       # debug "\tstopping timers ..."
1263:       # @timer.stop
1264:       # debug "Closing registries"
1265:       # @registry.close
1266:       debug "\t\tcleaning up the db environment ..."
1267:       DBTree.cleanup_env
1268:       log "rbot quit (#{message})"
1269:     end
1270:   end

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

[Source]

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

[Source]

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

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

[Source]

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

trap signals

[Source]

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

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

[Source]

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

set the nick wanted by the bot

[Source]

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

asking whois

[Source]

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

Private Instance methods

delegate sent messages

[Source]

      # File lib/rbot/ircbot.rb, line 1423
1423:   def delegate_sent(type, where, message)
1424:     args = [self, server, myself, server.user_or_channel(where.to_s), message]
1425:     case type
1426:       when "NOTICE"
1427:         m = NoticeMessage.new(*args)
1428:       when "PRIVMSG"
1429:         m = PrivMessage.new(*args)
1430:       when "QUIT"
1431:         m = QuitMessage.new(*args)
1432:         m.was_on = myself.channels
1433:     end
1434:     @plugins.delegate('sent', m)
1435:   end

[Validate]