mod_spox 0.0.4 → 0.0.5

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 (122) hide show
  1. data/CHANGELOG +16 -0
  2. data/INSTALL +4 -1
  3. data/bin/mod_spox +3 -1
  4. data/data/mod_spox/extras/AOLSpeak.rb +2 -2
  5. data/data/mod_spox/extras/AutoKick.rb +3 -8
  6. data/data/mod_spox/extras/AutoMode.rb +135 -0
  7. data/data/mod_spox/extras/EightBall.rb +1 -0
  8. data/data/mod_spox/extras/Headers.rb +4 -3
  9. data/data/mod_spox/extras/PhpFuncLookup.rb +14 -11
  10. data/data/mod_spox/extras/Roulette.rb +3 -3
  11. data/data/mod_spox/extras/Search.rb +3 -2
  12. data/data/mod_spox/extras/Talk.rb +1 -9
  13. data/data/mod_spox/extras/Translate.rb +3 -3
  14. data/data/mod_spox/extras/UrbanDictionary.rb +5 -3
  15. data/data/mod_spox/extras/Weather.rb +5 -4
  16. data/data/mod_spox/plugins/Authenticator.rb +2 -2
  17. data/data/mod_spox/plugins/Banner.rb +207 -2
  18. data/data/mod_spox/plugins/Helper.rb +1 -1
  19. data/data/mod_spox/plugins/PluginLoader.rb +22 -6
  20. data/data/mod_spox/plugins/Triggers.rb +6 -4
  21. data/lib/mod_spox/Action.rb +2 -0
  22. data/lib/mod_spox/BaseConfig.rb +2 -0
  23. data/lib/mod_spox/Bot.rb +32 -6
  24. data/lib/mod_spox/BotConfig.rb +3 -0
  25. data/lib/mod_spox/Cache.rb +57 -0
  26. data/lib/mod_spox/ConfigurationWizard.rb +6 -2
  27. data/lib/mod_spox/Helpers.rb +36 -18
  28. data/lib/mod_spox/Loader.rb +5 -54
  29. data/lib/mod_spox/Logger.rb +49 -3
  30. data/lib/mod_spox/MessageFactory.rb +5 -0
  31. data/lib/mod_spox/Monitors.rb +14 -12
  32. data/lib/mod_spox/Pipeline.rb +38 -6
  33. data/lib/mod_spox/Plugin.rb +3 -0
  34. data/lib/mod_spox/PluginHolder.rb +22 -0
  35. data/lib/mod_spox/PluginManager.rb +123 -25
  36. data/lib/mod_spox/Pool.rb +52 -23
  37. data/lib/mod_spox/Socket.rb +21 -10
  38. data/lib/mod_spox/Timer.rb +32 -6
  39. data/lib/mod_spox/handlers/BadNick.rb +2 -0
  40. data/lib/mod_spox/handlers/Bounce.rb +3 -0
  41. data/lib/mod_spox/handlers/Created.rb +2 -0
  42. data/lib/mod_spox/handlers/Handler.rb +5 -0
  43. data/lib/mod_spox/handlers/Invite.rb +2 -0
  44. data/lib/mod_spox/handlers/Join.rb +21 -5
  45. data/lib/mod_spox/handlers/Kick.rb +2 -0
  46. data/lib/mod_spox/handlers/LuserChannels.rb +2 -0
  47. data/lib/mod_spox/handlers/LuserClient.rb +1 -0
  48. data/lib/mod_spox/handlers/LuserMe.rb +1 -0
  49. data/lib/mod_spox/handlers/LuserOp.rb +2 -0
  50. data/lib/mod_spox/handlers/LuserUnknown.rb +2 -0
  51. data/lib/mod_spox/handlers/Mode.rb +2 -0
  52. data/lib/mod_spox/handlers/Motd.rb +5 -2
  53. data/lib/mod_spox/handlers/MyInfo.rb +2 -0
  54. data/lib/mod_spox/handlers/Names.rb +5 -1
  55. data/lib/mod_spox/handlers/Nick.rb +2 -0
  56. data/lib/mod_spox/handlers/NickInUse.rb +2 -0
  57. data/lib/mod_spox/handlers/Notice.rb +16 -4
  58. data/lib/mod_spox/handlers/Part.rb +2 -0
  59. data/lib/mod_spox/handlers/Ping.rb +2 -0
  60. data/lib/mod_spox/handlers/Pong.rb +2 -0
  61. data/lib/mod_spox/handlers/Privmsg.rb +16 -4
  62. data/lib/mod_spox/handlers/Quit.rb +2 -0
  63. data/lib/mod_spox/handlers/Topic.rb +2 -0
  64. data/lib/mod_spox/handlers/Welcome.rb +3 -0
  65. data/lib/mod_spox/handlers/Who.rb +3 -1
  66. data/lib/mod_spox/handlers/Whois.rb +8 -0
  67. data/lib/mod_spox/handlers/YourHost.rb +2 -0
  68. data/lib/mod_spox/messages/Messages.rb +6 -0
  69. data/lib/mod_spox/messages/incoming/BadNick.rb +1 -0
  70. data/lib/mod_spox/messages/incoming/Bounce.rb +1 -0
  71. data/lib/mod_spox/messages/incoming/Created.rb +1 -0
  72. data/lib/mod_spox/messages/incoming/Invite.rb +1 -0
  73. data/lib/mod_spox/messages/incoming/Join.rb +1 -0
  74. data/lib/mod_spox/messages/incoming/Kick.rb +1 -0
  75. data/lib/mod_spox/messages/incoming/LuserChannels.rb +1 -0
  76. data/lib/mod_spox/messages/incoming/LuserClient.rb +1 -0
  77. data/lib/mod_spox/messages/incoming/LuserMe.rb +1 -0
  78. data/lib/mod_spox/messages/incoming/LuserOp.rb +1 -0
  79. data/lib/mod_spox/messages/incoming/LuserUnknown.rb +1 -0
  80. data/lib/mod_spox/messages/incoming/Mode.rb +1 -0
  81. data/lib/mod_spox/messages/incoming/Motd.rb +1 -0
  82. data/lib/mod_spox/messages/incoming/MyInfo.rb +1 -0
  83. data/lib/mod_spox/messages/incoming/Names.rb +1 -0
  84. data/lib/mod_spox/messages/incoming/Nick.rb +1 -0
  85. data/lib/mod_spox/messages/incoming/NickInUse.rb +1 -0
  86. data/lib/mod_spox/messages/incoming/Notice.rb +1 -0
  87. data/lib/mod_spox/messages/incoming/Part.rb +1 -0
  88. data/lib/mod_spox/messages/incoming/Ping.rb +1 -0
  89. data/lib/mod_spox/messages/incoming/Pong.rb +1 -0
  90. data/lib/mod_spox/messages/incoming/Privmsg.rb +1 -0
  91. data/lib/mod_spox/messages/incoming/Quit.rb +1 -0
  92. data/lib/mod_spox/messages/incoming/Topic.rb +1 -0
  93. data/lib/mod_spox/messages/incoming/TopicInfo.rb +1 -0
  94. data/lib/mod_spox/messages/incoming/Welcome.rb +1 -0
  95. data/lib/mod_spox/messages/incoming/Who.rb +1 -0
  96. data/lib/mod_spox/messages/incoming/Whois.rb +1 -0
  97. data/lib/mod_spox/messages/incoming/YourHost.rb +1 -0
  98. data/lib/mod_spox/messages/internal/NickRequest.rb +1 -0
  99. data/lib/mod_spox/messages/internal/NickResponse.rb +1 -0
  100. data/lib/mod_spox/messages/internal/PluginLoadRequest.rb +1 -0
  101. data/lib/mod_spox/messages/internal/PluginLoadResponse.rb +1 -0
  102. data/lib/mod_spox/messages/internal/PluginModuleRequest.rb +1 -0
  103. data/lib/mod_spox/messages/internal/PluginModuleResponse.rb +1 -0
  104. data/lib/mod_spox/messages/internal/PluginReload.rb +10 -0
  105. data/lib/mod_spox/messages/internal/PluginRequest.rb +1 -0
  106. data/lib/mod_spox/messages/internal/PluginResponse.rb +1 -0
  107. data/lib/mod_spox/messages/internal/PluginUnloadRequest.rb +1 -0
  108. data/lib/mod_spox/messages/internal/PluginUnloadResponse.rb +1 -0
  109. data/lib/mod_spox/messages/internal/StatusResponse.rb +1 -0
  110. data/lib/mod_spox/messages/internal/TimerAdd.rb +1 -0
  111. data/lib/mod_spox/messages/internal/TimerClear.rb +8 -0
  112. data/lib/mod_spox/messages/internal/TimerResponse.rb +1 -0
  113. data/lib/mod_spox/messages/outgoing/Away.rb +1 -0
  114. data/lib/mod_spox/messages/outgoing/Notice.rb +1 -0
  115. data/lib/mod_spox/messages/outgoing/Ping.rb +1 -0
  116. data/lib/mod_spox/messages/outgoing/Quit.rb +1 -0
  117. data/lib/mod_spox/messages/outgoing/Raw.rb +16 -0
  118. data/lib/mod_spox/models/Models.rb +4 -0
  119. data/lib/mod_spox/models/Nick.rb +9 -0
  120. data/lib/mod_spox/models/Setting.rb +3 -5
  121. data/lib/mod_spox/models/Signature.rb +2 -3
  122. metadata +8 -2
