rbot 0.9.9 → 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. data/AUTHORS +8 -0
  2. data/ChangeLog +51 -0
  3. data/INSTALL +4 -0
  4. data/README +1 -0
  5. data/REQUIREMENTS +11 -0
  6. data/TODO +2 -0
  7. data/bin/rbot +21 -2
  8. data/data/rbot/languages/german.lang +4 -1
  9. data/data/rbot/languages/russian.lang +75 -0
  10. data/data/rbot/plugins/autoop.rb +42 -51
  11. data/data/rbot/plugins/bans.rb +205 -0
  12. data/data/rbot/plugins/bash.rb +56 -0
  13. data/data/rbot/plugins/chucknorris.rb +74 -0
  14. data/data/rbot/plugins/chucknorris.yml.gz +0 -0
  15. data/data/rbot/plugins/deepthoughts.rb +95 -0
  16. data/data/rbot/plugins/demauro.rb +95 -0
  17. data/data/rbot/plugins/digg.rb +51 -0
  18. data/data/rbot/plugins/figlet.rb +24 -0
  19. data/data/rbot/plugins/forecast.rb +133 -0
  20. data/data/rbot/plugins/freshmeat.rb +13 -7
  21. data/data/rbot/plugins/google.rb +2 -0
  22. data/data/rbot/plugins/grouphug.rb +36 -0
  23. data/data/rbot/plugins/imdb.rb +92 -0
  24. data/data/rbot/plugins/insult.rb +8 -1
  25. data/data/rbot/plugins/iplookup.rb +227 -0
  26. data/data/rbot/plugins/karma.rb +2 -2
  27. data/data/rbot/plugins/keywords.rb +470 -0
  28. data/data/rbot/plugins/lart.rb +132 -146
  29. data/data/rbot/plugins/lastfm.rb +25 -0
  30. data/data/rbot/plugins/markov.rb +204 -0
  31. data/data/rbot/plugins/math.rb +5 -1
  32. data/data/rbot/plugins/nickserv.rb +71 -11
  33. data/data/rbot/plugins/opme.rb +19 -19
  34. data/data/rbot/plugins/quakeauth.rb +2 -2
  35. data/data/rbot/plugins/quotes.rb +40 -25
  36. data/data/rbot/plugins/remind.rb +1 -1
  37. data/data/rbot/plugins/rot13.rb +2 -2
  38. data/data/rbot/plugins/roulette.rb +49 -15
  39. data/data/rbot/plugins/rss.rb +585 -0
  40. data/data/rbot/plugins/rubyurl.rb +39 -0
  41. data/data/rbot/plugins/seen.rb +2 -1
  42. data/data/rbot/plugins/slashdot.rb +5 -5
  43. data/data/rbot/plugins/spell.rb +5 -0
  44. data/data/rbot/plugins/theyfightcrime.rb +121 -0
  45. data/data/rbot/plugins/threat.rb +55 -0
  46. data/data/rbot/plugins/tinyurl.rb +39 -0
  47. data/data/rbot/plugins/topic.rb +204 -0
  48. data/data/rbot/plugins/urban.rb +71 -0
  49. data/data/rbot/plugins/url.rb +399 -4
  50. data/data/rbot/plugins/wow.rb +123 -0
  51. data/data/rbot/plugins/wserver.rb +1 -1
  52. data/data/rbot/templates/levels.rbot +2 -0
  53. data/lib/rbot/auth.rb +207 -96
  54. data/lib/rbot/channel.rb +5 -5
  55. data/lib/rbot/config.rb +125 -24
  56. data/lib/rbot/dbhash.rb +87 -21
  57. data/lib/rbot/httputil.rb +181 -13
  58. data/lib/rbot/ircbot.rb +525 -179
  59. data/lib/rbot/ircsocket.rb +330 -54
  60. data/lib/rbot/message.rb +66 -23
  61. data/lib/rbot/messagemapper.rb +25 -17
  62. data/lib/rbot/plugins.rb +244 -115
  63. data/lib/rbot/post-clean.rb +1 -0
  64. data/lib/rbot/{post-install.rb → post-config.rb} +1 -1
  65. data/lib/rbot/rbotconfig.rb +29 -14
  66. data/lib/rbot/registry.rb +111 -72
  67. data/lib/rbot/rfc2812.rb +208 -197
  68. data/lib/rbot/timer.rb +4 -0
  69. data/lib/rbot/utils.rb +2 -2
  70. metadata +127 -104
  71. data/data/rbot/plugins/rss.rb.disabled +0 -414
  72. data/lib/rbot/keywords.rb +0 -433
