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,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
+