@@ -23,12 +23,25 @@ class Banner < ModSpox::Plugin
23
23
  :description => 'List all currently active bans generated from the bot')
24
24
  Signature.find_or_create(:signature => 'banlist remove (\d+)', :plugin => name, :method => 'ban_remove', :group_id => admin.pk,
25
25
  :description => 'Remove a current ban').params = [:id]
26
+ Signature.find_or_create(:signature => 'exempt mode ([ov]) ?(\S+)?', :plugin => name, :method => 'exempt_mode', :group_id => admin.pk,
27
+ :description => 'Exempt given modes from kick. Apply to all channels if one is not provided').params = [:mode, :channel]
28
+ Signature.find_or_create(:signature => 'exempt nick (\S+) ?(\S+)?', :plugin => name, :method => 'exempt_nick', :group_id => admin.pk,
29
+ :description => 'Exempt a nick from kicks globally or per channel').params = [:nick, :channel]
30
+ Signature.find_or_create(:signature => 'exempt source (\S+)', :plugin => name, :method => 'exempt_source', :group_id => admin.pk,
31
+ :description => 'Exempt a source from kicks globally or per channel').params = [:source, :channel]
32
+ Signature.find_or_create(:signature => 'exempt list (nick|mode|source)', :plugin => name, :method => 'exempt_list', :group_id => admin.pk,
33
+ :description => 'List current exemptions of given type').params = [:type]
34
+ Signature.find_or_create(:signature => 'exempt remove (nick|mode|source) (\d+)', :plugin => name, :method => 'exempt_remove', :group_id => admin.pk,
35
+ :description => 'Remove exemption from given type').params = [:type, :id]
26
36
  @pipeline.hook(self, :mode_check, :Incoming_Mode)
