]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/imdb.rb
imdb plugin: add country and director to movie info
[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       m.compact!
49       unless m.empty?
50         return m.map { |mm|
51           mm[1]
52         }.uniq
53       end
54     elsif resp.code == "302"
55       debug "automatic redirection"
56       new_loc = resp['location'].gsub(IMDB, "")
57       if new_loc.match(/\/find\?q=(.*)/)
58         return do_search($1)
59       else
60         return [new_loc.gsub(/\?.*/, "")]
61       end
62     end
63     return nil
64   end
65
66   def info(rawstr)
67     urls = search(rawstr)
68     debug urls
69     if urls.nil_or_empty?
70       debug "IMDB: search returned NIL"
71       return nil
72     end
73     results = []
74     urls.each { |sr|
75       type = sr.match(/^\/([^\/]+)\//)[1].downcase.intern rescue nil
76       case type
77       when :title
78         results << info_title(sr)
79       when :name
80         results << info_name(sr)
81       else
82         results << "#{sr}"
83       end
84     }
85     return results
86   end
87
88   def grab_info(info, body)
89     /<div class="info">\s+<h5>#{info}:<\/h5>\s+(.*?)<\/div>/mi.match(body)[1] rescue nil
90   end
91
92   def info_title(sr)
93     resp = nil
94     begin
95       resp = @bot.httputil.get_response(IMDB + sr, :max_redir => -1)
96     rescue Exception => e
97       error e.message
98       warning e.backtrace.join("\n")
99       return nil
100     end
101
102     info = []
103
104     if resp.code == "200"
105       m = /<title>([^<]*)<\/title>/.match(resp.body)
106       return nil if !m
107       title_date = m[1]
108       title, date = title_date.scan(/^(.*)\((\d\d\d\d(?:[IV]+)?)\)$/).first
109       title.strip!
110
111       dir = nil
112       data = grab_info(/Directors?/, resp.body)
113       if data
114         dir = data.scan(NAME_MATCH).map { |url, name|
115           name
116         }.join(', ')
117       end
118
119       country = nil
120       data = grab_info(/Country/, resp.body)
121       if data
122         country = data.ircify_html
123       end
124
125       info << [title, "(#{country}, #{date})", dir ? "[#{dir}]" : nil, ": http://us.imdb.com#{sr}"].compact.join(" ")
126
127       m = /<b>([0-9.]+)\/10<\/b>\n?\r?\s+<small>\(<a href="ratings">([0-9,]+) votes?<\/a>\)<\/small>/.match(resp.body)
128       return nil if !m
129       score = m[1]
130       votes = m[2]
131
132       genre = Array.new
133       resp.body.scan(/<a href="\/Sections\/Genres\/[^\/]+\/">([^<]+)<\/a>/) do |gnr|
134         genre << gnr
135       end
136
137       info << "Ratings: #{score}/10 (#{votes} voters). Genre: #{genre.join('/')}"
138
139       plot = nil
140       data = grab_info(/Plot (?:Outline|Summary)/, resp.body)
141       if data
142         plot = "Plot: #{data.ircify_html.gsub(/\s+more$/,'')}"
143         info << plot
144       end
145
146       return info
147     end
148     return nil
149   end
150
151   def info_name(sr)
152     resp = nil
153     begin
154       resp = @bot.httputil.get_response(IMDB + sr, :max_redir => -1)
155     rescue Exception => e
156       error e.message
157       warning e.backtrace.join("\n")
158       return nil
159     end
160
161     info = []
162
163     if resp.code == "200"
164       m = /<title>([^<]*)<\/title>/.match(resp.body)
165       return nil if !m
166       name = m[1]
167
168       info << "#{name} : http://us.imdb.com#{sr}"
169
170       birth = nil
171       data = grab_info("Date of Birth", resp.body)
172       if data
173         birth = "Birth: #{data.ircify_html.gsub(/\s+more$/,'')}"
174       end
175
176       death = nil
177       data = grab_info("Date of Death", resp.body)
178       if data
179         death = "Death: #{data.ircify_html.gsub(/\s+more$/,'')}"
180       end
181
182       info << [birth, death].compact.join('. ')
183
184       movies = {}
185
186       filmorate = nil
187       begin
188         filmorate = @bot.httputil.get(IMDB + sr + "filmorate")
189       rescue Exception
190       end
191
192       if filmorate
193         filmorate.scan(/<div class="filmo">.*?<a href="\/title.*?<\/div>/m) { |str|
194           what = str.match(/<a name="[^"]+">([^<]+)<\/a>/)[1] rescue nil
195           # next unless what
196           next unless ['Actor', 'Director'].include?(what)
197           movies[what] = str.scan(TITLE_MATCH)[0..2].map { |url, tit|
198             tit
199           }
200         }
201       end
202
203       unless movies.empty?
204         ar = []
205         movies.keys.sort.each { |key|
206           ar << key.dup
207           ar.last << ": " + movies[key].join(', ')
208         }
209         info << "Top Movies:: " + ar.join('. ')
210       end
211       return info
212
213     end
214     return nil
215   end
216 end
217
218 class ImdbPlugin < Plugin
219   def help(plugin, topic="")
220     "imdb <string> => search http://www.imdb.org for <string>"
221   end
222
223   def imdb(m, params)
224     what = params[:what].to_s
225     i = Imdb.new(@bot)
226     info = i.info(what)
227     if !info
228       m.reply "Nothing found for #{what}"
229       return nil
230     end
231     if info.length == 1
232       m.reply Utils.decode_html_entities info.first.join("\n")
233     else
234       m.reply info.map { |i|
235         Utils.decode_html_entities i.join(" | ")
236       }.join("\n")
237     end
238   end
239 end
240
241 plugin = ImdbPlugin.new
242 plugin.map "imdb *what"
243