From 83fd5d3b11539a07b740048ad93c09e31e8d6701 Mon Sep 17 00:00:00 2001 From: Tom Gilbert Date: Thu, 4 Aug 2005 22:44:35 +0000 Subject: [PATCH] Thu Aug 04 23:03:30 BST 2005 Tom Gilbert * Improved ircd recognition of rfc2812.rb * de-string'd, de-cap'd rfc2812.rb, looks less shouty now * moved the Q auth stuff (for quakenet) into a new qauth plugin (untested!) * finish fixing the httputil --- ChangeLog | 7 + data/rbot/plugins/quakeauth.rb | 51 +++++++ lib/rbot/config.rb | 2 +- lib/rbot/httputil.rb | 1 + lib/rbot/ircbot.rb | 125 ++++++++-------- lib/rbot/plugins.rb | 3 + lib/rbot/rfc2812.rb | 265 +++++++++++++++++++++------------ 7 files changed, 296 insertions(+), 158 deletions(-) create mode 100644 data/rbot/plugins/quakeauth.rb diff --git a/ChangeLog b/ChangeLog index 92396298..bdd6c10d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Thu Aug 04 23:03:30 BST 2005 Tom Gilbert + + * Improved ircd recognition of rfc2812.rb + * de-string'd, de-cap'd rfc2812.rb, looks less shouty now + * moved the Q auth stuff (for quakenet) into a new qauth plugin (untested!) + * finish fixing the httputil + Thu Aug 04 00:11:52 BST 2005 Tom Gilbert * Tweaked the debug() stuff a bit. Need to do this more cleanly really diff --git a/data/rbot/plugins/quakeauth.rb b/data/rbot/plugins/quakeauth.rb new file mode 100644 index 00000000..8f412f1d --- /dev/null +++ b/data/rbot/plugins/quakeauth.rb @@ -0,0 +1,51 @@ +# automatically auths with Q on quakenet servers + +class QPlugin < Plugin + + def help(plugin, topic="") + case topic + when "" + return "quath plugin: handles Q auths. topics set, identify" + when "set" + return "nickserv set : set the Q user and password and use it to identify in future" + when "identify" + return "quath identify: identify with Q (if user and auth are set)" + end + end + + def initialize + super + # this plugin only wants to store strings! + class << @registry + def store(val) + val + end + def restore(val) + val + end + end + end + + def set(m, params) + @registry['quakenet.user'] = params[:user] + @registry['quakenet.auth'] = params[:passwd] + m.okay + end + + def connect + identify(nil, nil) + end + def identify(m, params) + if @registry.has_key?('quakenet.user') && @registry.has_key?('quakenet.auth') + debug "authing with Q using #{@registry['quakenet.user']} #{@registry['quakenet.auth']}" + @bot.sendmsg "PRIVMSG", "Q@CServe.quakenet.org", "auth #{@registry['quakenet.user']} #{@registry['quakenet.auth']}" + m.okay if m + else + m.reply "not configured, try 'qauth set :nick :passwd'" if m + end + end + +end +plugin = QPlugin.new +plugin.map 'qauth set :nick :passwd', :action => "set" +plugin.map 'quath identify', :action => "identify" diff --git a/lib/rbot/config.rb b/lib/rbot/config.rb index e4237e81..957c63a3 100644 --- a/lib/rbot/config.rb +++ b/lib/rbot/config.rb @@ -134,7 +134,7 @@ module Irc string end def desc - "#{@desc} [valid values are: " + @values.join(", ") + "]" + "#{@desc} [valid values are: " + values.join(", ") + "]" end end diff --git a/lib/rbot/httputil.rb b/lib/rbot/httputil.rb index e234dc10..b4f25c95 100644 --- a/lib/rbot/httputil.rb +++ b/lib/rbot/httputil.rb @@ -23,6 +23,7 @@ class HttpUtil :default => [], :desc => "List of regexps to check against a URI's hostname/ip to see if we should use the proxy to access this URI. All URIs are proxied by default if the proxy is set, so this is only required to re-include URIs that might have been excluded by the exclude list. e.g. exclude /.*\.foo\.com/, include bar\.foo\.com") BotConfig.register BotConfigArrayValue.new('http.proxy_exclude', + :default => [], :desc => "List of regexps to check against a URI's hostname/ip to see if we should use avoid the proxy to access this URI and access it directly") def initialize(bot) diff --git a/lib/rbot/ircbot.rb b/lib/rbot/ircbot.rb index f99dd940..24ee6de3 100644 --- a/lib/rbot/ircbot.rb +++ b/lib/rbot/ircbot.rb @@ -159,35 +159,36 @@ class IrcBot end @client = IrcClient.new - @client["PRIVMSG"] = proc { |data| - message = PrivMessage.new(self, data["SOURCE"], data["TARGET"], data["MESSAGE"]) + @client[:privmsg] = proc { |data| + message = PrivMessage.new(self, data[:source], data[:target], data[:message]) onprivmsg(message) } - @client["NOTICE"] = proc { |data| - message = NoticeMessage.new(self, data["SOURCE"], data["TARGET"], data["MESSAGE"]) + @client[:notice] = proc { |data| + message = NoticeMessage.new(self, data[:source], data[:target], data[:message]) # pass it off to plugins that want to hear everything @plugins.delegate "listen", message } - @client["MOTD"] = proc { |data| - data['MOTD'].each_line { |line| + @client[:motd] = proc { |data| + data[:motd].each_line { |line| log "MOTD: #{line}", "server" } } - @client["NICKTAKEN"] = proc { |data| - nickchg "#{@nick}_" + @client[:nicktaken] = proc { |data| + nickchg "#{data[:nick]}_" } - @client["BADNICK"] = proc {|data| - puts "WARNING, bad nick (#{data['NICK']})" + @client[:badnick] = proc {|data| + puts "WARNING, bad nick (#{data[:nick]})" } - @client["PING"] = proc {|data| + @client[:ping] = proc {|data| # (jump the queue for pongs) - @socket.puts "PONG #{data['PINGID']}" + @socket.puts "PONG #{data[:pingid]}" } - @client["NICK"] = proc {|data| - sourcenick = data["SOURCENICK"] - nick = data["NICK"] - m = NickMessage.new(self, data["SOURCE"], data["SOURCENICK"], data["NICK"]) + @client[:nick] = proc {|data| + sourcenick = data[:sourcenick] + nick = data[:nick] + m = NickMessage.new(self, data[:source], data[:sourcenick], data[:nick]) if(sourcenick == @nick) + debug "my nick is now #{nick}" @nick = nick end @channels.each {|k,v| @@ -200,13 +201,13 @@ class IrcBot @plugins.delegate("listen", m) @plugins.delegate("nick", m) } - @client["QUIT"] = proc {|data| - source = data["SOURCE"] - sourcenick = data["SOURCENICK"] - sourceurl = data["SOURCEADDRESS"] - message = data["MESSAGE"] - m = QuitMessage.new(self, data["SOURCE"], data["SOURCENICK"], data["MESSAGE"]) - if(data["SOURCENICK"] =~ /#{@nick}/i) + @client[:quit] = proc {|data| + source = data[:source] + sourcenick = data[:sourcenick] + sourceurl = data[:sourceaddress] + message = data[:message] + m = QuitMessage.new(self, data[:source], data[:sourcenick], data[:message]) + if(data[:sourcenick] =~ /#{@nick}/i) else @channels.each {|k,v| if(v.users.has_key?(sourcenick)) @@ -218,27 +219,24 @@ class IrcBot @plugins.delegate("listen", m) @plugins.delegate("quit", m) } - @client["MODE"] = proc {|data| - source = data["SOURCE"] - sourcenick = data["SOURCENICK"] - sourceurl = data["SOURCEADDRESS"] - channel = data["CHANNEL"] - targets = data["TARGETS"] - modestring = data["MODESTRING"] + @client[:mode] = proc {|data| + source = data[:source] + sourcenick = data[:sourcenick] + sourceurl = data[:sourceaddress] + channel = data[:channel] + targets = data[:targets] + modestring = data[:modestring] log "@ Mode #{modestring} #{targets} by #{sourcenick}", channel } - @client["WELCOME"] = proc {|data| - log "joined server #{data['SOURCE']} as #{data['NICK']}", "server" - debug "I think my nick is #{@nick}, server thinks #{data['NICK']}" - if data['NICK'] && data['NICK'].length > 0 - @nick = data['NICK'] - end - if(@config['irc.quser']) - # TODO move this to a plugin - debug "authing with Q using #{@config['quakenet.user']} #{@config['quakenet.auth']}" - @socket.puts "PRIVMSG Q@CServe.quakenet.org :auth #{@config['quakenet.user']} #{@config['quakenet.auth']}" + @client[:welcome] = proc {|data| + log "joined server #{data[:source]} as #{data[:nick]}", "server" + debug "I think my nick is #{@nick}, server thinks #{data[:nick]}" + if data[:nick] && data[:nick].length > 0 + @nick = data[:nick] end + @plugins.delegate("connect") + @config['irc.join_channels'].each {|c| debug "autojoining channel #{c}" if(c =~ /^(\S+)\s+(\S+)$/i) @@ -248,47 +246,47 @@ class IrcBot end } } - @client["JOIN"] = proc {|data| - m = JoinMessage.new(self, data["SOURCE"], data["CHANNEL"], data["MESSAGE"]) + @client[:join] = proc {|data| + m = JoinMessage.new(self, data[:source], data[:channel], data[:message]) onjoin(m) } - @client["PART"] = proc {|data| - m = PartMessage.new(self, data["SOURCE"], data["CHANNEL"], data["MESSAGE"]) + @client[:part] = proc {|data| + m = PartMessage.new(self, data[:source], data[:channel], data[:message]) onpart(m) } - @client["KICK"] = proc {|data| - m = KickMessage.new(self, data["SOURCE"], data["TARGET"],data["CHANNEL"],data["MESSAGE"]) + @client[:kick] = proc {|data| + m = KickMessage.new(self, data[:source], data[:target],data[:channel],data[:message]) onkick(m) } - @client["INVITE"] = proc {|data| - if(data["TARGET"] =~ /^#{@nick}$/i) - join data["CHANNEL"] if (@auth.allow?("join", data["SOURCE"], data["SOURCENICK"])) + @client[:invite] = proc {|data| + if(data[:target] =~ /^#{@nick}$/i) + join data[:channel] if (@auth.allow?("join", data[:source], data[:sourcenick])) end } - @client["CHANGETOPIC"] = proc {|data| - channel = data["CHANNEL"] - sourcenick = data["SOURCENICK"] - topic = data["TOPIC"] - timestamp = data["UNIXTIME"] || Time.now.to_i + @client[:changetopic] = proc {|data| + channel = data[:channel] + sourcenick = data[:sourcenick] + topic = data[:topic] + timestamp = data[:unixtime] || Time.now.to_i if(sourcenick == @nick) log "@ I set topic \"#{topic}\"", channel else log "@ #{sourcenick} set topic \"#{topic}\"", channel end - m = TopicMessage.new(self, data["SOURCE"], data["CHANNEL"], timestamp, data["TOPIC"]) + m = TopicMessage.new(self, data[:source], data[:channel], timestamp, data[:topic]) ontopic(m) @plugins.delegate("listen", m) @plugins.delegate("topic", m) } - @client["TOPIC"] = @client["TOPICINFO"] = proc {|data| - channel = data["CHANNEL"] - m = TopicMessage.new(self, data["SOURCE"], data["CHANNEL"], data["UNIXTIME"], data["TOPIC"]) + @client[:topic] = @client[:topicinfo] = proc {|data| + channel = data[:channel] + m = TopicMessage.new(self, data[:source], data[:channel], data[:unixtime], data[:topic]) ontopic(m) } - @client["NAMES"] = proc {|data| - channel = data["CHANNEL"] - users = data["USERS"] + @client[:names] = proc {|data| + channel = data[:channel] + users = data[:users] unless(@channels[channel]) puts "bug: got names for channel '#{channel}' I didn't think I was in\n" exit 2 @@ -298,8 +296,9 @@ class IrcBot @channels[channel].users[u[0].sub(/^[@&~+]/, '')] = ["mode", u[1]] } } - @client["UNKNOWN"] = proc {|data| - debug "UNKNOWN: #{data['SERVERSTRING']}" + @client[:unknown] = proc {|data| + #debug "UNKNOWN: #{data[:serverstring]}" + log data[:serverstring], ":unknown" } end diff --git a/lib/rbot/plugins.rb b/lib/rbot/plugins.rb index 4e618f61..bffba227 100644 --- a/lib/rbot/plugins.rb +++ b/lib/rbot/plugins.rb @@ -78,6 +78,9 @@ module Plugins # topic(TopicMessage):: # Called when a user (or the bot) changes a channel # topic + # + # connect():: Called when a server is joined successfully, but + # before autojoin channels are joined (no params) # # save:: Called when you are required to save your plugin's # state, if you maintain data between sessions diff --git a/lib/rbot/rfc2812.rb b/lib/rbot/rfc2812.rb index 6f459c80..df9b9bb6 100644 --- a/lib/rbot/rfc2812.rb +++ b/lib/rbot/rfc2812.rb @@ -15,8 +15,10 @@ module Irc # - The server sends Replies 001 to 004 to a user upon # successful registration. # - RPL_BOUNCE=005 - # "Try server , port " + # RPL_BOUNCE=005 + # # "Try server , port " + RPL_ISUPPORT=005 + # "005 nick PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server" # # - Sent by the server to a user to suggest an alternative # server. This is often used when the connection is @@ -354,6 +356,13 @@ module Irc # being displayed anyway. # RPL_TRACEEND is sent to indicate the end of the list. # + RPL_LOCALUSERS=265 + # ":Current local users: 3 Max: 4" + RPL_GLOBALUSERS=266 + # ":Current global users: 3 Max: 4" + RPL_STATSCONN=250 + # "::Highest connection count: 4 (4 clients) (251 since server was + # (re)started)" RPL_STATSLINKINFO=211 # " # @@ -799,7 +808,6 @@ module Irc RPL_STATSSLINE=244 RPL_STATSPING=246 RPL_STATSBLINE=247 - RPL_STATSDLINE=250 ERR_NOSERVICEHOST=492 # implements RFC 2812 and prior IRC RFCs. @@ -818,29 +826,31 @@ module Irc # # ==server events currently supported: # - # PING:: server pings you (default handler returns a pong) - # NICKTAKEN:: you tried to change nick to one that's in use - # BADNICK:: you tried to change nick to one that's invalid - # TOPIC:: someone changed the topic of a channel - # TOPICINFO:: on joining a channel or asking for the topic, tells you - # who set it and when - # NAMES:: server sends list of channel members when you join - # WELCOME:: server welcome message on connect - # MOTD:: server message of the day - # PRIVMSG:: privmsg, the core of IRC, a message to you from someone - # PUBLIC:: optionally instead of getting privmsg you can hook to only - # the public ones... - # MSG:: or only the private ones, or both - # KICK:: someone got kicked from a channel - # PART:: someone left a channel - # QUIT:: someone quit IRC - # JOIN:: someone joined a channel - # CHANGETOPIC:: the topic of a channel changed - # INVITE:: you are invited to a channel - # NICK:: someone changed their nick - # MODE:: a mode change - # NOTICE:: someone sends you a notice - # UNKNOWN:: any other message not handled by the above + # :created when the server was started + # :yourhost your host details (on connection) + # :ping:: server pings you (default handler returns a pong) + # :nicktaken:: you tried to change nick to one that's in use + # :badnick:: you tried to change nick to one that's invalid + # :topic:: someone changed the topic of a channel + # :topicinfo:: on joining a channel or asking for the topic, tells you + # who set it and when + # :names:: server sends list of channel members when you join + # :welcome:: server welcome message on connect + # :motd:: server message of the day + # :privmsg:: privmsg, the core of IRC, a message to you from someone + # :public:: optionally instead of getting privmsg you can hook to only + # the public ones... + # :msg:: or only the private ones, or both + # :kick:: someone got kicked from a channel + # :part:: someone left a channel + # :quit:: someone quit IRC + # :join:: someone joined a channel + # :changetopic:: the topic of a channel changed + # :invite:: you are invited to a channel + # :nick:: someone changed their nick + # :mode:: a mode change + # :notice:: someone sends you a notice + # :unknown:: any other message not handled by the above def []=(key, value) @handlers[key] = value end @@ -856,7 +866,7 @@ module Irc # sending it a hash containing the data from the server def process(serverstring) data = Hash.new - data["SERVERSTRING"] = serverstring + data[:serverstring] = serverstring unless serverstring =~ /^(:(\S+)\s)?(\S+)(\s(.*))?/ raise "Unparseable Server Message!!!: #{serverstring}" @@ -865,10 +875,10 @@ module Irc prefix, command, params = $2, $3, $5 if prefix != nil - data['SOURCE'] = prefix + data[:source] = prefix if prefix =~ /^(\S+)!(\S+)$/ - data['SOURCENICK'] = $1 - data['SOURCEADDRESS'] = $2 + data[:sourcenick] = $1 + data[:sourceaddress] = $2 end end @@ -878,31 +888,47 @@ module Irc case command when 'PING' - data['PINGID'] = argv[0] - handle('PING', data) + data[:pingid] = argv[0] + handle(:ping, data) when /^(\d+)$/ # numeric server message num=command.to_i case num + when RPL_YOURHOST + # "Your host is , running version " + # TODO how standard is this "version ? should i parse it? + data[:message] = argv[1] + handle(:yourhost, data) + when RPL_CREATED + # "This server was created " + data[:message] = argv[1] + handle(:created, data) + when RPL_MYINFO + # " + # " + data[:servername] = argv[1] + data[:version] = argv[2] + data[:usermodes] = argv[3] + data[:chanmodes] = argv[4] when ERR_NICKNAMEINUSE # "* :Nickname is already in use" - data['NICK'] = argv[1] - data['MESSAGE'] = argv[2] - handle('NICKTAKEN', data) + data[:nick] = argv[1] + data[:message] = argv[2] + handle(:nicktaken, data) when ERR_ERRONEUSNICKNAME # "* :Erroneous nickname" - data['NICK'] = argv[1] - data['MESSAGE'] = argv[2] - handle('BADNICK', data) + data[:nick] = argv[1] + data[:message] = argv[2] + handle(:badnick, data) when RPL_TOPIC - data['CHANNEL'] = argv[1] - data['TOPIC'] = argv[2] - handle('TOPIC', data) + data[:channel] = argv[1] + data[:topic] = argv[2] + handle(:topic, data) when RPL_TOPIC_INFO - data['NICK'] = argv[0] - data['CHANNEL'] = argv[1] - data['SOURCE'] = argv[2] - data['UNIXTIME'] = argv[3] - handle('TOPICINFO', data) + data[:nick] = argv[0] + data[:channel] = argv[1] + data[:source] = argv[2] + data[:unixtime] = argv[3] + handle(:topicinfo, data) when RPL_NAMREPLY # "( "=" / "*" / "@" ) # :[ "@" / "+" ] *( " " [ "@" / "+" ] ) @@ -916,26 +942,77 @@ module Irc end } when RPL_ENDOFNAMES - data['CHANNEL'] = argv[1] - data['USERS'] = @users - handle('NAMES', data) + data[:channel] = argv[1] + data[:users] = @users + handle(:names, data) @users = Array.new + when RPL_ISUPPORT + # "PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server" + # "MODES=4 CHANLIMIT=#:20 NICKLEN=16 USERLEN=10 HOSTLEN=63 + # TOPICLEN=450 KICKLEN=450 CHANNELLEN=30 KEYLEN=23 CHANTYPES=# + # PREFIX=(ov)@+ CASEMAPPING=ascii CAPAB IRCD=dancer :are available + # on this server" + # + argv.each {|a| + if a =~ /^(.*)=(.*)$/ + data[$1.downcase.to_sym] = $2 + end + } + handle(:isupport, data) + when RPL_LUSERCLIENT + # ":There are users and + # services on servers" + data[:message] = argv[1] + handle(:luserclient, data) + when RPL_LUSEROP + # " :operator(s) online" + data[:ops] = argv[1].to_i + handle(:luserop, data) + when RPL_LUSERUNKNOWN + # " :unknown connection(s)" + data[:unknown] = argv[1].to_i + handle(:luserunknown, data) + when RPL_LUSERCHANNELS + # " :channels formed" + data[:channels] = argv[1].to_i + handle(:luserchannels, data) + when RPL_LUSERME + # ":I have clients and servers" + data[:message] = argv[1] + handle(:luserme, data) + when ERR_NOMOTD + # ":MOTD File is missing" + data[:message] = argv[1] + handle(:motd_missing, data) + when RPL_LOCALUSERS + # ":Current local users: 3 Max: 4" + data[:message] = argv[1] + handle(:localusers, data) + when RPL_GLOBALUSERS + # ":Current global users: 3 Max: 4" + data[:message] = argv[1] + handle(:globalusers, data) + when RPL_STATSCONN + # ":Highest connection count: 4 (4 clients) (251 since server was + # (re)started)" + data[:message] = argv[1] + handle(:statsconn, data) when RPL_WELCOME # "Welcome to the Internet Relay Network # !@" case argv[1] when /((\S+)!(\S+))/ - data['NETMASK'] = $1 - data['NICK'] = $2 - data['ADDRESS'] = $3 + data[:netmask] = $1 + data[:nick] = $2 + data[:address] = $3 when /Welcome to the Internet Relay Network\s(\S+)/ - data['NICK'] = $1 + data[:nick] = $1 when /Welcome.*\s+(\S+)$/ - data['NICK'] = $1 + data[:nick] = $1 when /^(\S+)$/ - data['NICK'] = $1 + data[:nick] = $1 end - handle('WELCOME', data) + handle(:welcome, data) when RPL_MOTDSTART # " :- Message of the Day -" if argv[1] =~ /^-\s+(\S+)\s/ @@ -948,68 +1025,68 @@ module Irc @motd << "\n" end when RPL_ENDOFMOTD - data['MOTD'] = @motd - handle('MOTD', data) + data[:motd] = @motd + handle(:motd, data) else - handle('UNKNOWN', data) + handle(:unknown, data) end # end of numeric replies 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'] = argv[0] - data['MESSAGE'] = argv[1] - handle('PRIVMSG', data) + data[:target] = argv[0] + data[:message] = argv[1] + handle(:privmsg, data) # Now we split it - if(data['TARGET'] =~ /^(#|&).*/) - handle('PUBLIC', data) + if(data[:target] =~ /^(#|&).*/) + handle(:public, data) else - handle('MSG', data) + handle(:msg, data) end when 'KICK' - data['CHANNEL'] = argv[0] - data['TARGET'] = argv[1] - data['MESSAGE'] = argv[2] - handle('KICK', data) + data[:channel] = argv[0] + data[:target] = argv[1] + data[:message] = argv[2] + handle(:kick, data) when 'PART' - data['CHANNEL'] = argv[0] - data['MESSAGE'] = argv[1] - handle('PART', data) + data[:channel] = argv[0] + data[:message] = argv[1] + handle(:part, data) when 'QUIT' - data['MESSAGE'] = argv[0] - handle('QUIT', data) + data[:message] = argv[0] + handle(:quit, data) when 'JOIN' - data['CHANNEL'] = argv[0] - handle('JOIN', data) + data[:channel] = argv[0] + handle(:join, data) when 'TOPIC' - data['CHANNEL'] = argv[0] - data['TOPIC'] = argv[1] - handle('CHANGETOPIC', data) + data[:channel] = argv[0] + data[:topic] = argv[1] + handle(:changetopic, data) when 'INVITE' - data['TARGET'] = argv[0] - data['CHANNEL'] = argv[1] - handle('INVITE', data) + data[:target] = argv[0] + data[:channel] = argv[1] + handle(:invite, data) when 'NICK' - data['NICK'] = argv[0] - handle('NICK', data) + data[:nick] = argv[0] + handle(:nick, data) when 'MODE' - data['CHANNEL'] = argv[0] - data['MODESTRING'] = argv[1] - data['TARGETS'] = argv[2] - handle('MODE', data) + data[:channel] = argv[0] + data[:modestring] = argv[1] + data[:targets] = argv[2] + handle(:mode, data) when 'NOTICE' - data['TARGET'] = argv[0] - data['MESSAGE'] = argv[1] - if data['SOURCENICK'] - handle('NOTICE', data) + data[:target] = argv[0] + data[:message] = argv[1] + if data[:sourcenick] + handle(:notice, data) else # "server notice" (not from user, noone to reply to - handle('SNOTICE', data) + handle(:snotice, data) end else - handle('UNKNOWN', data) + handle(:unknown, data) end end -- 2.39.2