]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/search.rb
search plugin: update google search
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / search.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Google and Wikipedia search plugin for rbot
5 #
6 # Author:: Tom Gilbert (giblet) <tom@linuxbrit.co.uk>
7 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
8 #
9 # Copyright:: (C) 2002-2005 Tom Gilbert
10 # Copyright:: (C) 2006 Tom Gilbert, Giuseppe Bilotta
11 # Copyright:: (C) 2006-2007 Giuseppe Bilotta
12
13 # TODO:: use lr=lang_<code> or whatever is most appropriate to let google know
14 #        it shouldn't use the bot's location to find the preferred language
15 # TODO:: support localized uncyclopedias -- not easy because they have different names
16 #        for most languages
17
18 GOOGLE_SEARCH = "http://www.google.com/search?oe=UTF-8&q="
19 GOOGLE_WAP_SEARCH = "http://www.google.com/m/search?hl=en&q="
20 # GOOGLE_WAP_LINK = /<a accesskey="(\d)" href=".*?u=(.*?)">(.*?)<\/a>/im
21 GOOGLE_WAP_LINK = /<a href="(?:.*?u=(.*?)|(http:\/\/.*?))">(.*?)<\/a>/im
22 GOOGLE_CALC_RESULT = %r{<img src=/images/calc_img\.gif(?: width=40 height=30 alt="")?>.*?<h2 class=r[^>]*><b>(.+?)</b>}
23 GOOGLE_COUNT_RESULT = %r{<font size=-1>Results <b>1<\/b> - <b>10<\/b> of about <b>(.*)<\/b> for}
24 GOOGLE_DEF_RESULT = %r{<p> (Web definitions for .*?)<br/>(.*?)<br/>(.*?)\s-\s+<a href}
25 GOOGLE_TIME_RESULT = %r{alt="Clock"></td><td valign=[^>]+>(.+?)<(br|/td)>}
26
27 class SearchPlugin < Plugin
28   Config.register Config::IntegerValue.new('google.hits',
29     :default => 3,
30     :desc => "Number of hits to return from Google searches")
31   Config.register Config::IntegerValue.new('google.first_par',
32     :default => 0,
33     :desc => "When set to n > 0, the bot will return the first paragraph from the first n search hits")
34   Config.register Config::IntegerValue.new('wikipedia.hits',
35     :default => 3,
36     :desc => "Number of hits to return from Wikipedia searches")
37   Config.register Config::IntegerValue.new('wikipedia.first_par',
38     :default => 1,
39     :desc => "When set to n > 0, the bot will return the first paragraph from the first n wikipedia search hits")
40
41   def help(plugin, topic="")
42     case topic
43     when "search", "google"
44       "#{topic} <string> => search google for <string>"
45     when "gcalc"
46       "gcalc <equation> => use the google calculator to find the answer to <equation>"
47     when "gdef"
48       "gdef <term(s)> => use the google define mechanism to find a definition of <term(s)>"
49     when "gtime"
50       "gtime <location> => use the google clock to find the current time at <location>"
51     when "wp"
52       "wp [<code>] <string> => search for <string> on Wikipedia. You can select a national <code> to only search the national Wikipedia"
53     when "unpedia"
54       "unpedia <string> => search for <string> on Uncyclopedia"
55     else
56       "search <string> (or: google <string>) => search google for <string> | wp <string> => search for <string> on Wikipedia | unpedia <string> => search for <string> on Uncyclopedia"
57     end
58   end
59
60   def google(m, params)
61     what = params[:words].to_s
62     searchfor = CGI.escape what
63     # This method is also called by other methods to restrict searching to some sites
64     if params[:site]
65       site = "site:#{params[:site]}+"
66     else
67       site = ""
68     end
69     # It is also possible to choose a filter to remove constant parts from the titles
70     # e.g.: "Wikipedia, the free encyclopedia" when doing Wikipedia searches
71     filter = params[:filter] || ""
72
73     url = GOOGLE_WAP_SEARCH + site + searchfor
74
75     hits = params[:hits] || @bot.config['google.hits']
76
77     first_pars = params[:firstpar] || @bot.config['google.first_par']
78
79     single = (hits == 1 and first_pars == 1)
80
81     begin
82       wml = @bot.httputil.get(url)
83       raise unless wml
84     rescue => e
85       m.reply "error googling for #{what}"
86       return
87     end
88     results = wml.scan(GOOGLE_WAP_LINK)
89     if results.length == 0
90       m.reply "no results found for #{what}"
91       return
92     end
93     single ||= (results.length==1)
94     urls = Array.new
95     n = 0
96     results = results[0...hits].map { |res|
97       n += 1
98       t = Utils.decode_html_entities res[2].gsub(filter, '').strip
99       u = URI.unescape(res[0] || res[1])
100       urls.push(u)
101       single ? u : "#{n}. #{Bold}#{t}#{Bold}: #{u}"
102     }.join(" | ")
103
104     if params[:lucky]
105       m.reply urls.first
106       return
107     end
108
109     # If we return a single, full result, change the output to a more compact representation
110     if single
111       m.reply "Result for %s: %s -- %s" % [what, results, Utils.get_first_pars(urls, first_pars)], :overlong => :truncate
112       return
113     end
114
115     m.reply "Results for #{what}: #{results}", :split_at => /\s+\|\s+/
116
117     return unless first_pars > 0
118
119     Utils.get_first_pars urls, first_pars, :message => m
120
121   end
122
123   def lucky(m, params)
124     params.merge!(:lucky => true)
125     google(m, params)
126   end
127
128   def gcalc(m, params)
129     what = params[:words].to_s
130     searchfor = CGI.escape(what)
131
132     debug "Getting gcalc thing: #{searchfor.inspect}"
133     url = GOOGLE_SEARCH + searchfor
134
135     begin
136       html = @bot.httputil.get(url)
137     rescue => e
138       m.reply "error googlecalcing #{what}"
139       return
140     end
141
142     debug "#{html.size} bytes of html recieved"
143
144     results = html.scan(GOOGLE_CALC_RESULT)
145     debug "results: #{results.inspect}"
146
147     if results.length != 1
148       m.reply "couldn't calculate #{what}"
149       return
150     end
151
152     result = results[0][0].ircify_html
153     debug "replying with: #{result.inspect}"
154     m.reply "#{result}"
155   end
156
157   def gcount(m, params)
158     what = params[:words].to_s
159     searchfor = CGI.escape(what)
160
161     debug "Getting gcount thing: #{searchfor.inspect}"
162     url = GOOGLE_SEARCH + searchfor
163
164     begin
165       html = @bot.httputil.get(url)
166     rescue => e
167       m.reply "error googlecounting #{what}"
168       return
169     end
170
171     debug "#{html.size} bytes of html recieved"
172
173     results = html.scan(GOOGLE_COUNT_RESULT)
174     debug "results: #{results.inspect}"
175
176     if results.length != 1
177       m.reply "couldn't count #{what}"
178       return
179     end
180
181     result = results[0][0].ircify_html
182     debug "replying with: #{result.inspect}"
183     m.reply "total results: #{result}"
184
185   end
186
187   def gdef(m, params)
188     what = params[:words].to_s
189     searchfor = CGI.escape("define " + what)
190
191     debug "Getting gdef thing: #{searchfor.inspect}"
192     url = GOOGLE_WAP_SEARCH + searchfor
193
194     begin
195       html = @bot.httputil.get(url)
196     rescue => e
197       m.reply "error googledefining #{what}"
198       return
199     end
200
201     debug html
202     results = html.scan(GOOGLE_DEF_RESULT)
203     debug "results: #{results.inspect}"
204
205     if results.length != 1
206       m.reply "couldn't find a definition for #{what} on Google"
207       return
208     end
209
210     head = results[0][0].ircify_html
211     text = results[0][1].ircify_html
212     link = results[0][2]
213     m.reply "#{head} -- #{link}\n#{text}"
214   end
215
216   def wikipedia(m, params)
217     lang = params[:lang]
218     site = "#{lang.nil? ? '' : lang + '.'}wikipedia.org"
219     debug "Looking up things on #{site}"
220     params[:site] = site
221     params[:filter] = / - Wikipedia.*$/
222     params[:hits] = @bot.config['wikipedia.hits']
223     params[:firstpar] = @bot.config['wikipedia.first_par']
224     return google(m, params)
225   end
226
227   def unpedia(m, params)
228     site = "uncyclopedia.org"
229     debug "Looking up things on #{site}"
230     params[:site] = site
231     params[:filter] = / - Uncyclopedia.*$/
232     params[:hits] = @bot.config['wikipedia.hits']
233     params[:firstpar] = @bot.config['wikipedia.first_par']
234     return google(m, params)
235   end
236
237   def gtime(m, params)
238     where = params[:words].to_s
239     where.sub!(/^\s*in\s*/, '')
240     searchfor = CGI.escape("time in " + where)
241     url = GOOGLE_SEARCH + searchfor
242
243     begin
244       html = @bot.httputil.get(url)
245     rescue => e
246       m.reply "Error googletiming #{where}"
247       return
248     end
249
250     debug html
251     results = html.scan(GOOGLE_TIME_RESULT)
252     debug "results: #{results.inspect}"
253
254     if results.length != 1
255       m.reply "Couldn't find the time for #{where} on Google"
256       return
257     end
258
259     time = results[0][0].ircify_html
260     m.reply "#{time}"
261   end
262 end
263
264 plugin = SearchPlugin.new
265
266 plugin.map "search *words", :action => 'google', :threaded => true
267 plugin.map "google *words", :action => 'google', :threaded => true
268 plugin.map "lucky *words", :action => 'lucky', :threaded => true
269 plugin.map "gcount *words", :action => 'gcount', :threaded => true
270 plugin.map "gcalc *words", :action => 'gcalc', :threaded => true
271 plugin.map "gdef *words", :action => 'gdef', :threaded => true
272 plugin.map "gtime *words", :action => 'gtime', :threaded => true
273 plugin.map "wp :lang *words", :action => 'wikipedia', :requirements => { :lang => /^\w\w\w?$/ }, :threaded => true
274 plugin.map "wp *words", :action => 'wikipedia', :threaded => true
275 plugin.map "unpedia *words", :action => 'unpedia', :threaded => true
276