data/lib/rbot/channel.rb CHANGED
@@ -4,23 +4,23 @@ module Irc
4
4
  class IRCChannel
5
5
  # name of channel
6
6
  attr_reader :name
7
-
7
+
8
8
  # current channel topic
9
9
  attr_reader :topic
10
-
10
+
11
11
  # hash containing users currently in the channel
12
12
  attr_accessor :users
13
-
13
+
14
14
  # if true, bot won't talk in this channel
15
15
  attr_accessor :quiet
16
-
16
+
17
17
  # name:: channel name
18
18
  # create a new IRCChannel
19
19
  def initialize(name)
20
20
  @name = name
21
21
  @users = Hash.new
22
22
  @quiet = false
23
- @topic = Topic.new
23
+ @topic = Topic.new
24
24
  end
25
25
 
26
26
  # eg @bot.channels[chan].topic = topic
data/lib/rbot/config.rb CHANGED
@@ -3,6 +3,14 @@ module Irc
3
3
  require 'yaml'
4
4
  require 'rbot/messagemapper'
5
5
 
6
+ unless YAML.respond_to?(:load_file)
7
+ def YAML.load_file( filepath )
8
+ File.open( filepath ) do |f|
9
+ YAML::load( f )
10
+ end
11
+ end
12
+ end
13
+
6
14
  class BotConfigValue
7
15
  # allow the definition order to be preserved so that sorting by
8
16
  # definition order is possible. The BotConfigWizard does this to allow
@@ -15,12 +23,15 @@ module Irc
15
23
  attr_reader :requires_restart
16
24
  attr_reader :order
17
25
  def initialize(key, params)
18
- unless key =~ /^.+\..+$/
26
+ # Keys must be in the form 'module.name'.
27
+ # They will be internally passed around as symbols,
28
+ # but we accept them both in string and symbol form.
29
+ unless key.to_s =~ /^.+\..+$/
19
30
  raise ArgumentError,"key must be of the form 'module.name'"
20
31
  end
21
32
  @order = @@order
22
33
  @@order += 1
23
- @key = key
34
+ @key = key.intern
24
35
  if params.has_key? :default
25
36
  @default = params[:default]
26
37
  else
@@ -114,6 +125,15 @@ module Irc
114
125
  def to_s
115
126
  get.join(", ")
116
127
  end
128
+ def add(val)
129
+ curval = self.get
130
+ set(curval + [val]) unless curval.include?(val)
131
+ end
132
+ def rm(val)
133
+ curval = self.get
134
+ raise ArgumentError, "value #{val} not present" unless curval.include?(val)
135
+ set(curval - [val])
136
+ end
117
137
  end
118
138
  class BotConfigEnumValue < BotConfigValue
119
139
  def initialize(key, params)
@@ -128,8 +148,8 @@ module Irc
128
148
  end
129
149
  end
130
150
  def parse(string)
131
- unless @values.include?(string)
132
- raise ArgumentError, "invalid value #{string}, allowed values are: " + @values.join(", ")
151
+ unless values.include?(string)
152
+ raise ArgumentError, "invalid value #{string}, allowed values are: " + values.join(", ")
133
153
  end
134
154
  string
135
155
  end
@@ -167,8 +187,17 @@ module Irc
167
187
  # supported via []
