4 # :title: Slashdot plugin for rbot
6 require 'rexml/document'
8 class SlashdotPlugin < Plugin
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.)"
14 # This method defines a filter for /. pages. It's needed because the generic
15 # summarization grabs a comment, not the actual article.
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/)
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
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?
34 # see if the div tag with generaltitle class is present
35 artitle = (el/"div.generaltitle").first
37 tits << artitle.to_html.ircify_html
40 # otherwise, check for skin+datitle a tags
41 datitle = (el/"a.datitle").first
43 skin = (el/"a.skin").first
45 skin ? skin.innerHTML.ircify_html : nil,
46 datitle.innerHTML.ircify_html
50 content = tits.join(" | ")
52 det = (arts.first/"div.details").first.to_html.ircify_html
53 body = (arts.first/"div.body").first.to_html.ircify_html
54 content = [det, body].join(' ')
56 return {:title => title, :content => content}
62 @bot.register_filter(:slashdot, :htmlinfo) { |s| slashdot_filter(s) }
66 def search_slashdot(m, params)
67 max = params[:limit].to_i
68 search = params[:search].to_s
70 xml = @bot.httputil.get("http://slashdot.org/search.pl?content_type=rss&query=#{CGI.escape(search)}")
72 m.reply "search for #{search} failed"
77 doc = Document.new xml
78 rescue REXML::ParseException => e
80 m.reply "couldn't parse output XML: #{e.class}"
84 m.reply "search for #{search} failed"
90 doc.elements.each("*/item") {|e|
91 desc = e.elements["title"].text
92 desc.gsub!(/(.{150}).*/, '\1..')
93 reply = sprintf("%s | %s", e.elements["link"].text, desc.ircify_html)
99 m.reply "search for #{search} failed"
103 def slashdot(m, params)
105 max = params[:limit].to_i
106 debug "max is #{max}"
107 xml = @bot.httputil.get('http://slashdot.org/slashdot.xml')
109 m.reply "slashdot news parse failed"
112 doc = Document.new xml
114 m.reply "slashdot news parse failed (invalid xml)"
125 doc.elements.each("*/story") {|e|
126 matches << [ e.elements["title"].text,
127 e.elements["author"].text,
128 e.elements["time"].text.gsub(/\d{4}-(\d{2})-(\d{2})/, "\\2/\\1").gsub(/:\d\d$/, "") ]
133 m.reply matches.collect{|mat| mat[0]}.join(" | ")
136 m.reply sprintf("%36s | %8s | %8s", mat[0][0,36], mat[1][0,8], mat[2])
141 plugin = SlashdotPlugin.new
142 plugin.map 'slashdot search :limit *search', :action => 'search_slashdot',
143 :defaults => {:limit => 4}, :requirements => {:limit => /^-?\d+$/}
144 plugin.map 'slashdot :limit', :defaults => {:limit => 4},
145 :requirements => {:limit => /^-?\d+$/}