X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=data%2Frbot%2Fplugins%2Frss.rb;h=13689bf14efdfeb63136de3a437ecbd64a71a6a3;hb=9d29f400bb3a354779185d61049ce7cdfa7744ee;hp=8f5420c5ea73d228ee759fcb17f6bb62cc0388a0;hpb=f10419698c7889d84d325e37250f8534f2645767;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/data/rbot/plugins/rss.rb b/data/rbot/plugins/rss.rb index 8f5420c5..13689bf1 100644 --- a/data/rbot/plugins/rss.rb +++ b/data/rbot/plugins/rss.rb @@ -1,7 +1,12 @@ +#-- vim:sw=2:et +#++ +# # RSS feed plugin for RubyBot # (c) 2004 Stanislav Karchebny # (c) 2005 Ian Monroe # (c) 2005 Mark Kretschmann +# (c) 2006 Giuseppe Bilotta +# # Licensed under MIT License. require 'rss/parser' @@ -26,10 +31,6 @@ class ::String def riphtml self.gsub(/<[^>]+>/, '').gsub(/&/,'&').gsub(/"/,'"').gsub(/</,'<').gsub(/>/,'>').gsub(/&ellip;/,'...').gsub(/'/, "'").gsub("\n",'') end - - def mysqlize - self.gsub(/'/, "''") - end end class ::RssBlob @@ -46,7 +47,17 @@ class ::RssBlob @handle = url end @type = type - @watchers = watchers + @watchers=[] + sanitize_watchers(watchers) + end + + # Downcase all watchers, possibly turning them into Strings if they weren't + def sanitize_watchers(list=@watchers) + ls = list.dup + @watchers.clear + ls.each { |w| + add_watch(w) + } end def watched? @@ -54,19 +65,19 @@ class ::RssBlob end def watched_by?(who) - @watchers.include?(who) + @watchers.include?(who.downcase) end def add_watch(who) if watched_by?(who) return nil end - @watchers << who unless watched_by?(who) + @watchers << who.downcase return who end def rm_watch(who) - @watchers.delete(who) + @watchers.delete(who.downcase) end def to_a @@ -79,7 +90,7 @@ class ::RssBlob else a = self.to_a[0,3] end - a.join(" | ") + a.compact.join(" | ") end end @@ -94,42 +105,57 @@ class RSSFeedsPlugin < Plugin BotConfig.register BotConfigIntegerValue.new('rss.thread_sleep', :default => 300, :validate => Proc.new{|v| v > 30}, - :desc => "How many characters to use of a RSS item text") - - @@watchThreads = Hash.new - @@mutex = Mutex.new + :desc => "How many seconds to sleep before checking RSS feeds again") def initialize super - kill_threads if @registry.has_key?(:feeds) @feeds = @registry[:feeds] + @feeds.keys.grep(/[A-Z]/) { |k| + @feeds[k.downcase] = @feeds[k] + @feeds.delete(k) + } + @feeds.each { |k, f| + f.sanitize_watchers + } else @feeds = Hash.new end + @watch = Hash.new rewatch_rss end + def name + "rss" + end + def watchlist @feeds.select { |h, f| f.watched? } end def cleanup - kill_threads + stop_watches end def save @registry[:feeds] = @feeds end - def kill_threads - @@mutex.synchronize { - # Abort all running threads. - @@watchThreads.each { |url, thread| - debug "Killing thread for #{url}" - thread.kill - } - @@watchThreads = Hash.new + def stop_watch(handle) + if @watch.has_key?(handle) + begin + debug "Stopping watch #{handle}" + @bot.timer.remove(@watch[handle]) + @watch.delete(handle) + rescue => e + report_problem("Failed to stop watch for #{handle}", e, nil) + end + end + end + + def stop_watches + @watch.each_key { |k| + stop_watch(k) } end @@ -182,11 +208,11 @@ class RSSFeedsPlugin < Plugin rev = lims[1].to_i > lims[2].to_i else ll = 0 - ul = [[lims[1].to_i-1, 1].max, 14].min + ul = [[lims[1].to_i-1, 0].max, 14].min rev = false end - feed = @feeds.fetch(handle, nil) + feed = @feeds.fetch(handle.downcase, nil) unless feed m.reply "I don't know any feeds named #{handle}" return @@ -194,9 +220,7 @@ class RSSFeedsPlugin < Plugin m.reply "lemme fetch it..." title = items = nil - @@mutex.synchronize { - title, items = fetchRss(feed, m) - } + title, items = fetchRss(feed, m) return unless items # We sort the feeds in freshness order (newer ones first) @@ -211,8 +235,8 @@ class RSSFeedsPlugin < Plugin end def itemDate(item,ex=nil) - return item.pubDate if item.respond_to?(:pubDate) - return item.date if item.respond_to?(:date) + return item.pubDate if item.respond_to?(:pubDate) and item.pubDate + return item.date if item.respond_to?(:date) and item.date return ex end @@ -226,13 +250,11 @@ class RSSFeedsPlugin < Plugin def list_rss(m, params) wanted = params[:handle] reply = String.new - @@mutex.synchronize { - @feeds.each { |handle, feed| - next if wanted and !handle.match(wanted) - reply << "#{feed.handle}: #{feed.url} (in format: #{feed.type ? feed.type : 'default'})" - (reply << " (watched)") if feed.watched_by?(m.replyto) - reply << "\n" - } + @feeds.each { |handle, feed| + next if wanted and !handle.match(/#{wanted}/i) + reply << "#{feed.handle}: #{feed.url} (in format: #{feed.type ? feed.type : 'default'})" + (reply << " (watched)") if feed.watched_by?(m.replyto) + reply << "\n" } if reply.empty? reply = "no feeds found" @@ -244,12 +266,10 @@ class RSSFeedsPlugin < Plugin def watched_rss(m, params) wanted = params[:handle] reply = String.new - @@mutex.synchronize { - watchlist.each { |handle, feed| - next if wanted and !handle.match(wanted) - next unless feed.watched_by?(m.replyto) - reply << "#{feed.handle}: #{feed.url} (in format: #{feed.type ? feed.type : 'default'})\n" - } + watchlist.each { |handle, feed| + next if wanted and !handle.match(/#{wanted}/i) + next unless feed.watched_by?(m.replyto) + reply << "#{feed.handle}: #{feed.url} (in format: #{feed.type ? feed.type : 'default'})\n" } if reply.empty? reply = "no watched feeds" @@ -266,17 +286,15 @@ class RSSFeedsPlugin < Plugin return end type = params[:type] - if @feeds.fetch(handle, nil) && !force - m.reply "There is already a feed named #{handle} (URL: #{@feeds[handle].url})" + if @feeds.fetch(handle.downcase, nil) && !force + m.reply "There is already a feed named #{handle} (URL: #{@feeds[handle.downcase].url})" return end unless url m.reply "You must specify both a handle and an url to add an RSS feed" return end - @@mutex.synchronize { - @feeds[handle] = RssBlob.new(url,handle,type) - } + @feeds[handle.downcase] = RssBlob.new(url,handle,type) reply = "Added RSS #{url} named #{handle}" if type reply << " (format: #{type})" @@ -291,19 +309,17 @@ class RSSFeedsPlugin < Plugin m.reply "someone else is watching #{feed.handle}, I won't remove it from my list" return end - @@mutex.synchronize { - @feeds.delete(feed.handle) - } + @feeds.delete(feed.handle.downcase) m.okay unless pass return end def replace_rss(m, params) handle = params[:handle] - if @feeds.key?(handle) + if @feeds.key?(handle.downcase) del_rss(m, {:handle => handle}, true) end - if @feeds.key?(handle) + if @feeds.key?(handle.downcase) m.reply "can't replace #{feed.handle}" else add_rss(m, params, true) @@ -321,26 +337,21 @@ class RSSFeedsPlugin < Plugin if url add_rss(m, params) end - feed = nil - @@mutex.synchronize { - feed = @feeds.fetch(handle, nil) - } + feed = @feeds.fetch(handle.downcase, nil) if feed - @@mutex.synchronize { - if feed.add_watch(m.replyto) - watchRss(feed, m) - m.okay - else - m.reply "Already watching #{feed.handle}" - end - } + if feed.add_watch(m.replyto) + watchRss(feed, m) + m.okay + else + m.reply "Already watching #{feed.handle}" + end else m.reply "Couldn't watch feed #{handle} (no such feed found)" end end def unwatch_rss(m, params, pass=false) - handle = params[:handle] + handle = params[:handle].downcase unless @feeds.has_key?(handle) m.reply("dunno that feed") return @@ -352,19 +363,13 @@ class RSSFeedsPlugin < Plugin m.reply("#{m.replyto} wasn't watching #{feed.handle}") unless pass end if !feed.watched? - @@mutex.synchronize { - if @@watchThreads[handle].kind_of? Thread - @@watchThreads[handle].kill - debug "rmwatch: Killed thread for #{handle}" - @@watchThreads.delete(handle) - end - } + stop_watch(handle) end return feed end - def rewatch_rss(m=nil) - kill_threads + def rewatch_rss(m=nil, params=nil) + stop_watches # Read watches from list. watchlist.each{ |handle, feed| @@ -375,60 +380,64 @@ class RSSFeedsPlugin < Plugin private def watchRss(feed, m=nil) - if @@watchThreads.has_key?(feed.handle) + if @watch.has_key?(feed.handle) report_problem("watcher thread for #{feed.handle} is already running", nil, m) return end - @@watchThreads[feed.handle] = Thread.new do + status = Hash.new + status[:oldItems] = [] + status[:firstRun] = true + status[:failures] = 0 + @watch[feed.handle] = @bot.timer.add(0, status) { debug "watcher for #{feed} started" - oldItems = [] - firstRun = true - failures = 0 - loop do - begin - debug "fetching #{feed}" - title = newItems = nil - @@mutex.synchronize { - title, newItems = fetchRss(feed) - } - unless newItems - debug "no items in feed #{feed}" - failures +=1 + oldItems = status[:oldItems] + firstRun = status[:firstRun] + failures = status[:failures] + begin + debug "fetching #{feed}" + title = newItems = nil + title, newItems = fetchRss(feed) + unless newItems + debug "no items in feed #{feed}" + failures +=1 + else + debug "Checking if new items are available for #{feed}" + if firstRun + debug "First run, we'll see next time" + firstRun = false else - debug "Checking if new items are available for #{feed}" - if firstRun - debug "First run, we'll see next time" - firstRun = false - else - otxt = oldItems.map { |item| item.to_s } - dispItems = newItems.reject { |item| - otxt.include?(item.to_s) + otxt = oldItems.map { |item| item.to_s } + dispItems = newItems.reject { |item| + otxt.include?(item.to_s) + } + if dispItems.length > 0 + debug "Found #{dispItems.length} new items in #{feed}" + # When displaying watched feeds, publish them from older to newer + dispItems.reverse.each { |item| + printFormattedRss(feed, item) } - if dispItems.length > 0 - debug "Found #{dispItems.length} new items in #{feed}" - dispItems.each { |item| - @@mutex.synchronize { - printFormattedRss(feed, item) - } - } - else - debug "No new items found in #{feed}" - end + else + debug "No new items found in #{feed}" end - oldItems = newItems.dup end - rescue Exception => e - error "Error watching #{feed}: #{e.inspect}" - debug e.backtrace.join("\n") - failures += 1 + oldItems = newItems.dup end - - seconds = @bot.config['rss.thread_sleep'] * (failures + 1) - seconds += seconds * (rand(100)-50)/100 - debug "watcher for #{feed} going to sleep #{seconds} seconds.." - sleep seconds + rescue Exception => e + error "Error watching #{feed}: #{e.inspect}" + debug e.backtrace.join("\n") + failures += 1 end - end + + status[:oldItems] = oldItems + status[:firstRun] = firstRun + status[:failures] = failures + + seconds = @bot.config['rss.thread_sleep'] * (failures + 1) + seconds += seconds * (rand(100)-50)/100 + debug "watcher for #{feed} going to sleep #{seconds} seconds.." + @bot.timer.reschedule(@watch[feed.handle], seconds) + } + debug "watcher for #{feed} added" end def printFormattedRss(feed, item, opts=nil) @@ -485,7 +494,7 @@ class RSSFeedsPlugin < Plugin def fetchRss(feed, m=nil) begin # Use 60 sec timeout, cause the default is too low - xml = @bot.httputil.get_cached(feed.url,60,60) + xml = @bot.httputil.get_cached(feed.url, 60, 60) rescue URI::InvalidURIError, URI::BadURIError => e report_problem("invalid rss feed #{feed.url}", e, m) return @@ -578,6 +587,6 @@ plugin.map 'rss unwatch :handle', :action => 'unwatch_rss' plugin.map 'rss rmwatch :handle', :action => 'unwatch_rss' -plugin.map 'rss rewatch :handle', +plugin.map 'rss rewatch', :action => 'rewatch_rss'