]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/slashdot.rb
plugins: raise a descriptive LoadError when the db is corrupt on load
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / slashdot.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Slashdot plugin for rbot
5
6 require 'rexml/document'
7
8 class SlashdotPlugin < Plugin
9   include REXML
10   def help(plugin, topic="")
11     "slashdot search <string> [<max>=4] => search slashdot for <string>, slashdot [<max>=4] => return up to <max> slashdot headlines (use negative max to return that many headlines, but all on one line.)"
12   end
13
14   # This method defines a filter for /. pages. It's needed because the generic
15   # summarization grabs a comment, not the actual article.
16   #
17   # This filter relies on Hpricot being available, since REXML isn't too
18   # happy with the /. pages
19   def slashdot_filter(s)
20     return nil unless defined? Hpricot
21     loc = Utils.check_location(s, /slashdot\.org/)
22     return nil unless loc
23     h = Hpricot(s[:text])
24     # If we have no title tag in a head tag, return as this is not
25     # a /. page (it's probably a Slashdot RSS
26     ht = h/"head/title"
27     return nil if ht.empty?
28     title = ht.first.to_html.ircify_html
29     arts = (h/"div.article")
30     return nil if arts.empty?
31     if arts.length > 1
32       tits = []
33       arts.each { |el|
34         artitle = (el/"div.generaltitle").first.to_html.ircify_html
35         tits << artitle
36       }
37       content = tits.join(" | ")
38     else
39       det = (arts.first/"div.details").first.to_html.ircify_html
40       body = (arts.first/"div.body").first.to_html.ircify_html
41       content = [det, body].join(' ')
42     end
43     return {:title => title, :content => content}
44   end
45
46   def initialize
47     super
48     if defined? Hpricot
49       @bot.register_filter(:slashdot, :htmlinfo) { |s| slashdot_filter(s) }
50     end
51   end
52   
53   def search_slashdot(m, params)
54     max = params[:limit].to_i
55     search = params[:search].to_s
56
57     xml = @bot.httputil.get("http://slashdot.org/search.pl?content_type=rss&query=#{CGI.escape(search)}")
58     unless xml
59       m.reply "search for #{search} failed"
60       return
61     end
62     debug xml.inspect
63     begin
64       doc = Document.new xml
65     rescue REXML::ParseException => e
66       warning e.inspect
67       m.reply "couldn't parse output XML: #{e.class}"
68       return
69     end
70     unless doc
71       m.reply "search for #{search} failed"
72       return
73     end
74     debug doc.inspect
75     max = 8 if max > 8
76     done = 0
77     doc.elements.each("*/item") {|e|
78       desc = e.elements["title"].text
79       desc.gsub!(/(.{150}).*/, '\1..')
80       reply = sprintf("%s | %s", e.elements["link"].text, desc.ircify_html)
81       m.reply reply
82       done += 1
83       break if done >= max
84     }
85     unless done > 0
86       m.reply "search for #{search} failed"
87     end
88   end
89   
90   def slashdot(m, params)
91     debug params.inspect
92     max = params[:limit].to_i
93     debug "max is #{max}"
94     xml = @bot.httputil.get('http://slashdot.org/slashdot.xml')
95     unless xml
96       m.reply "slashdot news parse failed"
97       return
98     end
99     doc = Document.new xml
100     unless doc
101       m.reply "slashdot news parse failed (invalid xml)"
102       return
103     end
104     done = 0
105     oneline = false
106     if max < 0
107       max = (0 - max)
108       oneline = true
109     end
110     max = 8 if max > 8
111     matches = Array.new
112     doc.elements.each("*/story") {|e|
113       matches << [ e.elements["title"].text, 
114                    e.elements["author"].text, 
115                    e.elements["time"].text.gsub(/\d{4}-(\d{2})-(\d{2})/, "\\2/\\1").gsub(/:\d\d$/, "") ]
116       done += 1
117       break if done >= max
118     } 
119     if oneline
120       m.reply matches.collect{|mat| mat[0]}.join(" | ")
121     else
122       matches.each {|mat|
123         m.reply sprintf("%36s | %8s | %8s", mat[0][0,36], mat[1][0,8], mat[2])
124       }
125     end
126   end
127 end
128 plugin = SlashdotPlugin.new
129 plugin.map 'slashdot search :limit *search', :action => 'search_slashdot',
130            :defaults => {:limit => 4}, :requirements => {:limit => /^-?\d+$/}
131 plugin.map 'slashdot :limit', :defaults => {:limit => 4},
132                               :requirements => {:limit => /^-?\d+$/}