X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=data%2Frbot%2Fplugins%2Fgeoip.rb;h=3b7bf75169a4d7e7de883abd5da6bd15a839008f;hb=4c9408181e395b7dcb7fd95a991b9073088f662c;hp=15372a8ba7f4fa1d75f8f64e6ac83a271922d510;hpb=9125a2e6d1ef8af8d380da3766c704f9028dec69;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/data/rbot/plugins/geoip.rb b/data/rbot/plugins/geoip.rb index 15372a8b..3b7bf751 100755 --- a/data/rbot/plugins/geoip.rb +++ b/data/rbot/plugins/geoip.rb @@ -9,42 +9,133 @@ # # Resolves the geographic locations of users (network-wide) and IP addresses -module GeoIP - GEO_IP = "http://www.geoiptool.com/en/?IP=" - REGEX = { - :country => %r{Country:.*? (.*?)}m, - :region => %r{Region:.*?(.*?)}m, - :city => %r{City:.*?(.*?)}m - } +module ::GeoIP + class InvalidHostError < RuntimeError; end + class BadAPIError < RuntimeError; end + HOST_NAME_REGEX = /^[a-z0-9\-]+(?:\.[a-z0-9\-]+)*\.[a-z]{2,4}/i - def self.resolve(hostname) + def self.valid_host?(hostname) + hostname =~ HOST_NAME_REGEX || + hostname =~ Resolv::IPv4::Regex && (hostname.split(".").map { |e| e.to_i }.max <= 255) + end + + def self.geoiptool(ip) + url = "http://www.geoiptool.com/en/?IP=" + regexes = { + :country => %r{Country:.*? (.*?)}m, + :region => %r{Region:.*?(.*?)}m, + :city => %r{City:.*?(.*?)}m, + :lat => %r{Latitude:.*?(.*?)}m, + :lon => %r{Longitude:.*?(.*?)}m + } res = {} - raw = Irc::Utils.bot.httputil.get_response(GEO_IP+hostname) + raw = Irc::Utils.bot.httputil.get_response(url+ip) raw = raw.decompress_body(raw.raw_body) - REGEX.each { |key, regex| res[key] = Iconv.conv('utf-8', 'ISO-8859-1', raw.scan(regex).to_s) } + regexes.each { |key, regex| res[key] = Iconv.conv('utf-8', 'ISO-8859-1', raw.scan(regex).to_s) } return res end + + def self.kapsi(ip) + url = "http://lakka.kapsi.fi:40086/lookup.yaml?host=" + yaml = Irc::Utils.bot.httputil.get(url+ip) + return YAML::load(yaml) + end + + def self.blogama(ip) + url = "http://ipinfodb.com/ip_query.php?ip=" + debug "Requesting #{url+ip}" + + xml = Irc::Utils.bot.httputil.get(url+ip) + + if xml + obj = REXML::Document.new(xml) + debug "Found #{obj}" + newobj = { + :country => obj.elements["Response"].elements["CountryName"].text, + :city => obj.elements["Response"].elements["City"].text, + :region => obj.elements["Response"].elements["RegionName"].text, + } + debug "Returning #{newobj}" + return newobj + else + raise InvalidHostError + end + end + + def self.resolve(hostname, api) + raise InvalidHostError unless valid_host?(hostname) + + begin + ip = Resolv.getaddress(hostname) + rescue Resolv::ResolvError + raise InvalidHostError + end + + jump_table = { + "blogama" => Proc.new { |ip| blogama(ip) }, + "kapsi" => Proc.new { |ip| kapsi(ip) }, + "geoiptool" => Proc.new { |ip| geoiptool(ip) }, + } + + raise BadAPIError unless jump_table.key?(api) + + return jump_table[api].call(ip) + end +end + +class Stack + def initialize + @hash = {} + end + + def [](nick) + @hash[nick] = [] unless @hash[nick] + @hash[nick] + end + + def has_nick?(nick) + @hash.has_key?(nick) + end + + def clear(nick) + @hash.delete(nick) + end end class GeoIpPlugin < Plugin + Config.register Config::ArrayValue.new('geoip.sources', + :default => [ "blogama", "kapsi", "geoiptool" ], + :desc => "Which API to use for lookups. Supported values: blogama, kapsi, geoiptool") + def help(plugin, topic="") "geoip [] => returns the geographic location of whichever has been given -- note: user can be anyone on the network" end + def initialize + super + + @stack = Stack.new + end + def whois(m) + nick = m.whois[:nick].downcase + # need to see if the whois reply was invoked by this plugin - return unless m.whois[:nick] == @nick + return unless @stack.has_nick?(nick) if m.target - @bot.say @source, host2output(m.target.host, m.target.nick) + msg = host2output(m.target.host, m.target.nick) else - @bot.say @source, "no such user on "+@bot.server.hostname.split(".")[-2] + msg = "no such user on "+@bot.server.hostname.split(".")[-2] + end + @stack[nick].each do |source| + @bot.say source, msg end - @nick, @source = nil + @stack.clear(nick) end def geoip(m, params) @@ -63,25 +154,41 @@ class GeoIpPlugin < Plugin end # input is a host name or an IP - if params[:input] =~ /[a-z0-9\-]+(?:\.[a-z0-9\-]+)*\.[a-z]{2,3}/i || - params[:input] =~ Resolv::IPv4::Regex - m.reply host2output(params[:input]) + if GeoIP::valid_host?(params[:input]) + m.reply host2output(params[:input]) # assume input is a nick - else - @source = m.replyto - @nick = params[:input] + elsif params[:input] !~ /\./ + nick = params[:input].downcase - @bot.whois(@nick) + @stack[nick] << m.replyto + @bot.whois(nick) + else + m.reply "invalid input" end end end def host2output(host, nick=nil) - geo = GeoIP::resolve(host) - - if geo[:country].empty? - return _("#{nick ? "#{nick}'s location" : host} could not be resolved") + return "127.0.0.1 could not be res.. wait, what?" if host == "127.0.0.1" + + geo = {:country => ""} + begin + apis = @bot.config['geoip.sources'] + apis.compact.each { |api| + geo = GeoIP::resolve(host, api) + if geo[:country] != "" + break + end + } + rescue GeoIP::InvalidHostError, RuntimeError + if nick + return _("#{nick}'s location could not be resolved") + else + return _("#{host} could not be resolved") + end + rescue GeoIP::BadAPIError + return _("The owner configured me to use an API that doesn't exist, bug them!") end res = _("%{thing} is #{nick ? "from" : "located in"}") % { @@ -89,21 +196,21 @@ class GeoIpPlugin < Plugin :country => geo[:country] } - res << " %{city}," % { + res << " %{city}" % { :city => geo[:city] - } unless geo[:city].empty? + } unless geo[:city].to_s.empty? + + res << " %{region}," % { + :region => geo[:region] + } unless geo[:region].to_s.empty? || geo[:region] == geo[:city] res << " %{country}" % { :country => geo[:country] } - res << " (%{region})" % { - :region => geo[:region] - } unless geo[:region].empty? || geo[:region] == geo[:city] - return res end end plugin = GeoIpPlugin.new -plugin.map "geoip [:input]", :action => 'geoip' +plugin.map "geoip [:input]", :action => 'geoip', :thread => true