mod_spox 0.1.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/CHANGELOG +36 -0
  2. data/INSTALL +2 -2
  3. data/README +0 -1
  4. data/bin/mod_spox +51 -12
  5. data/data/mod_spox/extras/AOLSpeak.rb +5 -18
  6. data/data/mod_spox/extras/AutoKick.rb +44 -23
  7. data/data/mod_spox/extras/AutoMode.rb +2 -5
  8. data/data/mod_spox/extras/AutoRejoin.rb +21 -0
  9. data/data/mod_spox/extras/Bouncer.rb +10 -10
  10. data/data/mod_spox/extras/Bytes.rb +12 -0
  11. data/data/mod_spox/extras/Confess.rb +131 -52
  12. data/data/mod_spox/extras/DCC.rb +189 -0
  13. data/data/mod_spox/extras/DevWatch.rb +32 -33
  14. data/data/mod_spox/extras/FloodKicker.rb +129 -0
  15. data/data/mod_spox/extras/GoogleIt.rb +13 -0
  16. data/data/mod_spox/extras/Headers.rb +31 -4
  17. data/data/mod_spox/extras/Karma.rb +103 -49
  18. data/data/mod_spox/extras/Logger.rb +45 -30
  19. data/data/mod_spox/extras/LolSpeak.rb +1 -1
  20. data/data/mod_spox/extras/NickServ.rb +83 -0
  21. data/data/mod_spox/extras/PhpCli.rb +12 -15
  22. data/data/mod_spox/extras/PhpFuncLookup.rb +57 -25
  23. data/data/mod_spox/extras/Quotes.rb +5 -4
  24. data/data/mod_spox/extras/RegexTracker.rb +160 -0
  25. data/data/mod_spox/extras/Roulette.rb +22 -23
  26. data/data/mod_spox/extras/Search.rb +3 -2
  27. data/data/mod_spox/extras/Slashdot.rb +35 -0
  28. data/data/mod_spox/extras/Topten.rb +5 -5
  29. data/data/mod_spox/extras/TracTicket.rb +68 -0
  30. data/data/mod_spox/extras/Translate.rb +69 -30
  31. data/data/mod_spox/extras/Twitter.rb +372 -0
  32. data/data/mod_spox/extras/UrbanDictionary.rb +21 -12
  33. data/data/mod_spox/extras/Weather.rb +1 -1
  34. data/data/mod_spox/plugins/Authenticator.rb +63 -30
  35. data/data/mod_spox/plugins/Banner.rb +164 -151
  36. data/data/mod_spox/plugins/Helper.rb +18 -7
  37. data/data/mod_spox/plugins/PluginLoader.rb +46 -22
  38. data/data/mod_spox/plugins/PoolConfig.rb +52 -0
  39. data/data/mod_spox/plugins/Quitter.rb +1 -1
  40. data/data/mod_spox/plugins/Status.rb +28 -0
  41. data/lib/mod_spox/Action.rb +20 -3
  42. data/lib/mod_spox/BaseConfig.rb +1 -0
  43. data/lib/mod_spox/Bot.rb +98 -75
  44. data/lib/mod_spox/BotConfig.rb +14 -6
  45. data/lib/mod_spox/ConfigurationWizard.rb +94 -105
  46. data/lib/mod_spox/Database.rb +33 -13
  47. data/lib/mod_spox/Helpers.rb +67 -38
  48. data/lib/mod_spox/Loader.rb +25 -5
  49. data/lib/mod_spox/Logger.rb +20 -62
  50. data/lib/mod_spox/MessageFactory.rb +34 -25
  51. data/lib/mod_spox/Monitors.rb +5 -0
  52. data/lib/mod_spox/Pipeline.rb +40 -51
  53. data/lib/mod_spox/Plugin.rb +40 -9
  54. data/lib/mod_spox/PluginManager.rb +46 -38
  55. data/lib/mod_spox/Pool.rb +129 -143
  56. data/lib/mod_spox/Socket.rb +41 -50
  57. data/lib/mod_spox/Sockets.rb +211 -0
  58. data/lib/mod_spox/Timer.rb +86 -69
  59. data/lib/mod_spox/handlers/BadNick.rb +1 -1
  60. data/lib/mod_spox/handlers/Created.rb +1 -1
  61. data/lib/mod_spox/handlers/Handler.rb +9 -0
  62. data/lib/mod_spox/handlers/Invite.rb +1 -1
  63. data/lib/mod_spox/handlers/Join.rb +2 -2
  64. data/lib/mod_spox/handlers/Kick.rb +1 -1
  65. data/lib/mod_spox/handlers/LuserChannels.rb +1 -1
  66. data/lib/mod_spox/handlers/LuserOp.rb +1 -1
  67. data/lib/mod_spox/handlers/LuserUnknown.rb +1 -1
  68. data/lib/mod_spox/handlers/Mode.rb +2 -2
  69. data/lib/mod_spox/handlers/MyInfo.rb +1 -1
  70. data/lib/mod_spox/handlers/Names.rb +1 -1
  71. data/lib/mod_spox/handlers/Nick.rb +20 -3
  72. data/lib/mod_spox/handlers/NickInUse.rb +3 -3
  73. data/lib/mod_spox/handlers/Notice.rb +5 -15
  74. data/lib/mod_spox/handlers/Part.rb +1 -1
  75. data/lib/mod_spox/handlers/Ping.rb +1 -1
  76. data/lib/mod_spox/handlers/Pong.rb +1 -1
  77. data/lib/mod_spox/handlers/Privmsg.rb +2 -2
  78. data/lib/mod_spox/handlers/Quit.rb +1 -1
  79. data/lib/mod_spox/handlers/Topic.rb +2 -1
  80. data/lib/mod_spox/handlers/Welcome.rb +3 -3
  81. data/lib/mod_spox/handlers/Who.rb +9 -7
  82. data/lib/mod_spox/handlers/Whois.rb +29 -16
  83. data/lib/mod_spox/handlers/YourHost.rb +1 -1
  84. data/lib/mod_spox/messages/incoming/Privmsg.rb +38 -19
  85. data/lib/mod_spox/messages/internal/DCCListener.rb +12 -0
  86. data/lib/mod_spox/messages/internal/DCCRequest.rb +12 -0
  87. data/lib/mod_spox/messages/internal/DCCSocket.rb +19 -0
  88. data/lib/mod_spox/messages/internal/StatusRequest.rb +2 -1
  89. data/lib/mod_spox/messages/outgoing/Privmsg.rb +21 -5
  90. data/lib/mod_spox/migrations/001_initialize_models.rb +115 -0
  91. data/lib/mod_spox/models/Auth.rb +24 -16
  92. data/lib/mod_spox/models/AuthGroup.rb +4 -3
  93. data/lib/mod_spox/models/Channel.rb +20 -12
  94. data/lib/mod_spox/models/ChannelMode.rb +2 -2
  95. data/lib/mod_spox/models/Config.rb +11 -3
  96. data/lib/mod_spox/models/Group.rb +6 -1
  97. data/lib/mod_spox/models/Nick.rb +93 -33
  98. data/lib/mod_spox/models/NickChannel.rb +8 -6
  99. data/lib/mod_spox/models/NickGroup.rb +16 -0
  100. data/lib/mod_spox/models/NickMode.rb +3 -3
  101. data/lib/mod_spox/models/Server.rb +6 -2
  102. data/lib/mod_spox/models/Setting.rb +12 -6
  103. data/lib/mod_spox/models/Signature.rb +7 -13
  104. data/lib/mod_spox/models/Trigger.rb +1 -1
  105. metadata +125 -100