168
188
  def [](key)
169
189
  return @@items[key].value if @@items.has_key?(key)
190
+ return @@items[key.intern].value if @@items.has_key?(key.intern)
170
191
  # try to still support unregistered lookups
171
- return @@config[key] if @@config.has_key?(key)
192
+ # but warn about them
193
+ if @@config.has_key?(key)
194
+ warning "Unregistered lookup #{key.inspect}"
195
+ return @@config[key]
196
+ end
197
+ if @@config.has_key?(key.intern)
198
+ warning "Unregistered lookup #{key.intern.inspect}"
199
+ return @@config[key.intern]
200
+ end
172
201
  return false
173
202
  end
174
203
 
@@ -185,7 +214,7 @@ module Irc
185
214
  modules = []
186
215
  if params[:module]
187
216
  @@items.each_key do |key|
188
- mod, name = key.split('.')
217
+ mod, name = key.to_s.split('.')
189
218
  next unless mod == params[:module]
190
219
  modules.push key unless modules.include?(name)
191
220
  end
@@ -196,7 +225,7 @@ module Irc
196
225
  end
197
226
  else
198
227
  @@items.each_key do |key|
199
- name = key.split('.').first
228
+ name = key.to_s.split('.').first
200
229
  modules.push name unless modules.include?(name)
201
230
  end
202
231
  m.reply "modules: " + modules.join(", ")
@@ -204,7 +233,7 @@ module Irc
204
233
  end
205
234
 
206
235
  def handle_get(m, params)
207
- key = params[:key]
236
+ key = params[:key].to_s.intern
208
237
  unless @@items.has_key?(key)
209
238
  m.reply "no such config key #{key}"
210
239
  return
@@ -214,7 +243,7 @@ module Irc
214
243
  end
215
244
 
216
245
  def handle_desc(m, params)
217
- key = params[:key]
246
+ key = params[:key].to_s.intern
218
247
  unless @@items.has_key?(key)
219
248
  m.reply "no such config key #{key}"
220
249
  end
@@ -223,17 +252,18 @@ module Irc
223
252
  end
224
253
 
225
254
  def handle_unset(m, params)
226
- key = params[:key]
255
+ key = params[:key].to_s.intern
227
256
  unless @@items.has_key?(key)
228
257
  m.reply "no such config key #{key}"
229
258
  end
230
259
  @@items[key].unset
231
260
  handle_get(m, params)
261
+ m.reply "this config change will take effect on the next restart" if @@items[key].requires_restart
232
262
  end
233
263
 
234
264
  def handle_set(m, params)
235
- key = params[:key]
236
- value = params[:value].to_s
265
+ key = params[:key].to_s.intern
266
+ value = params[:value].join(" ")
237
267
  unless @@items.has_key?(key)
238
268
  m.reply "no such config key #{key}"
239
269
  return
@@ -251,11 +281,53 @@ module Irc
251
281
  end
252
282
  end
253
283
 
284
+ def handle_add(m, params)
285
+ key = params[:key].to_s.intern
286
+ value = params[:value]
287
+ unless @@items.has_key?(key)
288
+ m.reply "no such config key #{key}"
289
+ return
290
+ end
291
+ unless @@items[key].class <= BotConfigArrayValue
292
+ m.reply "config key #{key} is not an array"
293
+ return
294
+ end
295
+ begin
296
+ @@items[key].add(value)
297
+ rescue ArgumentError => e
298
+ m.reply "failed to add #{value} to #{key}: #{e.message}"
299
+ return
300
+ end
301
+ handle_get(m,{:key => key})
302
+ m.reply "this config change will take effect on the next restart" if @@items[key].requires_restart
303
+ end
304
+
305
+ def handle_rm(m, params)
306
+ key = params[:key].to_s.intern
307
+ value = params[:value]
308
+ unless @@items.has_key?(key)
309
+ m.reply "no such config key #{key}"
310
+ return
311
+ end
312
+ unless @@items[key].class <= BotConfigArrayValue
313
+ m.reply "config key #{key} is not an array"
314
+ return
315
+ end
316
+ begin
317
+ @@items[key].rm(value)
318
+ rescue ArgumentError => e
319
+ m.reply "failed to remove #{value} from #{key}: #{e.message}"
320
+ return
321
+ end
322
+ handle_get(m,{:key => key})
323
+ m.reply "this config change will take effect on the next restart" if @@items[key].requires_restart
324
+ end
325
+
254
326
  def handle_help(m, params)
