rbot 0.9.9

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 (76) hide show
  1. data/AUTHORS +16 -0
  2. data/COPYING +21 -0
  3. data/ChangeLog +418 -0
  4. data/INSTALL +8 -0
  5. data/README +44 -0
  6. data/REQUIREMENTS +34 -0
  7. data/TODO +5 -0
  8. data/Usage_en.txt +129 -0
  9. data/bin/rbot +81 -0
  10. data/data/rbot/contrib/plugins/figlet.rb +20 -0
  11. data/data/rbot/contrib/plugins/ri.rb +83 -0
  12. data/data/rbot/contrib/plugins/stats.rb +232 -0
  13. data/data/rbot/contrib/plugins/vandale.rb +49 -0
  14. data/data/rbot/languages/dutch.lang +73 -0
  15. data/data/rbot/languages/english.lang +75 -0
  16. data/data/rbot/languages/french.lang +39 -0
  17. data/data/rbot/languages/german.lang +67 -0
  18. data/data/rbot/plugins/autoop.rb +68 -0
  19. data/data/rbot/plugins/autorejoin.rb +16 -0
  20. data/data/rbot/plugins/cal.rb +15 -0
  21. data/data/rbot/plugins/dice.rb +81 -0
  22. data/data/rbot/plugins/eightball.rb +19 -0
  23. data/data/rbot/plugins/excuse.rb +470 -0
  24. data/data/rbot/plugins/fish.rb +61 -0
  25. data/data/rbot/plugins/fortune.rb +22 -0
  26. data/data/rbot/plugins/freshmeat.rb +98 -0
  27. data/data/rbot/plugins/google.rb +51 -0
  28. data/data/rbot/plugins/host.rb +14 -0
  29. data/data/rbot/plugins/httpd.rb.disabled +35 -0
  30. data/data/rbot/plugins/insult.rb +258 -0
  31. data/data/rbot/plugins/karma.rb +85 -0
  32. data/data/rbot/plugins/lart.rb +181 -0
  33. data/data/rbot/plugins/math.rb +122 -0
  34. data/data/rbot/plugins/nickserv.rb +89 -0
  35. data/data/rbot/plugins/nslookup.rb +43 -0
  36. data/data/rbot/plugins/opme.rb +19 -0
  37. data/data/rbot/plugins/quakeauth.rb +51 -0
  38. data/data/rbot/plugins/quotes.rb +321 -0
  39. data/data/rbot/plugins/remind.rb +228 -0
  40. data/data/rbot/plugins/roshambo.rb +54 -0
  41. data/data/rbot/plugins/rot13.rb +10 -0
  42. data/data/rbot/plugins/roulette.rb +147 -0
  43. data/data/rbot/plugins/rss.rb.disabled +414 -0
  44. data/data/rbot/plugins/seen.rb +89 -0
  45. data/data/rbot/plugins/slashdot.rb +94 -0
  46. data/data/rbot/plugins/spell.rb +36 -0
  47. data/data/rbot/plugins/tube.rb +71 -0
  48. data/data/rbot/plugins/url.rb +88 -0
  49. data/data/rbot/plugins/weather.rb +649 -0
  50. data/data/rbot/plugins/wserver.rb +71 -0
  51. data/data/rbot/plugins/xmlrpc.rb.disabled +52 -0
  52. data/data/rbot/templates/keywords.rbot +4 -0
  53. data/data/rbot/templates/lart/larts +98 -0
  54. data/data/rbot/templates/lart/praises +5 -0
  55. data/data/rbot/templates/levels.rbot +30 -0
  56. data/data/rbot/templates/users.rbot +1 -0
  57. data/lib/rbot/auth.rb +203 -0
  58. data/lib/rbot/channel.rb +54 -0
  59. data/lib/rbot/config.rb +363 -0
  60. data/lib/rbot/dbhash.rb +112 -0
  61. data/lib/rbot/httputil.rb +141 -0
  62. data/lib/rbot/ircbot.rb +808 -0
  63. data/lib/rbot/ircsocket.rb +185 -0
  64. data/lib/rbot/keywords.rb +433 -0
  65. data/lib/rbot/language.rb +69 -0
  66. data/lib/rbot/message.rb +256 -0
  67. data/lib/rbot/messagemapper.rb +262 -0
  68. data/lib/rbot/plugins.rb +291 -0
  69. data/lib/rbot/post-install.rb +8 -0
  70. data/lib/rbot/rbotconfig.rb +36 -0
  71. data/lib/rbot/registry.rb +271 -0
  72. data/lib/rbot/rfc2812.rb +1104 -0
  73. data/lib/rbot/timer.rb +201 -0
  74. data/lib/rbot/utils.rb +83 -0
  75. data/setup.rb +1360 -0
  76. metadata +129 -0