data/lib/mod_spox/Bot.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  ['mod_spox/Logger',
2
- 'mod_spox/Socket',
2
+ 'mod_spox/Sockets',
3
3
  'mod_spox/Pipeline',
4
4
  'mod_spox/PluginManager',
5
5
  'mod_spox/MessageFactory',
@@ -7,110 +7,126 @@
7
7
  'mod_spox/Timer',
8
8
  'mod_spox/messages/Messages',
9
9
  'mod_spox/models/Models',
10
- 'mod_spox/Monitors',
11
- 'mod_spox/Helpers'].each{|f|require f}
10
+ 'mod_spox/Helpers',
11
+ 'mod_spox/Pool'].each{|f|require f}
12
12
  module ModSpox
13
13
 
14
14
  class Bot
15
-
15
+
16
16
  # bot timer
17
17
  attr_reader :timer
18
-
18
+
19
19
  # message pipeline
20
20
  attr_reader :pipeline
21
-
21
+
22
22
  # plugin manager
23
23
  attr_reader :plugin_manager
24
-
24
+
25
25
  # message factory
26
26
  attr_reader :factory
27
-
27
+
28
+ # DCC sockets
29
+ attr_reader :dcc_sockets
30
+
28
31
  # Create a Bot
29
32
  def initialize
