]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/rfc2812.rb
Penalty-based flood protection
[user/henk/code/ruby/rbot.git] / lib / rbot / rfc2812.rb
index c8e2e410c8b5cbdb6ac175b47a32721a090588ea..9fa81cb5f9eb5d5fe54caa8897c290a52cd9d7eb 100644 (file)
@@ -1,3 +1,67 @@
+class ::String
+  # Calculate the penalty which will be assigned to this message
+  # by the IRCd
+  def irc_send_penalty
+    # According to eggrdop, the initial penalty is
+    penalty = 1 + self.length/100
+    # on everything but UnderNET where it's
+    # penalty = 2 + self.length/120
+
+    cmd, pars = self.split($;,2)
+    debug "cmd: #{cmd}, pars: #{pars.inspect}"
+    case cmd.to_sym
+    when :KICK
+      chan, nick, msg = pars.split
+      chan = chan.split(',')
+      nick = nick.split(',')
+      penalty += nick.length
+      penalty *= chan.length
+    when :MODE
+      chan, modes, argument = pars.split
+      extra = 0
+      if modes
+        extra = 1
+        if argument
+          extra += modes.split(/\+|-/).length
+        else
+          extra += 3 * modes.split(/\+|-/).length
+        end
+      end
+      if argument
+        extra += 2 * argument.split.length
+      end
+      penalty += extra * chan.split.length
+    when :TOPIC
+      penalty += 1
+      penalty += 2 unless pars.split.length < 2
+    when :PRIVMSG, :NOTICE
+      dests = pars.split($;,2).first
+      penalty += dests.split(',').length
+    when :WHO
+      # I'm too lazy to implement this one correctly
+      penalty += 5
+    when :AWAY, :JOIN, :VERSION, :TIME, :TRACE, :WHOIS, :DNS
+      penalty += 2
+    when :INVITE, :NICK
+      penalty += 3
+    when :ISON
+      penalty += 1
+    else # Unknown messages
+      penalty += 1
+    end
+    if penalty > 99
+      debug "Wow, more than 99 secs of penalty!"
+      penalty = 99
+    end
+    if penalty < 2
+      debug "Wow, less than 2 secs of penalty!"
+      penalty = 2
+    end
+    debug "penalty: #{penalty}"
+    return penalty
+  end
+end
+
 module Irc
   # RFC 2812   Internet Relay Chat: Client Protocol
   #
@@ -820,8 +884,8 @@ module Irc
 
     # create a new IrcClient instance
     def initialize
-      @server = Server.new      # The Server
-      @client = User.new        # The User representing the client on this Server
+      @server = Server.new         # The Server
+      @client = @server.user("")   # The User representing the client on this Server
 
       @handlers = Hash.new
 
@@ -888,7 +952,7 @@ module Irc
       if prefix != nil
         data[:source] = prefix
         if prefix =~ /^(\S+)!(\S+)$/
-          data[:source] = @server.user($1)
+          data[:source] = @server.user(prefix)
         else
           if @server.hostname && @server.hostname != data[:source]
             warning "Unknown origin #{data[:source]} for message\n#{serverstring.inspect}"
@@ -922,6 +986,7 @@ module Irc
             data[:nick] = $2
             data[:address] = $3
             @client = @server.user(data[:netmask])
+            set = true
           when /Welcome to the Internet Relay Network\s(\S+)/
             data[:nick] = $1
           when /Welcome.*\s+(\S+)$/
@@ -929,7 +994,7 @@ module Irc
           when /^(\S+)$/
             data[:nick] = $1
           end
-          @user ||= @server.user(data[:nick])
+          @client = @server.user(data[:nick]) unless set
           handle(:welcome, data)
         when RPL_YOURHOST
           # "Your host is <servername>, running version <ver>"
@@ -954,7 +1019,7 @@ module Irc
           # PREFIX=(ov)@+ CASEMAPPING=ascii CAPAB IRCD=dancer :are available
           # on this server"
           #
-          @server.parse_isupport(params.split(' ', 2).last)
+          @server.parse_isupport(argv[1..-2].join(' '))
           handle(:isupport, data)
         when ERR_NICKNAMEINUSE
           # "* <nick> :Nickname is already in use"