255
327
  topic = params[:topic]
256
328
  case topic
257
329
  when false
258
- m.reply "config module - bot configuration. usage: list, desc, get, set, unset"
330
+ m.reply "config module - bot configuration. usage: list, desc, get, set, unset, add, rm"
259
331
  when "list"
260
332
  m.reply "config list => list configuration modules, config list <module> => list configuration keys for module <module>"
261
333
  when "get"
@@ -266,6 +338,10 @@ module Irc
266
338
  m.reply "config set <key> <value> => set configuration value for key <key> to <value>"
267
339
  when "desc"
268
340
  m.reply "config desc <key> => describe what key <key> configures"
341
+ when "add"
342
+ m.reply "config add <value> to <key> => add value <value> to key <key> if <key> is an array"
343
+ when "rm"
344
+ m.reply "config rm <value> from <key> => remove value <value> from key <key> if <key> is an array"
269
345
  else
270
346
  m.reply "no help for config #{topic}"
271
347
  end
@@ -296,27 +372,52 @@ module Irc
296
372
  @handler.map 'config desc :key', :action => 'handle_desc'
297
373
  @handler.map 'config describe :key', :action => 'handle_desc'
298
374
  @handler.map 'config set :key *value', :action => 'handle_set'
375
+ @handler.map 'config add :value to :key', :action => 'handle_add'
376
+ @handler.map 'config rm :value from :key', :action => 'handle_rm'
377
+ @handler.map 'config del :value from :key', :action => 'handle_rm'
378
+ @handler.map 'config delete :value from :key', :action => 'handle_rm'
299
379
  @handler.map 'config unset :key', :action => 'handle_unset'
380
+ @handler.map 'config reset :key', :action => 'handle_unset'
300
381
  @handler.map 'config help :topic', :action => 'handle_help',
301
382
  :defaults => {:topic => false}
302
383
  @handler.map 'help config :topic', :action => 'handle_help',
303
384
  :defaults => {:topic => false}
304
385
 
305
386
  if(File.exist?("#{@@bot.botclass}/conf.yaml"))
306
- newconfig = YAML::load_file("#{@@bot.botclass}/conf.yaml")
307
- @@config.update newconfig
308
- else
309
- # first-run wizard!
310
- BotConfigWizard.new(@@bot).run
311
- # save newly created config
312
- save
387
+ begin
388
+ newconfig = YAML::load_file("#{@@bot.botclass}/conf.yaml")
389
+ newconfig.each { |key, val|
390
+ @@config[key.intern] = val
391
+ }
392
+ return
393
+ rescue
394
+ error "failed to read conf.yaml: #{$!}"
395
+ end
313
396
  end
397
+ # if we got here, we need to run the first-run wizard
398
+ BotConfigWizard.new(@@bot).run
399
+ # save newly created config
400
+ save
314
401
  end
315
402
 
316
- # write current configuration to #{botclass}/conf.rbot
403
+ # write current configuration to #{botclass}/conf.yaml
317
404
  def save