27
37
  @pipeline.hook(self, :join_check, :Incoming_Join)
28
38
  @pipeline.hook(self, :who_check, :Incoming_Who)
29
39
  @pipeline.hook(self, :get_action, :Internal_TimerResponse)
30
40
  BanRecord.create_table unless BanRecord.table_exists?
31
41
  BanMask.create_table unless BanMask.table_exists?
42
+ BanNickExempt.create_table unless BanNickExempt.table_exists?
43
+ BanModeExempt.create_table unless BanModeExempt.table_exists?
44
+ BanSourceExempt.create_table unless BanSourceExempt.table_exists?
32
45
  @actions = []
33
46
  @up_sync = Mutex.new
34
47
  @timer_sync = Mutex.new
@@ -60,7 +73,7 @@ class Banner < ModSpox::Plugin
60
73
  # params:: parameters
61
74
  # Ban nick in given channel for given time providing given message
62
75
  def full_ban(message, params)
63
- nick = Models::Nick.filter(:nick => params[:nick]).first
76
+ nick = Helpers.find_model(params[:nick], false)
64
77
  channel = Channel.filter(:name => params[:channel]).first
65
78
  if(!me.is_op?(message.target))
66
79
  reply(message.replyto, "Error: I'm not a channel operator")
@@ -86,6 +99,8 @@ class Banner < ModSpox::Plugin
86
99
  raise NotOperator.new("I am not an operator in #{channel.name}")
87
100
  elsif(!nick.channels.include?(channel))
88
101
  raise NotInChannel.new("#{nick.nick} is not in channel: #{channel.name}")
102
+ elsif(check_exempt(nick, channel))
103
+ raise BanExemption.new("This nick is exempt from bans: #{nick.nick}")
89
104
  else
90
105
  mask = nick.source.nil? || nick.source.empty? ? "#{nick.nick}!*@*" : "*!*@#{nick.address}"