30
- Logger.severity($VERBOSITY)
31
- Logger.fd
33
+ Logger.initialize($LOGTO, $LOGLEVEL)
34
+ Pool.instance
32
35
  clean_models
33
36
  @start_time = Time.now
34
37
  @pipeline = Pipeline.new
35
38
  @timer = Timer.new(@pipeline)
39
+ @timer.start
36
40
  @config = BaseConfig.new(BotConfig[:userconfigpath])
37
41
  @factory = MessageFactory.new(@pipeline)
38
42
  @socket = nil
39
43
  @plugin_manager = PluginManager.new(@pipeline)
44
+ if(@config[:plugin_upgrade] == 'yes')
45
+ @plugin_manager.upgrade_plugins
46
+ Logger.info('Main bot thread is now sleeping for 10 seconds to allow upgrade to conclude')
47
+ sleep(10)
48
+ Logger.info('Main bot thread sleep completed. Continuing loading.')
49
+ end
50
+ @config[:plugin_upgrade] = 'no'
51
+ @config.write_configuration
40
52
  @shutdown = false
41
- @socket = nil
53
+ @socket = Sockets.new(self)
42
54
  @nick = nil
43
55
  @thread = Thread.current
44
- @waiter = Monitors::Boolean.new
56
+ @lock = Mutex.new
57
+ @waiter = ConditionVariable.new
45
58
  hook_pipeline
46
59
  end
47
-
60
+
48
61
  # Run the bot
49
62
  def run
50
- trap('SIGTERM'){ Logger.log("Caught SIGTERM"); @shutdown = true; @waiter.wakeup; sleep(0.1); Thread.current.exit; Logger.kill; }
51
- trap('SIGKILL'){ Logger.log("Caught SIGKILL"); @shutdown = true; @waiter.wakeup; sleep(0.1); Thread.current.exit; Logger.kill; }
52
- trap('SIGINT'){ Logger.log("Caught SIGINT"); @shutdown = true; @waiter.wakeup; sleep(0.1); Thread.current.exit; Logger.kill; }
53
- trap('SIGQUIT'){ Logger.log("Caught SIGQUIT"); @shutdown = true; @waiter.wakeup; sleep(0.1); Thread.current.exit; Logger.kill; }
63
+ trap('SIGTERM'){ Logger.warn("Caught SIGTERM"); start_shutdown }
64
+ trap('SIGKILL'){ Logger.warn("Caught SIGKILL"); start_shutdown }
65
+ trap('SIGINT'){ Logger.warn("Caught SIGINT"); start_shutdown }
66
+ trap('SIGQUIT'){ Logger.warn("Caught SIGQUIT"); start_shutdown }
54
67
  until @shutdown do
55
- @timer.start
56
68
  @pipeline << Messages::Internal::BotInitialized.new
57
69
  begin
58
- @waiter.wait
70
+ @lock.synchronize do
71
+ Logger.info('Putting main execution thread to sleep until shutdown')
72
+ @waiter.wait(@lock)
73
+ Logger.info('Main execution thread has been restored.')
74
+ end
59
75
  rescue Object => boom
60
- Logger.log("Caught exception: #{boom}")
76
+ Logger.fatal("Caught exception: #{boom}")
77
+ ensure
78
+ shutdown
61
79
  end
62
- shutdown
63
80
  end
64
81
  end
65
-
82
+
66
83
  # Shut the bot down
67
84
  def shutdown
68
85
  @shutdown = true
86
+ Logger.info('Shutdown sequence initiated')
69
87
  @plugin_manager.destroy_plugins
70
- @thread.run
71
88
  @timer.stop
72
- @timer.destroy
73
- @factory.destroy
74
89
  @pipeline << Messages::Internal::Shutdown.new
75
90
  sleep(0.1)
76
- @pipeline.destroy
77
91
  @socket.shutdown unless @socket.nil?
78
92
  clean_models
79
93
  end
80
-
94
+
81
95
  # Reload the bot (basically a restart)
82
96
  def reload
83
- @thread.run
97
+ @lock.synchronize do
98
+ @waiter.signal
99
+ end
84
100
  end