318
- File.open("#{@@bot.botclass}/conf.yaml", "w") do |file|
319
- file.puts @@config.to_yaml
405
+ begin
406
+ debug "Writing new conf.yaml ..."
407
+ File.open("#{@@bot.botclass}/conf.yaml.new", "w") do |file|
408
+ savehash = {}
409
+ @@config.each { |key, val|
410
+ savehash[key.to_s] = val
411
+ }
412
+ file.puts savehash.to_yaml
413
+ end
414
+ debug "Officializing conf.yaml ..."
415
+ File.rename("#{@@bot.botclass}/conf.yaml.new",
416
+ "#{@@bot.botclass}/conf.yaml")
417
+ rescue => e
418
+ error "failed to write configuration file conf.yaml! #{$!}"
419
+ error "#{e.class}: #{e}"
420
+ error e.backtrace.join("\n")
320
421
  end
321
422
  end
322
423
 
@@ -345,7 +446,7 @@ module Irc
345
446
  @questions.sort{|a,b| a.order <=> b.order }.each do |q|
346
447
  puts q.desc
347
448
  begin
348
- print q.key + " [#{q.to_s}]: "
449
+ print q.key.to_s + " [#{q.to_s}]: "
349
450
  response = STDIN.gets
350
451
  response.chop!
351
452
  unless response.empty?
data/lib/rbot/dbhash.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  begin
2
2
  require 'bdb'
3
3
  rescue Exception => e
4
- puts "Got exception: "+e
5
- puts "rbot couldn't load the bdb module, perhaps you need to install it? try: http://www.ruby-lang.org/en/raa-list.rhtml?name=bdb"
4
+ error "Got exception: "+e
5
+ error "rbot couldn't load the bdb module, perhaps you need to install it? try: http://www.ruby-lang.org/en/raa-list.rhtml?name=bdb"
6
6
  exit 2
7
7
  end
8
8
 
@@ -10,7 +10,10 @@ end
10
10
  module BDB
11
11
  class CIBtree < Btree
12
12
  def bdb_bt_compare(a, b)
13
- a.downcase <=> b.downcase
13
+ if a == nil || b == nil
14
+ warning "CIBTree: comparing #{a.inspect} (#{self[a].inspect}) with #{b.inspect} (#{self[b].inspect})"
15
+ end
16
+ (a||'').downcase <=> (b||'').downcase
14
17
  end
15
18
  end
16
19
  end
@@ -22,7 +25,7 @@ module Irc
22
25
  # mydata.db, if it exists, it will load and reference that db.
23
26
  # Otherwise it'll create and empty db called mydata.db
24
27
  class DBHash
25
-
28
+
26
29
  # absfilename:: use +key+ as an actual filename, don't prepend the bot's
27
30
  # config path and don't append ".db"
28
31
  def initialize(bot, key, absfilename=false)
@@ -50,29 +53,39 @@ module Irc
50
53
  def DBHash.create_db(name)
51
54
  debug "DBHash: creating empty db #{name}"
52
55
  return BDB::Hash.open(name, nil,
53
- BDB::CREATE | BDB::EXCL | BDB::TRUNCATE,
54
- 0600, "set_pagesize" => 1024,
55
- "set_cachesize" => [(0), (32 * 1024), (0)])
56
+ BDB::CREATE | BDB::EXCL, 0600)
56
57
  end
57
58
 
58
59
  def DBHash.open_db(name)
59
60
  debug "DBHash: opening existing db #{name}"
60
- return BDB::Hash.open(name, nil,
61
- "r+", 0600, "set_pagesize" => 1024,
62
- "set_cachesize" => [(0), (32 * 1024), (0)])
61
+ return BDB::Hash.open(name, nil, "r+", 0600)
63
62
  end
64
-
63
+
65
64
  end
66
65
 
67
-
66
+
68
67
  # DBTree is a BTree equivalent of DBHash, with case insensitive lookups.
69
68
  class DBTree
70
-
69
+ @@env=nil
70
+ # TODO: make this customizable
71
+ # Note that it must be at least four times lg_bsize
72
+ @@lg_max = 8*1024*1024
71
73
  # absfilename:: use +key+ as an actual filename, don't prepend the bot's
72
74
  # config path and don't append ".db"
73
75
  def initialize(bot, key, absfilename=false)
74
76
  @bot = bot
75
77
  @key = key