91
106
  BanRecord.new(:nick_id => nick.pk, :bantime => time.to_i, :remaining => time.to_i,
@@ -332,6 +347,144 @@ class Banner < ModSpox::Plugin
332
347
  end
333
348
  end
334
349
 
350
+ # message:: ModSpox::Messages::Incoming::Privmsg
351
+ # params:: parameters
352
+ # Add ban exemption for a given mode
353
+ def exempt_mode(message, params)
354
+ response = nil
355
+ if(params[:channel])
356
+ channel = Helpers.find_model(params[:channel])
357
+ if(channel.is_a?(Models::Channel))
358
+ BanModeExempt.find_or_create(:channel_id => channel.pk, :mode => params[:mode])
359
+ response = 'Mode exemption has been added'
360
+ else
361
+ response = "Failed to find given channel: #{params[:channel]}"
362
+ end
363
+ else
364
+ BanModeExempt.find_or_create(:channel_id => nil, :mode => params[:mode])
365
+ response = 'Mode exemption has been added'
366
+ end
367
+ reply message.replyto, response
368
+ end
369
+
370
+ # message:: ModSpox::Messages::Incoming::Privmsg
371
+ # params:: parameters
372
+ # Add ban exemption for a given nick
373
+ def exempt_nick(message, params)
374
+ response = ''
375
+ nick = Helpers.find_model(params[:nick])
376
+ unless(nick.is_a?(Models::Nick))
377
+ reply message.replyto, "\2Error:\2 Failed to find record of: #{params[:nick]}"
378
+ return
379
+ end
380
+ channel = params[:channel] ? Helpers.find_model(params[:channel]) : nil
381
+ if(channel)
382
+ if(channel.is_a?(Models::Channel))
383
+ BanNickExempt.find_or_create(:channel_id => channel.pk, :nick_id => nick.pk)
384
+ response = "Nick exemption for \2#{params[:nick]}\2 has been added to channel: \2#{params[:channel]}\2"
385
+ else
386
+ response = "Failed to find given channel: #{params[:channel]}"
387
+ end
388
+ else
389
+ BanNickExempt.find_or_create(:nick_id => nick.pk)
390
+ response = "Nick exemption for \2#{params[:nick]}\2 has been added for all channels"
391
+ end
392
+ reply message.replyto, response
393
+ end
394
+
395
+ # message:: ModSpox::Messages::Incoming::Privmsg
396
+ # params:: parameters
397
+ # Add ban exemption for sources matching a given mask
398
+ def exempt_source(message, params)
399
+ channel = params[:channel] ? Helpers.find_model(params[:channel]) : nil
400
+ response = ''
401
+ if(channel)
402
+ if(channel.is_a?(Models::Channel))
403
+ BanSourceExempt.find_or_create(:channel_id => channel.pk, :source => params[:source])
404
+ reponse = "Added exempt source: #{params[:source]} to channel: #{params[:channel]}"
405
+ else
406
+ response = "Failed to find given channel: #{params[:channel]}"
407
+ end
408
+ else
409
+ BanSourceExempt.find_or_create(:channel_id => nil, :source => params[:source])
410
+ response = "Added global exemption for source: #{params[:source]}"
411
+ end
412
+ reply message.replyto, response
413
+ end
414
+
415
+ # message:: ModSpox::Messages::Incoming::Privmsg
416
+ # params:: parameters
417
+ # List given type of current ban exemptions
418
+ def exempt_list(message, params)
419
+ output = []
420
+ if(params[:type] == 'nick')
421
+ output << 'Current nick exemptions:'
422
+ BanNickExempt.all.each do |record|
423
+ if(record.channel)
424
+ output << "\2#{record.pk}:\2 \2#{record.nick.nick}\2 is exempt in \2#{record.channel.name}\2"
425
+ else
426
+ output << "\2#{record.pk}:\2 \2#{record.nick.nick}\2 is exempt in \2all\2 channels"
427
+ end
428
+ end
429
+ elsif(params[:type] == 'mode')
430
+ output << 'Current mode exemptions:'
431
+ BanModeExempt.all.each do |record|
432
+ mode = record.mode == 'o' ? 'operator' : 'voice'
433
+ if(record.channel)
434
+ output << "\2#{record.pk}:\2 mode: \2#{mode}\2 is exempt in \2#{record.channel.name}\2"
435
+ else
436
+ output << "\2#{record.pk}:\2 mode: \2#{mode}\2 is exempt in \2all\2 channels"
437
+ end
438
+ end
439
+ elsif(params[:type] == 'source')
440
+ output << 'Current source exemptions:'
441
+ BanSourceExempt.all.each do |record|
442
+ if(record.channel)
443
+ output << "\2#{record.pk}:\2 sources matching: #{record.source} are exempt in \2#{record.channel.name}\2"
444
+ else
445
+ output << "\2#{record.pk}:\2 sources matching: #{record.source} are exempt in \2all\2 channels"
446
+ end
447
+ end
448
+ end
449
+ reply message.replyto, output
450
+ end
451
+
452
+ # message:: ModSpox::Messages::Incoming::Privmsg
453
+ # params:: parameters
454
+ # Remove given exemption from given list
455
+ def exempt_remove(message, params)
456
+ response = 'Exemption has been removed'
457
+ record = nil
458
+ case params[:type]
459
+ when 'nick'
460
+ record = BanNickExempt[params[:id].to_i]
461
+ when 'mode'
462
+ record = BanModeExempt[params[:id].to_i]
463
+ when 'source'
464
+ record = BanSourceExempt[params[:id].to_i]
465
+ end
466
+ if(record)
467
+ record.destroy
468
+ else
469
+ response = "Failed to find exemption of type: #{params[:type]} with ID: #{params[:id]}"
470
+ end
471
+ reply message.replyto, response
472
+ end
473
+
474
+ # nick:: ModSpox::Models::Nick
475
+ # channel:: ModSpox::Models::Channel
476
+ # Check if nick is currently exempt from bans
477
+ def check_exempt(nick, channel)
478
+ return true unless BanNickExempt.filter('nick_id = ? AND (channel_id = ? OR channel_id is null)', nick.pk, channel.pk).first.nil?
479
+ return true if !BanModeExempt.filter("mode = 'o' AND (channel_id = ? OR channel_id is null)", channel.pk).first.nil? && nick.is_op?(channel)
480
+ return true if !BanModeExempt.filter("mode = 'v' AND (channel_id = ? OR channel_id is null)", channel.pk).first.nil? && nick.is_voice?(channel)
481
+ BanSourceExempt.filter('channel_id = ? OR channel_id is null', channel.pk).each do |record|
482
+ regex = Regexp.new(record.source)
483
+ return true unless regex.match(nick.source).nil?
484
+ end
485
+ return false
486
+ end
487
+
335
488
  class BanRecord < Sequel::Model
336
489
  set_schema do
337
490
  primary_key :id
@@ -346,7 +499,7 @@ class Banner < ModSpox::Plugin
346
499
  end
347
500
 
348
501
  before_create do
349
- set :stamp => Time.now
502
+ update_values(:stamp => Time.now)
350
503
  end
351
504
 
352
505
  def channel
@@ -383,10 +536,62 @@ class Banner < ModSpox::Plugin
383
536
  end
384
537
  end
385
538
 
539
+ class BanNickExempt < Sequel::Model
540
+ set_schema do
541
+ primary_key :id
542
+ foreign_key :nick_id, :table => :nicks, :null => false
543
+ foreign_key :channel_id, :table => :channels
544
+ end
545
+
546
+ def nick
547
+ return Models::Nick[nick_id]
548
+ end
549
+
550
+ def channel
551
+ return Models::Channel[channel_id]
552
+ end
553
+ end
554
+
555
+ class BanSourceExempt < Sequel::Model
556
+ set_schema do
557
+ primary_key :id
558
+ varchar :source, :null => false
559
+ foreign_key :channel_id, :table => :channels
560
+ end
561
+
562
+ def channel
563
+ return Models::Channel[channel_id]
564
+ end
565
+
566
+ def mask
567
+ return values[:source] ? Marshal.load(values[:source].unpack('m')[0]) : nil
568
+ end
569
+
570
+ def mask=(val)
571
+ update_values(:source => [Marshal.dump(val)].pack('m'))
572
+ end
573
+
574
+ end
575
+
576
+ class BanModeExempt < Sequel::Model
577
+ set_schema do
578
+ primary_key :id
579
+ varchar :mode, :null => false
580
+ foreign_key :channel_id, :table => :channels, :unique => true
581
+ end
582
+
583
+ def channel
584
+ return Models::Channel[channel_id]
585
+ end
586
+ end
587
+
386
588
  class NotOperator < Exceptions::BotException
387
589
  end
388
590
 
389
591
  class NotInChannel < Exceptions::BotException
390
592
  end
593
+
594
+ class BanExemption < Exceptions::BotException
595
+ end
391
596
 
392
597
  end
@@ -11,7 +11,7 @@ class Helper < ModSpox::Plugin
11
11
  end
12
12
 
13
13
  def default_help(message, params)
14
- plugins = Signature.map(:plugin)
14
+ plugins = Signature.select(:plugin).map(:plugin)
15
15
  plugins.uniq!
16
16
  reply message.replyto, "Plugins currently available for help: #{plugins.join(', ')}"
17
17
  reply message.replyto, "Request help on a plugin: !help Plugin"
@@ -11,8 +11,8 @@ class PluginLoader < ModSpox::Plugin
11
11
  :group_id => admin.pk, :description => 'Load the given plugin').params = [:plugin]
