]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/imdb.rb
imdb plugin: show both popular and exact matches
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / imdb.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: IMDB plugin for rbot
5 #
6 # Author:: Arnaud Cornet <arnaud.cornet@gmail.com>
7 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
8 #
9 # Copyright:: (C) 2005 Arnaud Cornet
10 # Copyright:: (C) 2007 Giuseppe Bilotta
11 #
12 # License:: MIT license
13
14 require 'uri/common'
15
16 class Imdb
17   IMDB = "http://us.imdb.com"
18   TITLE_OR_NAME_MATCH = /<a href="(\/(?:title|name)\/(?:tt|nm)[0-9]+\/?)[^"]*"(?:[^>]*)>([^<]*)<\/a>/
19   TITLE_MATCH = /<a href="(\/title\/tt[0-9]+\/?)[^"]*"(?:[^>]*)>([^<]*)<\/a>/
20   NAME_MATCH = /<a href="(\/name\/nm[0-9]+\/?)[^"]*"(?:[^>]*)>([^<]*)<\/a>/
21
22   def initialize(bot)
23     @bot = bot
24   end
25
26   def search(rawstr)
27     str = URI.escape(rawstr) << ";site=aka"
28     return do_search(str)
29   end
30
31   def do_search(str)
32     resp = nil
33     begin
34       resp = @bot.httputil.get_response(IMDB + "/find?q=#{str}",
35                                         :max_redir => -1)
36     rescue Exception => e
37       error e.message
38       warning e.backtrace.join("\n")
39       return nil
40     end
41
42     if resp.code == "200"
43       m = []
44       m << TITLE_OR_NAME_MATCH.match(resp.body)
45       if resp.body.match(/\(Exact Matches\)<\/b>/)
46         m << TITLE_OR_NAME_MATCH.match($')
47       end
48       debug m.inspect
49       m.compact!
50       debug m.inspect
51       unless m.empty?
52         return m.map { |mm|
53           mm[1]
54         }.uniq
55       end
56     elsif resp.code == "302"
57       debug "automatic redirection"
58       new_loc = resp['location'].gsub(IMDB, "")
59       if new_loc.match(/\/find\?q=(.*)/)
60         return do_search($1)
61       else
62         return [new_loc.gsub(/\?.*/, "")]
63       end
64     end
65     return nil
66   end
67
68   def info(rawstr)
69     urls = search(rawstr)
70     debug urls
71     if urls.nil_or_empty?
72       debug "IMDB: search returned NIL"
73       return nil
74     end
75     results = []
76     urls.each { |sr|
77       type = sr.match(/^\/([^\/]+)\//)[1].downcase.intern rescue nil
78       case type
79       when :title
80         results << info_title(sr)
81       when :name
82         results << info_name(sr)
83       else
84         results << "#{sr}"
85       end
86     }
87     return results
88   end
89
90   def grab_info(info, body)
91     /<div class="info">\s+<h5>#{info}:<\/h5>\s+(.*?)<\/div>/mi.match(body)[1] rescue nil
92   end
93
94   def info_title(sr)
95     resp = nil
96     begin
97       resp = @bot.httputil.get_response(IMDB + sr, :max_redir => -1)
98     rescue Exception => e
99       error e.message
100       warning e.backtrace.join("\n")
101       return nil
102     end
103
104     if resp.code == "200"
105       m = /<title>([^<]*)<\/title>/.match(resp.body)
106       return nil if !m
107       title = Utils.decode_html_entities(m[1])
108
109       m = /<b>([0-9.]+)\/10<\/b>\n?\r?\s+<small>\(<a href="ratings">([0-9,]+) votes?<\/a>\)<\/small>/.match(resp.body)
110       return nil if !m
111       score = m[1]
112       votes = m[2]
113
114       plot = nil
115       data = grab_info(/Plot (?:Outline|Summary)/, resp.body)
116       if data
117         plot = "Plot: #{data.ircify_html.gsub(/\s+more$/,'')}"
118       end
119
120       genre = Array.new
121       resp.body.scan(/<a href="\/Sections\/Genres\/[^\/]+\/">([^<]+)<\/a>/) do |gnr|
122         genre << gnr
123       end
124       info = "#{title} : http://us.imdb.com#{sr}\n"
125       info << "Ratings: #{score}/10 (#{votes} voters). Genre: #{genre.join('/')}\n"
126       info << plot if plot
127       return info
128     end
129     return nil
130   end
131
132   def info_name(sr)
133     resp = nil
134     begin
135       resp = @bot.httputil.get_response(IMDB + sr, :max_redir => -1)
136     rescue Exception => e
137       error e.message
138       warning e.backtrace.join("\n")
139       return nil
140     end
141
142     if resp.code == "200"
143       m = /<title>([^<]*)<\/title>/.match(resp.body)
144       return nil if !m
145       name = Utils.decode_html_entities(m[1])
146
147       birth = nil
148       data = grab_info("Date of Birth", resp.body)
149       if data
150         birth = "Birth: #{data.ircify_html.gsub(/\s+more$/,'')}"
151       end
152
153       death = nil
154       data = grab_info("Date of Death", resp.body)
155       if data
156         death = "Death: #{data.ircify_html.gsub(/\s+more$/,'')}"
157       end
158
159       movies = {}
160
161       filmorate = nil
162       begin
163         filmorate = @bot.httputil.get(IMDB + sr + "filmorate")
164       rescue Exception
165       end
166
167       if filmorate
168         filmorate.scan(/<div class="filmo">.*?<a href="\/title.*?<\/div>/m) { |str|
169           what = str.match(/<a name="[^"]+">([^<]+)<\/a>/)[1] rescue nil
170           # next unless what
171           next unless ['Actor', 'Director'].include?(what)
172           movies[what] = str.scan(TITLE_MATCH)[0..2].map { |url, tit|
173             Utils.decode_html_entities(tit)
174           }
175         }
176       end
177       debug movies.inspect
178
179       info = "#{name} : http://us.imdb.com#{sr}\n"
180       info << [birth, death].compact.join('. ') << "\n"
181       unless movies.empty?
182         info << "Top Movies:: "
183         ar = []
184         movies.keys.sort.each { |key|
185           ar << key.dup
186           ar.last << ": " + movies[key].join(', ')
187         }
188         info << ar.join('. ')
189       end
190       return info
191
192     end
193     return nil
194   end
195 end
196
197 class ImdbPlugin < Plugin
198   def help(plugin, topic="")
199     "imdb <string> => search http://www.imdb.org for <string>"
200   end
201
202   def imdb(m, params)
203     what = params[:what].to_s
204     i = Imdb.new(@bot)
205     info = i.info(what)
206     if !info
207       m.reply "Nothing found for #{what}"
208       return nil
209     end
210     if info.length == 1
211       m.reply info
212     else
213       m.reply info.map { |i|
214         i.gsub(/\n+/, " | ")
215       }.join("\n")
216     end
217   end
218 end
219
220 plugin = ImdbPlugin.new
221 plugin.map "imdb *what"
222