rbot 0.9.9 → 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. data/AUTHORS +8 -0
  2. data/ChangeLog +51 -0
  3. data/INSTALL +4 -0
  4. data/README +1 -0
  5. data/REQUIREMENTS +11 -0
  6. data/TODO +2 -0
  7. data/bin/rbot +21 -2
  8. data/data/rbot/languages/german.lang +4 -1
  9. data/data/rbot/languages/russian.lang +75 -0
  10. data/data/rbot/plugins/autoop.rb +42 -51
  11. data/data/rbot/plugins/bans.rb +205 -0
  12. data/data/rbot/plugins/bash.rb +56 -0
  13. data/data/rbot/plugins/chucknorris.rb +74 -0
  14. data/data/rbot/plugins/chucknorris.yml.gz +0 -0
  15. data/data/rbot/plugins/deepthoughts.rb +95 -0
  16. data/data/rbot/plugins/demauro.rb +95 -0
  17. data/data/rbot/plugins/digg.rb +51 -0
  18. data/data/rbot/plugins/figlet.rb +24 -0
  19. data/data/rbot/plugins/forecast.rb +133 -0
  20. data/data/rbot/plugins/freshmeat.rb +13 -7
  21. data/data/rbot/plugins/google.rb +2 -0
  22. data/data/rbot/plugins/grouphug.rb +36 -0
  23. data/data/rbot/plugins/imdb.rb +92 -0
  24. data/data/rbot/plugins/insult.rb +8 -1
  25. data/data/rbot/plugins/iplookup.rb +227 -0
  26. data/data/rbot/plugins/karma.rb +2 -2
  27. data/data/rbot/plugins/keywords.rb +470 -0
  28. data/data/rbot/plugins/lart.rb +132 -146
  29. data/data/rbot/plugins/lastfm.rb +25 -0
  30. data/data/rbot/plugins/markov.rb +204 -0
  31. data/data/rbot/plugins/math.rb +5 -1
  32. data/data/rbot/plugins/nickserv.rb +71 -11
  33. data/data/rbot/plugins/opme.rb +19 -19
  34. data/data/rbot/plugins/quakeauth.rb +2 -2
  35. data/data/rbot/plugins/quotes.rb +40 -25
  36. data/data/rbot/plugins/remind.rb +1 -1
  37. data/data/rbot/plugins/rot13.rb +2 -2
  38. data/data/rbot/plugins/roulette.rb +49 -15
  39. data/data/rbot/plugins/rss.rb +585 -0
  40. data/data/rbot/plugins/rubyurl.rb +39 -0
  41. data/data/rbot/plugins/seen.rb +2 -1
  42. data/data/rbot/plugins/slashdot.rb +5 -5
  43. data/data/rbot/plugins/spell.rb +5 -0
  44. data/data/rbot/plugins/theyfightcrime.rb +121 -0
  45. data/data/rbot/plugins/threat.rb +55 -0
  46. data/data/rbot/plugins/tinyurl.rb +39 -0
  47. data/data/rbot/plugins/topic.rb +204 -0
  48. data/data/rbot/plugins/urban.rb +71 -0
  49. data/data/rbot/plugins/url.rb +399 -4
  50. data/data/rbot/plugins/wow.rb +123 -0
  51. data/data/rbot/plugins/wserver.rb +1 -1
  52. data/data/rbot/templates/levels.rbot +2 -0
  53. data/lib/rbot/auth.rb +207 -96
  54. data/lib/rbot/channel.rb +5 -5
  55. data/lib/rbot/config.rb +125 -24
  56. data/lib/rbot/dbhash.rb +87 -21
  57. data/lib/rbot/httputil.rb +181 -13
  58. data/lib/rbot/ircbot.rb +525 -179
  59. data/lib/rbot/ircsocket.rb +330 -54
  60. data/lib/rbot/message.rb +66 -23
  61. data/lib/rbot/messagemapper.rb +25 -17
  62. data/lib/rbot/plugins.rb +244 -115
  63. data/lib/rbot/post-clean.rb +1 -0
  64. data/lib/rbot/{post-install.rb → post-config.rb} +1 -1
  65. data/lib/rbot/rbotconfig.rb +29 -14
  66. data/lib/rbot/registry.rb +111 -72
  67. data/lib/rbot/rfc2812.rb +208 -197
  68. data/lib/rbot/timer.rb +4 -0
  69. data/lib/rbot/utils.rb +2 -2
  70. metadata +127 -104
  71. data/data/rbot/plugins/rss.rb.disabled +0 -414
  72. data/lib/rbot/keywords.rb +0 -433