12
12
  Models::Signature.find_or_create(:signature => 'plugins unload (\S+)', :plugin => name, :method => 'unload_plugin',
13
13
  :group_id => admin.pk, :description => 'Unload given plugin').params = [:plugin]
14
- Models::Signature.find_or_create(:signature => 'plugins reload', :plugin => name, :method => 'reload_plugin',
15
- :group_id => admin.pk, :description => 'Reload plugins')
14
+ Models::Signature.find_or_create(:signature => 'plugins reload ?(\S+)?', :plugin => name, :method => 'reload_plugin',
15
+ :group_id => admin.pk, :description => 'Reload single plugin or all plugins if names not provided').params = [:plugin]
16
16
  @pipeline.hook(self, :get_module, :Internal_PluginModuleResponse)
17
17
  @plugins_mod = nil
18
18
  end
@@ -21,10 +21,11 @@ class PluginLoader < ModSpox::Plugin
21
21
  # params:: matching signature params
22
22
  # Output currently available plugins for loading
23
23
  def available_plugins(message, params)
24
- @pipeline << Messages::Outgoing::Privmsg.new(message.replyto, "\2Currently available plugins:\2")
24
+ output = ["\2Currently available plugins:\2"]
25
25
  find_plugins.each_pair do | plugin, path |
26
- @pipeline << Messages::Outgoing::Privmsg.new(message.replyto, "\2#{plugin}:\2 #{path}")
26
+ output << "\2#{plugin}:\2 #{path}"
27
27
  end
28
+ reply message.replyto, output
28
29
  end
29
30
 
30
31
  # message:: ModSpox::Messages::Incoming::Privmsg
@@ -65,8 +66,23 @@ class PluginLoader < ModSpox::Plugin
65
66
  # params:: matching signature params
66
67
  # Reloads plugins
67
68
  def reload_plugin(message, params)
68
- @pipeline << Messages::Internal::PluginReload.new
69
- @pipeline << Messages::Outgoing::Privmsg.new(message.replyto, 'Okay')
69
+ if(params[:plugin])
70
+ users = plugin_discovery(BotConfig[:userpluginpath])
71
+ extras = plugin_discovery(BotConfig[:pluginextraspath])
72
+ fresh = nil
73
+ stale = nil
74
+ users.each_pair{|name, path| stale = path if name == params[:plugin]}
75
+ extras.each_pair{|name, path| fresh = path if name == params[:plugin]}
76
+ if(fresh && stale)
77
+ @pipeline << Messages::Internal::PluginReload.new(fresh, stale)
78
+ reply message.replyto, "Reloading #{params[:plugin]}"
79
+ else
80
+ reply message.replyto, "\2Error:\2 Failed to find new and stale versions of: #{params[:plugin]}"
81
+ end
82
+ else
83
+ @pipeline << Messages::Internal::PluginReload.new
84
+ @pipeline << Messages::Outgoing::Privmsg.new(message.replyto, 'Full plugin reload requested')
85
+ end
70
86
  end