85
-
101
+
86
102
  # message:: Messages::Internal::EstablishConnection message
87
103
  # Initialize connection to IRC server
88
104
  def bot_connect(message)
89
- Logger.log("Received a connection command", 10)
105
+ Logger.info("Received a connection command")
90
106
  begin
91
- @socket = Socket.new(self, message.server, message.port)
92
- @socket.connect
107
+ @socket = Sockets.new(self) if @socket.nil?
108
+ @socket.irc_connect(message.server, message.port)
93
109
  @pipeline << Messages::Internal::Connected.new(message.server, message.port)
94
110
  rescue Object => boom
95
- Logger.log("Failed connection to server: #{boom}")
111
+ Logger.warn("Failed connection to server: #{boom}")
96
112
  @pipeline << Messages::Internal::ConnectionFailed.new(message.server, message.port)
97
113
  end
98
114
  end
99
-
115
+
100
116
  # message:: Messages::Internal::StatusRequest message
101
117
  # Returns the current status of the bot
102
118
  def status(message)
103
- @pipeline << Messages::Internal::StatusResponse(message.requester, stats)
119
+ @pipeline << Messages::Internal::StatusResponse.new(message.requester, bot_stats)
104
120
  end
105
-
121
+
106
122
  # Returns status of the bot in a formatted string
107
- def stats
108
- return ["Uptime: #{Helpers::format_seconds(@start_time - Time.now)}",
109
- "Plugins: #{@plugins.plugins.size} loaded",
110
- "Lines sent: #{@socket.sent}",
111
- "Lines received: #{@socket.received}"].join(' ')
123
+ def bot_stats
124
+ return {:uptime => Helpers::format_seconds(Time.now - @start_time),
125
+ :plugins => @plugin_manager.plugins.size,
126
+ :sent => @socket.irc_socket.sent,
127
+ :received => @socket.irc_socket.received}
112
128
  end
113
-
129
+
114
130
  # Adds hooks to pipeline for processing messages
115
131
  def hook_pipeline
116
132
  {:Outgoing_Admin => :admin, :Outgoing_Away => :away,
@@ -141,46 +157,46 @@ module ModSpox
141
157
  :Outgoing_Raw => :raw
142
158
  }.each_pair{ |type,method| @pipeline.hook(self, method, type) }
143
159
  end
144
-
160
+
145
161
  # message:: Messages::Internal::TimerClear
146
162
  # Clear all actions from timer
147
163
  def clear_timer(message)
148
164
  @timer.clear
149
165
  end
150
-
166
+
151
167
  # message:: Messages::Internal::Disconnected
152
168
  # Disconnect the bot from the IRC server
153
169
  def disconnected(message)
154
170
  @socket.shutdown
155
171
  @socket = nil
156
172
  end
157
-
173
+
158
174
  # Stop the bot
159
175
  def halt(message)
160
176
  @shutdown = true
161
- @thread.run
177
+ reload
162
178
  end
163
-
179
+
164
180
  # message:: Messages::Internal::ChangeNick message
165
181
  # Changes the bot's nick to the given nick
166
182
  def set_nick(message)
167
183
  @nick = message.new_nick
168
184
  end
169
-
185
+
170
186
  # message:: Messages::Internal::NickRequest
171
187
  # Sends the bot's nick to plugins
172
188
  def get_nick(message)
173
189
  @pipeline << Messages::Internal::NickResponse(message.requester, @nick)
174
190
  end
175
-
191
+
176
192
  # message:: Messages::Outgoing::Pass message
177
193
  # Sends PASS message to server
178
194
  def pass(message)
179
195
  @socket << "PASS #{message.password}"
180
196
  end
181
-
197
+
182
198
  # message:: Messages::Outgoing::Nick message
183
- # Sends NICK message to server
199
+ # Sends NICK message to server
184
200
  def nick(message)
185
201
  nick = message.nick.is_a?(Models::Nick) ? message.nick.nick : message.nick
186
202
  @socket << "NICK #{nick}"
@@ -211,7 +227,7 @@ module ModSpox
211
227
  def quit(message)
212
228
  @socket << "QUIT :#{message.message}"
213
229
  end
214
-
230
+
215
231
  # message:: Messages::Outgoing::Squit message
216
232
  # Sends SQUIT message to server
217
233
  def squit(message)