@@ -0,0 +1,54 @@
1
+ # Play the game of roshambo (rock-paper-scissors)
2
+ # Copyright (C) 2004 Hans Fugal
3
+ # Distributed under the same license as rbot itself
4
+ require 'time'
5
+ class RoshamboPlugin < Plugin
6
+ def initialize
7
+ super
8
+ @scoreboard = {}
9
+ end
10
+ def help(plugin, topic="")
11
+ "roshambo <rock|paper|scissors> => play roshambo"
12
+ end
13
+ def privmsg(m)
14
+ # simultaneity
15
+ choice = choose
16
+
17
+ # init scoreboard
18
+ if (not @scoreboard.has_key?(m.sourcenick) or (Time.now - @scoreboard[m.sourcenick]['timestamp']) > 3600)
19
+ @scoreboard[m.sourcenick] = {'me'=>0,'you'=>0,'timestamp'=>Time.now}
20
+ end
21
+ case m.params
22
+ when 'rock','paper','scissors'
23
+ s = score(choice,m.params)
24
+ @scoreboard[m.sourcenick]['timestamp'] = Time.now
25
+ myscore=@scoreboard[m.sourcenick]['me']
26
+ yourscore=@scoreboard[m.sourcenick]['you']
27
+ case s
28
+ when 1
29
+ yourscore=@scoreboard[m.sourcenick]['you'] += 1
30
+ m.reply "#{choice}. You win. Score: me #{myscore} you #{yourscore}"
31
+ when 0
32
+ m.reply "#{choice}. We tie. Score: me #{myscore} you #{yourscore}"
33
+ when -1
34
+ myscore=@scoreboard[m.sourcenick]['me'] += 1
35
+ m.reply "#{choice}! I win! Score: me #{myscore} you #{yourscore}"
36
+ end
37
+ else
38
+ m.reply "incorrect usage: " + help(m.plugin)
39
+ end
40
+ end
41
+
42
+ def choose
43
+ ['rock','paper','scissors'][rand(3)]
44
+ end
45
+ def score(a,b)
46
+ beats = {'rock'=>'scissors', 'paper'=>'rock', 'scissors'=>'paper'}
47
+ return -1 if beats[a] == b
48
+ return 1 if beats[b] == a
49
+ return 0
50
+ end
51
+ end
52
+ plugin = RoshamboPlugin.new
53
+ plugin.register("roshambo")
54
+ plugin.register("rps")
@@ -0,0 +1,10 @@
1
+ class RotPlugin < Plugin
2
+ def help(plugin, topic="")
3
+ "rot13 <string> => encode <string> to rot13 or back"
4
+ end
5
+ def rot13(m, params)
6
+ m.reply params[:string].tr("A-Za-z", "N-ZA-Mn-za-m");
7
+ end
8
+ end
9
+ plugin = RotPlugin.new
10
+ plugin.map 'rot13 :string'
@@ -0,0 +1,147 @@
1
+ RouletteHistory = Struct.new("RouletteHistory", :games, :shots, :deaths, :misses, :wins)
2
+
3
+ class RoulettePlugin < Plugin
4
+ def initialize
5
+ super
6
+ reset_chambers
7
+ @players = Array.new
8
+ end
9
+ def help(plugin, topic="")
10
+ "roulette => play russian roulette - starts a new game if one isn't already running. One round in a six chambered gun. Take turns to say roulette to the bot, until somebody dies. roulette reload => force the gun to reload, roulette stats => show stats from all games, roulette stats <player> => show stats for <player>, roulette clearstats => clear stats (config level auth required)"
11
+ end
12
+ def clearstats(m, params)
13
+ @registry.clear
14
+ m.okay
15
+ end
16
+
17
+ def roulette(m, params)
18
+ if m.private?
19
+ m.reply "you gotta play roulette in channel dude"
20
+ return
21
+ end
22
+
23
+ playerdata = nil
24
+ if @registry.has_key?(m.sourcenick)
25
+ playerdata = @registry[m.sourcenick]
26
+ else
27
+ playerdata = RouletteHistory.new(0,0,0,0,0)
28
+ end
29
+
30
+ unless @players.include?(m.sourcenick)
31
+ @players << m.sourcenick
32
+ playerdata.games += 1
33
+ end
34
+ playerdata.shots += 1
35
+
36
+ shot = @chambers.pop
37
+ if shot
38
+ m.reply "#{m.sourcenick}: chamber #{6 - @chambers.length} of 6 => *BANG*"
39
+ playerdata.deaths += 1
40
+ @players.each {|plyr|
41
+ next if plyr == m.sourcenick
42
+ pdata = @registry[plyr]
43
+ next if pdata == nil
44
+ pdata.wins += 1
45
+ @registry[plyr] = pdata
46
+ }
47
+ else
48
+ m.reply "#{m.sourcenick}: chamber #{6 - @chambers.length} of 6 => +click+"
49
+ playerdata.misses += 1
50
+ end
51
+
52
+ @registry[m.sourcenick] = playerdata
53
+
54
+ if shot || @chambers.empty?
55
+ reload(m)
56
+ end
57
+ end
58
+ def reload(m, params = {})
59
+ @bot.action m.replyto, "reloads"
60
+ reset_chambers
61
+ # all players win on a reload
62
+ # (allows you to play 3-shot matches etc)
63
+ @players.each {|plyr|
64
+ pdata = @registry[plyr]
65
+ next if pdata == nil
66
+ pdata.wins += 1
67
+ @registry[plyr] = pdata
68
+ }
69
+ @players = Array.new
70
+ end
71
+ def reset_chambers
72
+ @chambers = [false, false, false, false, false, false]
73
+ @chambers[rand(@chambers.length)] = true
74
+ end
75
+ def playerstats(m, params)
76
+ player = params[:player]
77
+ pstats = @registry[player]
78
+ if pstats.nil?
79
+ m.reply "#{player} hasn't played enough games yet"
80
+ else
81
+ m.reply "#{player} has played #{pstats.games} games, won #{pstats.wins} and lost #{pstats.deaths}. #{player} pulled the trigger #{pstats.shots} times and found the chamber empty on #{pstats.misses} occasions."
82
+ end
83
+ end
84
+ def stats(m, params)
85
+ total_players = 0
86
+ total_games = 0
87
+ total_shots = 0
88
+
89
+ died_most = [nil,0]
90
+ won_most = [nil,0]
91
+ h_win_percent = [nil,0]
92
+ l_win_percent = [nil,0]
93
+ h_luck_percent = [nil,0]
94
+ l_luck_percent = [nil,0]
95
+ @registry.each {|k,v|
96
+ total_players += 1
97
+ total_games += v.deaths
98
+ total_shots += v.shots
99
+
100
+ win_rate = v.wins.to_f / v.games * 100
101
+ if h_win_percent[0].nil? || win_rate > h_win_percent[1] && v.games > 2
102
+ h_win_percent = [[k], win_rate]
103
+ elsif win_rate == h_win_percent[1] && v.games > 2
104
+ h_win_percent[0] << k
105
+ end
106
+ if l_win_percent[0].nil? || win_rate < l_win_percent[1] && v.games > 2
107
+ l_win_percent = [[k], win_rate]
108
+ elsif win_rate == l_win_percent[1] && v.games > 2
109
+ l_win_percent[0] << k
110
+ end
111
+
112
+ luck = v.misses.to_f / v.shots * 100
113
+ if h_luck_percent[0].nil? || luck > h_luck_percent[1] && v.games > 2
114
+ h_luck_percent = [[k], luck]
115
+ elsif luck == h_luck_percent[1] && v.games > 2
116
+ h_luck_percent[0] << k
117
+ end
118
+ if l_luck_percent[0].nil? || luck < l_luck_percent[1] && v.games > 2
119
+ l_luck_percent = [[k], luck]
120
+ elsif luck == l_luck_percent[1] && v.games > 2
121
+ l_luck_percent[0] << k
122
+ end
123
+
124
+ if died_most[0].nil? || v.deaths > died_most[1]
125
+ died_most = [[k], v.deaths]
126
+ elsif v.deaths == died_most[1]
127
+ died_most[0] << k
128
+ end
129
+ if won_most[0].nil? || v.wins > won_most[1]
130
+ won_most = [[k], v.wins]
131
+ elsif v.wins == won_most[1]
132
+ won_most[0] << k
133
+ end
134
+ }
135
+ if total_games < 1
136
+ m.reply "roulette stats: no games played yet"
137
+ else
138
+ m.reply "roulette stats: #{total_games} games completed, #{total_shots} shots fired at #{total_players} players. Luckiest: #{h_luck_percent[0].join(',')} (#{sprintf '%.1f', h_luck_percent[1]}% clicks). Unluckiest: #{l_luck_percent[0].join(',')} (#{sprintf '%.1f', l_luck_percent[1]}% clicks). Highest survival rate: #{h_win_percent[0].join(',')} (#{sprintf '%.1f', h_win_percent[1]}%). Lowest survival rate: #{l_win_percent[0].join(',')} (#{sprintf '%.1f', l_win_percent[1]}%). Most wins: #{won_most[0].join(',')} (#{won_most[1]}). Most deaths: #{died_most[0].join(',')} (#{died_most[1]})."
139
+ end
140
+ end
141
+ end
142
+ plugin = RoulettePlugin.new
143
+ plugin.map 'roulette reload', :action => 'reload'
144
+ plugin.map 'roulette stats :player', :action => 'playerstats'
145
+ plugin.map 'roulette stats', :action => 'stats'
146
+ plugin.map 'roulette clearstats', :action => 'clearstats', :auth => 'config'
147
+ plugin.map 'roulette'
@@ -0,0 +1,414 @@
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
+ # TODO
8
+ # checks feeds very often - does it use HEAD to check it needs to d/l?
9
+ # some usage issues
10
+ # why can't I do this here? :
11
+ # Class String
12
+ # def foo
13
+ # foo
14
+ # end
15
+ # end
16
+
17
+ require 'rss/parser'
18
+ require 'rss/1.0'
19
+ require 'rss/2.0'
20
+ require 'rss/dublincore'
21
+ begin
22
+ require 'rss/dublincore/2.0'
23
+ rescue LoadError => e
24
+ puts "rss plugin: no dublincore 2.0 found, should be okay tho"
25
+ end
26
+
27
+
28
+ class RSSFeedsPlugin < Plugin
29
+ @@watchThreads = Hash.new
30
+
31
+ # Keep a 1:1 relation between commands and handlers
32
+ @@handlers = {
33
+ "rss" => "handle_rss",
34
+ "addrss" => "handle_addrss",
35
+ "rmrss" => "handle_rmrss",
36
+ "listrss" => "handle_listrss",
37
+ "listwatches" => "handle_listrsswatch",
38
+ "rewatch" => "handle_rewatch",
39
+ "watchrss" => "handle_watchrss",
40
+ "rmwatch" => "handle_rmwatch"
41
+ }
42
+
43
+ def name
44
+ 'rss'
45
+ end
46
+
47
+ def initialize
48
+ super
49
+ @feeds = Hash.new
50
+ @watchList = Hash.new
51
+
52
+ if FileTest.exists?("#{@bot.botclass}/rss/feeds")
53
+ IO.foreach("#{@bot.botclass}/rss/feeds") { |line|
54
+ s = line.chomp.split("|", 2)
55
+ @feeds[s[0]] = s[1]
56
+ }
57
+ end
58
+
59
+ if FileTest.exists?("#{@bot.botclass}/rss/watchlist")
60
+ IO.foreach("#{@bot.botclass}/rss/watchlist") { |line|
61
+ s = line.chomp.split("|", 3)
62
+ @watchList[s[0]] = [s[1], s[2]]
63
+ watchRss( s[2], s[0], s[1] )
64
+ }
65
+ end
66
+ end
67
+
68
+ def cleanup
69
+ kill_threads
70
+ end
71
+
72
+ def save
73
+ Dir.mkdir("#{@bot.botclass}/rss") if not FileTest.directory?("#{@bot.botclass}/rss")
74
+ File.open("#{@bot.botclass}/rss/feeds", "w") { |file|
75
+ @feeds.each { |k,v|
76
+ file.puts(k + "|" + v)
77
+ }
78
+ }
79
+ File.open("#{@bot.botclass}/rss/watchlist", "w") { |file|
80
+ @watchList.each { |url, d|
81
+ feedFormat = d[0] || ''
82
+ whichChan = d[1] || 'markey'
83
+ file.puts(url + '|' + feedFormat + '|' + whichChan)
84
+ }
85
+ }
86
+ end
87
+
88
+ def kill_threads
89
+ Thread.critical=true
90
+ # Abort all running threads.
91
+ @@watchThreads.each { |url, thread|
92
+ puts "Killing thread for #{url}"
93
+ thread.kill
94
+ }
95
+ # @@watchThreads.each { |url, thread|
96
+ # puts "Joining on killed thread for #{url}"
97
+ # thread.join
98
+ # }
99
+ @@watchThreads = Hash.new
100
+ Thread.critical=false
101
+ end
102
+
103
+ def help(plugin,topic="")
104
+ "RSS Reader: rss name [limit] => read a named feed [limit maximum posts, default 5], addrss [force] name url => add a feed, listrss => list all available feeds, rmrss name => remove the named feed, watchrss url [type] => watch a rss feed for changes (type may be 'amarokblog', 'amarokforum', 'mediawiki', 'gmame' or empty - it defines special formatting of feed items), rewatch => restart all rss watches, rmwatch url => stop watching for changes in url"
105
+ end
106
+
107
+ def privmsg(m)
108
+ meth = self.method(@@handlers[m.plugin])
109
+ meth.call(m)
110
+ end
111
+
112
+ def handle_rss(m)
113
+ unless m.params
114
+ m.reply("incorrect usage: " + help(m.plugin))
115
+ return
116
+ end
117
+ limit = 5
118
+ if m.params =~ /\s+(\d+)$/
119
+ limit = $1.to_i
120
+ if limit < 1 || limit > 15
121
+ m.reply("weird, limit not in [1..15], reverting to default")
122
+ limit = 5
123
+ end
124
+ m.params.gsub!(/\s+\d+$/, '')
125
+ end
126
+ unless @feeds.has_key?(m.params)
127
+ m.reply(m.params + "? what is that feed about?")
128
+ return
129
+ end
130
+
131
+ m.reply("Please wait, querying...")
132
+ title = ''
133
+ items = fetchRSS(m.replyto, @feeds[m.params], title)
134
+ if(items == nil)
135
+ return
136
+ end
137
+ m.reply("Channel : #{title}")
138
+ # FIXME: optional by-date sorting if dates present
139
+ items[0...limit].each do |item|
140
+ printRSSItem(m.replyto,item)
141
+ end
142
+ end
143
+
144
+ def handle_addrss(m)
145
+ unless m.params
146
+ m.reply "incorrect usage: " + help(m.plugin)
147
+ return
148
+ end
149
+ if m.params =~ /^force /
150
+ forced = true
151
+ m.params.gsub!(/^force /, '')
152
+ end
153
+ feed = m.params.scan(/^(\S+)\s+(\S+)$/)
154
+ unless feed.length == 1 && feed[0].length == 2
155
+ m.reply("incorrect usage: " + help(m.plugin))
156
+ return
157
+ end
158
+ if @feeds.has_key?(feed[0][0]) && !forced
159
+ m.reply("But there is already a feed named #{feed[0][0]} with url #{@feeds[feed[0][0]]}")
160
+ return
161
+ end
162
+ feed[0][0].gsub!("|", '_')
163
+ @feeds[feed[0][0]] = feed[0][1]
164
+ m.reply("RSS: Added #{feed[0][1]} with name #{feed[0][0]}")
165
+ end
166
+
167
+ def handle_rmrss(m)
168
+ unless m.params
169
+ m.reply "incorrect usage: " + help(m.plugin)
170
+ return
171
+ end
172
+ unless @feeds.has_key?(m.params)
173
+ m.reply("dunno that feed")
174
+ return
175
+ end
176
+ @feeds.delete(m.params)
177
+ m.okay
178
+ end
179
+
180
+ def handle_rmwatch(m)
181
+ unless m.params
182
+ m.reply "incorrect usage: " + help(m.plugin)
183
+ return
184
+ end
185
+ unless @watchList.has_key?(m.params)
186
+ m.reply("no such watch")
187
+ return
188
+ end
189
+ unless @watchList[m.params][1] == m.replyto
190
+ m.reply("no such watch for this channel/nick")
191
+ return
192
+ end
193
+ @watchList.delete(m.params)
194
+ Thread.critical=true
195
+ if @@watchThreads[m.params].kind_of? Thread
196
+ @@watchThreads[m.params].kill
197
+ puts "rmwatch: Killed thread for #{m.params}"
198
+ # @@watchThreads[m.params].join
199
+ # puts "rmwatch: Joined killed thread for #{m.params}"
200
+ @@watchThreads.delete(m.params)
201
+ end
202
+ Thread.critical=false
203
+ m.okay
204
+ end
205
+
206
+ def handle_listrss(m)
207
+ reply = ''
208
+ if @feeds.length == 0
209
+ reply = "No feeds yet."
210
+ else
211
+ @feeds.each { |k,v|
212
+ reply << k + ": " + v + "\n"
213
+ }
214
+ end
215
+ m.reply(reply)
216
+ end
217
+
218
+ def handle_listrsswatch(m)
219
+ reply = ''
220
+ if @watchList.length == 0
221
+ reply = "No watched feeds yet."
222
+ else
223
+ @watchList.each { |url,v|
224
+ reply << url + " for " + v[1] + " (in format: " + (v[0]?v[0]:"default") + ")\n"
225
+ }
226
+ end
227
+ m.reply(reply)
228
+ end
229
+
230
+ def handle_rewatch(m)
231
+ kill_threads
232
+
233
+ # Read watches from list.
234
+ @watchList.each{ |url, d|
235
+ feedFormat = d[0]
236
+ whichChan = d[1]
237
+ watchRss(whichChan, url,feedFormat)
238
+ }
239
+ m.okay
240
+ end
241
+
242
+ def handle_watchrss(m)
243
+ unless m.params
244
+ m.reply "incorrect usage: " + help(m.plugin)
245
+ return
246
+ end
247
+ feed = m.params.scan(/^(\S+)\s+(\S+)$/)
248
+ url = feed[0][0]
249
+ feedFormat = feed[0][1]
250
+ if @watchList.has_key?(url)
251
+ m.reply("But there is already a watch for feed #{url} on chan #{@watchList[url][1]}")
252
+ return
253
+ end
254
+ @watchList[url] = [feedFormat, m.replyto]
255
+ watchRss(m.replyto, url,feedFormat)
256
+ m.okay
257
+ end
258
+
259
+ private
260
+ def watchRss(whichChan, url, feedFormat)
261
+ if @@watchThreads.has_key?(url)
262
+ @bot.say whichChan, "ERROR: watcher thread for #{url} is already running! #{@@watchThreads[url]}"
263
+ return
264
+ end
265
+ @@watchThreads[url] = Thread.new do
266
+ puts 'watchRss thread started.'
267
+ oldItems = []
268
+ firstRun = true
269
+ loop do
270
+ begin # exception
271
+ title = ''
272
+ puts 'Fetching rss feed..'
273
+ newItems = fetchRSS(whichChan, url, title)
274
+ if( newItems.empty? )
275
+ @bot.say whichChan, "Oops - Item is empty"
276
+ break
277
+ end
278
+ puts "Checking if new items are available"
279
+ if (firstRun)
280
+ firstRun = false
281
+ else
282
+ newItems.each do |nItem|
283
+ showItem = true;
284
+ oldItems.each do |oItem|
285
+ if (nItem.to_s == oItem.to_s)
286
+ showItem = false
287
+ end
288
+ end
289
+ if showItem
290
+ puts "showing #{nItem.title}"
291
+ printFormatedRSS(whichChan, nItem,feedFormat)
292
+ else
293
+ puts "not showing #{nItem.title}"
294
+ break
295
+ end
296
+ end
297
+ end
298
+ oldItems = newItems
299
+ rescue Exception
300
+ $stderr.print "IO failed: " + $! + "\n"
301
+ end
302
+
303
+ seconds = 150 + rand(100)
304
+ puts "Thread going to sleep #{seconds} seconds.."
305
+ sleep seconds
306
+ end
307
+ end
308
+ end
309
+
310
+ def printRSSItem(whichChan,item)
311
+ if item.kind_of?(RSS::RDF::Item)
312
+ @bot.say whichChan, shorten(riphtml(item.title.chomp), 20) + " @ " + item.link
313
+ else
314
+ @bot.say whichChan, "#{item.pubDate.to_s.chomp+": " if item.pubDate}#{shorten(riphtml(item.title.chomp), 20)+" :: " if item.title}#{" @ "+item.link.chomp if item.link}"
315
+ end
316
+ end
317
+
318
+ def printFormatedRSS(whichChan,item, type)
319
+ case type
320
+ when 'amarokblog'
321
+ @bot.say whichChan, "::#{item.category.content} just blogged at #{item.link}::"
322
+ @bot.say whichChan, "::#{shorten(riphtml(item.title.chomp), 20)} - #{shorten(riphtml(item.description.chomp),60)}::"
323
+ when 'amarokforum'
324
+ @bot.say whichChan, "::Forum:: #{item.pubDate.to_s.chomp+": " if item.pubDate}#{shorten(riphtml(item.title.chomp), 20)+" :: " if item.title}#{" @ "+item.link.chomp if item.link}"
325
+ when 'mediawiki'
326
+ @bot.say whichChan, "::Wiki:: #{item.title} has been edited by #{item.dc_creator}. #{shorten(riphtml(item.description.split("\n")[0].chomp),60)} #{item.link} ::"
327
+ puts "mediawiki #{item.title}"
328
+ when "gmame"
329
+ @bot.say whichChan, "::amarok-devel:: Message #{item.title} sent by #{item.dc_creator}. #{shorten(riphtml(item.description.split("\n")[0].chomp),60)}::"
330
+ else
331
+ printRSSItem(whichChan,item)
332
+ end
333
+ end
334
+
335
+ def fetchRSS(whichChan, url, title)
336
+ begin
337
+ # Use 60 sec timeout, cause the default is too low
338
+ xml = Utils.http_get(url,60,60)
339
+ rescue URI::InvalidURIError, URI::BadURIError => e
340
+ @bot.say whichChan, "invalid rss feed #{url}"
341
+ return
342
+ end
343
+ puts 'fetched'
344
+ unless xml
345
+ @bot.say whichChan, "reading feed #{url} failed"
346
+ return
347
+ end
348
+
349
+ begin
350
+ ## do validate parse
351
+ rss = RSS::Parser.parse(xml)
352
+ puts 'parsed'
353
+ rescue RSS::InvalidRSSError
354
+ ## do non validate parse for invalid RSS 1.0
355
+ begin
356
+ rss = RSS::Parser.parse(xml, false)
357
+ rescue RSS::Error
358
+ @bot.say whichChan, "parsing rss stream failed, whoops =("
359
+ return
360
+ end
361
+ rescue RSS::Error
362
+ @bot.say whichChan, "parsing rss stream failed, oioi"
363
+ return
364
+ rescue
365
+ @bot.say whichChan, "processing error occured, sorry =("
366
+ return
367
+ end
368
+ items = []
369
+ if rss.nil?
370
+ @bot.say whichChan, "#{m.params} does not include RSS 1.0 or 0.9x/2.0"
371
+ else
372
+ begin
373
+ rss.output_encoding = "euc-jp"
374
+ rescue RSS::UnknownConvertMethod
375
+ @bot.say whichChan, "bah! something went wrong =("
376
+ return
377
+ end
378
+ rss.channel.title ||= "Unknown"
379
+ title.replace(rss.channel.title)
380
+ rss.items.each do |item|
381
+ item.title ||= "Unknown"
382
+ items << item
383
+ end
384
+ end
385
+
386
+ if items.empty?
387
+ @bot.say whichChan, "no items found in the feed, maybe try weed?"
388
+ return
389
+ end
390
+ return items
391
+ end
392
+
393
+ def riphtml(str)
394
+ str.gsub(/<[^>]+>/, '').gsub(/&amp;/,'&').gsub(/&quot;/,'"').gsub(/&lt;/,'<').gsub(/&gt;/,'>').gsub(/&ellip;/,'...').gsub(/&apos;/, "'").gsub("\n",'')
395
+ end
396
+
397
+ def shorten(str, limit)
398
+ if str.length > limit
399
+ str+". " =~ /^(.{#{limit}}[^.!;?]*[.!;?])/mi
400
+ return $1
401
+ end
402
+ str
403
+ end
404
+ end
405
+
406
+ plugin = RSSFeedsPlugin.new
407
+ plugin.register("rss")
408
+ plugin.register("addrss")
409
+ plugin.register("rmrss")
410
+ plugin.register("listrss")
411
+ plugin.register("rewatch")
412
+ plugin.register("watchrss")
413
+ plugin.register("listwatches")
414
+ plugin.register("rmwatch")