X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;ds=sidebyside;f=lib%2Frbot%2Fircbot.rb;h=c8b0d46f0ee4aa7ea4ac15317e66d54171f428d9;hb=c9ada317c9bd32828765fc4ed25aeaf2c00ac18f;hp=e503924817f66abf8406e37c224a8660f1b01461;hpb=601a5f7f8b817f331c54165b20ed6482618b1c62;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/ircbot.rb b/lib/rbot/ircbot.rb index e5039248..c8b0d46f 100644 --- a/lib/rbot/ircbot.rb +++ b/lib/rbot/ircbot.rb @@ -10,9 +10,26 @@ $daemonize = false unless $daemonize $dateformat = "%Y/%m/%d %H:%M:%S" $logger = Logger.new($stderr) $logger.datetime_format = $dateformat -$logger.level = $cl_loglevel if $cl_loglevel +$logger.level = $cl_loglevel if defined? $cl_loglevel $logger.level = 0 if $debug +require 'pp' + +unless Kernel.instance_methods.include?("pretty_inspect") + def pretty_inspect + PP.pp(self, '') + end + public :pretty_inspect +end + +class Exception + def pretty_print(q) + q.group(1, "#<%s: %s" % [self.class, self.message], ">") { + q.seplist(self.backtrace, lambda { "\n" }) { |v| v } if self.backtrace + } + end +end + def rawlog(level, message=nil, who_pos=1) call_stack = caller if call_stack.length > who_pos @@ -23,7 +40,14 @@ def rawlog(level, message=nil, who_pos=1) # Output each line. To distinguish between separate messages and multi-line # messages originating at the same time, we blank #{who} after the first message # is output. - message.to_s.each_line { |l| + # Also, we output strings as-is but for other objects we use pretty_inspect + case message + when String + str = message + else + str = message.pretty_inspect + end + str.each_line { |l| $logger.add(level, l.chomp, who) who.gsub!(/./," ") } @@ -66,6 +90,8 @@ fatal "fatal test" # The following global is used for the improved signal handling. $interrupted = 0 +require 'rbot/load-gettext' + # these first require 'rbot/rbotconfig' require 'rbot/config' @@ -193,7 +219,7 @@ class Bot :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'") BotConfig.register BotConfigArrayValue.new('irc.ignore_users', - :default => [], + :default => [], :desc => "Which users to ignore input from. This is mainly to avoid bot-wars triggered by creative people") BotConfig.register BotConfigIntegerValue.new('core.save_every', @@ -257,7 +283,7 @@ class Bot }, :desc => "String used to replace newlines when send.newlines is set to join") BotConfig.register BotConfigIntegerValue.new('send.max_lines', - :default => 0, + :default => 5, :validate => Proc.new { |v| v >= 0 }, :on_change => Proc.new { |bot, v| bot.set_default_send_options :max_lines => v @@ -289,6 +315,7 @@ class Bot :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") @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?" @@ -343,9 +370,8 @@ class Bot begin @config = BotConfig.configmanager @config.bot_associate(self) - rescue => e - fatal e.inspect - fatal e.backtrace.join("\n") + rescue Exception => e + fatal e log_session_end exit 2 end @@ -360,7 +386,7 @@ class Bot end # See http://blog.humlab.umu.se/samuel/archives/000107.html - # for the backgrounding code + # for the backgrounding code if $daemonize begin exit if fork @@ -368,8 +394,10 @@ class Bot exit if fork rescue NotImplementedError warning "Could not background, fork not supported" - rescue => e - warning "Could not background. #{e.inspect}" + 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. @@ -398,11 +426,15 @@ class Bot $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 $cl_loglevel + $logger.level = $cl_loglevel if defined? $cl_loglevel $logger.level = 0 if $debug log_session_start + File.open($opts['pidfile'] || "#{@botclass}/rbot.pid", 'w') do |pf| + pf << "#{$$}\n" + end + @registry = BotRegistry.new self @timer = Timer::Timer.new(1.0) # only need per-second granularity @@ -423,9 +455,8 @@ class Bot @auth = Auth::authmanager @auth.bot_associate(self) # @auth.load("#{botclass}/botusers.yaml") - rescue => e - fatal e.inspect - fatal e.backtrace.join("\n") + rescue Exception => e + fatal e log_session_end exit 2 end @@ -526,7 +557,7 @@ class Bot } } @client[:nicktaken] = proc { |data| - new = "#{data[:nick]}_" + 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 @@ -538,7 +569,7 @@ class Bot @plugins.delegate "nicktaken", data[:nick] } @client[:badnick] = proc {|data| - arning "bad nick (#{data[:nick]})" + warning "bad nick (#{data[:nick]})" } @client[:ping] = proc {|data| sendq "PONG #{data[:pingid]}" @@ -581,6 +612,7 @@ class Bot @plugins.delegate("listen", m) @plugins.delegate("join", m) + sendq "WHO #{data[:channel]}", data[:channel], 2 } @client[:part] = proc {|data| m = PartMessage.new(self, server, data[:source], data[:channel], data[:message]) @@ -711,9 +743,9 @@ class Bot trap("SIGTERM") { got_sig("SIGTERM") } trap("SIGHUP") { got_sig("SIGHUP") } rescue ArgumentError => e - debug "failed to trap signals (#{e.inspect}): running on Windows?" - rescue => e - debug "failed to trap signals: #{e.inspect}" + debug "failed to trap signals (#{e.pretty_inspect}): running on Windows?" + rescue Exception => e + debug "failed to trap signals: #{e.pretty_inspect}" end begin quit if $interrupted > 0 @@ -724,7 +756,7 @@ class Bot quit if $interrupted > 0 realname = @config['irc.name'].clone || 'Ruby bot' - realname << ' ' + COPYRIGHT_NOTICE if @config['irc.name_copyright'] + 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}" @@ -765,24 +797,20 @@ class Bot log_session_end exit 0 rescue Errno::ETIMEDOUT, Errno::ECONNABORTED, TimeoutError, SocketError => e - error "network exception: #{e.class}: #{e}" - debug e.backtrace.join("\n") + error "network exception: #{e.pretty_inspect}" quit_msg = e.to_s rescue BDB::Fatal => e - fatal "fatal bdb error: #{e.class}: #{e}" - fatal e.backtrace.join("\n") + 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.class}: #{e}" - error e.backtrace.join("\n") + error "non-net exception: #{e.pretty_inspect}" quit_msg = e.to_s rescue => e - fatal "unexpected exception: #{e.class}: #{e}" - fatal e.backtrace.join("\n") + fatal "unexpected exception: #{e.pretty_inspect}" log_session_end exit 2 end @@ -879,12 +907,13 @@ class Bot 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 while msg.size > 0 + end until msg.empty? sub_lines when :truncate line.slice(0, left - truncate.size) << truncate @@ -896,8 +925,8 @@ class Bot if opts[:max_lines] > 0 and all_lines.length > opts[:max_lines] lines = all_lines[0...opts[:max_lines]] - lines.last.slice!(0, left - truncate.size) - lines.last << truncate + new_last = lines.last.slice(0, left - truncate.size) << truncate + lines.last.replace(new_last) else lines = all_lines end @@ -1007,7 +1036,7 @@ class Bot # disconnect from the server and cleanup all plugins and modules def shutdown(message = nil) @quit_mutex.synchronize do - debug "Shutting down ..." + debug "Shutting down:" ## No we don't restore them ... let everything run through # begin # trap("SIGINT", "DEFAULT") @@ -1016,16 +1045,19 @@ class Bot # rescue => e # debug "failed to restore signals: #{e.inspect}\nProbably running on windows?" # end + debug "\tdisconnecting..." disconnect - debug "Saving" + debug "\tsaving ..." save - debug "Cleaning up" + debug "\tcleaning up ..." @save_mutex.synchronize do @plugins.cleanup end + debug "\tstopping timers ..." + @timer.stop # debug "Closing registries" # @registry.close - debug "Cleaning up the db environment" + debug "\t\tcleaning up the db environment ..." DBTree.cleanup_env log "rbot quit (#{message})" end @@ -1046,9 +1078,18 @@ class Bot msg = message ? message : "restarting, back in #{@config['server.reconnect_wait']}..." shutdown(msg) sleep @config['server.reconnect_wait'] - # now we re-exec - # Note, this fails on Windows - exec($0, *@argv) + begin + # now we re-exec + # Note, this fails on Windows + debug "going to exec #{$0} #{@argv.inspect} from #{@run_dir}" + Dir.chdir(@run_dir) + exec($0, *@argv) + rescue Errno::ENOENT + exec("ruby", *(@argv.unshift $0)) + rescue Exception => e + $interrupted += 1 + raise e + end end # call the save method for all of the botmodules