71
87
 
72
88
  # message:: ModSpox::Messages::Internal::PluginModuleResponse
@@ -16,10 +16,11 @@ include Messages::Outgoing
16
16
  def active(message, params)
17
17
  triggers = Models::Trigger.filter(:active => true)
18
18
  if(triggers)
19
- @pipeline << Privmsg.new(message.replyto, "\2Currently active triggers:\2")
19
+ output = ["\2Currently active triggers:\2"]
20
20
  triggers.each do |t|
21
- @pipeline << Privmsg.new(message.replyto, "#{t.pk}: #{t.trigger}")
21
+ output << "#{t.pk}: #{t.trigger}"
22
22
  end
23
+ reply message.replyto, output
23
24
  else
24
25
  @pipeline << Privmsg.new(message.replyto, 'No triggers are currently active')
25
26
  end
@@ -28,10 +29,11 @@ include Messages::Outgoing
28
29
  def list(message, params)
29
30
  triggers = Models::Trigger.all
30
31
  if(triggers)
31
- @pipeline << Privmsg.new(message.replyto, "\2Trigger listing:\2")
32
+ output = ["\2Trigger listing:\2"]
32
33
  triggers.each do |t|
33
- @pipeline << Privmsg.new(message.replyto, "#{t.pk}: #{t.trigger} -> \2#{t.active ? "activated" : "not activated"}\2")
34
+ output << "#{t.pk}: #{t.trigger} -> \2#{t.active ? "activated" : "not activated"}\2"
34
35
  end
36
+ reply message.replyto, output
35
37
  else
36
38
  @pipeline << Privmsg.new(message.replyto, 'No triggers found')
37
39
  end
@@ -1,3 +1,5 @@
1
+ require 'mod_spox/Logger'
2
+
1
3
  module ModSpox
2
4
 
3
5
  class Action
@@ -1,3 +1,5 @@
1
+ require 'mod_spox/Exceptions'
2
+
1
3
  module ModSpox
2
4
 
3
5
  class BaseConfig
data/lib/mod_spox/Bot.rb CHANGED
@@ -1,3 +1,14 @@
1
+ ['mod_spox/Logger',
2
+ 'mod_spox/Socket',
3
+ 'mod_spox/Pipeline',
4
+ 'mod_spox/PluginManager',
5
+ 'mod_spox/MessageFactory',
6
+ 'mod_spox/BaseConfig',
7
+ 'mod_spox/Timer',
8
+ 'mod_spox/messages/Messages',
9
+ 'mod_spox/models/Models',
10
+ 'mod_spox/Monitors',
11
+ 'mod_spox/Helpers'].each{|f|require f}
1
12
  module ModSpox
2
13
 
3
14
  class Bot
@@ -36,10 +47,10 @@ module ModSpox
36
47
 
37
48
  # Run the bot
38
49
  def run
39
- trap('SIGTERM'){ Logger.log("Caught SIGTERM"); @shutdown = true; @waiter.wakeup; sleep(0.1); Thread.current.exit }
40
- trap('SIGKILL'){ Logger.log("Caught SIGKILL"); @shutdown = true; @waiter.wakeup; sleep(0.1); Thread.current.exit }
41
- trap('SIGINT'){ Logger.log("Caught SIGINT"); @shutdown = true; @waiter.wakeup; sleep(0.1); Thread.current.exit }
42
- trap('SIGQUIT'){ Logger.log("Caught SIGQUIT"); @shutdown = true; @waiter.wakeup; sleep(0.1); Thread.current.exit }
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; }
43
54
  until @shutdown do
44
55
  @timer.start
45
56
  @pipeline << Messages::Internal::BotInitialized.new
@@ -126,7 +137,8 @@ module ModSpox
126
137
  :Outgoing_Whois => :whois, :Internal_EstablishConnection => :bot_connect,
127
138
  :Internal_StatusRequest => :status, :Internal_ChangeNick => :set_nick,
128
139
  :Internal_NickRequest => :get_nick, :Internal_HaltBot => :halt,
129
- :Internal_Disconnected => :disconnected, :Internal_TimerClear => :clear_timer
140
+ :Internal_Disconnected => :disconnected, :Internal_TimerClear => :clear_timer,
141
+ :Outgoing_Raw => :raw
130
142
  }.each_pair{ |type,method| @pipeline.hook(self, method, type) }
131
143
  end
132
144
 
@@ -273,7 +285,15 @@ module ModSpox
273
285
  target = message.target.name if message.target.is_a?(Models::Channel)
274
286
  target = message.target.nick if message.target.is_a?(Models::Nick)
275
287
  target = message.target unless target
276
- @socket << "PRIVMSG #{target} :#{message.is_action? ? "\cAACTION #{message.message}\cA" : message.message}"
288
+ messages = message.message.is_a?(Array) ? message.message : [message.message]
289
+ messages.each do |content|
290
+ while(content.size > 450)
291
+ output = content[0..449]
292
+ content.slice!(0, 449) #(450, content.size)
293
+ @socket << "PRIVMSG #{target} :#{message.is_action? ? "\cAACTION #{output}\cA" : output}"
294
+ end
295
+ @socket << "PRIVMSG #{target} :#{message.is_action? ? "\cAACTION #{content}\cA" : content}"
296
+ end
277
297
  end
