rbot 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +16 -0
- data/COPYING +21 -0
- data/ChangeLog +418 -0
- data/INSTALL +8 -0
- data/README +44 -0
- data/REQUIREMENTS +34 -0
- data/TODO +5 -0
- data/Usage_en.txt +129 -0
- data/bin/rbot +81 -0
- data/data/rbot/contrib/plugins/figlet.rb +20 -0
- data/data/rbot/contrib/plugins/ri.rb +83 -0
- data/data/rbot/contrib/plugins/stats.rb +232 -0
- data/data/rbot/contrib/plugins/vandale.rb +49 -0
- data/data/rbot/languages/dutch.lang +73 -0
- data/data/rbot/languages/english.lang +75 -0
- data/data/rbot/languages/french.lang +39 -0
- data/data/rbot/languages/german.lang +67 -0
- data/data/rbot/plugins/autoop.rb +68 -0
- data/data/rbot/plugins/autorejoin.rb +16 -0
- data/data/rbot/plugins/cal.rb +15 -0
- data/data/rbot/plugins/dice.rb +81 -0
- data/data/rbot/plugins/eightball.rb +19 -0
- data/data/rbot/plugins/excuse.rb +470 -0
- data/data/rbot/plugins/fish.rb +61 -0
- data/data/rbot/plugins/fortune.rb +22 -0
- data/data/rbot/plugins/freshmeat.rb +98 -0
- data/data/rbot/plugins/google.rb +51 -0
- data/data/rbot/plugins/host.rb +14 -0
- data/data/rbot/plugins/httpd.rb.disabled +35 -0
- data/data/rbot/plugins/insult.rb +258 -0
- data/data/rbot/plugins/karma.rb +85 -0
- data/data/rbot/plugins/lart.rb +181 -0
- data/data/rbot/plugins/math.rb +122 -0
- data/data/rbot/plugins/nickserv.rb +89 -0
- data/data/rbot/plugins/nslookup.rb +43 -0
- data/data/rbot/plugins/opme.rb +19 -0
- data/data/rbot/plugins/quakeauth.rb +51 -0
- data/data/rbot/plugins/quotes.rb +321 -0
- data/data/rbot/plugins/remind.rb +228 -0
- data/data/rbot/plugins/roshambo.rb +54 -0
- data/data/rbot/plugins/rot13.rb +10 -0
- data/data/rbot/plugins/roulette.rb +147 -0
- data/data/rbot/plugins/rss.rb.disabled +414 -0
- data/data/rbot/plugins/seen.rb +89 -0
- data/data/rbot/plugins/slashdot.rb +94 -0
- data/data/rbot/plugins/spell.rb +36 -0
- data/data/rbot/plugins/tube.rb +71 -0
- data/data/rbot/plugins/url.rb +88 -0
- data/data/rbot/plugins/weather.rb +649 -0
- data/data/rbot/plugins/wserver.rb +71 -0
- data/data/rbot/plugins/xmlrpc.rb.disabled +52 -0
- data/data/rbot/templates/keywords.rbot +4 -0
- data/data/rbot/templates/lart/larts +98 -0
- data/data/rbot/templates/lart/praises +5 -0
- data/data/rbot/templates/levels.rbot +30 -0
- data/data/rbot/templates/users.rbot +1 -0
- data/lib/rbot/auth.rb +203 -0
- data/lib/rbot/channel.rb +54 -0
- data/lib/rbot/config.rb +363 -0
- data/lib/rbot/dbhash.rb +112 -0
- data/lib/rbot/httputil.rb +141 -0
- data/lib/rbot/ircbot.rb +808 -0
- data/lib/rbot/ircsocket.rb +185 -0
- data/lib/rbot/keywords.rb +433 -0
- data/lib/rbot/language.rb +69 -0
- data/lib/rbot/message.rb +256 -0
- data/lib/rbot/messagemapper.rb +262 -0
- data/lib/rbot/plugins.rb +291 -0
- data/lib/rbot/post-install.rb +8 -0
- data/lib/rbot/rbotconfig.rb +36 -0
- data/lib/rbot/registry.rb +271 -0
- data/lib/rbot/rfc2812.rb +1104 -0
- data/lib/rbot/timer.rb +201 -0
- data/lib/rbot/utils.rb +83 -0
- data/setup.rb +1360 -0
- 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,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(/&/,'&').gsub(/"/,'"').gsub(/</,'<').gsub(/>/,'>').gsub(/&ellip;/,'...').gsub(/'/, "'").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")
|