@@ -241,7 +257,7 @@ module ModSpox
241
257
  channel = channel.name if channel.is_a?(Models::Channel)
242
258
  @socket << "MODE #{channel} #{message.mode} #{target}"
243
259
  end
244
-
260
+
245
261
  # message:: Messages::Outgoing::Topic message
246
262
  # Sends TOPIC message to server
247
263
  def topic(message)
@@ -262,7 +278,7 @@ module ModSpox
262
278
  channel = message.channel.is_a?(Models::Channel) ? message.channel.name : message.channel
263
279
  @socket << "LIST #{channel}"
264
280
  end
265
-
281
+
266
282
  # message:: Messages::Outgoing::Invite message
267
283
  # Sends INVITE message to server
268
284
  def invite(message)
@@ -270,7 +286,7 @@ module ModSpox
270
286
  channel = message.channel.is_a?(Models::Channel) ? message.channel.name : message.channel
271
287
  @socket << "INVITE #{nick} #{channel}"
272
288
  end
273
-
289
+
274
290
  # message:: Messages::Outgoing::Kick message
275
291
  # Sends KICK message to server
276
292
  def kick(message)
@@ -290,10 +306,10 @@ module ModSpox
290
306
  part.split("\n").each do |content|
291
307
  while(content.size > 450)
292
308
  output = content[0..450]
293
- content.slice!(0, 450) #(450, content.size)
294
- @socket << "PRIVMSG #{target} :#{message.is_action? ? "\cAACTION #{output}\cA" : output}"
309
+ content.slice!(0, 451) #(450, content.size)
310
+ @socket << "PRIVMSG #{target} :#{message.is_ctcp? ? "\cA#{message.ctcp_type} #{output}\cA" : output}"
295
311
  end
296
- @socket << "PRIVMSG #{target} :#{message.is_action? ? "\cAACTION #{content}\cA" : content}"
312
+ @socket << "PRIVMSG #{target} :#{message.is_ctcp? ? "\cA#{message.ctcp_type} #{content}\cA" : content}"
297
313
  end
298
314
  end
299
315
  end
@@ -305,13 +321,13 @@ module ModSpox
305
321
  target = message.target.nick if message.target.is_a?(Models::Nick)
306
322
  @socket << "NOTICE #{target} :#{message}"
307
323
  end
308
-
324
+
309
325
  # message:: Messages::Outgoing::Motd message
310
326
  # Sends MOTD message to server
311
327
  def motd(message)
312
328
  @socket << "MOTD #{message.target}"
313
329
  end
314
-
330
+
315
331
  # message:: Messages::Outgoing::Lusers message
316
332
  # Sends LUSERS message to server
317
333
  def lusers(message)
@@ -323,20 +339,20 @@ module ModSpox
323
339
  def version(message)
324
340
  @socket << "VERSION #{message.target}"
325
341
  end
326
-
342
+
327
343
  # message:: Messages::Outgoing::Stats message
328
344
  # Sends STATS message to server
329
345
  def stats(message)
330
346
  raise Exceptions::InvalidValue.new('Query must be a single character') unless message.query =~ /^[a-z]$/
331
347
  @socket << "STATS #{message.query} #{message.target}"
332
348
  end
333
-
349
+
334
350
  # message:: Messages::Outgoing::Links message
335
351
  # Sends LINKS message to server
336
352
  def links(message)
337
353
  @socket << "LIST #{message.server} #{message.mask}"
338
354
  end
339
-
355
+
340
356
  # message:: Messages::Outgoing::Time message
341
357
  # Sends TIME message to server
342
358
  def time(message)
@@ -366,7 +382,7 @@ module ModSpox
366
382
  def info(message)
367
383
  @socket << "INFO #{message.target}"
368
384
  end
369
-
385
+
370
386
  # message:: Messages::Outgoing::ServList message
371
387
  # Sends SERVLIST message to server
372
388
  def servlist(message)
@@ -392,7 +408,7 @@ module ModSpox
392
408
  nick = message.nick.is_a?(Models::Nick) ? message.nick.nick : message.nick
393
409
  @socket << "WHOIS #{message.target_server} #{nick}"
394
410
  end
395
-
411
+
396
412
  # message:: Messages::Outgoing::WhoWas message
397
413
  # Sends WHOWAS message to server
398
414
  def whowas(message)
@@ -418,25 +434,25 @@ module ModSpox
418
434
  def pong(message)
