X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=data%2Frbot%2Fplugins%2Fgeoip.rb;h=c76310d5b553fcb417a977b83dcd32e15d50b9cf;hb=052217de30c59206d7025b582d4604557a747470;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..c76310d5 100755
--- a/data/rbot/plugins/geoip.rb
+++ b/data/rbot/plugins/geoip.rb
@@ -9,42 +9,141 @@
#
# 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.valid_host?(hostname)
+ hostname =~ HOST_NAME_REGEX ||
+ hostname =~ Resolv::IPv4::Regex && (hostname.split(".").map { |e| e.to_i }.max <= 255)
+ end
- def self.resolve(hostname)
+ 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
+
+ IPINFODB_URL = "http://api.ipinfodb.com/v2/ip_query.php?key=%{key}&ip=%{ip}"
+
+ def self.ipinfodb(ip)
+ url = IPINFODB_URL % {
+ :ip => ip,
+ :key => Irc::Utils.bot.config['geoip.ipinfodb_key']
+ }
+ debug "Requesting #{url}"
+
+ xml = Irc::Utils.bot.httputil.get(url)
+
+ 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
+
+ JUMP_TABLE = {
+ "ipinfodb" => Proc.new { |ip| ipinfodb(ip) },
+ "kapsi" => Proc.new { |ip| kapsi(ip) },
+ "geoiptool" => Proc.new { |ip| geoiptool(ip) },
+ }
+
+ def self.resolve(hostname, api)
+ raise InvalidHostError unless valid_host?(hostname)
+
+ begin
+ ip = Resolv.getaddress(hostname)
+ rescue Resolv::ResolvError
+ raise InvalidHostError
+ end
+
+ 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 => [ "ipinfodb", "kapsi", "geoiptool" ],
+ :desc => "Which API to use for lookups. Supported values: ipinfodb, kapsi, geoiptool")
+ Config.register Config::StringValue.new('geoip.ipinfodb_key',
+ :default => "",
+ :desc => "API key for the IPinfoDB geolocation service")
+
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)
@@ -54,7 +153,7 @@ class GeoIpPlugin < Plugin
if m.replyto.class == Channel
# check if there is an user on the channel with nick same as input given
- user = m.replyto.users.find { |user| user.nick == params[:input] }
+ user = m.replyto.users.find { |usr| usr.nick == params[:input] }
if user
m.reply host2output(user.host, user.nick)
@@ -63,47 +162,61 @@ 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") % { :nick => nick }
+ else
+ return _("%{host} could not be resolved") % { :host => host }
+ 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"}") % {
- :thing => (nick ? nick : Resolv::getaddress(host)),
- :country => geo[:country]
- }
+ location = []
+ location << geo[:city] unless geo[:city].nil_or_empty?
+ location << geo[:region] unless geo[:region].nil_or_empty? or geo[:region] == geo[:city]
+ location << geo[:country] unless geo[:country].nil_or_empty?
- res << " %{city}," % {
- :city => geo[:city]
- } unless geo[:city].empty?
+ if nick
+ res = _("%{nick} is from %{location}")
+ else
+ res = _("%{host} is located in %{location}")
+ end
- res << " %{country}" % {
- :country => geo[:country]
+ return res % {
+ :nick => nick,
+ :host => host,
+ :location => location.join(', ')
}
-
- 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