@@ -1001,12 +1066,13 @@ module Irc
 
           chan = @server.get_channel(data[:channel])
           unless chan
-            warning "Received topic #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
+            warning "Received names #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
             return
           end
 
           users = []
           argv[3].scan(/\S+/).each { |u|
+            # FIXME beware of servers that allow multiple prefixes
             if(u =~ /^(#{@server.supports[:prefix][:prefixes].join})?(.*)$/)
               umode = $1
               user = $2
@@ -1016,9 +1082,9 @@ module Irc
 
           users.each { |ar|
             u = @server.user(ar[0])
-            chan.users << u
+            chan.users << u unless chan.users.include?(u)
             if ar[1]
-              m = @server.supports[:prefix][:prefixes].index(ar[1])
+              m = @server.supports[:prefix][:prefixes].index(ar[1].to_sym)
               m = @server.supports[:prefix][:modes][m]
               chan.mode[m.to_sym].set(u)
             end
@@ -1093,24 +1159,40 @@ module Irc
         # parse it yourself, or you can bind to 'MSG', 'PUBLIC',
         # etc and get it all nicely split up for you.
 
-        data[:target] = @server.user_or_channel(argv[0])
+        begin
+          data[:target] = @server.user_or_channel(argv[0])
+        rescue
+          # The previous may fail e.g. when the target is a server or something
+          # like that (e.g. $<mask>). In any of these cases, we just use the
+          # String as a target
+          # FIXME we probably want to explicitly check for the #<mask> $<mask>
+          data[:target] = argv[0]
+        end
         data[:message] = argv[1]
         handle(:privmsg, data)
 
         # Now we split it
-        if(data[:target].class <= Channel)
+        if data[:target].kind_of?(Channel)
           handle(:public, data)
         else
           handle(:msg, data)
         end
       when 'NOTICE'
-        data[:target] = @server.user_or_channel(argv[0])
+        begin
+          data[:target] = @server.user_or_channel(argv[0])
+        rescue
+          # The previous may fail e.g. when the target is a server or something
+          # like that (e.g. $<mask>). In any of these cases, we just use the
+          # String as a target
+          # FIXME we probably want to explicitly check for the #<mask> $<mask>
+          data[:target] = argv[0]
+        end
         data[:message] = argv[1]
         case data[:source]
         when User
           handle(:notice, data)
         else
-          # "server notice" (not from user, noone to reply to
+          # "server notice" (not from user, noone to reply to)
           handle(:snotice, data)
         end
       when 'KICK'
@@ -1138,6 +1220,7 @@ module Irc
         data[:message] = argv[0]
         data[:was_on] = @server.channels.inject(ChannelList.new) { |list, ch|
           list << ch if ch.users.include?(data[:source])
+         list
         }
 
         @server.delete_user(data[:source])
@@ -1150,8 +1233,8 @@ module Irc
         handle(:join, data)
       when 'TOPIC'
         data[:channel] = @server.channel(argv[0])
-        data[:topic] = ChannelTopic.new(argv[1], data[:source], Time.new)
-        data[:channel].topic = data[:topic]
+        data[:topic] = Channel::Topic.new(argv[1], data[:source], Time.new)
+        data[:channel].topic.replace(data[:topic])
 
         handle(:changetopic, data)
       when 'INVITE'
@@ -1162,11 +1245,13 @@ module Irc
       when 'NICK'
         data[:is_on] = @server.channels.inject(ChannelList.new) { |list, ch|
           list << ch if ch.users.include?(data[:source])
+          list
         }
 
         data[:newnick] = argv[0]
         data[:oldnick] = data[:source].nick.dup
-        data[:source].nick = data[:nick]
+        data[:source].nick = data[:newnick]
+
         debug "#{data[:oldnick]} (now #{data[:newnick]}) was on #{data[:is_on].join(', ')}"
 
         handle(:nick, data)
@@ -1195,7 +1280,8 @@ module Irc
           argv[1..-1].each { |arg|
             setting = arg[0].chr
             if "+-".include?(setting)
-              arg[1..-1].each_byte { |m|
+              arg[1..-1].each_byte { |b|
+                m = b.chr
                 case m.to_sym
                 when *@server.supports[:chanmodes][:typea]
                   data[:modes] << [setting + m]