278
298
 
279
299
  # message:: Messages::Outgoing::Notice message
@@ -458,6 +478,12 @@ module ModSpox
458
478
  @socket << "ISON #{nick}"
459
479
  end
460
480
 
481
+ # message:: Messages::Outoing::Raw message
482
+ # Send raw message to server
483
+ def raw(message)
484
+ @socket << message.message
485
+ end
486
+
461
487
  private
462
488
 
463
489
  # Cleans information from models to avoid
@@ -1,3 +1,6 @@
1
+ require 'etc'
2
+ require 'mod_spox/Exceptions'
3
+
1
4
  module ModSpox
2
5
 
3
6
  class BotConfig
@@ -0,0 +1,57 @@
1
+ module ModSpox
2
+ class Cache
3
+ def initialize(size)
4
+ @size = size
5
+ @cache = Hash.new
6
+ @times = Hash.new
7
+ @store_lock = Mutex.new
8
+ @read_lock = Mutex.new
9
+ end
10
+
11
+ def [](key)
12
+ if(@cache.has_key?(key.to_sym))
13
+ @times[key.to_sym] = Time.now
14
+ return @cache[key.to_sym]
15
+ else
16
+ return nil
17
+ end
18
+ end
19
+
20
+ def has_key?(key)
21
+ @read_lock.synchronize do
22
+ return @cache.has_key?(key.to_sym)
23
+ end
24
+ end
25
+
26
+ def []=(key, value)
27
+ @store_lock.synchronize do
28
+ if(@cache.size > @size)
29
+ del_key = oldest_key
30
+ unless(key.nil?)
31
+ @cache.delete(del_key)
32
+ @times.delete(del_key)
33
+ end
34
+ end
35
+ @cache[key.to_sym] = value
36
+ @times[key.to_sym] = Time.now
37
+ end
38
+ end
39
+
40
+ def delete(key)
41
+ @read_lock.synchronize do
42
+ @cache.delete(key.to_sym) if @cache.has_key?(key.to_sym)
43
+ @times.delete(key.to_sym) if @times.has_key?(key.to_sym)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def oldest_key
50
+ result = nil
51
+ @read_lock.synchronize do
52
+ result = @times.sort{|a,b| a[1] <=> b[1]}[0][0]
53
+ end
54
+ return result
55
+ end
56
+ end
57
+ end
@@ -1,5 +1,9 @@
1
- ['etc', 'mod_spox/Loader',
2
- 'mod_spox/BotConfig', 'mod_spox/BaseConfig'].each{|f|require f}
1
+ ['etc',
2
+ 'mod_spox/Database',
3
+ 'mod_spox/BotConfig',
4
+ 'mod_spox/BaseConfig'
5
+ 'mod_spox/models/Models',
6
+ 'mod_spox/Helpers'].each{|f|require f}
3
7
 
4
8
 
5
9
  module ModSpox
@@ -1,5 +1,11 @@
1
- require 'timeout'
2
- require 'net/http'
1
+ ['timeout',
2
+ 'net/http',
3
+ 'mod_spox/models/Nick',
4
+ 'mod_spox/models/Channel',
5
+ 'mod_spox/models/Server',
6
+ 'mod_spox/Cache',
7
+ 'mod_spox/Logger'].each{|f|require f}
8
+
3
9
  module ModSpox
4
10
  module Helpers
5
11
  # secs:: number of seconds
@@ -60,48 +66,60 @@ module ModSpox
60
66
  # or channel name. If the string given does not match the required
61
67
  # pattern for a channel or nick, the string is returned.
62
68
  def Helpers.find_model(string, create=true)