419
435
  @socket << "PONG #{message.server} #{message.string.nil? ? '' : ":#{message.string}"}"
420
436
  end
421
-
437
+
422
438
  # message:: Messages::Outgoing::Away message
423
439
  # Sends AWAY message to server
424
440
  def away(message)
425
441
  @socket << "AWAY :#{message.message}"
426
442
  end
427
-
443
+
428
444
  # message:: Messages::Outgoing::Unaway message
429
445
  # Sends AWAY message to server
430
446
  def unaway(message)
431
447
  @socket << "AWAY"
432
448
  end
433
-
449
+
434
450
  # message:: Messages::Outgoing::Rehash message
435
451
  # Sends REHASH message to server
436
452
  def rehash(message)
437
453
  @socket << "REHASH"
438
454
  end
439
-
455
+
440
456
  # message:: Messages::Outgoing::Die message
441
457
  # Sends DIE message to server
442
458
  def die(message)
@@ -456,13 +472,13 @@ module ModSpox
456
472
  channel = message.channel.is_a?(Models::Channel) ? message.channel.name : message.channel
457
473
  @socket << "SUMMON #{nick} #{message.target} #{channel}"
458
474
  end
459
-
475
+
460
476
  # message:: Messages::Outgoing::Users message
461
477
  # Sends USERS message to server
462
478
  def users(message)
463
479
  @socket << "USERS #{message.target}"
464
480
  end
465
-
481
+
466
482
  def wallops
467
483
  end
468
484
 
@@ -478,16 +494,23 @@ module ModSpox
478
494
  def ison(message)
479
495
  nick = message.nick.is_a?(Models::Nick) ? message.nick.nick : message.nick
480
496
  @socket << "ISON #{nick}"
481
- end
482
-
497
+ end
498
+
483
499
  # message:: Messages::Outoing::Raw message
484
500
  # Send raw message to server
485
501
  def raw(message)
486
502
  @socket << message.message
487
503
  end
488
-
504
+
489
505
  private
490
-
506
+
507
+ def start_shutdown
508
+ @shutdown = true
509
+ @lock.synchronize do
510
+ @waiter.signal
511
+ end
512
+ end
513
+
491
514
  # Cleans information from models to avoid
492
515
  # stale values
493
516
  def clean_models
@@ -18,7 +18,7 @@ module ModSpox
18
18
  # :userpath => path to mod_spox directory in user's home directory
19
19
  # :userpluginpath => path to user's plugin directory
20
20
  # :userconfigpath => path to the user configuration file
21
- def self.populate
21
+ def BotConfig.populate(createdir=true)
22
22
  gemname, gem = Gem.source_index.find{|name, spec|
23
23
  spec.name == 'mod_spox' && spec.version.version = $BOTVERSION
24
24
  }
@@ -33,24 +33,32 @@ module ModSpox
33
33
  :userpath => "#{up}/.mod_spox",
34
34
  :userpluginpath => "#{up}/.mod_spox/plugins",
35
35
  :userconfigpath => "#{up}/.mod_spox/config"}
36
- [@@config[:userpath], @@config[:userpluginpath]].each do |path|
37
- Dir.mkdir(path) unless File.exists?(path)
36
+ if(createdir)
37
+ [@@config[:userpath], @@config[:userpluginpath]].each do |path|
38
+ Dir.mkdir(path) unless File.exists?(path)
39
+ end
38
40
  end
39
41
  else
40
- p gem
41
- p gemname
42
42
  raise Exceptions::InstallationError.new('Failed to find mod_spox gem')
43
43
  end
44
44
  end
45
45
 
46
46
  # name:: Name of the path string you would like
47
47
  # Provides access to important path values
48
- def self.[](name)
48
+ def BotConfig.[](name)
49
49
  BotConfig.populate unless @@config
50
50
  name = name.to_sym unless name.is_a?(Symbol)
51
51
  raise Exceptions::UnknownKey.new("Failed to find given key: #{name}") unless @@config.has_key?(name)
52
52
  return @@config[name]
53
53
  end
54
+
55
+ # Returns if the bot has been configured
56
+ def BotConfig.configured?
57
+ BotConfig.populate(false)
58
+ path = BotConfig[:userpath]
59
+ @@config = nil
60
+ return File.exists?(path)
61
+ end
54
62
 
55
63
  end
56
64