+ # Auxiliary method used to collect two lines for rss output filters,
+ # running substitutions against DataStream _s_ optionally joined
+ # with hash _h_.
+ #
+ # For substitutions, *_wrap keys can be used to alter the content of
+ # other nonempty keys. If the value of *_wrap is a String, it will be
+ # put before and after the corresponding key; if it's an Array, the first
+ # and second elements will be used for wrapping; if it's nil, no wrapping
+ # will be done (useful to override a default wrapping).
+ #
+ # For example:
+ # :handle_wrap => '::'::
+ # will wrap s[:handle] by prefixing and postfixing it with '::'
+ # :date_wrap => [nil, ' :: ']::
+ # will put ' :: ' after s[:date]
+ def make_stream(line1, line2, s, h={})
+ ss = s.merge(h)
+ subs = {}
+ wraps = {}
+ ss.each do |k, v|
+ kk = k.to_s.chomp!('_wrap')
+ if kk
+ nk = kk.intern
+ case v
+ when String
+ wraps[nk] = ss[nk].wrap_nonempty(v, v)
+ when Array
+ wraps[nk] = ss[nk].wrap_nonempty(*v)
+ when nil
+ # do nothing
+ else
+ warning "ignoring #{v.inspect} wrapping of unknown class"
+ end unless ss[nk].nil?
+ else
+ subs[k] = v
+ end
+ end
+ subs.merge! wraps
+ DataStream.new([line1, line2].compact.join("\n") % subs, ss)
+ end
+
+ # Auxiliary method used to define rss output filters
+ def rss_type(key, &block)
+ @bot.register_filter(key, @outkey, &block)
+ end
+
+ # Define default output filters (rss types), and load custom ones.
+ # Custom filters are looked for in the plugin's default filter locations
+ # and in rss/types.rb under botclass.
+ # Preferably, the rss_type method should be used in these files, e.g.:
+ # rss_type :my_type do |s|
+ # line1 = "%{handle} and some %{author} info"
+ # make_stream(line1, nil, s)
+ # end
+ # to define the new type 'my_type'. The keys available in the DataStream
+ # are:
+ # item::
+ # the actual rss item
+ # handle::
+ # the item handle
+ # date::
+ # the item date
+ # title::
+ # the item title
+ # desc, link, category, author::
+ # the item description, link, category, author
+ # at::
+ # the string ' @ ' if the item has both an title and a link
+ # handle_wrap, date_wrap, title_wrap, ...::
+ # these keys can be defined to wrap the corresponding elements if they
+ # are nonempty. By default handle is wrapped with '::', date has a ' ::'
+ # appended and title is enbolden
+ #
+ def define_filters
+ @outkey ||= :"rss.out"
+
+ # Define an HTML info filter
+ @bot.register_filter(:rss, :htmlinfo) { |s| htmlinfo_filter(s) }
+ # This is the output format used by the input filter
+ rss_type :htmlinfo do |s|
+ line1 = "%{title}%{at}%{link}"
+ make_stream(line1, nil, s)
+ end
+
+ # the default filter
+ rss_type :default do |s|
+ line1 = "%{handle}%{date}%{title}%{at}%{link}"
+ line1 << " (by %{author})" if s[:author]
+ make_stream(line1, nil, s)
+ end
+
+ @user_types ||= datafile 'types.rb'
+ load_filters
+ load_filters :path => @user_types
+ 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
+