4 # :title: Freshmeat plugin for rbot
6 require 'rexml/document'
8 class FreshmeatPlugin < Plugin
10 def help(plugin, topic="")
11 "freshmeat search [<max>=4] <string> => search freshmeat for <string>, freshmeat [<max>=4] => return up to <max> freshmeat headlines"
14 REL_ENTRY = %r{<a href="/(release)s/(\d+)/"><font color="#000000">(.*?)</font></a>}
15 PRJ_ENTRY = %r{<a href="/(project)s/(\S+?)/"><b>(.*?)</b></a>}
17 # This method defines a filter for fm pages. It's needed because the generic
18 # summarization grabs a comment, not the actual article.
20 def freshmeat_filter(s)
21 loc = Utils.check_location(s, /freshmeat\.net/)
24 s[:text].scan(/#{REL_ENTRY}|#{PRJ_ENTRY}/) { |m|
26 :type => ($1 || $4).dup,
27 :code => ($2 || $5).dup,
28 :name => ($3 || $6).dup
32 return nil if entries.empty?
33 title = s[:text].ircify_html_title
34 content = entries.inject([]) { |l, e| l << e[:name] }.join(" | ")
35 return {:title => title, :content => content}
40 @bot.register_filter(:freshmeat, :htmlinfo) { |s| freshmeat_filter(s) }
43 def search_freshmeat(m, params)
44 max = params[:limit].to_i
45 search = params[:search].to_s
47 xml = @bot.httputil.get("http://freshmeat.net/search-xml/?orderby=locate_projectname_full_DESC&q=#{CGI.escape(search)}")
49 m.reply "search for #{search} failed"
54 doc = Document.new xml
60 m.reply "search for #{search} failed"
68 doc.elements.each("*/match") {|e|
69 name = e.elements["projectname_short"].text
70 url = "http://freshmeat.net/projects/#{name}/"
71 desc = e.elements["desc_short"].text
72 title = e.elements["projectname_full"].text
73 #title_width = title.length if title.length > title_width
74 url_width = url.length if url.length > url_width
75 matches << [title, url, desc]
79 if matches.length == 0
80 m.reply "not found: #{search}"
86 desc.gsub!(/(.{#{max_width - 3 - url_width}}).*/, '\1..')
87 reply = sprintf("%s | %s", url.ljust(url_width), desc)
92 def freshmeat(m, params)
93 max = params[:limit].to_i
96 xml = @bot.httputil.get('http://freshmeat.net/backend/fm-releases-global.xml')
98 m.reply "freshmeat news parse failed"
101 doc = Document.new xml
103 m.reply "freshmeat news parse failed"
107 m.reply "freshmeat news parse failed"
115 doc.elements.each("*/channel/item") {|e|
116 desc = e.elements["description"].text.ircify_html
117 title = e.elements["title"].text.ircify_html
118 #title.gsub!(/\s+\(.*\)\s*$/, "")
120 title_width = title.length if title.length > title_width
121 matches << [title, desc]
128 #desc.gsub!(/(.{#{max_width - 3 - title_width}}).*/, '\1..')
129 #reply = sprintf("%#{title_width}s | %s", title, desc)
134 plugin = FreshmeatPlugin.new
135 plugin.map 'freshmeat search :limit *search', :action => 'search_freshmeat',
136 :defaults => {:limit => 4}, :requirements => {:limit => /^\d+$/}
137 plugin.map 'freshmeat :limit', :defaults => {:limit => 4},
138 :requirements => {:limit => /^\d+$/}