X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Frfc2812.rb;h=ac44888330d6b10726fa11be5a2eba04ff464d15;hb=2f2644f03203b36fd6aa105e62f4cd15070e1cbe;hp=9de781a8276845763e8b3a311d041ff7a686961b;hpb=922c6a35bb34598da397d2a0078adc397e84a853;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/rfc2812.rb b/lib/rbot/rfc2812.rb index 9de781a8..ac448883 100644 --- a/lib/rbot/rfc2812.rb +++ b/lib/rbot/rfc2812.rb @@ -4,7 +4,7 @@ # :title: RFC 2821 Client Protocol module # # This module defines the Irc::Client class, a class that can handle and -# dispatch messages based on RFC 2821i (Internet Relay Chat: Client Protocol) +# dispatch messages based on RFC 2821 (Internet Relay Chat: Client Protocol) module Irc # - The server sends Replies 001 to 004 to a user upon @@ -144,6 +144,12 @@ module Irc # " " RPL_CHANNELMODEIS=324 + # " " + RPL_CREATIONTIME=329 + + # " " + RPL_CHANNEL_URL=328 + # " :No topic is set" RPL_NOTOPIC=331 @@ -954,7 +960,7 @@ module Irc # Create a new Client instance def initialize @server = Server.new # The Server - @user = @server.user("") # The User representing the client on this Server + @user = @server.user("*!*@*") # The User representing the client on this Server @handlers = Hash.new @@ -966,7 +972,7 @@ module Irc # Clear the server and reset the user def reset @server.clear - @user = @server.user("") + @user = @server.user("*!*@*") end # key:: server event to handle @@ -1069,13 +1075,14 @@ module Irc num=command.to_i case num when RPL_WELCOME + data[:message] = argv[1] # "Welcome to the Internet Relay Network # !@" if not_us warning "Server thinks client (#{@user.inspect}) has a different nick" @user.nick = data[:target] end - if argv[1] =~ /([^@!\s]+)(?:!([^@!\s]+?))?@(\S+)/ + if data[:message] =~ /([^@!\s]+)(?:!([^@!\s]+?))?@(\S+)/ nick = $1 user = $2 host = $3 @@ -1121,19 +1128,14 @@ module Irc data[:message] = argv[2] handle(:badnick, data) when RPL_TOPIC - data[:channel] = @server.get_channel(argv[1]) + data[:channel] = @server.channel(argv[1]) data[:topic] = argv[2] - - if data[:channel] - data[:channel].topic.text = data[:topic] - else - warning "Received topic #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on" - end + data[:channel].topic.text = data[:topic] handle(:topic, data) when RPL_TOPIC_INFO data[:nick] = @server.user(argv[0]) - data[:channel] = @server.get_channel(argv[1]) + data[:channel] = @server.channel(argv[1]) # This must not be an IRC::User because it might not be an actual User, # and we risk overwriting valid User data @@ -1141,12 +1143,8 @@ module Irc data[:time] = Time.at(argv[3].to_i) - if data[:channel] - data[:channel].topic.set_by = data[:source] - data[:channel].topic.set_on = data[:time] - else - warning "Received topic #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on" - end + data[:channel].topic.set_by = data[:source] + data[:channel].topic.set_on = data[:time] handle(:topicinfo, data) when RPL_NAMREPLY @@ -1178,8 +1176,7 @@ module Irc chan.add_user(u, :silent => true) debug "Adding user #{u}" if ar[1] - m = @server.supports[:prefix][:prefixes].index(ar[1].to_sym) - ms = @server.supports[:prefix][:modes][m] + ms = @server.mode_for_prefix(ar[1].to_sym) debug "\twith mode #{ar[1]} (#{ms})" chan.mode[ms].set(u) end @@ -1285,7 +1282,21 @@ module Irc handle(:who, data) when RPL_ENDOFWHO handle(:eowho, data) + when RPL_CHANNELMODEIS + parse_mode(serverstring, argv[1..-1], data) + handle(:mode, data) + when RPL_CREATIONTIME + data[:channel] = argv[1] + data[:time] = Time.at(argv[2].to_i) + @server.get_channel(data[:channel]).creation_time=data[:time] + handle(:creationtime, data) + when RPL_CHANNEL_URL + data[:channel] = argv[1] + data[:url] = argv[2] + @server.get_channel(data[:channel]).url=data[:url].dup + handle(:channel_url, data) else + warning "Unknown message #{serverstring.inspect}" handle(:unknown, data) end return # We've processed the numeric reply @@ -1401,96 +1412,127 @@ module Irc handle(:nick, data) when :MODE - # MODE ([+-] ()*)* - # When a MODE message is received by a server, - # Type C will have parameters too, so we must - # be able to consume parameters for all - # but Type D modes - - data[:channel] = @server.user_or_channel(argv[0]) - data[:modestring] = argv[1..-1].join(" ") - 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 - # or an array with a flag which needs parameters - # and the corresponding parameter - data[:modes] = [] - # array of indices in data[:modes] where parameters - # are needed - who_wants_params = [] - - argv[1..-1].each { |arg| - setting = arg[0].chr - if "+-".include?(setting) - arg[1..-1].each_byte { |b| - m = b.chr - case m.to_sym + parse_mode(serverstring, argv, data) + handle(:mode, data) + else + warning "Unknown message #{serverstring.inspect}" + handle(:unknown, data) + end + end + + private + + # key:: server event name + # data:: hash containing data about the event, passed to the proc + # call client's proc for an event, if they set one as a handler + def handle(key, data) + if(@handlers.has_key?(key)) + @handlers[key].call(data) + end + end + + # RPL_CHANNELMODEIS + # MODE ([+-] ()*)* + # When a MODE message is received by a server, + # Type C will have parameters too, so we must + # be able to consume parameters for all + # but Type D modes + def parse_mode(serverstring, argv, data) + data[:target] = @server.user_or_channel(argv[0]) + data[:modestring] = argv[1..-1].join(" ") + # data[:modes] is an array where each element + # is an array with two elements, the first of which + # is either :set or :reset, and the second symbol + # is the mode letter. An optional third element + # is present e.g. for channel modes that need + # a parameter + data[:modes] = [] + case data[:target] + when User + # User modes aren't currently handled internally, + # but we still parse them and delegate to the client + warning "Unhandled user mode message '#{serverstring}'" + argv[1..-1].each { |arg| + setting = arg[0].chr + if "+-".include?(setting) + setting = setting == "+" ? :set : :reset + arg[1..-1].each_byte { |b| + m = b.chr.intern + data[:modes] << [setting, m] + } + else + # Although typically User modes don't take an argument, + # this is not true for all modes on all servers. Since + # we have no knowledge of which modes take parameters + # and which don't we just assign it to the last + # mode. This is not going to do strange things often, + # as usually User modes are only set one at a time + warning "Unhandled user mode parameter #{arg} found" + data[:modes].last << arg + end + } + else + # array of indices in data[:modes] where parameters + # are needed + who_wants_params = [] + + modes = argv[1..-1].dup + debug modes + getting_args = false + while arg = modes.shift + debug arg + if getting_args + # getting args for previously set modes + idx = who_wants_params.shift + if idx.nil? + warning "Oops, problems parsing #{serverstring.inspect}" + break + end + data[:modes][idx] << arg + getting_args = false if who_wants_params.empty? + else + debug @server.supports[:chanmodes] + setting = :set + arg.each_byte do |c| + m = c.chr.intern + case m + when :+ + setting = :set + when :- + setting = :reset + else + data[:modes] << [setting, m] + case m when *@server.supports[:chanmodes][:typea] - data[:modes] << [setting + m] who_wants_params << data[:modes].length - 1 when *@server.supports[:chanmodes][:typeb] - data[:modes] << [setting + m] who_wants_params << data[:modes].length - 1 when *@server.supports[:chanmodes][:typec] - if setting == "+" - data[:modes] << [setting + m] + if setting == :set who_wants_params << data[:modes].length - 1 - else - data[:modes] << setting + m end when *@server.supports[:chanmodes][:typed] - data[:modes] << setting + m + # Nothing to do when *@server.supports[:prefix][:modes] - data[:modes] << [setting + m] who_wants_params << data[:modes].length - 1 else - warning "Unknown mode #{m} in #{serverstring.inspect}" + warning "Ignoring unknown mode #{m} in #{serverstring.inspect}" + data[:modes].pop end - } - else - idx = who_wants_params.shift - if idx.nil? - warning "Oops, problems parsing #{serverstring.inspect}" - break end - data[:modes][idx] << arg end - } + getting_args = true unless who_wants_params.empty? + end end data[:modes].each { |mode| - case mode - when Array - set = mode[0][0].chr == "+" ? :set : :reset - key = mode[0][1].chr.to_sym - val = mode[1] - data[:channel].mode[key].send(set, val) + set, key, val = mode + if val + data[:target].mode[key].send(set, val) else - set = mode[0].chr == "+" ? :set : :reset - key = mode[1].chr.to_sym - data[:channel].mode[key].send(set) + data[:target].mode[key].send(set) end - } if data[:modes] - - handle(:mode, data) - else - warning "Unknown message #{serverstring.inspect}" - handle(:unknown, data) - end - end - - private - - # key:: server event name - # data:: hash containing data about the event, passed to the proc - # call client's proc for an event, if they set one as a handler - def handle(key, data) - if(@handlers.has_key?(key)) - @handlers[key].call(data) + } end end end