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