]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/rfc2812.rb
message: add #thanks method, similar to okay
[user/henk/code/ruby/rbot.git] / lib / rbot / rfc2812.rb
index d781c0f1f7e9bb0a805e081d4c5a5954e1cd4eb1..0839d1d52593db514d4458a92c19b57c4c6c7b1a 100644 (file)
@@ -6,6 +6,9 @@
 # This module defines the Irc::Client class, a class that can handle and
 # dispatch messages based on RFC 2821 (Internet Relay Chat: Client Protocol)
 
+class ServerMessageParseError < ServerError
+end
+
 module Irc
   # - The server sends Replies 001 to 004 to a user upon
   #   successful registration.
@@ -946,6 +949,9 @@ module Irc
   ERR_NOSERVICEHOST=492
   RPL_DATASTR=290
 
+  # A structure to hold LIST data, in the Irc namespace
+  ListData = Struct.new :channel, :users, :topic
+
   # Implements RFC 2812 and prior IRC RFCs.
   #
   # Clients should register Proc{}s to handle the various server events, and
@@ -967,6 +973,9 @@ module Irc
       # This is used by some messages to build lists of users that
       # will be delegated when the ENDOF... message is received
       @tmpusers = []
+
+      # Same as above, just for bans
+      @tmpbans = []
     end
 
     # Clear the server and reset the user
@@ -981,7 +990,6 @@ module Irc
     #
     # ==server events currently supported:
     #
-    # TODO handle errors ERR_NOSUCHNICK, ERR_NOSUCHCHANNEL
     # TODO handle errors ERR_CHANOPRIVSNEEDED, ERR_CANNOTSENDTOCHAN
     #
     # welcome::     server welcome message on connect
@@ -1028,7 +1036,7 @@ module Irc
       data[:serverstring] = serverstring
 
       unless serverstring.chomp =~ /^(:(\S+)\s)?(\S+)(\s(.*))?$/
-        raise "Unparseable Server Message!!!: #{serverstring.inspect}"
+        raise ServerMessageParseError, (serverstring.chomp rescue serverstring)
       end
 
       prefix, command, params = $2, $3, $5
@@ -1153,13 +1161,7 @@ module Irc
           # - "@" is used for secret channels, "*" for private
           # channels, and "=" for others (public channels).
           data[:channeltype] = argv[1]
-          data[:channel] = argv[2]
-
-          chan = @server.get_channel(data[:channel])
-          unless chan
-            warning "Received names #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
-            return
-          end
+          data[:channel] = chan = @server.channel(argv[2])
 
           users = []
           argv[3].scan(/\S+/).each { |u|
@@ -1183,10 +1185,21 @@ module Irc
           }
           @tmpusers += users
         when RPL_ENDOFNAMES
-          data[:channel] = argv[1]
+          data[:channel] = @server.channel(argv[1])
           data[:users] = @tmpusers
           handle(:names, data)
           @tmpusers = Array.new
+        when RPL_BANLIST
+          data[:channel] = @server.channel(argv[1])
+          data[:mask] = argv[2]
+          data[:by] = argv[3]
+          data[:at] = argv[4]
+          @tmpbans << data
+        when RPL_ENDOFBANLIST
+          data[:channel] = @server.channel(argv[1])
+          data[:bans] = @tmpbans
+          handle(:banlist, data)
+          @tmpbans = Array.new
         when RPL_LUSERCLIENT
           # ":There are <integer> users and <integer>
           # services on <integer> servers"
@@ -1245,17 +1258,16 @@ module Irc
           data[:text] = argv[1]
           handle(:datastr, data)
         when RPL_AWAY
-          data[:nick] = argv[1]
+          data[:nick] = user = @server.user(argv[1])
           data[:message] = argv[-1]
-          user = @server.get_user(data[:nick])
           user.away = data[:message]
           handle(:away, data)
        when RPL_WHOREPLY
-          data[:channel] = argv[1]
+          data[:channel] = channel = @server.channel(argv[1])
           data[:user] = argv[2]
           data[:host] = argv[3]
           data[:userserver] = argv[4]
-          data[:nick] = argv[5]
+          data[:nick] = user = @server.user(argv[5])
           if argv[6] =~ /^(H|G)(\*)?(.*)?$/
             data[:away] = ($1 == 'G')
             data[:ircop] = $2
@@ -1268,8 +1280,6 @@ module Irc
           end
           data[:hopcount], data[:real_name] = argv[7].split(" ", 2)
 
-          user = @server.get_user(data[:nick])
-
           user.user = data[:user]
           user.host = data[:host]
           user.away = data[:away] # FIXME doesn't provide the actual message
@@ -1278,8 +1288,6 @@ module Irc
           # TODO hopcount
           user.real_name = data[:real_name]
 
