]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/ircbot.rb
Penalty-based flood protection
[user/henk/code/ruby/rbot.git] / lib / rbot / ircbot.rb
index df0659b59ad4537e65addbe6faaa1d8acc121d38..4bf3e8d3fa78837c787a78f1511d45d651e77209 100644 (file)
@@ -73,9 +73,7 @@ require 'rbot/utils'
 
 require 'rbot/irc'
 require 'rbot/rfc2812'
-require 'rbot/keywords'
 require 'rbot/ircsocket'
-# require 'rbot/auth'
 require 'rbot/botuser'
 require 'rbot/timer'
 require 'rbot/plugins'
@@ -175,10 +173,6 @@ class IrcBot
       :default => 4, :validate => Proc.new{|v| v >= 0},
       :desc => "(flood prevention) max lines to burst to the server before throttling. Most ircd's allow bursts of up 5 lines",
       :on_change => Proc.new {|bot, v| bot.socket.sendq_burst = v })
-    BotConfig.register BotConfigStringValue.new('server.byterate',
-      :default => "400/2", :validate => Proc.new{|v| v.match(/\d+\/\d/)},
-      :desc => "(flood prevention) max bytes/seconds rate to send the server. Most ircd's have limits of 512 bytes/2 seconds",
-      :on_change => Proc.new {|bot, v| bot.socket.byterate = v })
     BotConfig.register BotConfigIntegerValue.new('server.ping_timeout',
       :default => 30, :validate => Proc.new{|v| v >= 0},
       :on_change => Proc.new {|bot, v| bot.start_server_pings},
@@ -268,6 +262,8 @@ class IrcBot
 
     Dir.mkdir("#{botclass}/logs") unless File.exist?("#{botclass}/logs")
     Dir.mkdir("#{botclass}/registry") unless File.exist?("#{botclass}/registry")
+    Dir.mkdir("#{botclass}/safe_save") unless File.exist?("#{botclass}/safe_save")
+    Utils.set_safe_save_dir("#{botclass}/safe_save")
 
     @ping_timer = nil
     @pong_timer = nil
@@ -342,14 +338,14 @@ class IrcBot
     @timer = Timer::Timer.new(1.0) # only need per-second granularity
     @save_mutex = Mutex.new
     @timer.add(@config['core.save_every']) { save } if @config['core.save_every']
+    @quit_mutex = Mutex.new
 
     @logs = Hash.new
 
     @httputil = Utils::HttpUtil.new(self)
 
-    @lang = Language::Language.new(@config['core.language'])
-
-    # @keywords = Keywords.new(self)
+    @plugins = nil
+    @lang = Language::Language.new(self, @config['core.language'])
 
     begin
       @auth = Auth::authmanager
@@ -411,8 +407,9 @@ class IrcBot
     }
     @client[:privmsg] = proc { |data|
       m = PrivMessage.new(self, server, data[:source], data[:target], data[:message])
-      debug "Message target is #{data[:target].inspect}"
-      debug "Bot is #{myself.inspect}"
+      # debug "Message source is #{data[:source].inspect}"
+      # debug "Message target is #{data[:target].inspect}"
+      # debug "Bot is #{myself.inspect}"
 
       # TODO use the new Netmask class
       # @config['irc.ignore_users'].each { |mask| return if Irc.netmaskmatch(mask,m.source) }
@@ -553,14 +550,12 @@ class IrcBot
   def got_sig(sig)
     debug "received #{sig}, queueing quit"
     $interrupted += 1
+    quit unless @quit_mutex.locked?
     debug "interrupted #{$interrupted} times"
-    if $interrupted >= 5
+    if $interrupted >= 3
       debug "drastic!"
       log_session_end
       exit 2
-    elsif $interrupted >= 3
-      debug "quitting"
-      quit
     end
   end
 
@@ -581,8 +576,10 @@ class IrcBot
     rescue => e
       raise e.class, "failed to connect to IRC server at #{@config['server.name']} #{@config['server.port']}: " + e
     end
+    quit if $interrupted > 0
     @socket.emergency_puts "PASS " + @config['server.password'] if @config['server.password']
     @socket.emergency_puts "NICK #{@config['irc.nick']}\nUSER #{@config['irc.user']} 4 #{@config['server.name']} :Ruby bot. (c) Tom Gilbert"
+    quit if $interrupted > 0
     start_server_pings
   end
 
@@ -595,11 +592,11 @@ class IrcBot
         @timer.start
 
         while @socket.connected?
+         quit if $interrupted > 0
           if @socket.select
             break unless reply = @socket.gets
             @client.process reply
           end
-         quit if $interrupted > 0
         end
 
       # I despair of this. Some of my users get "connection reset by peer"
@@ -608,7 +605,7 @@ class IrcBot
       rescue SystemExit
         log_session_end
         exit 0
-      rescue Errno::ETIMEDOUT, TimeoutError, SocketError => e
+      rescue Errno::ETIMEDOUT, Errno::ECONNABORTED, TimeoutError, SocketError => e
         error "network exception: #{e.class}: #{e}"
         debug e.backtrace.join("\n")
       rescue BDB::Fatal => e
@@ -653,12 +650,10 @@ class IrcBot
   # relevant say() or notice() methods. This one should be used for IRCd
   # extensions you want to use in modules.
   def sendmsg(type, where, message, chan=nil, ring=0)
-    # limit it according to the byterate, splitting the message
-    # taking into consideration the actual message length
-    # and all the extra stuff
+    # Split the message so that each line sent is not longher than 510 bytes
     # TODO allow something to do for commands that produce too many messages
     # TODO example: math 10**10000
-    left = @socket.bytes_per - type.length - where.to_s.length - 4
+    left = 510 - type.length - where.to_s.length - 3
     begin
       if(left >= message.length)
         sendq "#{type} #{where} :#{message}", chan, ring
@@ -784,39 +779,43 @@ class IrcBot
 
   # disconnect from the server and cleanup all plugins and modules
   def shutdown(message = nil)
-    debug "Shutting down ..."
-    ## 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
-    message = @lang.get("quit") if (message.nil? || message.empty?)
-    if @socket.connected?
-      debug "Clearing socket"
-      @socket.clearq
-      debug "Sending quit message"
-      @socket.emergency_puts "QUIT :#{message}"
-      debug "Flushing socket"
-      @socket.flush
-      debug "Shutting down socket"
-      @socket.shutdown
+    @quit_mutex.synchronize do
+      debug "Shutting down ..."
+      ## 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
+      message = @lang.get("quit") if (message.nil? || message.empty?)
+      if @socket.connected?
+        debug "Clearing socket"
+        @socket.clearq
+        debug "Sending quit message"
+        @socket.emergency_puts "QUIT :#{message}"
+        debug "Flushing socket"
+        @socket.flush
+        debug "Shutting down socket"
+        @socket.shutdown
+      end
+      debug "Logging quits"
+      server.channels.each { |ch|
+        irclog "@ quit (#{message})", ch
+      }
+      debug "Saving"
+      save
+      debug "Cleaning up"
+      @save_mutex.synchronize do
+        @plugins.cleanup
+      end
+      # debug "Closing registries"
+      # @registry.close
+      debug "Cleaning up the db environment"
+      DBTree.cleanup_env
+      log "rbot quit (#{message})"
     end
-    debug "Logging quits"
-    server.channels.each { |ch|
-      irclog "@ quit (#{message})", ch
-    }
-    debug "Saving"
-    save
-    debug "Cleaning up"
-    @plugins.cleanup
-    # debug "Closing registries"
-    # @registry.close
-    debug "Cleaning up the db environment"
-    DBTree.cleanup_env
-    log "rbot quit (#{message})"
   end
 
   # message:: optional IRC quit message
@@ -839,22 +838,20 @@ class IrcBot
     exec($0, *@argv)
   end
 
-  # call the save method for bot's config, keywords, auth and all plugins
+  # call the save method for all of the botmodules
   def save
     @save_mutex.synchronize do
-      # @config.save
-      # @keywords.save
-      # @auth.save
       @plugins.save
       DBTree.cleanup_logs
     end
   end
 
-  # call the rescan method for the bot's lang, keywords and all plugins
+  # call the rescan method for all of the botmodules
   def rescan
-    @lang.rescan
-    @plugins.rescan
-    # @keywords.rescan
+    @save_mutex.synchronize do
+      @lang.rescan
+      @plugins.rescan
+    end
   end
 
   # channel:: channel to join
@@ -883,6 +880,11 @@ class IrcBot
       sendq "MODE #{channel} #{mode} #{target}", channel, 2
   end
 
+  # kicking a user
+  def kick(channel, user, msg)
+      sendq "KICK #{channel} #{user} :#{msg}", channel, 2
+  end
+
   # m::     message asking for help
   # topic:: optional topic help is requested for
   # respond to online help requests