63
- @@channel_cache = {} unless Helpers.class_variable_defined?(:@@channel_cache)
64
- @@nick_cache = {} unless Helpers.class_variable_defined?(:@@nick_cache)
69
+ Helpers.initialize_caches
65
70
  if(string =~ /^[A-Za-z\|\\\{\}\[\]\^\`~\_\-]+[A-Za-z0-9\|\\\{\}\[\]\^\`~\_\-]*$/)
66
- Logger.log("Model: #{string} -> Nick")
71
+ Logger.log("Model: #{string} -> Nick", 30)
67
72
  nick = nil
68
- if(@@nick_cache.has_key?(string.to_sym))
73
+ if(@@nick_cache.has_key?(string.downcase.to_sym))
69
74
  begin
70
- nick = Models::Nick[@@nick_cache[string.to_sym]]
75
+ nick = Models::Nick[@@nick_cache[string.downcase.to_sym]]
71
76
  Logger.log("Handler cache hit for nick: #{string}", 30)
77
+ if(nick.nick.downcase != string.downcase)
78
+ Logger.log("Nick returned from cache invalid. Expecting #{string} but got #{nick.nick}", 30)
79
+ nick = nil
80
+ end
72
81
  rescue Object => boom
73
- Logger.log("Failed to grab cached nick: #{boom}")
82
+ Logger.log("Failed to grab cached nick: #{boom}", 30)
74
83
  end
75
84
  end
76
85
  unless(nick)
77
86
  nick = Models::Nick.locate(string, create)
78
- @@nick_cache[string.to_sym] = nick.pk if nick.is_a?(Models::Nick)
79
- Logger.log("Nick was retrieved from database")
87
+ @@nick_cache[string.downcase.to_sym] = nick.pk if nick.is_a?(Models::Nick)
88
+ Logger.log("Nick was retrieved from database", 30)
80
89
  end
81
90
  return nick
82
91
  elsif(string =~ /^[&#+!]/)
83
- Logger.log("Model: #{string} -> Channel")
84
- if(@@channel_cache.has_key?(string.to_sym))
92
+ Logger.log("Model: #{string} -> Channel", 30)
93
+ if(@@channel_cache.has_key?(string.downcase.to_sym))
85
94
  begin
86
- channel = Models::Channel[@@channel_cache[string.to_sym]]
95
+ channel = Models::Channel[@@channel_cache[string.downcase.to_sym]]
87
96
  Logger.log("Handler cache hit for channel: #{string}", 30)
97
+ if(string.downcase != channel.name.downcase)
98
+ Logger.log("Channel returned from cache invalid. Expecting #{string} but got #{channel.name}", 30)
99
+ channel = nil
100
+ end
88
101
  rescue Object => boom
89
- Logger.log("Failed to grab cached channel: #{boom}")
102
+ Logger.log("Failed to grab cached channel: #{boom}", 30)
90
103
  end
91
104
  end
92
105
  unless(channel)
93
106
  channel = Models::Channel.locate(string, create)
94
- @@channel_cache[string.to_sym] = channel.pk if channel.is_a?(Models::Channel)
95
- Logger.log("Channel was retrieved from database")
107
+ @@channel_cache[string.downcase.to_sym] = channel.pk if channel.is_a?(Models::Channel)
108
+ Logger.log("Channel was retrieved from database", 30)
96
109
  end
97
110
  return channel
98
111
  elsif(model = Models::Server.filter(:host => string, :connected => true).first)
99
- Logger.log("Model: #{string} -> Server")
112
+ Logger.log("Model: #{string} -> Server", 30)
100
113
  return model
101
114
  else
102
- Logger.log("FAIL Model: #{string} -> No match")
115
+ Logger.log("FAIL Model: #{string} -> No match", 30)
103
116
  return string
104
117
  end
105
118
  end
119
+
120
+ def Helpers.initialize_caches
121
+ @@nick_cache = Cache.new(20) unless Helpers.class_variable_defined?(:@@nick_cache)
122
+ @@channel_cache = Cache.new(5) unless Helpers.class_variable_defined?(:@@channel_cache)
123
+ end
106
124
  end
107
125
  end
@@ -1,66 +1,17 @@
1
- module ModSpox
1
+ ['mod_spox/Exceptions',
2
+ 'mod_spox/BotConfig',
3
+ 'mod_spox/BaseConfig',
4
+ 'mod_spox/Database'].each{|f|require f}
2
5
 
3
- # directory:: path to directory
4
- # Requires all .rb files found within the given directory
5
- # and all its subdirectories
6
- def load_directory(directory='')
7
- base = File.dirname(__FILE__)
8
- Dir.new("#{base}/#{directory}").each{|item|
9
- next if ['.', '..'].include?(item)
10
- if(File.directory?("#{base}/#{directory}/#{item}"))
11
- load_directory("#{directory}/#{item}")
12
- elsif(item =~ /\.rb$/)
13
- item = "#{directory}/#{item}" if directory.length > 0
14
- begin
15
- require "mod_spox/#{item}"
16
- rescue Object => boom
17
- @@failed << item
18
- end
19
- end
20
- }
21
- @@failed.each{|f|
22
- begin
23
- require "mod_spox/#{f}"
24
- @@failed.delete(f)
25
- rescue Object => boom
26
- # do nothing #
27
- end
28
- }
29
- end
6
+ module ModSpox
30
7
 
31
8
  # Loads all files needed by the bot
32
9
  def initialize_bot
33
10
  setup_adapter
34
- @@failed = Array.new
35
- load_directory
36
- tries = 0
37
- message = nil
38
- until @@failed.empty? || tries > 5 do
39
- @@failed.each{|f|
40
- begin
41
- require "mod_spox/#{f}"
42
- @@failed.delete(f)
43
- rescue Object => boom
44
- message = boom
45
- end
46
- }
47
- tries += 1
48
- end
49
- if(tries > 5)
50
- puts 'Failed'
51
- puts "ERROR: Failed to load required libraries"
52
- puts "Reason: #{message}"
53
- puts "#{message.backtrace.join("\n")}"
54
- exit
55
- end
56
11
  end
57
12
 
58
13
  # Setup the DataMapper adapter
59
14
  def setup_adapter
60
- require 'mod_spox/Exceptions'
61
- require 'mod_spox/BotConfig'
62
- require 'mod_spox/BaseConfig'
63
- require 'mod_spox/Database'
64
15
  memcache = false
65
16
  config = BaseConfig.new(BotConfig[:userconfigpath])
66
17
  if(config[:memcache] == 'on')