end
end
+ # Auxiliary method used to collect two lines for rss output filters,
+ # running substitutions against DataStream _s_ optionally joined
+ # with hash _h_
+ def make_stream(line1, line2, s, h={})
+ ss = s.merge(h)
+ DataStream.new([line1, line2].compact.join("\n") % ss, ss)
+ end
+
+ # Define default RSS filters
+ #
+ # TODO: load personal ones
+ def define_filters
+ @outkey = :"rss.out"
+ @bot.register_filter(:blog, @outkey) { |s|
+ author = s[:author] ? (s[:author] + " ") : ""
+ abt = s[:category] ? "about #{s[:category]} " : ""
+ line1 = "%{handle}%{date}%{author}blogged %{abt}at %{link}"
+ line2 = "%{handle}%{title} - %{desc}"
+ make_stream(line1, line2, s, :author => author, :abt => abt)
+ }
+ @bot.register_filter(:photoblog, @outkey) { |s|
+ author = s[:author] ? (s[:author] + " ") : ""
+ abt = s[:category] ? "under #{s[:category]} " : ""
+ line1 = "%{handle}%{date}%{author}added an image %{abt}at %{link}"
+ line2 = "%{handle}%{title} - %{desc}"
+ make_stream(line1, line2, s, :author => author, :abt => abt)
+ }
+ @bot.register_filter(:news, @outkey) { |s|
+ line1 = "%{handle}%{date}%{title} @ %{link}" % s
+ line2 = "%{handle}%{date}%{desc}" % s
+ make_stream(line1, line2, s)
+ }
+ @bot.register_filter(:git, @outkey) { |s|
+ author = s[:author] ? (s[:author] + " ") : ""
+ line1 = "%{handle}%{date}%{author}committed %{title} @ %{link}"
+ make_stream(line1, nil, s, :author => author)
+ }
+ @bot.register_filter(:forum, @outkey) { |s|
+ line1 = "%{handle}%{date}%{title}%{at}%{link}"
+ make_stream(line1, nil, s)
+ }
+ @bot.register_filter(:wiki, @outkey) { |s|
+ line1 = "%{handle}%{date}%{title}%{at}%{link}"
+ line1 << "has been edited by %{author}. %{desc}"
+ make_stream(line1, nil, s)
+ }
+ @bot.register_filter(:gmane, @outkey) { |s|
+ line1 = "%{handle}%{date}Message %{title} sent by %{author}. %{desc}"
+ make_stream(line1, nil, s)
+ }
+ @bot.register_filter(:trac, @outkey) { |s|
+ author = s[:author].sub(/@\S+?\s*>/, "@...>") + ": " if s[:author]
+ line1 = "%{handle}%{date}%{author}%{title} @ %{link}"
+ line2 = nil
+ unless s[:item].title =~ /^(?:Changeset \[(?:[\da-f]+)\]|\(git commit\))/
+ line2 = "%{handle}%{date}%{desc}"
+ end
+ make_stream(line1, line2, s, :author => author)
+ }
+ @bot.register_filter(:"/.", @outkey) { |s|
+ dept = "(from the #{s[:item].slash_department} dept) " rescue nil
+ sec = " in section #{s[:item].slash_section}" rescue nil
+ line1 = "%{handle}%{date}%{dept}%{title}%{at}%{link} "
+ line1 << "(posted by %{author}%{sec})"
+ make_stream(line1, nil, s, :dept => dept, :sec => sec)
+ }
+ @bot.register_filter(:default, @outkey) { |s|
+ line1 = "%{handle}%{date}%{title}%{at}%{link}"
+ line1 << " (by %{author})" if s[:author]
+ make_stream(line1, nil, s)
+ }
+
+ # Define an HTML info filter too
+ @bot.register_filter(:rss, :htmlinfo) { |s| htmlinfo_filter(s) }
+
+ # This is the output format used by the input filter
+ @bot.register_filter(:htmlinfo, @outkey) { |s|
+ line1 = "%{title}%{at}%{link}"
+ make_stream(line1, nil, s)
+ }
+ end
+
+ FEED_NS = %r{xmlns.*http://(purl\.org/rss|www.w3c.org/1999/02/22-rdf)}
+ def htmlinfo_filter(s)
+ return nil unless s[:headers] and s[:headers]['x-rbot-location']
+ return nil unless s[:headers]['content-type'].first.match(/xml|rss|atom|rdf/i) or
+ (s[:text].include?("<rdf:RDF") and s[:text].include?("<channel")) or
+ s[:text].include?("<rss") or s[:text].include?("<feed") or
+ s[:text].match(FEED_NS)
+ blob = RssBlob.new(s[:headers]['x-rbot-location'],"", :htmlinfo)
+ unless (fetchRss(blob, nil) and parseRss(blob, nil) rescue nil)
+ debug "#{s.pretty_inspect} is not an RSS feed, despite the appearances"
+ return nil
+ end
+ output = []
+ blob.items.each { |it|
+ output << printFormattedRss(blob, it)[:text]
+ }
+ return {:title => blob.title, :content => output.join(" | ")}
+ end
+
+ # Display the known rss types
+ def rss_types(m, params)
+ ar = @bot.filter_names(@outkey)
+ ar.delete(:default)
+ m.reply ar.map { |k| k.to_s }.sort!.join(", ")
+ end
+
attr_reader :feeds
def initialize
super
+
+ define_filters
+
if @registry.has_key?(:feeds)
# When migrating from Ruby 1.8.5 to 1.8.6, dumped Mutexes may render the
# data unrestorable. If this happens, we patch the data, thus allowing
"rss who watches #{Bold}handle#{Bold}: lists watches for rss #{Bold}handle#{Bold}"
when "rewatch"
"rss rewatch : restart threads that watch for changes in watched rss"
+ when "types"
+ "rss types : show the rss types for which an output format existi (all other types will use the default one)"
else
- "manage RSS feeds: rss show|list|watched|add|change|del(ete)|rm|(force)replace|watch|unwatch|rmwatch|rewatch|who watches"
+ "manage RSS feeds: rss types|show|list|watched|add|change|del(ete)|rm|(force)replace|watch|unwatch|rmwatch|rewatch|who watches"
end
end
if params and handle = params[:handle]
feed = @feeds.fetch(handle.downcase, nil)
if feed
- @bot.timer.reschedule(@watch[feed.handle], 0)
+ @bot.timer.reschedule(@watch[feed.handle], (params[:delay] || 0).to_f)
m.okay if m
else
m.reply _("no such feed %{handle}") % { :handle => handle } if m
def select_nonempty(*ar)
debug ar
- ret = ar.map { |i| (i && i.empty?) ? nil : i }.compact.first
- (ret && ret.empty?) ? nil : ret
+ ar.each { |i| return i unless i.nil_or_empty? }
+ return nil
end
def printFormattedRss(feed, item, opts=nil)
debug item
places = feed.watchers
- handle = "::#{feed.handle}:: "
+ handle = feed.handle.empty? ? "" : "::#{feed.handle}:: "
date = String.new
if opts
places = opts[:places] if opts.key?(:places)
desc = item.content_encoded.ircify_html(desc_opt)
elsif item.respond_to?(:description) && item.description
desc = item.description.ircify_html(desc_opt)
- else
+ elsif item.respond_to?(:content) && item.content
if item.content.type == "html"
desc = item.content.content.ircify_html(desc_opt)
else
desc = desc.slice(0, desc_opt[:limit]) + "#{Reverse}...#{Reverse}"
end
end
+ else
+ desc = "(?)"
end
link = item.link.href rescue item.link.chomp rescue nil
at = ((item.title && item.link) ? ' @ ' : '')
- case feed.type
- when 'blog'
- author += " " if author
- abt = category ? "about #{category} " : ""
- line1 = "#{handle}#{date}#{author}blogged #{abt}at #{link}"
- line2 = "#{handle}#{title} - #{desc}"
- when 'photoblog'
- author += " " if author
- abt = category ? "under #{category} " : ""
- line1 = "#{handle}#{date}#{author}added an image #{abt}at #{link}"
- line2 = "#{handle}#{title} - #{desc}"
- when 'news'
- line1 = "#{handle}#{date}#{title} @ #{link}"
- line2 = line2 = "#{handle}#{date}#{desc}"
- when 'git'
- author += " " if author
- line1 = "#{handle}#{date}#{author}commited #{title} @ #{link}"
- when 'forum'
- line1 = "#{handle}#{date}#{title}#{at}#{link}"
- when 'wiki'
- line1 = "#{handle}#{date}#{title}#{at}#{link} has been edited by #{author}. #{desc}"
- when 'gmane'
- line1 = "#{handle}#{date}Message #{title} sent by #{author}. #{desc}"
- when 'trac'
- author = author.sub(/@\S+?\s*>/, "@...>") + ": " if author
- line1 = "#{handle}#{date}#{author}#{title} @ #{link}"
- unless item.title =~ /^(?:Changeset \[(?:[\da-f]+)\]|\(git commit\))/
- line2 = "#{handle}#{date}#{desc}"
- end
- when '/.'
- dept = "(from the #{item.slash_department} dept) " rescue nil
- sec = " in section #{item.slash_section}" rescue nil
+ key = @bot.global_filter_name(feed.type, @outkey)
+ key = @bot.global_filter_name(:default, @outkey) unless @bot.has_filter?(key)
+
+ output = @bot.filter(key, :item => item, :handle => handle, :date => date,
+ :title => title, :desc => desc, :link => link,
+ :category => category, :author => author, :at => at)
+
+ return output if places.empty?
- line1 = "#{handle}#{date}#{dept}#{title}#{at}#{link} (posted by #{author}#{sec})"
- else
- line1 = "#{handle}#{date}#{title}#{at}#{link}"
- line1 << " (by #{author})" if author
- end
places.each { |loc|
- @bot.say loc, line1, :overlong => :truncate
- next unless line2
- @bot.say loc, line2, :overlong => :truncate
+ output.to_s.each_line { |line|
+ @bot.say loc, line, :overlong => :truncate
+ }
}
end
return nil
end
if rss.respond_to? :channel
- rss.channel.title ||= "Unknown"
+ rss.channel.title ||= "(?)"
title = rss.channel.title
else
title = rss.title.content
end
rss.items.each do |item|
- item.title ||= "Unknown"
+ item.title ||= "(?)"
items << item
end
end
:action => 'unwatch_rss'
plugin.map 'rss rmwatch :handle [in :chan]',
:action => 'unwatch_rss'
-plugin.map 'rss rewatch [:handle]',
+plugin.map 'rss rewatch [:handle] [:delay]',
:action => 'rewatch_rss'
+plugin.map 'rss types',
+ :action => 'rss_types'