-          channel = @server.get_channel(data[:channel])
-
           channel.add_user(user, :silent=>true)
           data[:modes].map { |mode|
             channel.mode[mode].set(user)
@@ -1295,7 +1303,7 @@ module Irc
           @whois[:host] = argv[3]
           @whois[:real_name] = argv[-1]
 
-          user = @server.get_user(@whois[:nick])
+          user = @server.user(@whois[:nick])
           user.user = @whois[:user]
           user.host = @whois[:host]
           user.real_name = @whois[:real_name]
@@ -1313,7 +1321,7 @@ module Irc
         when RPL_WHOISIDLE
           @whois ||= Hash.new
           @whois[:nick] = argv[1]
-          user = @server.get_user(@whois[:nick])
+          user = @server.user(@whois[:nick])
           @whois[:idle] = argv[2].to_i
           user.idle_since = Time.now - @whois[:idle]
           if argv[-1] == 'seconds idle, signon time'
@@ -1329,34 +1337,58 @@ module Irc
         when RPL_WHOISCHANNELS
           @whois ||= Hash.new
           @whois[:nick] = argv[1]
-          @whois[:channels] = []
-          user = @server.get_user(@whois[:nick])
+          @whois[:channels] ||= []
+          user = @server.user(@whois[:nick])
           argv[-1].split.each do |prechan|
             pfx = prechan.scan(/[#{@server.supports[:prefix][:prefixes].join}]/)
             modes = pfx.map { |p| @server.mode_for_prefix p }
             chan = prechan[pfx.length..prechan.length]
 
-            channel = @server.get_channel(chan)
-            if channel
-              channel.add_user(user, :silent => true)
-              modes.map { |mode| channel.mode[mode].set(user) }
-            end
+            channel = @server.channel(chan)
+            channel.add_user(user, :silent => true)
+            modes.map { |mode| channel.mode[mode].set(user) }
 
             @whois[:channels] << [chan, modes]
           end
+        when RPL_LISTSTART
+          # ignore
+        when RPL_LIST
+          @list ||= Hash.new
+          chan = argv[1]
+          users = argv[2]
+          topic = argv[3]
+          @list[chan] = ListData.new(chan, users, topic)
+        when RPL_LISTEND
+          @list ||= Hash.new
+          data[:list] = @list
+          handle(:list, data)
         when RPL_CHANNELMODEIS
           parse_mode(serverstring, argv[1..-1], data)
           handle(:mode, data)
         when RPL_CREATIONTIME
-          data[:channel] = argv[1]
+          data[:channel] = @server.channel(argv[1])
           data[:time] = Time.at(argv[2].to_i)
-          @server.get_channel(data[:channel]).creation_time=data[:time]
+          data[:channel].creation_time=data[:time]
           handle(:creationtime, data)
         when RPL_CHANNEL_URL
-          data[:channel] = argv[1]
+          data[:channel] = @server.channel(argv[1])
           data[:url] = argv[2]
-          @server.get_channel(data[:channel]).url=data[:url].dup
+          data[:channel].url=data[:url].dup
           handle(:channel_url, data)
+        when ERR_NOSUCHNICK
+          data[:target] = argv[1]
+          data[:message] = argv[2]
+          handle(:nosuchtarget, data)
+          if user = @server.get_user(data[:target])
+            @server.delete_user(user)
+          end
+        when ERR_NOSUCHCHANNEL
+          data[:target] = argv[1]
+          data[:message] = argv[2]
+          handle(:nosuchtarget, data)
+          if channel = @server.get_channel(data[:target])
+            @server.delete_channel(channel)
+          end
         else
           warning "Unknown message #{serverstring.inspect}"
           handle(:unknown, data)
@@ -1476,6 +1508,9 @@ module Irc
       when :MODE
         parse_mode(serverstring, argv, data)
         handle(:mode, data)
+      when :ERROR
+        data[:message] = argv[1]
+        handle(:error, data)
       else
         warning "Unknown message #{serverstring.inspect}"
         handle(:unknown, data)
@@ -1533,7 +1568,7 @@ module Irc
             data[:modes].last << arg
           end
         }
-      else
+      when Channel
         # array of indices in data[:modes] where parameters
         # are needed
         who_wants_params = []
@@ -1586,6 +1621,10 @@ module Irc
             getting_args = true unless who_wants_params.empty?
           end
         end
+        unless who_wants_params.empty?
+          warning "Unhandled malformed modeline #{data[:modestring]} (unexpected empty arguments)"
+          return
+        end
 
         data[:modes].each { |mode|
           set, key, val = mode
@@ -1595,6 +1634,8 @@ module Irc
             data[:target].mode[key].send(set)
           end
         }
+      else
+        warning "Ignoring #{data[:modestring]} for unrecognized target #{argv[0]} (#{data[:target].inspect})"
       end
     end
   end