78
+ if @@env.nil?
79
+ begin
80
+ @@env = BDB::Env.open("#{@bot.botclass}", BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER, "set_lg_max" => @@lg_max)
81
+ debug "DBTree: environment opened with max log size #{@@env.conf['lg_max']}"
82
+ rescue => e
83
+ debug "DBTree: failed to open environment: #{e}. Retrying ..."
84
+ @@env = BDB::Env.open("#{@bot.botclass}", BDB::INIT_TRANSACTION | BDB::CREATE | BDB::RECOVER)
85
+ end
86
+ #@@env = BDB::Env.open("#{@bot.botclass}", BDB::CREATE | BDB::INIT_MPOOL | BDB::RECOVER)
87
+ end
88
+
76
89
  if absfilename && File.exist?(key)
77
90
  # db already exists, use it
78
91
  @db = DBTree.open_db(key)
@@ -94,19 +107,72 @@ module Irc
94
107
 
95
108
  def DBTree.create_db(name)
96
109
  debug "DBTree: creating empty db #{name}"
97
- return BDB::CIBtree.open(name, nil,
98
- BDB::CREATE | BDB::EXCL | BDB::TRUNCATE,
99
- 0600, "set_pagesize" => 1024,
100
- "set_cachesize" => [(0), (32 * 1024), (0)])
110
+ return @@env.open_db(BDB::CIBtree, name, nil, BDB::CREATE | BDB::EXCL, 0600)
101
111
  end
102
112
 
103
113
  def DBTree.open_db(name)
104
114
  debug "DBTree: opening existing db #{name}"
105
- return BDB::CIBtree.open(name, nil,
106
- "r+", 0600, "set_pagesize" => 1024,
107
- "set_cachesize" => [0, 32 * 1024, 0])
115
+ return @@env.open_db(BDB::CIBtree, name, nil, "r+", 0600)
116
+ end
117
+
118
+ def DBTree.cleanup_logs()
119
+ begin
120
+ debug "DBTree: checkpointing ..."
121
+ @@env.checkpoint
122
+ rescue => e
123
+ debug "Failed: #{e}"
124
+ end
125
+ begin
126
+ debug "DBTree: flushing log ..."
127
+ @@env.log_flush
128
+ logs = @@env.log_archive(BDB::ARCH_ABS)
129
+ debug "DBTree: deleting archivable logs: #{logs.join(', ')}."
130
+ logs.each { |log|
131
+ File.delete(log)
132
+ }
133
+ rescue => e
134
+ debug "Failed: #{e}"
135
+ end
136
+ end
137
+
138
+ def DBTree.stats()
139
+ begin
140
+ debug "General stats:"
141
+ debug @@env.stat
142
+ debug "Lock stats:"
143
+ debug @@env.lock_stat
144
+ debug "Log stats:"
145
+ debug @@env.log_stat
146
+ debug "Txn stats:"
147
+ debug @@env.txn_stat
148
+ rescue
149
+ debug "Couldn't dump stats"
150
+ end
108
151
  end
109
-
152
+
153
+ def DBTree.cleanup_env()
154
+ begin
155
+ debug "DBTree: checking transactions ..."
156
+ has_active_txn = @@env.txn_stat["st_nactive"] > 0
157
+ if has_active_txn
158
+ warning "DBTree: not all transactions completed!"
159
+ end
160
+ DBTree.cleanup_logs
161
+ debug "DBTree: closing environment #{@@env}"
162
+ path = @@env.home
163
+ @@env.close
164
+ @@env = nil
165
+ if has_active_txn
166
+ debug "DBTree: keeping file because of incomplete transactions"
167
+ else
168
+ debug "DBTree: cleaning up environment in #{path}"
169
+ BDB::Env.remove("#{path}")
170
+ end
171
+ rescue => e
172
+ error "failed to clean up environment: #{e.inspect}"
173
+ end
174
+ end
175
+
110
176
  end
111
177
 
112
178
  end