]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/dict.rb
plugins: raise a descriptive LoadError when the db is corrupt on load
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / dict.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Dictionary lookup plugin for rbot
5 #
6 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7 # Copyright:: (C) 2006-2007 Giuseppe Bilotta
8 # License:: GPL v2
9 #
10 # Provides a link to the definition of a word in one of the supported
11 # dictionaries. Currently available are
12 #   * the Oxford dictionary for (British) English
13 #   * the De Mauro/Paravia dictionary for Italian
14 #   * the Chambers dictionary for English (accepts both US and UK)
15 #   * the Littré dictionary for French
16 #
17 # Other plugins can use this one to check if a given word is valid in italian
18 # or english or french by using the is_italian?, is_british?, is_english?,
19 # is_french? methods
20 #
21 # TODO: cache results and reuse them if get_cached returns a cache copy
22
23 DEMAURO_LEMMA = /<anchor>(.*?)(?: - (.*?))<go href="lemma.php\?ID=(\d+)"\/><\/anchor>/
24 CHAMBERS_LEMMA = /<p><span class="hwd">(.*?)<\/span> <span class="psa">(.*?)<\/span>(.*?)<\/p>/
25
26 class DictPlugin < Plugin
27   Config.register Config::IntegerValue.new('dict.hits',
28     :default => 3,
29     :desc => "Number of hits to return from a dictionary lookup")
30   Config.register Config::IntegerValue.new('dict.first_par',
31     :default => 0,
32     :desc => "When set to n > 0, the bot will return the first paragraph from the first n dictionary hits")
33
34   def demauro_filter(s)
35     # check if it's a page we can handle
36     loc = Utils.check_location(s, @dmurlrx)
37     # the location might be not good, but we might still be able to handle the
38     # page
39     if !loc and s[:text] !~ /<!-- Il dizionario della lingua italiana Paravia: /
40       debug "not our business"
41       return
42     end
43     # we want to grab the content from the WAP page, since it's in a much
44     # cleaner HTML, so first try to get the word ID
45     if s[:text] !~ %r{<li><a href="(\d+)" title="vai al lemma precedente" accesskey="p">lemma precedente</a></li>}
46       return
47     end
48     id = $1.to_i + 1
49     title = s[:text].ircify_html_title
50     content = @bot.filter(:htmlinfo, URI.parse(@dmwaplemma % id))[:content]
51     return {:title => title, :content => content.sub(/^\S+\s+-\s+/,'')}
52   end
53
54   def initialize
55     super
56     @dmurl = "http://www.demauroparavia.it/"
57     @dmurlrx = %r{http://(?:www\.)?demauroparavia\.it/(\d+)}
58     @dmwapurl = "http://wap.demauroparavia.it/index.php?lemma=%s"
59     @dmwaplemma = "http://wap.demauroparavia.it/lemma.php?ID=%s"
60     @oxurl = "http://www.askoxford.com/concise_oed/%s"
61     @chambersurl = "http://www.chambersharrap.co.uk/chambers/features/chref/chref.py/main?query=%s&title=21st"
62     @littreurl = "http://francois.gannaz.free.fr/Littre/xmlittre.php?requete=%s"
63
64     @bot.register_filter(:demauro, :htmlinfo) { |s| demauro_filter(s) }
65   end
66
67
68   def help(plugin, topic="")
69     case topic
70     when "demauro"
71       return "demauro <word> => provides a link to the definition of <word> from the De Mauro/Paravia dictionary"
72     when "oxford"
73       return "oxford <word> => provides a link to the definition of <word> (it can also be an expression) from the Concise Oxford dictionary"
74     when "chambers"
75       return "chambers <word> => provides a link to the definition of <word> (it can also be an expression) from the Chambers 21st Century Dictionary"
76     when "littre"
77       return "littre <word> => provides a link to the definition of <word> (it can also be an expression) from the Littré online dictionary"
78     end
79     return "<dictionary> <word>: check for <word> on <dictionary> where <dictionary> can be one of: demauro, oxford, chambers, littre"
80   end
81
82   def demauro(m, params)
83     justcheck = params[:justcheck]
84
85     word = params[:word].downcase
86     url = @dmwapurl % CGI.escape(word)
87     xml = nil
88     info = @bot.httputil.get_response(url) rescue nil
89     xml = info.body if info
90     if xml.nil?
91       info = info ? " (#{info.code} - #{info.message})" : ""
92       return false if justcheck
93       m.reply "An error occurred while looking for #{word}#{info}"
94       return
95     end
96     if xml=~ /Non ho trovato occorrenze per/
97       return false if justcheck
98       m.reply "Nothing found for #{word}"
99       return
100     end
101     entries = xml.scan(DEMAURO_LEMMA)
102     text = word
103     urls = []
104     if not entries.transpose.first.grep(/\b#{word}\b/)
105       return false if justcheck
106       text += " not found. Similar words"
107     end
108     return true if justcheck
109     text += ": "
110     n = 0
111     hits = @bot.config['dict.hits']
112     text += entries[0...hits].map { |ar|
113       n += 1
114       urls << @dmwaplemma % ar[2]
115       "#{n}. #{Bold}#{ar[0]}#{Bold} - #{ar[1].gsub(/<\/?em>/,'')}: #{@dmurl}#{ar[2]}"
116     }.join(" | ")
117     m.reply text
118
119     first_pars = @bot.config['dict.first_par']
120
121     return unless first_pars > 0
122
123     Utils.get_first_pars urls, first_pars, :message => m,
124       :strip => /^.+?\s+-\s+/
125
126   end
127
128   def is_italian?(word)
129     return demauro(nil, :word => word, :justcheck => true)
130   end
131
132
133   def oxford(m, params)
134     justcheck = params[:justcheck]
135
136     word = params[:word].join
137     [word, word + "_1"].each { |check|
138       url = @oxurl % CGI.escape(check)
139       if params[:british]
140         url << "?view=uk"
141       end
142       h = @bot.httputil.get(url, :max_redir => 5)
143       if h and h.match(/<h2>#{word}<\/h2>(.*)Perform/m)
144         m.reply("#{word} : #{url}") unless justcheck
145         defn = $1
146         m.reply("#{Bold}%s#{Bold}: %s" % [word, defn.ircify_html(:nbsp => :space)], :overlong => :truncate)
147         return true
148       end
149     }
150     return false if justcheck
151     m.reply "#{word} not found"
152   end
153
154   def is_british?(word)
155     return oxford(nil, :word => word, :justcheck => true, :british => true)
156   end
157
158
159   def chambers(m, params)
160     justcheck = params[:justcheck]
161
162     word = params[:word].to_s.downcase
163     url = @chambersurl % CGI.escape(word)
164     xml = nil
165     info = @bot.httputil.get_response(url) rescue nil
166     xml = info.body if info
167     case xml
168     when nil
169       info = info ? " (#{info.code} - #{info.message})" : ""
170       return false if justcheck
171       m.reply "An error occurred while looking for #{word}#{info}"
172       return
173     when /Sorry, no entries for <b>.*?<\/b> were found./
174       return false if justcheck
175       m.reply "Nothing found for #{word}"
176       return
177     when /No exact matches for <b>.*?<\/b>, but the following may be helpful./
178       return false if justcheck
179       m.reply "Nothing found for #{word}, but see #{url} for possible suggestions"
180       return
181     end
182     # Else, we have a hit
183     return true if justcheck
184     m.reply "#{word}: #{url}"
185     entries = xml.scan(CHAMBERS_LEMMA)
186     hits = @bot.config['dict.hits']
187     entries[0...hits].map { |ar|
188       m.reply(("#{Bold}%s#{Bold} #{Underline}%s#{Underline}%s" % ar).ircify_html, :overlong => :truncate)
189     }
190   end
191
192   def is_english?(word)
193     return chambers(nil, :word => word, :justcheck => true)
194   end
195
196   def littre(m, params)
197     justcheck = params[:justcheck]
198
199     word = params[:word].to_s.downcase
200     url = @littreurl % CGI.escape(word)
201     xml = nil
202     info = @bot.httputil.get_response(url) rescue nil
203     xml = info.body if info
204     head ||= xml.match(/<div class="entree">(.*?)<\/div>/)[1] rescue nil
205     case xml
206     when nil
207       info = info ? " (#{info.code} - #{info.message})" : ""
208       return false if justcheck
209       m.reply "An error occurred while looking for #{word}#{info}"
210       return
211     when /Erreur : le mot <STRONG>.*?<\/STRONG> n'a pas./
212       return false if justcheck
213       if head
214         m.reply "Nothing found for #{word}, I'll assume you meant #{head}"
215       else
216         m.reply "Nothing found for #{word}"
217         return
218       end
219     end
220     return true if justcheck
221     entete = xml.match(/<div class="entete">(.*?)<\/div>/m)[1] rescue nil
222     m.reply "#{head}: #{url} : #{entete.ircify_html rescue nil}"
223     entries = xml.scan(/<span class="variante">(.*?)<\!--variante-->/m)
224     hits = @bot.config['dict.hits']
225     n = 0
226     entries[0...hits].map { |ar|
227       n += 1
228       m.reply(("#{Bold}#{n}#{Bold} %s" % ar).ircify_html, :overlong => :truncate)
229     }
230   end
231
232   def is_french?(word)
233     return littre(nil, :word => word, :justcheck => true)
234   end
235
236 end
237
238 plugin = DictPlugin.new
239 plugin.map 'demauro :word', :action => 'demauro', :thread => true
240 plugin.map 'oxford *word', :action => 'oxford', :thread => true
241 plugin.map 'chambers *word', :action => 'chambers', :thread => true
242 plugin.map 'littre *word', :action => 'littre', :thread => true
243