X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Frfc2812.rb;h=0839d1d52593db514d4458a92c19b57c4c6c7b1a;hb=fa639cb4885f63e887493afbd4e0dbacbe4a0e99;hp=378036de36be4db44cbea1ed3a61c01c49fae820;hpb=d5b21e2be33f7d8a3bb471f294a2c88410c84c5f;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/rfc2812.rb b/lib/rbot/rfc2812.rb index 378036de..0839d1d5 100644 --- a/lib/rbot/rfc2812.rb +++ b/lib/rbot/rfc2812.rb @@ -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. @@ -144,6 +147,12 @@ module Irc # " " RPL_CHANNELMODEIS=324 + # " " + RPL_CREATIONTIME=329 + + # " " + RPL_CHANNEL_URL=328 + # " :No topic is set" RPL_NOTOPIC=331 @@ -940,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 @@ -961,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 @@ -975,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 @@ -1022,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 @@ -1147,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| @@ -1177,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 users and # services on servers" @@ -1238,12 +1257,17 @@ module Irc when RPL_DATASTR data[:text] = argv[1] handle(:datastr, data) + when RPL_AWAY + data[:nick] = user = @server.user(argv[1]) + data[:message] = argv[-1] + 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 @@ -1256,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 @@ -1266,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) @@ -1276,9 +1296,99 @@ module Irc handle(:who, data) when RPL_ENDOFWHO handle(:eowho, data) + when RPL_WHOISUSER + @whois ||= Hash.new + @whois[:nick] = argv[1] + @whois[:user] = argv[2] + @whois[:host] = argv[3] + @whois[:real_name] = argv[-1] + + user = @server.user(@whois[:nick]) + user.user = @whois[:user] + user.host = @whois[:host] + user.real_name = @whois[:real_name] + when RPL_WHOISSERVER + @whois ||= Hash.new + @whois[:nick] = argv[1] + @whois[:server] = argv[2] + @whois[:server_info] = argv[-1] + # TODO update user info + when RPL_WHOISOPERATOR + @whois ||= Hash.new + @whois[:nick] = argv[1] + @whois[:operator] = argv[-1] + # TODO update user info + when RPL_WHOISIDLE + @whois ||= Hash.new + @whois[:nick] = argv[1] + 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' + @whois[:signon] = Time.at(argv[3].to_i) + user.signon = @whois[:signon] + end + when RPL_ENDOFWHOIS + @whois ||= Hash.new + @whois[:nick] = argv[1] + data[:whois] = @whois.dup + @whois.clear + handle(:whois, data) + when RPL_WHOISCHANNELS + @whois ||= Hash.new + @whois[:nick] = argv[1] + @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.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] = @server.channel(argv[1]) + data[:time] = Time.at(argv[2].to_i) + data[:channel].creation_time=data[:time] + handle(:creationtime, data) + when RPL_CHANNEL_URL + data[:channel] = @server.channel(argv[1]) + data[:url] = argv[2] + 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) @@ -1398,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) @@ -1455,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 = [] @@ -1508,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 @@ -1517,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