]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/ircbot.rb
requested for certain networks
[user/henk/code/ruby/rbot.git] / lib / rbot / ircbot.rb
index 3881771d37a659d190020642ccd52eb90b8c1f8b..271d0d70ea358d9590872fed932c377bfa37b119 100644 (file)
@@ -5,7 +5,8 @@ require 'fileutils'
 $debug = false unless $debug
 # print +message+ if debugging is enabled
 def debug(message=nil)
-  print "DEBUG: #{message}\n" if($debug && message)
+  stamp = Time.now.strftime("%Y/%m/%d %H:%M:%S")
+  print "D: [#{stamp}] #{message}\n" if($debug && message)
   #yield
 end
 
@@ -51,9 +52,6 @@ class IrcBot
   # bot's Language data
   attr_reader :lang
 
-  # bot's configured addressing prefixes
-  attr_reader :addressing_prefixes
-
   # channel info for channels the bot is in
   attr_reader :channels
 
@@ -124,8 +122,8 @@ class IrcBot
       exit 2
     end
     
-    #botclass = "#{Etc.getpwnam(Etc.getlogin).dir}/.rbot" unless botclass
-    botclass = "#{ENV['HOME']}/.rbot" unless botclass
+    botclass = "#{Etc.getpwuid(Process::Sys.geteuid)[:dir]}/.rbot" unless botclass
+    #botclass = "#{ENV['HOME']}/.rbot" unless botclass
     @botclass = botclass.gsub(/\/$/, "")
 
     unless FileTest.directory? botclass
@@ -140,7 +138,8 @@ class IrcBot
     Dir.mkdir("#{botclass}/logs") unless File.exist?("#{botclass}/logs")
 
     @ping_timer = nil
-    @timeout_timer = nil
+    @pong_timer = nil
+    @last_ping = nil
     @startup_time = Time.new
     @config = BotConfig.new(self)
 # TODO background self after botconfig has a chance to run wizard
@@ -186,9 +185,7 @@ class IrcBot
       @socket.puts "PONG #{data[:pingid]}"
     }
     @client[:pong] = proc {|data|
-      # cancel the timeout timer
-      debug "got a pong from the server, cancelling timeout"
-      remove_timeout_timer
+      @last_ping = nil
     }
     @client[:nick] = proc {|data|
       sourcenick = data[:sourcenick]
@@ -305,22 +302,22 @@ class IrcBot
     }
     @client[:unknown] = proc {|data|
       #debug "UNKNOWN: #{data[:serverstring]}"
-      log data[:serverstring], ":unknown"
+      log data[:serverstring], ".unknown"
     }
   end
 
   # connect the bot to IRC
   def connect
     begin
+      trap("SIGINT") { quit }
       trap("SIGTERM") { quit }
       trap("SIGHUP") { quit }
-      trap("SIGINT") { quit }
     rescue
       debug "failed to trap signals, probably running on windows?"
     end
     begin
       @socket.connect
-      rescue => e
+    rescue => e
       raise "failed to connect to IRC server at #{@config['server.name']} #{@config['server.port']}: " + e
     end
     @socket.puts "PASS " + @config['server.password'] if @config['server.password']
@@ -331,10 +328,10 @@ class IrcBot
   # begin event handling loop
   def mainloop
     while true
+      begin
       connect
       @timer.start
       
-      begin
         while true
           if @socket.select
             break unless reply = @socket.gets
@@ -345,8 +342,10 @@ class IrcBot
       # exceptions that ARENT SocketError's. How am I supposed to handle
       # that?
       #rescue TimeoutError, SocketError => e
+      rescue SystemExit
+        exit 0
       rescue Exception => e
-        puts "network exception: connection closed: #{e}"
+        puts "network exception: connection closed: #{e.inspect}"
         puts e.backtrace.join("\n")
         @socket.shutdown # now we reconnect
       rescue => e
@@ -356,6 +355,7 @@ class IrcBot
       end
       
       puts "disconnected"
+      @last_ping = nil
       @channels.clear
       @socket.clearq
       
@@ -439,6 +439,7 @@ class IrcBot
   def log(message, where="server")
     message.chomp!
     stamp = Time.now.strftime("%Y/%m/%d %H:%M:%S")
+    where.gsub!(/[:!?$*()\/\\<>|"']/, "_")
     unless(@logs.has_key?(where))
       @logs[where] = File.new("#{@botclass}/logs/#{where}", "a")
       @logs[where].sync = true
@@ -454,9 +455,13 @@ class IrcBot
 
   # disconnect from the server and cleanup all plugins and modules
   def shutdown(message = nil)
-    trap("SIGTERM", "DEFAULT")
-    trap("SIGHUP", "DEFAULT")
-    trap("SIGINT", "DEFAULT")
+    begin
+      trap("SIGINT", "DEFAULT")
+      trap("SIGTERM", "DEFAULT")
+      trap("SIGHUP", "DEFAULT")
+    rescue
+      debug "failed to trap signals, probably running on windows?"
+    end
     message = @lang.get("quit") if (message.nil? || message.empty?)
     @socket.clearq
     save
@@ -464,18 +469,21 @@ class IrcBot
     @channels.each_value {|v|
       log "@ quit (#{message})", v.name
     }
+    @registry.close
     @socket.puts "QUIT :#{message}"
     @socket.flush
     @socket.shutdown
-    @registry.close
     puts "rbot quit (#{message})"
   end
   
   # message:: optional IRC quit message
   # quit IRC, shutdown the bot
   def quit(message=nil)
-    shutdown(message)
-    exit 0
+    begin
+      shutdown(message)
+    ensure
+      exit 0
+    end
   end
 
   # totally shutdown and respawn the bot
@@ -569,39 +577,42 @@ class IrcBot
   # we'll ping the server every 30 seconds or so, and expect a response
   # before the next one come around..
   def start_server_pings
+    @last_ping = nil
     # stop existing timers if running
     unless @ping_timer.nil?
       @timer.remove @ping_timer
       @ping_timer = nil
     end
-    remove_timeout_timer
-    timeout = @config['server.ping_timeout']
-    return unless timeout > 0
-    timeout = 30 if timeout > 30
+    unless @pong_timer.nil?
+      @timer.remove @pong_timer
+      @pong_timer = nil
+    end
+    return unless @config['server.ping_timeout'] > 0
     # we want to respond to a hung server within 30 secs or so
     @ping_timer = @timer.add(30) {
-      remove_timeout_timer
+      @last_ping = Time.now
       @socket.puts "PING :rbot"
-      @timeout_timer = @timer.add_once(timeout) {
-        debug "no PONG from server for #{timeout} seconds, reconnecting"
-        begin
-          @socket.shutdown
-        rescue
-          debug "couldn't shutdown connection (already shutdown?)"
+    }
+    @pong_timer = @timer.add(10) {
+      unless @last_ping.nil?
+        diff = Time.now - @last_ping
+        unless diff < @config['server.ping_timeout']
+          debug "no PONG from server for #{diff} seconds, reconnecting"
+          begin
+            @socket.shutdown
+            # TODO
+            # raise an exception to get back to the mainloop
+          rescue
+            debug "couldn't shutdown connection (already shutdown?)"
+          end
+          @last_ping = nil
         end
-      } if @config['server.ping_timeout']
+      end
     }
   end
 
   private
 
-  def remove_timeout_timer
-    unless @timeout_timer.nil?
-      @timer.remove(@timeout_timer)
-      @timeout_timer = nil
-    end
-  end
-
   # handle help requests for "core" topics
   def corehelp(topic="")
     case topic