namespaces: move rbot-specific classes and modules from Irc::* to Irc::Bot::*
[rbot] / data / rbot / plugins / bash.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: bash.org quote retrieval
5 #
6 # Author:: Robin Kearney <robin@riviera.org.uk>
7 # Author:: cs
8 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
9 #
10 # Copyright:: (C) 2005 Robin Kearney
11 # Copyright:: (C) 2007 cs, Giuseppe Bilotta
12 #
13 # License:: public domain
14 #
15 # TODO improve output of quote
16 # TODO show more than one quote
17 # TODO allow selection of only quotes with vote > 0
18
19 require 'rexml/document'
20
21 class ::BashQuote
22   attr_accessor :num, :text, :irc_text, :vote
23
24   def initialize(num, text, vote)
25     @num = num.to_i
26     @text = text
27     @vote = vote
28     @irc_text = mk_irc_text
29   end
30
31   def url
32     "http://www.bash.org/?#{@num}"
33   end
34
35   private
36   def mk_irc_text
37     cur_nick = nil
38     last_nick = nil
39     text = String.new
40     @text.each_line { |l|
41       debug "line: #{l.inspect}"
42       cur_nick = l.match(/^\s*(&lt;.*?&gt;|\(.*?\)|.*?:)\s/)[1] rescue nil
43       debug "nick: #{cur_nick.inspect}; last: #{last_nick.inspect}"
44       if cur_nick and cur_nick == last_nick
45         text << l.sub(cur_nick,"")
46       else
47         last_nick = cur_nick.dup if cur_nick
48         text << l
49       end
50     }
51     debug text
52     # TODO: the gsub of br tags to | should be an ircify_html option
53     text.gsub(/(?:<br \/>\s*)+/, ' | ').ircify_html
54   end
55
56 end
57
58 class BashPlugin < Plugin
59
60   Config.register Config::EnumValue.new('bash.access',
61     :values => ['xml', 'html'], :default => 'html',
62     :desc => "Which method the bot should use to access bash.org quotes: xml files or standard webpages")
63
64   include REXML
65   def help(plugin, topic="")
66     "bash => print a random quote from bash.org, bash quote_id => print that quote id from bash.org, bash latest => print the latest quote from bash.org (currently broken, need to get josh@bash.org to fix the xml)"
67   end
68
69   def bash(m, params)
70     id = params[:id]
71     case @bot.config['bash.access'].intern
72     when :xml
73       xml_bash(m, id)
74     else
75       html_bash(m, :id => id)
76     end
77   end
78
79   def search(m, params)
80     esc = CGI.escape(params[:words].to_s)
81     html = @bot.httputil.get("http://bash.org/?search=#{esc}")
82     html_bash(m, :html => html)
83   end
84
85   def html_bash(m, opts={})
86     quotes = []
87
88     html = opts[:html]
89     if not html
90       id = opts[:id]
91       case id
92       when 'latest'
93         html = @bot.httputil.get("http://bash.org/?latest")
94       when nil
95         html = @bot.httputil.get("http://bash.org/?random", :cache => false)
96       else
97         html = @bot.httputil.get("http://bash.org/?" + id)
98       end
99     end
100
101     html_quotes = html.split(/<p class="quote">/)
102     html_quotes.each { |htqt|
103       # debug htqt.inspect
104       if htqt.match(/<a href="\?(\d+)"[^>]*>.*?\((-?\d+)\).*?<p class="qt">(.*)<\/p>\s+(?:<\/td>.*)?\z/m)
105         num = $1
106         vote = $2
107         text = $3
108         quotes << BashQuote.new(num, text, vote)
109       end
110     }
111
112     case quotes.length
113     when 0
114       m.reply "no quotes found"
115       return
116     when 1
117       quote = quotes.first
118     else
119       # For the time being, we only echo the first quote, but in the future we
120       # may want to echo more than one for latest/random
121       quote = quotes.first
122     end
123     m.reply "#%d (%d): %s" % [quote.num, quote.vote, quote.irc_text]
124   end
125
126   def xml_bash(m, id=nil)
127     case id
128     when 'latest'
129       xml = @bot.httputil.get("http://bash.org/xml/?latest&num=1")
130     when nil
131       xml = @bot.httputil.get("http://bash.org/xml/?random&num=1", :cache => false)
132     else
133       xml = @bot.httputil.get("http://bash.org/xml/?" + id + "&num=1")
134     end 
135
136     unless xml
137       m.reply "bash.org rss parse failed"
138       return
139     end
140     doc = Document.new xml
141     unless doc
142       m.reply "bash.org rss parse failed"
143       return
144     end
145     doc.elements.each("*/item") {|e|
146       if(id != 0) 
147         reply = e.elements["title"].text.gsub(/QDB: /,"") + " " + e.elements["link"].text.gsub(/QDB: /,"") + "\n"
148         reply = reply + e.elements["description"].text.gsub(/\<br \/\>/, "\n")
149       else
150         reply = e.elements["title"].text.gsub(/QDB: /,"") + " " + e.elements["link"].text.gsub(/QDB: /,"") + "\n"
151         reply = reply + e.elements["description"].text.gsub(/\<br \/\>/, "\n")
152       end
153       m.reply reply
154     }
155   end
156 end
157
158 plugin = BashPlugin.new
159
160 plugin.map "bash search *words", :action => :search
161 plugin.map "bash [:id]"
162