rbot 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
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,43 @@
1
+ class DnsPlugin < Plugin
2
+ require 'resolv'
3
+ def gethostname(address)
4
+ Resolv.getname(address)
5
+ end
6
+ def getaddresses(name)
7
+ Resolv.getaddresses(name)
8
+ end
9
+
10
+ def help(plugin, topic="")
11
+ "dns <hostname|ip> => show local resolution results for hostname or ip address"
12
+ end
13
+
14
+ def name_to_ip(m, params)
15
+ Thread.new do
16
+ begin
17
+ a = getaddresses(params[:host])
18
+ if a.length > 0
19
+ m.reply m.params + ": " + a.join(", ")
20
+ else
21
+ m.reply "#{params[:host]}: not found"
22
+ end
23
+ rescue StandardError => err
24
+ m.reply "#{params[:host]}: not found"
25
+ end
26
+ end
27
+ end
28
+
29
+ def ip_to_name(m, params)
30
+ Thread.new do
31
+ begin
32
+ a = gethostname(params[:ip])
33
+ m.reply m.params + ": " + a if a
34
+ rescue StandardError => err
35
+ m.reply "#{params[:ip]}: not found (does not reverse resolve)"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ plugin = DnsPlugin.new
41
+ plugin.map 'dns :ip', :action => 'ip_to_name',
42
+ :requirements => {:ip => /^\d+\.\d+\.\d+\.\d+$/}
43
+ plugin.map 'dns :host', :action => 'name_to_ip'
@@ -0,0 +1,19 @@
1
+ class OpMePlugin < Plugin
2
+
3
+ def help(plugin, topic="")
4
+ return "opme <channel> => grant user ops in <channel>"
5
+ end
6
+
7
+ def privmsg(m)
8
+ if(m.params)
9
+ channel = m.params
10
+ else
11
+ channel = m.channel
12
+ end
13
+ target = m.sourcenick
14
+ @bot.sendq("MODE #{channel} +o #{target}")
15
+ m.okay
16
+ end
17
+ end
18
+ plugin = OpMePlugin.new
19
+ plugin.register("opme")
@@ -0,0 +1,51 @@
1
+ # automatically auths with Q on quakenet servers
2
+
3
+ class QPlugin < Plugin
4
+
5
+ def help(plugin, topic="")
6
+ case topic
7
+ when ""
8
+ return "quath plugin: handles Q auths. topics set, identify"
9
+ when "set"
10
+ return "nickserv set <user> <passwd>: set the Q user and password and use it to identify in future"
11
+ when "identify"
12
+ return "quath identify: identify with Q (if user and auth are set)"
13
+ end
14
+ end
15
+
16
+ def initialize
17
+ super
18
+ # this plugin only wants to store strings!
19
+ class << @registry
20
+ def store(val)
21
+ val
22
+ end
23
+ def restore(val)
24
+ val
25
+ end
26
+ end
27
+ end
28
+
29
+ def set(m, params)
30
+ @registry['quakenet.user'] = params[:user]
31
+ @registry['quakenet.auth'] = params[:passwd]
32
+ m.okay
33
+ end
34
+
35
+ def connect
36
+ identify(nil, nil)
37
+ end
38
+ def identify(m, params)
39
+ if @registry.has_key?('quakenet.user') && @registry.has_key?('quakenet.auth')
40
+ debug "authing with Q using #{@registry['quakenet.user']} #{@registry['quakenet.auth']}"
41
+ @bot.sendmsg "PRIVMSG", "Q@CServe.quakenet.org", "auth #{@registry['quakenet.user']} #{@registry['quakenet.auth']}"
42
+ m.okay if m
43
+ else
44
+ m.reply "not configured, try 'qauth set :nick :passwd'" if m
45
+ end
46
+ end
47
+
48
+ end
49
+ plugin = QPlugin.new
50
+ plugin.map 'qauth set :nick :passwd', :action => "set"
51
+ plugin.map 'quath identify', :action => "identify"
@@ -0,0 +1,321 @@
1
+ Quote = Struct.new("Quote", "num", "date", "source", "quote")
2
+
3
+ class QuotePlugin < Plugin
4
+ def initialize
5
+ super
6
+ @lists = Hash.new
7
+ Dir["#{@bot.botclass}/quotes/*"].each {|f|
8
+ channel = File.basename(f)
9
+ @lists[channel] = Array.new if(!@lists.has_key?(channel))
10
+ IO.foreach(f) {|line|
11
+ if(line =~ /^(\d+) \| ([^|]+) \| (\S+) \| (.*)$/)
12
+ num = $1.to_i
13
+ @lists[channel][num] = Quote.new(num, $2, $3, $4)
14
+ end
15
+ }
16
+ }
17
+ end
18
+ def save
19
+ Dir.mkdir("#{@bot.botclass}/quotes") if(!FileTest.directory?("#{@bot.botclass}/quotes"))
20
+ @lists.each {|channel, quotes|
21
+ File.open("#{@bot.botclass}/quotes/#{channel}", "w") {|file|
22
+ quotes.compact.each {|q|
23
+ file.puts "#{q.num} | #{q.date} | #{q.source} | #{q.quote}"
24
+ }
25
+ }
26
+ }
27
+ end
28
+ def addquote(source, channel, quote)
29
+ @lists[channel] = Array.new if(!@lists.has_key?(channel))
30
+ num = @lists[channel].length
31
+ @lists[channel][num] = Quote.new(num, Time.new, source, quote)
32
+ return num
33
+ end
34
+ def getquote(source, channel, num=nil)
35
+ return nil unless(@lists.has_key?(channel))
36
+ return nil unless(@lists[channel].length > 0)
37
+ if(num)
38
+ if(@lists[channel][num])
39
+ return @lists[channel][num], @lists[channel].length - 1
40
+ end
41
+ else
42
+ # random quote
43
+ return @lists[channel].compact[rand(@lists[channel].nitems)],
44
+ @lists[channel].length - 1
45
+ end
46
+ end
47
+ def delquote(channel, num)
48
+ return false unless(@lists.has_key?(channel))
49
+ return false unless(@lists[channel].length > 0)
50
+ if(@lists[channel][num])
51
+ @lists[channel][num] = nil
52
+ return true
53
+ end
54
+ return false
55
+ end
56
+ def countquote(source, channel=nil, regexp=nil)
57
+ unless(channel)
58
+ total=0
59
+ @lists.each_value {|l|
60
+ total += l.compact.length
61
+ }
62
+ return total
63
+ end
64
+ return 0 unless(@lists.has_key?(channel))
65
+ return 0 unless(@lists[channel].length > 0)
66
+ if(regexp)
67
+ matches = @lists[channel].compact.find_all {|a| a.quote =~ /#{regexp}/i }
68
+ else
69
+ matches = @lists[channel].compact
70
+ end
71
+ return matches.length
72
+ end
73
+ def searchquote(source, channel, regexp)
74
+ return nil unless(@lists.has_key?(channel))
75
+ return nil unless(@lists[channel].length > 0)
76
+ matches = @lists[channel].compact.find_all {|a| a.quote =~ /#{regexp}/i }
77
+ if(matches.length > 0)
78
+ return matches[rand(matches.length)], @lists[channel].length - 1
79
+ else
80
+ return nil
81
+ end
82
+ end
83
+ def help(plugin, topic="")
84
+ case topic
85
+ when "addquote"
86
+ return "addquote [<channel>] <quote> => Add quote <quote> for channel <channel>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !addquote without addressing if so configured"
87
+ when "delquote"
88
+ return "delquote [<channel>] <num> => delete quote from <channel> with number <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !delquote without addressing if so configured"
89
+ when "getquote"
90
+ return "getquote [<channel>] [<num>] => get quote from <channel> with number <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Without <num>, a random quote will be returned. Responds to !getquote without addressing if so configured"
91
+ when "searchquote"
92
+ return "searchquote [<channel>] <regexp> => search for quote from <channel> that matches <regexp>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !searchquote without addressing if so configured"
93
+ when "topicquote"
94
+ return "topicquote [<channel>] [<num>] => set topic to quote from <channel> with number <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Without <num>, a random quote will be set. Responds to !topicquote without addressing if so configured"
95
+ when "countquote"
96
+ return "countquote [<channel>] <regexp> => count quotes from <channel> that match <regexp>. You only need to supply <channel> if you are addressing #{@bot.nick} privately. Responds to !countquote without addressing if so configured"
97
+ when "whoquote"
98
+ return "whoquote [<channel>] <num> => show who added quote <num>. You only need to supply <channel> if you are addressing #{@bot.nick} privately"
99
+ when "whenquote"
100
+ return "whenquote [<channel>] <num> => show when quote <num> was added. You only need to supply <channel> if you are addressing #{@bot.nick} privately"
101
+ else
102
+ return "Quote module (Quote storage and retrieval) topics: addquote, getquote, searchquote, topicquote, countquote, whoquote, whenquote"
103
+ end
104
+ end
105
+ def listen(m)
106
+ return unless(m.kind_of? PrivMessage)
107
+
108
+ command = m.message.dup
109
+ if(m.address? && m.private?)
110
+ case command
111
+ when (/^addquote\s+(#\S+)\s+(.*)/)
112
+ channel = $1
113
+ quote = $2
114
+ if(@bot.auth.allow?("addquote", m.source, m.replyto))
115
+ if(channel =~ /^#/)
116
+ num = addquote(m.source, channel, quote)
117
+ m.reply "added the quote (##{num})"
118
+ end
119
+ end
120
+ when (/^getquote\s+(#\S+)$/)
121
+ channel = $1
122
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
123
+ quote, total = getquote(m.source, channel)
124
+ if(quote)
125
+ m.reply "[#{quote.num}] #{quote.quote}"
126
+ else
127
+ m.reply "quote not found!"
128
+ end
129
+ end
130
+ when (/^getquote\s+(#\S+)\s+(\d+)$/)
131
+ channel = $1
132
+ num = $2.to_i
133
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
134
+ quote, total = getquote(m.source, channel, num)
135
+ if(quote)
136
+ m.reply "[#{quote.num}] #{quote.quote}"
137
+ else
138
+ m.reply "quote not found!"
139
+ end
140
+ end
141
+ when (/^whoquote\s+(#\S+)\s+(\d+)$/)
142
+ channel = $1
143
+ num = $2.to_i
144
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
145
+ quote, total = getquote(m.source, channel, num)
146
+ if(quote)
147
+ m.reply "quote #{quote.num} added by #{quote.source}"
148
+ else
149
+ m.reply "quote not found!"
150
+ end
151
+ end
152
+ when (/^whenquote\s+(#\S+)\s+(\d+)$/)
153
+ channel = $1
154
+ num = $2.to_i
155
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
156
+ quote, total = getquote(m.source, channel, num)
157
+ if(quote)
158
+ m.reply "quote #{quote.num} added on #{quote.date}"
159
+ else
160
+ m.reply "quote not found!"
161
+ end
162
+ end
163
+ when (/^topicquote\s+(#\S+)$/)
164
+ channel = $1
165
+ if(@bot.auth.allow?("topicquote", m.source, m.replyto))
166
+ quote, total = getquote(m.source, channel)
167
+ if(quote)
168
+ @bot.topic channel, "[#{quote.num}] #{quote.quote}"
169
+ else
170
+ m.reply "quote not found!"
171
+ end
172
+ end
173
+ when (/^topicquote\s+(#\S+)\s+(\d+)$/)
174
+ channel = $1
175
+ num = $2.to_i
176
+ if(@bot.auth.allow?("topicquote", m.source, m.replyto))
177
+ quote, total = getquote(m.source, channel, num)
178
+ if(quote)
179
+ @bot.topic channel, "[#{quote.num}] #{quote.quote}"
180
+ else
181
+ m.reply "quote not found!"
182
+ end
183
+ end
184
+ when (/^delquote\s+(#\S+)\s+(\d+)$/)
185
+ channel = $1
186
+ num = $2.to_i
187
+ if(@bot.auth.allow?("delquote", m.source, m.replyto))
188
+ if(delquote(channel, num))
189
+ m.okay
190
+ else
191
+ m.reply "quote not found!"
192
+ end
193
+ end
194
+ when (/^searchquote\s+(#\S+)\s+(.*)$/)
195
+ channel = $1
196
+ reg = $2
197
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
198
+ quote, total = searchquote(m.source, channel, reg)
199
+ if(quote)
200
+ m.reply "[#{quote.num}] #{quote.quote}"
201
+ else
202
+ m.reply "quote not found!"
203
+ end
204
+ end
205
+ when (/^countquote$/)
206
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
207
+ total = countquote(m.source)
208
+ m.reply "#{total} quotes"
209
+ end
210
+ when (/^countquote\s+(#\S+)\s*(.*)$/)
211
+ channel = $1
212
+ reg = $2
213
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
214
+ total = countquote(m.source, channel, reg)
215
+ if(reg.length > 0)
216
+ m.reply "#{total} quotes match: #{reg}"
217
+ else
218
+ m.reply "#{total} quotes"
219
+ end
220
+ end
221
+ end
222
+ elsif (m.address? || (@bot.config["QUOTE_LISTEN"] && command.gsub!(/^!/, "")))
223
+ case command
224
+ when (/^addquote\s+(.+)/)
225
+ if(@bot.auth.allow?("addquote", m.source, m.replyto))
226
+ num = addquote(m.source, m.target, $1)
227
+ m.reply "added the quote (##{num})"
228
+ end
229
+ when (/^getquote$/)
230
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
231
+ quote, total = getquote(m.source, m.target)
232
+ if(quote)
233
+ m.reply "[#{quote.num}] #{quote.quote}"
234
+ else
235
+ m.reply "no quotes found!"
236
+ end
237
+ end
238
+ when (/^getquote\s+(\d+)$/)
239
+ num = $1.to_i
240
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
241
+ quote, total = getquote(m.source, m.target, num)
242
+ if(quote)
243
+ m.reply "[#{quote.num}] #{quote.quote}"
244
+ else
245
+ m.reply "quote not found!"
246
+ end
247
+ end
248
+ when (/^whenquote\s+(\d+)$/)
249
+ num = $1.to_i
250
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
251
+ quote, total = getquote(m.source, m.target, num)
252
+ if(quote)
253
+ m.reply "quote #{quote.num} added on #{quote.date}"
254
+ else
255
+ m.reply "quote not found!"
256
+ end
257
+ end
258
+ when (/^whoquote\s+(\d+)$/)
259
+ num = $1.to_i
260
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
261
+ quote, total = getquote(m.source, m.target, num)
262
+ if(quote)
263
+ m.reply "quote #{quote.num} added by #{quote.source}"
264
+ else
265
+ m.reply "quote not found!"
266
+ end
267
+ end
268
+ when (/^topicquote$/)
269
+ if(@bot.auth.allow?("topicquote", m.source, m.replyto))
270
+ quote, total = getquote(m.source, m.target)
271
+ if(quote)
272
+ @bot.topic m.target, "[#{quote.num}] #{quote.quote}"
273
+ else
274
+ m.reply "no quotes found!"
275
+ end
276
+ end
277
+ when (/^topicquote\s+(\d+)$/)
278
+ num = $1.to_i
279
+ if(@bot.auth.allow?("topicquote", m.source, m.replyto))
280
+ quote, total = getquote(m.source, m.target, num)
281
+ if(quote)
282
+ @bot.topic m.target, "[#{quote.num}] #{quote.quote}"
283
+ else
284
+ m.reply "quote not found!"
285
+ end
286
+ end
287
+ when (/^delquote\s+(\d+)$/)
288
+ num = $1.to_i
289
+ if(@bot.auth.allow?("delquote", m.source, m.replyto))
290
+ if(delquote(m.target, num))
291
+ m.okay
292
+ else
293
+ m.reply "quote not found!"
294
+ end
295
+ end
296
+ when (/^searchquote\s+(.*)$/)
297
+ reg = $1
298
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
299
+ quote, total = searchquote(m.source, m.target, reg)
300
+ if(quote)
301
+ m.reply "[#{quote.num}] #{quote.quote}"
302
+ else
303
+ m.reply "quote not found!"
304
+ end
305
+ end
306
+ when (/^countquote(?:\s+(.*))?$/)
307
+ reg = $1
308
+ if(@bot.auth.allow?("getquote", m.source, m.replyto))
309
+ total = countquote(m.source, m.target, reg)
310
+ if(reg && reg.length > 0)
311
+ m.reply "#{total} quotes match: #{reg}"
312
+ else
313
+ m.reply "#{total} quotes"
314
+ end
315
+ end
316
+ end
317
+ end
318
+ end
319
+ end
320
+ plugin = QuotePlugin.new
321
+ plugin.register("quotes")
@@ -0,0 +1,228 @@
1
+ require 'rbot/utils'
2
+
3
+ class RemindPlugin < Plugin
4
+ # read a time in string format, turn it into "seconds from now".
5
+ # example formats handled are "5 minutes", "2 days", "five hours",
6
+ # "11:30", "15:45:11", "one day", etc.
7
+ #
8
+ # Throws:: RunTimeError "invalid time string" on parse failure
9
+ def timestr_offset(timestr)
10
+ case timestr
11
+ when (/^(\S+)\s+(\S+)$/)
12
+ mult = $1
13
+ unit = $2
14
+ if(mult =~ /^([\d.]+)$/)
15
+ num = $1.to_f
16
+ raise "invalid time string" unless num
17
+ else
18
+ case mult
19
+ when(/^(one|an|a)$/)
20
+ num = 1
21
+ when(/^two$/)
22
+ num = 2
23
+ when(/^three$/)
24
+ num = 3
25
+ when(/^four$/)
26
+ num = 4
27
+ when(/^five$/)
28
+ num = 5
29
+ when(/^six$/)
30
+ num = 6
31
+ when(/^seven$/)
32
+ num = 7
33
+ when(/^eight$/)
34
+ num = 8
35
+ when(/^nine$/)
36
+ num = 9
37
+ when(/^ten$/)
38
+ num = 10
39
+ when(/^fifteen$/)
40
+ num = 15
41
+ when(/^twenty$/)
42
+ num = 20
43
+ when(/^thirty$/)
44
+ num = 30
45
+ when(/^sixty$/)
46
+ num = 60
47
+ else
48
+ raise "invalid time string"
49
+ end
50
+ end
51
+ case unit
52
+ when (/^(s|sec(ond)?s?)$/)
53
+ return num
54
+ when (/^(m|min(ute)?s?)$/)
55
+ return num * 60
56
+ when (/^(h|h(ou)?rs?)$/)
57
+ return num * 60 * 60
58
+ when (/^(d|days?)$/)
59
+ return num * 60 * 60 * 24
60
+ else
61
+ raise "invalid time string"
62
+ end
63
+ when (/^(\d+):(\d+):(\d+)$/)
64
+ hour = $1.to_i
65
+ min = $2.to_i
66
+ sec = $3.to_i
67
+ now = Time.now
68
+ later = Time.mktime(now.year, now.month, now.day, hour, min, sec)
69
+ return later - now
70
+ when (/^(\d+):(\d+)$/)
71
+ hour = $1.to_i
72
+ min = $2.to_i
73
+ now = Time.now
74
+ later = Time.mktime(now.year, now.month, now.day, hour, min, now.sec)
75
+ return later - now
76
+ when (/^(\d+):(\d+)(am|pm)$/)
77
+ hour = $1.to_i
78
+ min = $2.to_i
79
+ ampm = $3
80
+ if ampm == "pm"
81
+ hour += 12
82
+ end
83
+ now = Time.now
84
+ later = Time.mktime(now.year, now.month, now.day, hour, min, now.sec)
85
+ return later - now
86
+ when (/^(\S+)$/)
87
+ num = 1
88
+ unit = $1
89
+ case unit
90
+ when (/^(s|sec(ond)?s?)$/)
91
+ return num
92
+ when (/^(m|min(ute)?s?)$/)
93
+ return num * 60
94
+ when (/^(h|h(ou)?rs?)$/)
95
+ return num * 60 * 60
96
+ when (/^(d|days?)$/)
97
+ return num * 60 * 60 * 24
98
+ else
99
+ raise "invalid time string"
100
+ end
101
+ else
102
+ raise "invalid time string"
103
+ end
104
+ end
105
+
106
+ def initialize
107
+ super
108
+ @reminders = Hash.new
109
+ end
110
+ def cleanup
111
+ @reminders.each_value {|v|
112
+ v.each_value {|vv|
113
+ @bot.timer.remove(vv)
114
+ }
115
+ }
116
+ @reminders.clear
117
+ end
118
+ def help(plugin, topic="")
119
+ "reminder plugin: remind <who> [about] <message> in <time>, remind <who> [about] <message> every <time>, remind <who> [about] <message> at <time>, remind <who> no more [about] <message>, remind <who> no more. Generally <who> should be 'me', but you can remind others (nick or channel) if you have remind_others auth"
120
+ end
121
+ def add_reminder(who, subject, timestr, repeat=false)
122
+ begin
123
+ period = timestr_offset(timestr)
124
+ rescue RuntimeError
125
+ return "couldn't parse that time string (#{timestr}) :("
126
+ end
127
+ if(period <= 0)
128
+ return "that time is in the past! (#{timestr})"
129
+ end
130
+ if(period < 30 && repeat)
131
+ return "repeats of less than 30 seconds are forbidden"
132
+ end
133
+ if(!@reminders.has_key?(who))
134
+ @reminders[who] = Hash.new
135
+ elsif(@reminders[who].has_key?(subject))
136
+ del_reminder(who, subject)
137
+ end
138
+
139
+ if(repeat)
140
+ @reminders[who][subject] = @bot.timer.add(period) {
141
+ time = Time.now + period
142
+ tstr = time.strftime("%H:%M:%S")
143
+ @bot.say who, "repeat reminder (next at #{tstr}): #{subject}"
144
+ }
145
+ else
146
+ @reminders[who][subject] = @bot.timer.add_once(period) {
147
+ time = Time.now + period
148
+ tstr = time.strftime("%H:%M:%S")
149
+ @bot.say who, "reminder (#{tstr}): #{subject}"
150
+ }
151
+ end
152
+ return false
153
+ end
154
+ def del_reminder(who, subject=nil)
155
+ if(subject)
156
+ if(@reminders.has_key?(who) && @reminders[who].has_key?(subject))
157
+ @bot.timer.remove(@reminders[who][subject])
158
+ @reminders[who].delete(subject)
159
+ return true
160
+ else
161
+ return false
162
+ end
163
+ else
164
+ if(@reminders.has_key?(who))
165
+ @reminders[who].each_value {|v|
166
+ @bot.timer.remove(v)
167
+ }
168
+ @reminders.delete(who)
169
+ return true
170
+ else
171
+ return false
172
+ end
173
+ end
174
+ end
175
+ def remind(m, params)
176
+ who = params.has_key?(:who) ? params[:who] : m.sourcenick
177
+ string = params[:string].to_s
178
+ puts "in remind, string is: #{string}"
179
+ if(string =~ /^(.*)\s+in\s+(.*)$/)
180
+ subject = $1
181
+ period = $2
182
+ if(err = add_reminder(who, subject, period))
183
+ m.reply "incorrect usage: " + err
184
+ return
185
+ end
186
+ elsif(string =~ /^(.*)\s+every\s+(.*)$/)
187
+ subject = $1
188
+ period = $2
189
+ if(err = add_reminder(who, subject, period, true))
190
+ m.reply "incorrect usage: " + err
191
+ return
192
+ end
193
+ elsif(string =~ /^(.*)\s+at\s+(.*)$/)
194
+ subject = $1
195
+ time = $2
196
+ if(err = add_reminder(who, subject, time))
197
+ m.reply "incorrect usage: " + err
198
+ return
199
+ end
200
+ else
201
+ usage(m)
202
+ return
203
+ end
204
+ m.okay
205
+ end
206
+ def no_more(m, params)
207
+ who = params.has_key?(:who) ? params[:who] : m.sourcenick
208
+ deleted = params.has_key?(:string) ?
209
+ del_reminder(who, params[:string].to_s) : del_reminder(who)
210
+ if deleted
211
+ m.okay
212
+ else
213
+ m.reply "but I wasn't going to :/"
214
+ end
215
+ end
216
+ end
217
+ plugin = RemindPlugin.new
218
+ plugin.map 'remind me no more', :action => 'no_more'
219
+ plugin.map 'remind me no more about *string', :action => 'no_more'
220
+ plugin.map 'remind me no more *string', :action => 'no_more'
221
+ plugin.map 'remind me about *string'
222
+ plugin.map 'remind me *string'
223
+ plugin.map 'remind :who no more', :auth => 'remind_other', :action => 'no_more'
224
+ plugin.map 'remind :who no more about *string', :auth => 'remind_other', :action => 'no_more'
225
+ plugin.map 'remind :who no more *string', :auth => 'remind_other', :action => 'no_more'
226
+ plugin.map 'remind :who about *string', :auth => 'remind_other'
227
+ plugin.map 'remind :who *string', :auth => 'remind_other'
228
+