]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/geoip.rb
geoip plugin: now stacking whois requests to prevent overlaps
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / geoip.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Geo IP Plugin
5 #
6 # Author:: Raine Virta <rane@kapsi.fi>
7 # Copyright:: (C) 2008 Raine Virta
8 # License:: GPL v2
9 #
10 # Resolves the geographic locations of users (network-wide) and IP addresses
11
12 module GeoIP
13   GEO_IP = "http://www.geoiptool.com/en/?IP="
14   REGEX  = {
15     :country => %r{Country:.*?<a href=".*?" target="_blank"> (.*?)</a>}m,
16     :region  => %r{Region:.*?<a href=".*?" target="_blank">(.*?)</a>}m,
17     :city    => %r{City:.*?<td align="left" class="arial_bold">(.*?)</td>}m
18   }
19
20   def self.resolve(hostname)
21     res = {}
22     raw = Irc::Utils.bot.httputil.get_response(GEO_IP+hostname)
23     raw = raw.decompress_body(raw.raw_body)
24
25     REGEX.each { |key, regex| res[key] = Iconv.conv('utf-8', 'ISO-8859-1', raw.scan(regex).to_s) }
26
27     return res
28   end
29 end
30
31 class Stack
32   def initialize
33     @hash = {}
34   end
35
36   def [](nick)
37     @hash[nick] = [] unless @hash[nick]
38     @hash[nick]
39   end
40
41   def has_nick?(nick)
42     @hash.has_key?(nick)
43   end
44
45   def clear(nick)
46     @hash.delete(nick)
47   end
48 end
49
50 class GeoIpPlugin < Plugin
51   def help(plugin, topic="")
52     "geoip [<user|hostname|ip>] => returns the geographic location of whichever has been given -- note: user can be anyone on the network"
53   end
54
55   def initialize
56     super
57
58     @stack = Stack.new
59   end
60
61   def whois(m)
62     nick = m.whois[:nick].downcase
63
64     # need to see if the whois reply was invoked by this plugin
65     return unless @stack.has_nick?(nick)
66
67     @stack[nick].each do |source|
68       if m.target
69         @bot.say source, host2output(m.target.host, m.target.nick)
70       else
71         @bot.say source, "no such user on "+@bot.server.hostname.split(".")[-2]
72       end
73     end
74
75     @stack.clear(m.whois[:nick])
76   end
77
78   def geoip(m, params)
79     if params.empty?
80       m.reply host2output(m.source.host, m.source.nick)
81     else
82       if m.replyto.class == Channel
83
84         # check if there is an user on the channel with nick same as input given
85         user = m.replyto.users.find { |user| user.nick == params[:input] }
86
87         if user
88           m.reply host2output(user.host, user.nick)
89           return
90         end
91       end
92
93       # input is a host name or an IP
94       if params[:input] =~ /[a-z0-9\-]+(?:\.[a-z0-9\-]+)*\.[a-z]{2,4}/i ||
95          params[:input] =~ Resolv::IPv4::Regex
96         m.reply host2output(params[:input])
97
98       # assume input is a nick
99       else
100         nick = params[:input].downcase
101
102         @stack[nick] << m.replyto
103         @bot.whois(nick)
104       end
105     end
106   end
107
108   def host2output(host, nick=nil)
109     geo = GeoIP::resolve(host)
110
111     if geo[:country].empty?
112       return _("#{nick ? "#{nick}'s location" : host} could not be resolved")
113     end
114
115     res = _("%{thing} is #{nick ? "from" : "located in"}") % {
116       :thing   => (nick ? nick : Resolv::getaddress(host)),
117       :country => geo[:country]
118     }
119
120     res << " %{city}," % {
121       :city => geo[:city]
122     } unless geo[:city].empty?
123
124     res << " %{country}" % {
125       :country => geo[:country]
126     }
127
128     res << " (%{region})" % {
129       :region  => geo[:region]
130     } unless geo[:region].empty? || geo[:region] == geo[:city]
131
132     return res
133   end
134 end
135
136 plugin = GeoIpPlugin.new
137 plugin.map "geoip [:input]", :action => 'geoip'