X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Frfc2812.rb;h=4a095dc8dac0b0dc0bfb95d812f702b53fedfd5a;hb=edd1cf77be07ae507014574141e920ad23eb164d;hp=c8e2e410c8b5cbdb6ac175b47a32721a090588ea;hpb=5cc6ece3d483db28f92f82a78b926ba6ce62769d;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/rfc2812.rb b/lib/rbot/rfc2812.rb index c8e2e410..4a095dc8 100644 --- a/lib/rbot/rfc2812.rb +++ b/lib/rbot/rfc2812.rb @@ -820,8 +820,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 @@ -836,6 +836,9 @@ 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 # yourhost:: your host details (on connection) # created:: when the server was started @@ -886,16 +889,27 @@ module Irc prefix, command, params = $2, $3, $5 if prefix != nil - data[:source] = prefix - if prefix =~ /^(\S+)!(\S+)$/ - data[:source] = @server.user($1) + # Most servers will send a full nick!user@host prefix for + # messages from users. Therefore, when the prefix doesn't match this + # syntax it's usually the server hostname. + # + # This is not always true, though, since some servers do not send a + # full hostmask for user messages. + # + if prefix =~ /^(?:\S+)(?:!\S+)?@(?:\S+)$/ + data[:source] = @server.user(prefix) else - if @server.hostname && @server.hostname != data[:source] - warning "Unknown origin #{data[:source]} for message\n#{serverstring.inspect}" + if @server.hostname + if @server.hostname != prefix + debug "Origin #{prefix} for message\n\t#{serverstring.inspect}\nis neither a user hostmask nor the server hostname, assuming it's a nick" + data[:source] = @server.user(prefix) + else + data[:source] = @server + end else @server.instance_variable_set(:@hostname, data[:source]) + data[:source] = @server end - data[:source] = @server end end @@ -903,36 +917,36 @@ module Irc argv = [] params.scan(/(?!:)(\S+)|:(.*)/) { argv << ($1 || $2) } if params - case command - when 'PING' - data[:pingid] = argv[0] - handle(:ping, data) - when 'PONG' - data[:pingid] = argv[0] - handle(:pong, data) - when /^(\d+)$/ # numerical server message + if command =~ /^(\d+)$/ # Numeric replies + data[:target] = argv[0] + # A numeric reply /should/ be directed at the client, except when we're connecting with a used nick, in which case + # it's directed at '*' + not_us = !([@client.nick, '*'].include?(data[:target])) + if not_us + warning "Server reply #{serverstring.inspect} directed at #{data[:target]} instead of client (#{@client.nick})" + end + num=command.to_i case num when RPL_WELCOME # "Welcome to the Internet Relay Network # !@" - case argv[1] - when /((\S+)!(\S+))/ - data[:netmask] = $1 - data[:nick] = $2 - data[:address] = $3 - @client = @server.user(data[:netmask]) - when /Welcome to the Internet Relay Network\s(\S+)/ - data[:nick] = $1 - when /Welcome.*\s+(\S+)$/ - data[:nick] = $1 - when /^(\S+)$/ - data[:nick] = $1 + if not_us + warning "Server thinks client (#{@client.inspect}) has a different nick" + @client.nick = data[:target] + end + if argv[1] =~ /(\S+)(?:!(\S+?))?@(\S+)/ + nick = $1 + user = $2 + host = $2 + warning "Welcome message nick mismatch (#{nick} vs #{data[:target]})" if nick != data[:target] + @client.user = user if user + @client.host = host if host end - @user ||= @server.user(data[:nick]) handle(:welcome, data) when RPL_YOURHOST # "Your host is , running version " + data[:message] = argv[1] handle(:yourhost, data) when RPL_CREATED # "This server was created " @@ -954,7 +968,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 # "* :Nickname is already in use" @@ -1001,13 +1015,14 @@ 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| - if(u =~ /^(#{@server.supports[:prefix][:prefixes].join})?(.*)$/) + # FIXME beware of servers that allow multiple prefixes + if(u =~ /^([#{@server.supports[:prefix][:prefixes].join}])?(.*)$/) umode = $1 user = $2 users << [user, umode] @@ -1016,11 +1031,13 @@ module Irc users.each { |ar| u = @server.user(ar[0]) - chan.users << u + chan.add_user(u, :silent => true) + debug "Adding user #{u}" if ar[1] - m = @server.supports[:prefix][:prefixes].index(ar[1]) - m = @server.supports[:prefix][:modes][m] - chan.mode[m.to_sym].set(u) + m = @server.supports[:prefix][:prefixes].index(ar[1].to_sym) + ms = @server.supports[:prefix][:modes][m] + debug "\twith mode #{ar[1]} (#{ms})" + chan.mode[ms].set(u) end } @tmpusers += users @@ -1087,33 +1104,59 @@ module Irc else handle(:unknown, data) end - # end of numeric replies - when 'PRIVMSG' + return # We've processed the numeric reply + end + + # Otherwise, the command should be a single word + case command.to_sym + when :PING + data[:pingid] = argv[0] + handle(:ping, data) + when :PONG + data[:pingid] = argv[0] + handle(:pong, data) + when :PRIVMSG # you can either bind to 'PRIVMSG', to get every one and # 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. $). In any of these cases, we just use the + # String as a target + # FIXME we probably want to explicitly check for the # $ + 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]) + when :NOTICE + 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. $). In any of these cases, we just use the + # String as a target + # FIXME we probably want to explicitly check for the # $ + 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' + when :KICK data[:channel] = @server.channel(argv[0]) data[:target] = @server.user(argv[1]) data[:message] = argv[2] @@ -1124,7 +1167,7 @@ module Irc end handle(:kick, data) - when 'PART' + when :PART data[:channel] = @server.channel(argv[0]) data[:message] = argv[1] @@ -1134,43 +1177,46 @@ module Irc end handle(:part, data) - when 'QUIT' + when :QUIT data[:message] = argv[0] data[:was_on] = @server.channels.inject(ChannelList.new) { |list, ch| - list << ch if ch.users.include?(data[:source]) + list << ch if ch.has_user?(data[:source]) + list } @server.delete_user(data[:source]) handle(:quit, data) - when 'JOIN' + when :JOIN data[:channel] = @server.channel(argv[0]) - data[:channel].users << data[:source] + data[:channel].add_user(data[:source]) handle(:join, data) - when 'TOPIC' + 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' + when :INVITE data[:target] = @server.user(argv[0]) data[:channel] = @server.channel(argv[1]) handle(:invite, data) - when 'NICK' + when :NICK data[:is_on] = @server.channels.inject(ChannelList.new) { |list, ch| - list << ch if ch.users.include?(data[:source]) + list << ch if ch.has_user?(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) - when 'MODE' + when :MODE # MODE ([+-] ()*)* # When a MODE message is received by a server, # Type C will have parameters too, so we must @@ -1182,6 +1228,7 @@ module Irc case data[:channel] when User # TODO + warning "Unhandled user mode message '#{serverstring}'" else # data[:modes] is an array where each element # is either a flag which doesn't need parameters @@ -1195,7 +1242,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] @@ -1216,13 +1264,13 @@ module Irc data[:modes] << [setting + m] who_wants_params << data[:modes].length - 1 else - warn "Unknown mode #{m} in #{serverstring}" + warning "Unknown mode #{m} in #{serverstring.inspect}" end } else idx = who_wants_params.shift if idx.nil? - warn "Oops, problems parsing #{serverstring}" + warning "Oops, problems parsing #{serverstring.inspect}" break end data[:modes][idx] << arg @@ -1246,6 +1294,7 @@ module Irc handle(:mode, data) else + warning "Unknown message #{serverstring.inspect}" handle(:unknown, data) end end