@@ -0,0 +1,585 @@
1
+ # RSS feed plugin for RubyBot
2
+ # (c) 2004 Stanislav Karchebny <berkus@madfire.net>
3
+ # (c) 2005 Ian Monroe <ian@monroe.nu>
4
+ # (c) 2005 Mark Kretschmann <markey@web.de>
5
+ # Licensed under MIT License.
6
+
7
+ require 'rss/parser'
8
+ require 'rss/1.0'
9
+ require 'rss/2.0'
10
+ require 'rss/dublincore'
11
+ # begin
12
+ # require 'rss/dublincore/2.0'
13
+ # rescue
14
+ # warning "Unable to load RSS libraries, RSS plugin functionality crippled"
15
+ # end
16
+
17
+ class ::String
18
+ def shorten(limit)
19
+ if self.length > limit
20
+ self+". " =~ /^(.{#{limit}}[^.!;?]*[.!;?])/mi
21
+ return $1
22
+ end
23
+ self
24
+ end
25
+
26
+ def riphtml
27
+ self.gsub(/<[^>]+>/, '').gsub(/&amp;/,'&').gsub(/&quot;/,'"').gsub(/&lt;/,'<').gsub(/&gt;/,'>').gsub(/&ellip;/,'...').gsub(/&apos;/, "'").gsub("\n",'')
28
+ end
29
+
30
+ def mysqlize
31
+ self.gsub(/'/, "''")
32
+ end
33
+ end
34
+
35
+ class ::RssBlob
36
+ attr :url
37
+ attr :handle
38
+ attr :type
39
+ attr :watchers
40
+
41
+ def initialize(url,handle=nil,type=nil,watchers=[])
42
+ @url = url
43
+ if handle
44
+ @handle = handle
45
+ else
46
+ @handle = url
47
+ end
48
+ @type = type
49
+ @watchers = watchers
50
+ end
51
+
52
+ def watched?
53
+ !@watchers.empty?
54
+ end
55
+
56
+ def watched_by?(who)
57
+ @watchers.include?(who)
58
+ end
59
+
60
+ def add_watch(who)
61
+ if watched_by?(who)
62
+ return nil
63
+ end
64
+ @watchers << who unless watched_by?(who)
65
+ return who
66
+ end
67
+
68
+ def rm_watch(who)
69
+ @watchers.delete(who)
70
+ end
71
+
72
+ def to_a
73
+ [@handle,@url,@type,@watchers]
74
+ end
75
+
76
+ def to_s(watchers=false)
77
+ if watchers
78
+ a = self.to_a.flatten
79
+ else
80
+ a = self.to_a[0,3]
81
+ end
82
+ a.join(" | ")
83
+ end
84
+ end
85
+
86
+ class RSSFeedsPlugin < Plugin
87
+ BotConfig.register BotConfigIntegerValue.new('rss.head_max',
88
+ :default => 30, :validate => Proc.new{|v| v > 0 && v < 200},
89
+ :desc => "How many characters to use of a RSS item header")
90
+
91
+ BotConfig.register BotConfigIntegerValue.new('rss.text_max',
92
+ :default => 90, :validate => Proc.new{|v| v > 0 && v < 400},
93
+ :desc => "How many characters to use of a RSS item text")
94
+
95
+ BotConfig.register BotConfigIntegerValue.new('rss.thread_sleep',
96
+ :default => 300, :validate => Proc.new{|v| v > 30},
97
+ :desc => "How many characters to use of a RSS item text")
98
+
99
+ @@watchThreads = Hash.new
100
+ @@mutex = Mutex.new
101
+
102
+ def initialize
103
+ super
104
+ kill_threads
105
+ if @registry.has_key?(:feeds)
106
+ @feeds = @registry[:feeds]
107
+ else
108
+ @feeds = Hash.new
109
+ end
110
+ rewatch_rss
111
+ end
112
+
113
+ def watchlist
114
+ @feeds.select { |h, f| f.watched? }
115
+ end
116
+
117
+ def cleanup
118
+ kill_threads
119
+ end
120
+
121
+ def save
122
+ @registry[:feeds] = @feeds
123
+ end
124
+
125
+ def kill_threads
126
+ @@mutex.synchronize {
127
+ # Abort all running threads.
128
+ @@watchThreads.each { |url, thread|
129
+ debug "Killing thread for #{url}"
130
+ thread.kill
131
+ }
132
+ @@watchThreads = Hash.new
133
+ }
134
+ end
135
+
136
+ def help(plugin,topic="")
137
+ case topic
138
+ when "show"
139
+ "rss show #{Bold}handle#{Bold} [#{Bold}limit#{Bold}] : show #{Bold}limit#{Bold} (default: 5, max: 15) entries from rss #{Bold}handle#{Bold}; #{Bold}limit#{Bold} can also be in the form a..b, to display a specific range of items"
140
+ when "list"
141
+ "rss list [#{Bold}handle#{Bold}] : list all rss feeds (matching #{Bold}handle#{Bold})"
142
+ when "watched"
143
+ "rss watched [#{Bold}handle#{Bold}] : list all watched rss feeds (matching #{Bold}handle#{Bold})"
144
+ when "add"
145
+ "rss add #{Bold}handle#{Bold} #{Bold}url#{Bold} [#{Bold}type#{Bold}] : add a new rss called #{Bold}handle#{Bold} from url #{Bold}url#{Bold} (of type #{Bold}type#{Bold})"
146
+ when /^(del(ete)?|rm)$/
147
+ "rss del(ete)|rm #{Bold}handle#{Bold} : delete rss feed #{Bold}handle#{Bold}"
148
+ when "replace"
149
+ "rss replace #{Bold}handle#{Bold} #{Bold}url#{Bold} [#{Bold}type#{Bold}] : try to replace the url of rss called #{Bold}handle#{Bold} with #{Bold}url#{Bold} (of type #{Bold}type#{Bold}); only works if nobody else is watching it"
150
+ when "forcereplace"
151
+ "rss forcereplace #{Bold}handle#{Bold} #{Bold}url#{Bold} [#{Bold}type#{Bold}] : replace the url of rss called #{Bold}handle#{Bold} with #{Bold}url#{Bold} (of type #{Bold}type#{Bold})"
152
+ when "watch"
153
+ "rss watch #{Bold}handle#{Bold} [#{Bold}url#{Bold} [#{Bold}type#{Bold}]] : watch rss #{Bold}handle#{Bold} for changes; when the other parameters are present, it will be created if it doesn't exist yet"
154
+ when /(un|rm)watch/
155
+ "rss unwatch|rmwatch #{Bold}handle#{Bold} : stop watching rss #{Bold}handle#{Bold} for changes"
156
+ when "rewatch"
157
+ "rss rewatch : restart threads that watch for changes in watched rss"
158
+ else
159
+ "manage RSS feeds: rss show|list|watched|add|del(ete)|rm|(force)replace|watch|unwatch|rmwatch|rewatch"
160
+ end
161
+ end
162
+
163
+ def report_problem(report, e=nil, m=nil)
164
+ if m && m.respond_to?(:reply)
165
+ m.reply report
166
+ else
167
+ warning report
168
+ end
169
+ if e
170
+ debug e.inspect
171
+ debug e.backtrace.join("\n") if e.respond_to?(:backtrace)
172
+ end
173
+ end
174
+
175
+ def show_rss(m, params)
176
+ handle = params[:handle]
177
+ lims = params[:limit].to_s.match(/(\d+)(?:..(\d+))?/)
178
+ debug lims.to_a.inspect
179
+ if lims[2]
180
+ ll = [[lims[1].to_i-1,lims[2].to_i-1].min, 0].max
181
+ ul = [[lims[1].to_i-1,lims[2].to_i-1].max, 14].min
182
+ rev = lims[1].to_i > lims[2].to_i
183
+ else
184
+ ll = 0
185
+ ul = [[lims[1].to_i-1, 1].max, 14].min
186
+ rev = false
187
+ end
188
+
189
+ feed = @feeds.fetch(handle, nil)
190
+ unless feed
191
+ m.reply "I don't know any feeds named #{handle}"
192
+ return
193
+ end
194
+
195
+ m.reply "lemme fetch it..."
196
+ title = items = nil
197
+ @@mutex.synchronize {
198
+ title, items = fetchRss(feed, m)
199
+ }
200
+ return unless items
201
+
202
+ # We sort the feeds in freshness order (newer ones first)
203
+ items = freshness_sort(items)
204
+ disp = items[ll..ul]
205
+ disp.reverse! if rev
206
+
207
+ m.reply "Channel : #{title}"
208
+ disp.each do |item|
209
+ printFormattedRss(feed, item, {:places=>[m.replyto],:handle=>nil,:date=>true})
210
+ end
211
+ end
212
+
213
+ def itemDate(item,ex=nil)
214
+ return item.pubDate if item.respond_to?(:pubDate)
215
+ return item.date if item.respond_to?(:date)
216
+ return ex
217
+ end
218
+
219
+ def freshness_sort(items)
220
+ notime = Time.at(0)
221
+ items.sort { |a, b|
222
+ itemDate(b, notime) <=> itemDate(a, notime)
223
+ }
224
+ end
225
+
226
+ def list_rss(m, params)
227
+ wanted = params[:handle]
228
+ reply = String.new
229
+ @@mutex.synchronize {
230
+ @feeds.each { |handle, feed|
231
+ next if wanted and !handle.match(wanted)
232
+ reply << "#{feed.handle}: #{feed.url} (in format: #{feed.type ? feed.type : 'default'})"
233
+ (reply << " (watched)") if feed.watched_by?(m.replyto)
234
+ reply << "\n"
235
+ }
236
+ }
237
+ if reply.empty?
238
+ reply = "no feeds found"
239
+ reply << " matching #{wanted}" if wanted
240
+ end
241
+ m.reply reply
242
+ end
243
+
244
+ def watched_rss(m, params)
245
+ wanted = params[:handle]
246
+ reply = String.new
247
+ @@mutex.synchronize {
248
+ watchlist.each { |handle, feed|
249
+ next if wanted and !handle.match(wanted)
250
+ next unless feed.watched_by?(m.replyto)
251
+ reply << "#{feed.handle}: #{feed.url} (in format: #{feed.type ? feed.type : 'default'})\n"
252
+ }
253
+ }
254
+ if reply.empty?
255
+ reply = "no watched feeds"
256
+ reply << " matching #{wanted}" if wanted
257
+ end
258
+ m.reply reply
259
+ end
260
+
261
+ def add_rss(m, params, force=false)
262
+ handle = params[:handle]
263
+ url = params[:url]
264
+ unless url.match(/https?/)
265
+ m.reply "I only deal with feeds from HTTP sources, so I can't use #{url} (maybe you forgot the handle?)"
266
+ return
267
+ end
268
+ type = params[:type]
269
+ if @feeds.fetch(handle, nil) && !force
270
+ m.reply "There is already a feed named #{handle} (URL: #{@feeds[handle].url})"
271
+ return
272
+ end
273
+ unless url
274
+ m.reply "You must specify both a handle and an url to add an RSS feed"
275
+ return
276
+ end
277
+ @@mutex.synchronize {
278
+ @feeds[handle] = RssBlob.new(url,handle,type)
279
+ }
280
+ reply = "Added RSS #{url} named #{handle}"
281
+ if type
282
+ reply << " (format: #{type})"
283
+ end
284
+ m.reply reply
285
+ return handle
286
+ end
287
+
288
+ def del_rss(m, params, pass=false)
289
+ feed = unwatch_rss(m, params, true)
290
+ if feed.watched?
291
+ m.reply "someone else is watching #{feed.handle}, I won't remove it from my list"
292
+ return
293
+ end
294
+ @@mutex.synchronize {
295
+ @feeds.delete(feed.handle)
296
+ }
297
+ m.okay unless pass
298
+ return
299
+ end
300
+
301
+ def replace_rss(m, params)
302
+ handle = params[:handle]
303
+ if @feeds.key?(handle)
304
+ del_rss(m, {:handle => handle}, true)
305
+ end
306
+ if @feeds.key?(handle)
307
+ m.reply "can't replace #{feed.handle}"
308
+ else
309
+ add_rss(m, params, true)
310
+ end
311
+ end
312
+
313
+ def forcereplace_rss(m, params)
314
+ add_rss(m, params, true)
315
+ end
316
+
317
+ def watch_rss(m, params)
318
+ handle = params[:handle]
319
+ url = params[:url]
320
+ type = params[:type]
321
+ if url
322
+ add_rss(m, params)
323
+ end
324
+ feed = nil
325
+ @@mutex.synchronize {
326
+ feed = @feeds.fetch(handle, nil)
327
+ }
328
+ if feed
329
+ @@mutex.synchronize {
330
+ if feed.add_watch(m.replyto)
331
+ watchRss(feed, m)
332
+ m.okay
333
+ else
334
+ m.reply "Already watching #{feed.handle}"
335
+ end
336
+ }
337
+ else
338
+ m.reply "Couldn't watch feed #{handle} (no such feed found)"
339
+ end
340
+ end
341
+
342
+ def unwatch_rss(m, params, pass=false)
343
+ handle = params[:handle]
344
+ unless @feeds.has_key?(handle)
345
+ m.reply("dunno that feed")
346
+ return
347
+ end
348
+ feed = @feeds[handle]
349
+ if feed.rm_watch(m.replyto)
350
+ m.reply "#{m.replyto} has been removed from the watchlist for #{feed.handle}"
351
+ else
352
+ m.reply("#{m.replyto} wasn't watching #{feed.handle}") unless pass
353
+ end
354
+ if !feed.watched?
355
+ @@mutex.synchronize {
356
+ if @@watchThreads[handle].kind_of? Thread
357
+ @@watchThreads[handle].kill
358
+ debug "rmwatch: Killed thread for #{handle}"
359
+ @@watchThreads.delete(handle)
360
+ end
361
+ }
362
+ end
363
+ return feed
364
+ end
365
+
366
+ def rewatch_rss(m=nil)
367
+ kill_threads
368
+
369
+ # Read watches from list.
370
+ watchlist.each{ |handle, feed|
371
+ watchRss(feed, m)
372
+ }
373
+ m.okay if m
374
+ end
375
+
376
+ private
377
+ def watchRss(feed, m=nil)
378
+ if @@watchThreads.has_key?(feed.handle)
379
+ report_problem("watcher thread for #{feed.handle} is already running", nil, m)
380
+ return
381
+ end
382
+ @@watchThreads[feed.handle] = Thread.new do
383
+ debug "watcher for #{feed} started"
384
+ oldItems = []
385
+ firstRun = true
386
+ failures = 0
387
+ loop do
388
+ begin
389
+ debug "fetching #{feed}"
390
+ title = newItems = nil
391
+ @@mutex.synchronize {
392
+ title, newItems = fetchRss(feed)
393
+ }
394
+ unless newItems
395
+ debug "no items in feed #{feed}"
396
+ failures +=1
397
+ else
398
+ debug "Checking if new items are available for #{feed}"
399
+ if firstRun
400
+ debug "First run, we'll see next time"
401
+ firstRun = false
402
+ else
403
+ otxt = oldItems.map { |item| item.to_s }
404
+ dispItems = newItems.reject { |item|
405
+ otxt.include?(item.to_s)
406
+ }
407
+ if dispItems.length > 0
408
+ debug "Found #{dispItems.length} new items in #{feed}"
409
+ dispItems.each { |item|
410
+ @@mutex.synchronize {
411
+ printFormattedRss(feed, item)
412
+ }
413
+ }
414
+ else
415
+ debug "No new items found in #{feed}"
416
+ end
417
+ end
418
+ oldItems = newItems.dup
419
+ end
420
+ rescue Exception => e
421
+ error "Error watching #{feed}: #{e.inspect}"
422
+ debug e.backtrace.join("\n")
423
+ failures += 1
424
+ end
425
+
426
+ seconds = @bot.config['rss.thread_sleep'] * (failures + 1)
427
+ seconds += seconds * (rand(100)-50)/100
428
+ debug "watcher for #{feed} going to sleep #{seconds} seconds.."
429
+ sleep seconds
430
+ end
431
+ end
432
+ end
433
+
434
+ def printFormattedRss(feed, item, opts=nil)
435
+ places = feed.watchers
436
+ handle = "::#{feed.handle}:: "
437
+ date = String.new
438
+ if opts
439
+ places = opts[:places] if opts.key?(:places)
440
+ handle = opts[:handle].to_s if opts.key?(:handle)
441
+ if opts.key?(:date) && opts[:date]
442
+ if item.respond_to?(:pubDate)
443
+ if item.pubDate.class <= Time
444
+ date = item.pubDate.strftime("%Y/%m/%d %H.%M.%S")
445
+ else
446
+ date = item.pubDate.to_s
447
+ end
448
+ elsif item.respond_to?(:date)
449
+ if item.date.class <= Time
450
+ date = item.date.strftime("%Y/%m/%d %H.%M.%S")
451
+ else
452
+ date = item.date.to_s
453
+ end
454
+ else
455
+ date = "(no date)"
456
+ end
457
+ date += " :: "
458
+ end
459
+ end
460
+ title = "#{Bold}#{item.title.chomp.riphtml}#{Bold}" if item.title
461
+ desc = item.description.gsub(/\s+/,' ').strip.riphtml.shorten(@bot.config['rss.text_max']) if item.description
462
+ link = item.link.chomp if item.link
463
+ places.each { |loc|
464
+ case feed.type
465
+ when 'blog'
466
+ @bot.say loc, "#{handle}#{date}#{item.category.content} blogged at #{link}"
467
+ @bot.say loc, "#{handle}#{title} - #{desc}"
468
+ when 'forum'
469
+ @bot.say loc, "#{handle}#{date}#{title}#{' @ ' if item.title && item.link}#{link}"
470
+ when 'wiki'
471
+ @bot.say loc, "#{handle}#{date}#{item.title} has been edited by #{item.dc_creator}. #{desc} #{link}"
472
+ when 'gmame'
473
+ @bot.say loc, "#{handle}#{date}Message #{title} sent by #{item.dc_creator}. #{desc}"
474
+ when 'trac'
475
+ @bot.say loc, "#{handle}#{date}#{title} @ #{link}"
476
+ unless item.title =~ /^Changeset \[(\d+)\]/
477
+ @bot.say loc, "#{handle}#{date}#{desc}"
478
+ end
479
+ else
480
+ @bot.say loc, "#{handle}#{date}#{title}#{' @ ' if item.title && item.link}#{link}"
481
+ end
482
+ }
483
+ end
484
+
485
+ def fetchRss(feed, m=nil)
486
+ begin
487
+ # Use 60 sec timeout, cause the default is too low
488
+ # Do not use get_cached for RSS until we have proper cache handling
489
+ # xml = @bot.httputil.get_cached(feed.url,60,60)
490
+ xml = @bot.httputil.get(feed.url,60,60)
491
+ rescue URI::InvalidURIError, URI::BadURIError => e
492
+ report_problem("invalid rss feed #{feed.url}", e, m)
493
+ return
494
+ rescue => e
495
+ report_problem("error getting #{feed.url}", e, m)
496
+ return
497
+ end
498
+ debug "fetched #{feed}"
499
+ unless xml
500
+ report_problem("reading feed #{feed} failed", nil, m)
501
+ return
502
+ end
503
+
504
+ begin
505
+ ## do validate parse
506
+ rss = RSS::Parser.parse(xml)
507
+ debug "parsed #{feed}"
508
+ rescue RSS::InvalidRSSError
509
+ ## do non validate parse for invalid RSS 1.0
510
+ begin
511
+ rss = RSS::Parser.parse(xml, false)
512
+ rescue RSS::Error => e
513
+ report_problem("parsing rss stream failed, whoops =(", e, m)
514
+ return
515
+ end
516
+ rescue RSS::Error => e
517
+ report_problem("parsing rss stream failed, oioi", e, m)
518
+ return
519
+ rescue => e
520
+ report_problem("processing error occured, sorry =(", e, m)
521
+ return
522
+ end
523
+ items = []
524
+ if rss.nil?
525
+ report_problem("#{feed} does not include RSS 1.0 or 0.9x/2.0", nil, m)
526
+ else
527
+ begin
528
+ rss.output_encoding = 'UTF-8'
529
+ rescue RSS::UnknownConvertMethod => e
530
+ report_problem("bah! something went wrong =(", e, m)
531
+ return
532
+ end
533
+ rss.channel.title ||= "Unknown"
534
+ title = rss.channel.title
535
+ rss.items.each do |item|
536
+ item.title ||= "Unknown"
537
+ items << item
538
+ end
539
+ end
540
+
541
+ if items.empty?
542
+ report_problem("no items found in the feed, maybe try weed?", e, m)
543
+ return
544
+ end
545
+ return [title, items]
546
+ end
547
+ end
548
+
549
+ plugin = RSSFeedsPlugin.new
550
+
551
+ plugin.map 'rss show :handle :limit',
552
+ :action => 'show_rss',
553
+ :requirements => {:limit => /^\d+(?:\.\.\d+)?$/},
554
+ :defaults => {:limit => 5}
555
+ plugin.map 'rss list :handle',
556
+ :action => 'list_rss',
557
+ :defaults => {:handle => nil}
558
+ plugin.map 'rss watched :handle',
559
+ :action => 'watched_rss',
560
+ :defaults => {:handle => nil}
561
+ plugin.map 'rss add :handle :url :type',
562
+ :action => 'add_rss',
563
+ :defaults => {:type => nil}
564
+ plugin.map 'rss del :handle',
565
+ :action => 'del_rss'
566
+ plugin.map 'rss delete :handle',
567
+ :action => 'del_rss'
568
+ plugin.map 'rss rm :handle',
569
+ :action => 'del_rss'
570
+ plugin.map 'rss replace :handle :url :type',
571
+ :action => 'replace_rss',
572
+ :defaults => {:type => nil}
573
+ plugin.map 'rss forcereplace :handle :url :type',
574
+ :action => 'forcereplace_rss',
575
+ :defaults => {:type => nil}
576
+ plugin.map 'rss watch :handle :url :type',
577
+ :action => 'watch_rss',
578
+ :defaults => {:url => nil, :type => nil}
579
+ plugin.map 'rss unwatch :handle',
580
+ :action => 'unwatch_rss'
581
+ plugin.map 'rss rmwatch :handle',
582
+ :action => 'unwatch_rss'
583
+ plugin.map 'rss rewatch :handle',
584
+ :action => 'rewatch_rss'
585
+