mod_spox 0.2.0 → 0.3.0

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 (145) hide show
  1. data/CHANGELOG +31 -1
  2. data/LICENSE +674 -0
  3. data/README.rdoc +73 -0
  4. data/bin/mod_spox +28 -28
  5. data/data/mod_spox/extras/AOLSpeak.rb +2 -3
  6. data/data/mod_spox/extras/AutoKick.rb +10 -23
  7. data/data/mod_spox/extras/AutoMode.rb +12 -23
  8. data/data/mod_spox/extras/Bash.rb +55 -0
  9. data/data/mod_spox/extras/Bouncer.rb +85 -57
  10. data/data/mod_spox/extras/Bullshit.rb +1 -1
  11. data/data/mod_spox/extras/Bytes.rb +1 -2
  12. data/data/mod_spox/extras/Confess.rb +27 -29
  13. data/data/mod_spox/extras/DCC.rb +11 -20
  14. data/data/mod_spox/extras/DevWatch.rb +21 -23
  15. data/data/mod_spox/extras/DownForEveryoneOrJustMe.rb +47 -0
  16. data/data/mod_spox/extras/EightBall.rb +1 -1
  17. data/data/mod_spox/extras/FML.rb +35 -0
  18. data/data/mod_spox/extras/Headers.rb +31 -50
  19. data/data/mod_spox/extras/Karma.rb +81 -29
  20. data/data/mod_spox/extras/Logger.rb +2 -2
  21. data/data/mod_spox/extras/LolSpeak.rb +1 -2
  22. data/data/mod_spox/extras/PhpCli.rb +138 -8
  23. data/data/mod_spox/extras/PhpFuncLookup.rb +20 -23
  24. data/data/mod_spox/extras/Pinger.rb +1 -1
  25. data/data/mod_spox/extras/Quotes.rb +8 -10
  26. data/data/mod_spox/extras/RegexTracker.rb +2 -4
  27. data/data/mod_spox/extras/Roulette.rb +20 -27
  28. data/data/mod_spox/extras/RubyCli.rb +93 -0
  29. data/data/mod_spox/extras/Search.rb +17 -3
  30. data/data/mod_spox/extras/Seen.rb +150 -0
  31. data/data/mod_spox/extras/SlashdotHeadlineGenerator.rb +500 -0
  32. data/data/mod_spox/extras/Talk.rb +2 -4
  33. data/data/mod_spox/extras/Topten.rb +10 -12
  34. data/data/mod_spox/extras/TracTicket.rb +3 -5
  35. data/data/mod_spox/extras/Translate.rb +20 -22
  36. data/data/mod_spox/extras/Twitter.rb +118 -33
  37. data/data/mod_spox/extras/UrbanDictionary.rb +8 -17
  38. data/data/mod_spox/extras/Weather.rb +1 -2
  39. data/data/mod_spox/plugins/Authenticator.rb +93 -98
  40. data/data/mod_spox/plugins/Banner.rb +26 -56
  41. data/data/mod_spox/plugins/Helper.rb +5 -6
  42. data/data/mod_spox/plugins/Initializer.rb +4 -14
  43. data/data/mod_spox/plugins/Joiner.rb +1 -1
  44. data/data/mod_spox/plugins/Nicker.rb +13 -0
  45. data/data/mod_spox/plugins/Parter.rb +2 -2
  46. data/data/mod_spox/plugins/Permissions.rb +60 -0
  47. data/data/mod_spox/plugins/PluginLoader.rb +7 -12
  48. data/data/mod_spox/plugins/Ponger.rb +51 -0
  49. data/data/mod_spox/plugins/Quitter.rb +1 -2
  50. data/data/mod_spox/plugins/Servers.rb +57 -0
  51. data/data/mod_spox/plugins/Status.rb +3 -2
  52. data/data/mod_spox/plugins/Triggers.rb +9 -9
  53. data/lib/mod_spox/Bot.rb +109 -33
  54. data/lib/mod_spox/BotConfig.rb +2 -2
  55. data/lib/mod_spox/ConfigurationWizard.rb +12 -12
  56. data/lib/mod_spox/Database.rb +1 -4
  57. data/lib/mod_spox/Exceptions.rb +26 -0
  58. data/lib/mod_spox/Helpers.rb +29 -68
  59. data/lib/mod_spox/Loader.rb +23 -24
  60. data/lib/mod_spox/Logger.rb +19 -17
  61. data/lib/mod_spox/MessageFactory.rb +50 -24
  62. data/lib/mod_spox/Pipeline.rb +21 -7
  63. data/lib/mod_spox/Plugin.rb +27 -3
  64. data/lib/mod_spox/PluginManager.rb +28 -15
  65. data/lib/mod_spox/PriorityQueue.rb +69 -0
  66. data/lib/mod_spox/Socket.rb +93 -51
  67. data/lib/mod_spox/Sockets.rb +76 -63
  68. data/lib/mod_spox/Timer.rb +21 -141
  69. data/lib/mod_spox/Version.rb +14 -0
  70. data/lib/mod_spox/handlers/BadNick.rb +1 -1
  71. data/lib/mod_spox/handlers/Bounce.rb +5 -5
  72. data/lib/mod_spox/handlers/Created.rb +13 -5
  73. data/lib/mod_spox/handlers/Handler.rb +12 -3
  74. data/lib/mod_spox/handlers/Invite.rb +14 -8
  75. data/lib/mod_spox/handlers/Join.rb +24 -20
  76. data/lib/mod_spox/handlers/Kick.rb +22 -13
  77. data/lib/mod_spox/handlers/Mode.rb +42 -36
  78. data/lib/mod_spox/handlers/Motd.rb +4 -0
  79. data/lib/mod_spox/handlers/Names.rb +66 -39
  80. data/lib/mod_spox/handlers/Nick.rb +20 -14
  81. data/lib/mod_spox/handlers/Part.rb +25 -8
  82. data/lib/mod_spox/handlers/Ping.rb +11 -5
  83. data/lib/mod_spox/handlers/Pong.rb +9 -5
  84. data/lib/mod_spox/handlers/Privmsg.rb +25 -17
  85. data/lib/mod_spox/handlers/Quit.rb +13 -8
  86. data/lib/mod_spox/handlers/Topic.rb +4 -0
  87. data/lib/mod_spox/handlers/Welcome.rb +16 -24
  88. data/lib/mod_spox/handlers/Who.rb +64 -48
  89. data/lib/mod_spox/handlers/Whois.rb +92 -60
  90. data/lib/mod_spox/messages/incoming/Nick.rb +2 -2
  91. data/lib/mod_spox/messages/incoming/Privmsg.rb +1 -1
  92. data/lib/mod_spox/messages/incoming/Whois.rb +1 -0
  93. data/lib/mod_spox/messages/internal/EstablishConnection.rb +1 -1
  94. data/lib/mod_spox/messages/internal/PluginsReady.rb +10 -0
  95. data/lib/mod_spox/messages/internal/QueueSocket.rb +8 -0
  96. data/lib/mod_spox/messages/internal/Reconnect.rb +8 -0
  97. data/lib/mod_spox/messages/internal/UnqueueSocket.rb +8 -0
  98. data/lib/mod_spox/migrations/002_persistent_sigs.rb +14 -0
  99. data/lib/mod_spox/migrations/003_auth_restructure.rb +31 -0
  100. data/lib/mod_spox/migrations/004_mode_index_fix.rb +18 -0
  101. data/lib/mod_spox/migrations/005_nick_mode_nopark.rb +18 -0
  102. data/lib/mod_spox/models/Auth.rb +16 -46
  103. data/lib/mod_spox/models/AuthMask.rb +13 -0
  104. data/lib/mod_spox/models/Channel.rb +46 -27
  105. data/lib/mod_spox/models/Config.rb +10 -19
  106. data/lib/mod_spox/models/Group.rb +20 -8
  107. data/lib/mod_spox/models/Models.rb +1 -1
  108. data/lib/mod_spox/models/Nick.rb +105 -113
  109. data/lib/mod_spox/models/NickMode.rb +23 -9
  110. data/lib/mod_spox/models/Server.rb +12 -1
  111. data/lib/mod_spox/models/Setting.rb +12 -16
  112. data/lib/mod_spox/models/Signature.rb +28 -8
  113. data/tests/BotHolder.rb +24 -0
  114. data/tests/handlers/tc_BadNick.rb +21 -0
  115. data/tests/handlers/tc_Created.rb +24 -0
  116. data/tests/handlers/tc_Invite.rb +50 -0
  117. data/tests/handlers/tc_Join.rb +33 -0
  118. data/tests/handlers/tc_Kick.rb +32 -0
  119. data/tests/handlers/tc_Mode.rb +85 -0
  120. data/tests/handlers/tc_Names.rb +35 -0
  121. data/tests/handlers/tc_Nick.rb +55 -0
  122. data/tests/handlers/tc_Part.rb +44 -0
  123. data/tests/handlers/tc_Ping.rb +40 -0
  124. data/tests/handlers/tc_Pong.rb +28 -0
  125. data/tests/handlers/tc_Privmsg.rb +85 -0
  126. data/tests/handlers/tc_Quit.rb +40 -0
  127. data/tests/handlers/tc_Who.rb +50 -0
  128. data/tests/handlers/tc_Whois.rb +61 -0
  129. data/tests/models/tc_Auth.rb +34 -0
  130. data/tests/models/tc_Channel.rb +52 -0
  131. data/tests/models/tc_Config.rb +19 -0
  132. data/tests/models/tc_Nick.rb +142 -0
  133. data/tests/models/tc_NickMode.rb +40 -0
  134. data/tests/models/tc_Setting.rb +21 -0
  135. data/tests/models/tc_Signature.rb +14 -0
  136. data/tests/run_tests.rb +4 -0
  137. metadata +284 -212
  138. data/README +0 -36
  139. data/lib/mod_spox/Cache.rb +0 -57
  140. data/lib/mod_spox/Monitors.rb +0 -84
  141. data/lib/mod_spox/Pool.rb +0 -164
  142. data/lib/mod_spox/models/AuthGroup.rb +0 -16
  143. data/lib/mod_spox/models/ChannelMode.rb +0 -14
  144. data/lib/mod_spox/models/NickChannel.rb +0 -45
  145. data/lib/mod_spox/models/NickGroup.rb +0 -16
@@ -4,17 +4,15 @@ class Helper < ModSpox::Plugin
4
4
 
5
5
  def initialize(pipeline)
6
6
  super(pipeline)
7
- Signature.find_or_create(:signature => 'help', :plugin => name, :method => 'default_help',
8
- :description => 'Display default help information')
9
- Signature.find_or_create(:signature => 'help (\S+)', :plugin => name, :method => 'plugin_help',
10
- :description => 'Display help information from given plugin').params = [:plugin]
7
+ add_sig(:sig => 'help', :method => :default_help, :desc => 'Display default help information')
8
+ add_sig(:sig => 'help (\S+)', :method => :plugin_help, :desc => 'Display help information from given plugin', :params => [:plugin])
11
9
  end
12
10
 
13
11
  def default_help(message, params)
14
12
  plugins = Signature.select(:plugin).map(:plugin)
15
13
  plugins.uniq!
16
14
  reply message.replyto, "Plugins currently available for help: #{plugins.sort.join(', ')}"
17
- reply message.replyto, "Request help on a plugin: !help Plugin"
15
+ reply message.replyto, "Request help on a plugin: #{Models::Trigger.filter(:active => true).first.trigger}help Plugin"
18
16
  end
19
17
 
20
18
  def plugin_help(message, params)
@@ -29,6 +27,7 @@ class Helper < ModSpox::Plugin
29
27
  help << "\2Parameters:\2 [#{sig.params.join(' | ')}]" if sig.params
30
28
  help << "\2Auth Group:\2 #{Group[sig.group_id].name}" if sig.group_id
31
29
  help << "\2Description:\2 #{sig.description}" if sig.description
30
+ help << "\2Requirement:\2 #{['public', 'private'].include?(sig.requirement) ? sig.requirement : 'both'}"
32
31
  output << help.join(' ')
33
32
  end
34
33
  if(message.is_dcc?)
@@ -37,7 +36,7 @@ class Helper < ModSpox::Plugin
37
36
  reply message.source, output
38
37
  end
39
38
  else
40
- Signature.all.each{|s| sigs << s.plugin if /#{s.signature.gsub(/\s.*$/, '')}/ =~ params[:plugin]}
39
+ Signature.all.each{|s| sigs << s.plugin if /#{s.signature.gsub(/(\s|\().*$/, '')}/ =~ params[:plugin]}
41
40
  if(sigs.count > 0)
42
41
  sigs.uniq!
43
42
  reply message.replyto, "Possible plugin matches for \2#{params[:plugin]}\2: #{sigs.sort.join(', ')}"
@@ -12,30 +12,20 @@ class Initializer < ModSpox::Plugin
12
12
  # message:: ModSpox::Messages::Internal::BotInitialized
13
13
  # Instructs bot to connect to server
14
14
  def connect(message)
15
- populate_servers if @servers.empty?
16
- s = @servers.pop
17
- @pipeline << Messages::Internal::EstablishConnection.new(s.host, s.port)
15
+ @pipeline << Messages::Internal::EstablishConnection.new
18
16
  end
19
17
 
20
18
  # message:: ModSpox::Messages::Internal::Connected
21
19
  # Send bot information to server when connection is established
22
20
  def send_info(message)
23
- @pipeline << Messages::Outgoing::Nick.new(Models::Config[:bot_nick])
24
- @pipeline << Messages::Outgoing::User.new(Models::Config[:bot_username], Models::Config[:bot_realname], 8)
21
+ @pipeline << Messages::Outgoing::Nick.new(Models::Config.val(:bot_nick))
22
+ @pipeline << Messages::Outgoing::User.new(Models::Config.val(:bot_username), Models::Config.val(:bot_realname), 8)
25
23
  end
26
24
 
27
25
  # message:: ModSpox::Messages::Internal::ConnectionFailed or ModSpox::Messages::Internal::Disconnected
28
26
  # Reconnect to server on disconnection or connection failure
29
27
  def reconnect(message)
30
- @pipeline << Messages::Internal::TimerAdd.new(self, Models::Config[:reconnect_wait].to_i, nil, true){ connect(nil) }
31
- end
32
-
33
- private
34
-
35
- def populate_servers
36
- Models::Server.reverse_order(:priority).each{|s|
37
- @servers << s
38
- }
28
+ @pipeline << Messages::Internal::Reconnect.new
39
29
  end
40
30
 
41
31
  end
@@ -2,7 +2,7 @@ class Joiner < ModSpox::Plugin
2
2
  def initialize(pipeline)
3
3
  super(pipeline)
4
4
  admin = Models::Group.filter(:name => 'admin').first
5
- Models::Signature.find_or_create(:signature => 'join (\S+)', :plugin => name, :method => 'join', :group_id => admin.pk).params = [:channel]
5
+ add_sig(:sig => 'join (\S+)', :method => :join, :group => admin, :params => [:channel])
6
6
  @pipeline.hook(self, :send_who, :Incoming_Join)
7
7
  end
8
8
 
@@ -0,0 +1,13 @@
1
+ class Nicker < ModSpox::Plugin
2
+ def initialize(pipeline)
3
+ super(pipeline)
4
+ admin = Models::Group.filter(:name => 'admin').first
5
+ add_sig(:sig => 'nick (\S+)', :method => :change_nick, :group => admin, :params => [:nick])
6
+ end
7
+
8
+ # message:: ModSpox::Messages::Incoming::Privmsg
9
+ # Join the given channel
10
+ def change_nick(message, params)
11
+ @pipeline << Messages::Outgoing::Nick.new(params[:nick])
12
+ end
13
+ end
@@ -3,8 +3,8 @@ class Parter < ModSpox::Plugin
3
3
  def initialize(pipeline)
4
4
  super(pipeline)
5
5
  admin = Models::Group.filter(:name => 'admin').first
6
- Models::Signature.find_or_create(:signature => 'part (\S+)', :plugin => name, :method => 'part', :group_id => admin.pk).params = [:channel]
7
- Models::Signature.find_or_create(:signature => 'part', :plugin => name, :method => 'direct_part', :group_id => admin.pk)
6
+ add_sig(:sig => 'part (\S+)', :method => :part, :group => admin, :params => [:channel])
7
+ add_sig(:sig => 'part', :method => :direct_part, :group => admin)
8
8
  end
9
9
 
10
10
  # message:: ModSpox::Messages::Incoming::Privmsg
@@ -0,0 +1,60 @@
1
+ class Permissions < ModSpox::Plugin
2
+
3
+ def initialize(pipe)
4
+ super
5
+ admin = Models::Group.find_or_create(:name => 'admin')
6
+ add_sig(:sig => 'perm (\S+)', :group => admin, :method => :show_perm, :desc => 'Display permissions for plugin', :params => [:plugin])
7
+ add_sig(:sig => 'perm (\d+) (\S+)', :group => admin, :method => :set_perm, :desc => 'Sets permission for signature with given ID', :params => [:id, :group])
8
+ add_sig(:sig => 'group add (\S+)', :group => admin, :method => :add_group, :desc => 'Add a new authentication group', :params => [:group])
9
+ add_sig(:sig => 'invalid', :group => admin, :method => :invalid, :desc => 'foobar')
10
+ end
11
+
12
+ def show_perm(m, params)
13
+ output = ["Permissions for signatures owned by \2#{params[:plugin]}\2"]
14
+ Models::Signature.filter(:plugin => params[:plugin]).each do |s|
15
+ g = s.group
16
+ output << "[#{s.pk}] \2#{g.is_a?(Models::Group) ? g.name : 'unset'}\2 - #{s.signature} - #{s.description}"
17
+ end
18
+ if(output.size > 1)
19
+ reply m.replyto, output
20
+ else
21
+ error m.replyto, "Failed to locate signatures for given plugin: #{params[:plugin]}"
22
+ end
23
+ end
24
+
25
+ def set_perm(m, params)
26
+ s = Models::Signature[params[:id].to_i]
27
+ if(s)
28
+ if(params[:group] == 'none')
29
+ g = true
30
+ else
31
+ g = Models::Group.filter(:name => params[:group]).first
32
+ end
33
+ if(g)
34
+ s.group_id = g.is_a?(Models::Group) ? g.pk : nil
35
+ s.save
36
+ information m.replyto, "Signature with ID #{s.pk} has been updated to group: #{g.is_a?(Models::Group) ? g.name : 'unset'}"
37
+ else
38
+ error m.replyto, "Group name is invalid. If it is a new group, add it first. (#{params[:group]})"
39
+ end
40
+ else
41
+ error m.replyto, "Failed to find signature with ID: #{params[:id]}"
42
+ end
43
+ end
44
+
45
+ def add_group(m, params)
46
+ begin
47
+ raise 'Invalid group name: none' if params[:group] == 'none'
48
+ Models::Group.find_or_create(:name => params[:group])
49
+ information m.replyto, "New group \2#{params[:group]}\2 is now available"
50
+ rescue Object => boom
51
+ error m.replyto, "Unknown error when adding new group: #{params[:group]}"
52
+ end
53
+ end
54
+
55
+ def invalid(m, params)
56
+ Models::Signature.dataset.update(:enabled => false)
57
+ information m.replyto, 'ok'
58
+ end
59
+
60
+ end
@@ -3,16 +3,11 @@ class PluginLoader < ModSpox::Plugin
3
3
  def initialize(pipeline)
4
4
  super(pipeline)
5
5
  admin = Models::Group.filter(:name => 'admin').first
6
- Models::Signature.find_or_create(:signature => 'plugins available', :plugin => name, :method => 'available_plugins',
7
- :group_id => admin.pk, :description => 'List all available plugins')
8
- Models::Signature.find_or_create(:signature => 'plugins loaded', :plugin => name, :method => 'loaded_plugins',
9
- :group_id => admin.pk, :description => 'List all plugins currently loaded')
10
- Models::Signature.find_or_create(:signature => 'plugins load (\S+)', :plugin => name, :method => 'load_plugin',
11
- :group_id => admin.pk, :description => 'Load the given plugin').params = [:plugin]
12
- Models::Signature.find_or_create(:signature => 'plugins unload (\S+)', :plugin => name, :method => 'unload_plugin',
13
- :group_id => admin.pk, :description => 'Unload given plugin').params = [:plugin]
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]
6
+ add_sig(:sig => 'plugins available', :method => :available_plugins, :group => admin, :desc => 'List all available plugins')
7
+ add_sig(:sig => 'plugins loaded', :method => :loaded_plugins, :group => admin, :desc => 'List all plugins currently loaded')
8
+ add_sig(:sig => 'plugins load (\S+)', :method => :load_plugin, :group => admin, :desc => 'Load the given plugin', :params => [:plugin])
9
+ add_sig(:sig => 'plugins unload (\S+)', :method => :unload_plugin, :group => admin, :desc => 'Unload given plugin', :params => [:plugin])
10
+ add_sig(:sig => 'plugins reload ?(\S+)?', :method => :reload_plugin, :group => admin, :desc => 'Reload single plugin or all plugins if names not provided', :params => [:plugin])
16
11
  @pipeline.hook(self, :get_module, :Internal_PluginModuleResponse)
17
12
  @plugins_mod = nil
18
13
  end
@@ -93,7 +88,7 @@ class PluginLoader < ModSpox::Plugin
93
88
 
94
89
  # Upgrades extra plugins to latest version
95
90
  def extras_upgrade
96
- Logger.info("Starting plugin upgrade to current version: #{$BOTVERSION}")
91
+ Logger.info("Starting plugin upgrade to current version: #{ModSpox.botversion}")
97
92
  extras = plugin_discovery(BotConfig[:pluginextraspath])
98
93
  pl = plugin_list
99
94
  extras.keys.each{|d| extras.delete(d) unless pl.include?(d)}
@@ -107,7 +102,7 @@ class PluginLoader < ModSpox::Plugin
107
102
  extras.each do |name, path|
108
103
  @pipeline << Messages::Internal::PluginLoadRequest.new(self, path)
109
104
  end
110
- Logger.info("Plugin upgrade is now complete. Upgraded to version: #{$BOTVERSION}")
105
+ Logger.info("Plugin upgrade is now complete. Upgraded to version: #{ModSpox.botversion}")
111
106
  end
112
107
 
113
108
  private
@@ -2,7 +2,16 @@ class Ponger < ModSpox::Plugin
2
2
 
3
3
  def initialize(pipeline)
4
4
  super(pipeline)
5
+ @lag = nil
6
+ @last = nil
7
+ @attempts = 0
8
+ add_sig(:sig => 'lag', :method => :print_lag, :desc => 'Shows bot\'s current lag to server')
5
9
  @pipeline.hook(self, :ping, :Incoming_Ping)
10
+ @pipeline.hook(self, :get_lag_pong, :Incoming_Pong)
11
+ @lock = Mutex.new
12
+ @pipeline.hook(self, :check_start_ponger, :Internal_PluginsReady)
13
+ @running = false
14
+ send_lag_ping
6
15
  end
7
16
 
8
17
  # message:: ModSpox::Messages::Incoming::Ping
@@ -10,5 +19,47 @@ class Ponger < ModSpox::Plugin
10
19
  def ping(message)
11
20
  @pipeline << Messages::Outgoing::Pong.new(message.server, message.string)
12
21
  end
22
+
23
+ def send_lag_ping
24
+ if(@attempts > 5)
25
+ @pipeline << Messages::Internal::Reconnect.new
26
+ @lag = nil
27
+ @last = nil
28
+ @attempts = 0
29
+ else
30
+ t = Time.now
31
+ @last = t.to_f.to_s
32
+ @pipeline << Messages::Outgoing::Ping.new("#{@last}")
33
+ @attempts += 1
34
+ end
35
+ end
36
+
37
+ def get_lag_pong(m)
38
+ @lock.synchronize do
39
+ t = m.string.to_f
40
+ return unless m.string == @last
41
+ @lag = Time.now.to_f - t
42
+ @last = nil
43
+ @attempts = 0
44
+ end
45
+ end
46
+
47
+ def print_lag(m, params)
48
+ if(@lag.nil?)
49
+ warning m.replyto, 'Lag time currently unavailable'
50
+ else
51
+ information m.replyto, "Current lag time: #{sprintf('%0.4f', @lag)} seconds"
52
+ end
53
+ end
54
+
55
+ def start_ponger(m)
56
+ @running = true
57
+ send_lag_ping
58
+ @pipeline << Messages::Internal::TimerAdd.new(self, 60){ send_lag_ping }
59
+ end
60
+
61
+ def check_start_ponger(m)
62
+ @pipeline << Messages::Internal::TimerAdd.new(self, 30, nil, true){ @lock.synchronize{start_ponger(nil) unless @running} }
63
+ end
13
64
 
14
65
  end
@@ -2,8 +2,7 @@ class Quitter < ModSpox::Plugin
2
2
 
3
3
  def initialize(pipeline)
4
4
  super(pipeline)
5
- Models::Signature.find_or_create(:signature => 'quit(\s.+)?', :plugin => name, :method => 'quit',
6
- :group_id => Models::Group.filter(:name => 'admin').first.pk).params = [:channel, :message]
5
+ add_sig(:sig => 'quit(\s.+)?', :method => :quit, :group => Models::Group.filter(:name => 'admin').first, :params => [:channel, :message])
7
6
  end
8
7
 
9
8
  # message:: ModSpox::Messages::Incoming::Privmsg
@@ -0,0 +1,57 @@
1
+ class Servers < ModSpox::Plugin
2
+ def initialize(pipeline)
3
+ super
4
+ admin = Models::Group.find_or_create(:name => 'admin')
5
+ add_sig(:sig => 'servers list', :method => :list, :group => admin, :desc => 'Show server list')
6
+ add_sig(:sig => 'servers add (\S+) (\d+)( \d+)?', :method => :add, :group => admin, :desc => 'Add server to list', :params => [:server, :port, :prio])
7
+ add_sig(:sig => 'servers del (\d+)', :method => :remove, :group => admin, :desc => 'Remove server from list', :params => [:id])
8
+ add_sig(:sig => 'servers prio (\d+) (\+|\-|\d+)', :method => :prio, :group => admin, :desc => 'Prioritize server', :params => [:id, :move])
9
+ end
10
+
11
+ def list(m, params)
12
+ output = ["\2Server list:\2"]
13
+ Models::Server.reverse_order(:priority).each do |s|
14
+ output << "\2[#{s.id}]\2 #{s.host}:#{s.port} \2Priority:\2 #{s.priority}"
15
+ end
16
+ reply m.replyto, output
17
+ end
18
+
19
+ def add(m, params)
20
+ begin
21
+ s = Models::Server.new(:host => params[:server], :port => params[:port].to_i)
22
+ s.priority = params[:prio].to_i if params[:prio]
23
+ s.save
24
+ information m.replyto, "New server has been added: #{params[:server]}:#{params[:port]}"
25
+ rescue Object => boom
26
+ error m.replyto, "Failed to add new server. Reason: #{boom}"
27
+ end
28
+ end
29
+
30
+ def remove(m, params)
31
+ begin
32
+ raise "List must contain one server" unless Models::Server.count > 1
33
+ Models::Server[params[:id].to_i].destroy
34
+ information m.replyto, 'Server has been removed'
35
+ rescue Object => boom
36
+ error m.replyto, "Failed to remove server. Reason: #{boom}"
37
+ end
38
+ end
39
+
40
+ def prio(m, params)
41
+ begin
42
+ s = Models::Server[params[:id].to_i]
43
+ if(params[:move] == '+')
44
+ s.priority += 1
45
+ elsif(params[:move] == '-')
46
+ s.priority -= 1 if s.priority > 0
47
+ else
48
+ s.priority = params[:move].to_i
49
+ end
50
+ s.save_changes
51
+ information m.replyto, 'Server priority has been updated.'
52
+ rescue Object => boom
53
+ error m.replyto, "Failed to update priority. Reason: #{boom}"
54
+ end
55
+ end
56
+
57
+ end
@@ -4,6 +4,7 @@ class Status < ModSpox::Plugin
4
4
  super
5
5
  add_sig(:sig => 'status', :method => :status, :desc => 'Show current status')
6
6
  add_sig(:sig => 'version', :method => :version, :desc => 'Show version information')
7
+ add_sig(:sig => 'VERSION', :method => :version, :desc => 'Show version information')
7
8
  @pipeline.hook(self, :get_status, :Internal_StatusResponse)
8
9
  @resp = []
9
10
  end
@@ -14,13 +15,13 @@ class Status < ModSpox::Plugin
14
15
  end
15
16
 
16
17
  def version(message, params)
17
- reply message.replyto, "mod_spox IRC bot - Version: \2#{$BOTVERSION}\2 (#{$BOTCODENAME}) [http://modspox.rubyforge.org]"
18
+ reply message.replyto, "mod_spox IRC bot - Version: \2#{ModSpox.botversion}\2 (#{ModSpox.botcodename}) [http://modspox.rubyforge.org]"
18
19
  end
19
20
 
20
21
  def get_status(m)
21
22
  @resp.uniq!
22
23
  @resp.each do |c|
23
- reply c, "\2Status:\2 \2Uptime:\2 #{m.status[:uptime]} \2Plugins:\2 #{m.status[:plugins]} loaded \2Lines sent:\2 #{m.status[:sent]} \2Lines Received:\2 #{m.status[:received]}"
24
+ reply c, "\2Status:\2 \2Uptime:\2 #{m.status[:uptime]} \2Plugins:\2 #{m.status[:plugins]} loaded \2Socket Connected:\2 #{m.status[:socket_connect].strftime("%Y/%m/%d-%H:%M:%S")} \2Lines sent:\2 #{m.status[:sent]} \2Lines Received:\2 #{m.status[:received]}"
24
25
  @resp.delete(c)
25
26
  end
26
27
  end
@@ -5,12 +5,12 @@ include Messages::Outgoing
5
5
  def initialize(pipeline)
6
6
  super(pipeline)
7
7
  admin = Models::Group.filter(:name => 'admin').first
8
- Models::Signature.find_or_create(:signature => 'triggers active', :plugin => name, :method => 'active', :group_id => admin.pk, :description => 'List all currently active triggers')
9
- Models::Signature.find_or_create(:signature => 'triggers list', :plugin => name, :method => 'list', :group_id => admin.pk, :description => 'List all triggers and their current status')
10
- Models::Signature.find_or_create(:signature => 'triggers add (\S+)', :plugin => name, :method => 'add', :group_id => admin.pk, :description => 'Add a new trigger and activate it').params = [:trigger]
11
- Models::Signature.find_or_create(:signature => 'triggers remove (\d+)', :plugin => name, :method => 'remove', :group_id => admin.pk, :description => 'Remove trigger').params = [:id]
12
- Models::Signature.find_or_create(:signature => 'triggers activate (\d+)', :plugin => name, :method => 'activate', :group_id => admin.pk, :description => 'Activate the trigger').params = [:id]
13
- Models::Signature.find_or_create(:signature => 'triggers deactivate (\d+)', :plugin => name, :method => 'deactivate', :group_id => admin.pk, :description => 'Deactivate the trigger').params = [:id]
8
+ add_sig(:sig => 'triggers active', :method => :active, :group => admin, :desc => 'List all currently active triggers')
9
+ add_sig(:sig => 'triggers list', :method => :list, :group => admin, :desc => 'List all triggers and their current status')
10
+ add_sig(:sig => 'triggers add (\S+)', :method => :add, :group => admin, :desc => 'Add a new trigger and activate it', :params => [:trigger])
11
+ add_sig(:sig => 'triggers remove (\d+)', :method => :remove, :group => admin, :desc => 'Remove trigger', :params => [:id])
12
+ add_sig(:sig => 'triggers activate (\d+)', :method => :activate, :group => admin, :desc => 'Activate the trigger', :params => [:id])
13
+ add_sig(:sig => 'triggers deactivate (\d+)', :method => :deactivate, :group => admin, :desc => 'Deactivate the trigger', :params => [:id])
14
14
  end
15
15
 
16
16
  def active(message, params)
@@ -40,7 +40,7 @@ include Messages::Outgoing
40
40
  end
41
41
 
42
42
  def add(message, params)
43
- Models::Trigger.find_or_create(:trigger => params[:trigger]).update_with_params(:active => true)
43
+ Models::Trigger.find_or_create(:trigger => params[:trigger]).update(:active => true)
44
44
  @pipeline << Privmsg.new(message.replyto, "Trigger #{params[:trigger]} is now active")
45
45
  @pipeline << Messages::Internal::TriggersUpdate.new
46
46
  end
@@ -60,7 +60,7 @@ include Messages::Outgoing
60
60
  def activate(message, params)
61
61
  trigger = Models::Trigger[params[:id]]
62
62
  if(trigger)
63
- trigger.update_with_params(:active => true)
63
+ trigger.update(:active => true)
64
64
  @pipeline << Privmsg.new(message.replyto, "Trigger #{trigger.trigger} has been activated")
65
65
  @pipeline << Messages::Internal::TriggersUpdate.new
66
66
  else
@@ -71,7 +71,7 @@ include Messages::Outgoing
71
71
  def deactivate(message, params)
72
72
  trigger = Models::Trigger[params[:id]]
73
73
  if(trigger)
74
- trigger.update_with_params(:active => false)
74
+ trigger.update(:active => false)
75
75
  @pipeline << Privmsg.new(message.replyto, "Trigger #{trigger.trigger} has been deactivated")
76
76
  @pipeline << Messages::Internal::TriggersUpdate.new
77
77
  else
@@ -4,17 +4,22 @@
4
4
  'mod_spox/PluginManager',
5
5
  'mod_spox/MessageFactory',
6
6
  'mod_spox/BaseConfig',
7
- 'mod_spox/Timer',
8
7
  'mod_spox/messages/Messages',
9
8
  'mod_spox/models/Models',
10
9
  'mod_spox/Helpers',
11
- 'mod_spox/Pool'].each{|f|require f}
10
+ 'mod_spox/Timer'].each{|f|require f}
11
+ require 'actionpool'
12
+ require 'actiontimer'
13
+
12
14
  module ModSpox
13
15
 
14
16
  class Bot
15
17
 
16
18
  # bot timer
17
19
  attr_reader :timer
20
+
21
+ # thread pool
22
+ attr_reader :pool
18
23
 
19
24
  # message pipeline
20
25
  attr_reader :pipeline
@@ -30,15 +35,19 @@ module ModSpox
30
35
 
31
36
  # Create a Bot
32
37
  def initialize
33
- Logger.initialize($LOGTO, $LOGLEVEL)
34
- Pool.instance
38
+ unless(ModSpox.logto.nil?)
39
+ logger = ::Logger.new(ModSpox.logto, 'daily')
40
+ Logger.initialize(logger, ModSpox.loglevel)
41
+ end
35
42
  clean_models
43
+ @servers = Array.new
44
+ @channels = Array.new
36
45
  @start_time = Time.now
37
- @pipeline = Pipeline.new
38
- @timer = Timer.new(@pipeline)
39
- @timer.start
46
+ @pool = ActionPool::Pool.new(10, 100, 60, nil, logger)
47
+ @pipeline = Pipeline.new(@pool)
48
+ @timer = Timer.new(ActionTimer::Timer.new(@pool, logger), @pipeline)
40
49
  @config = BaseConfig.new(BotConfig[:userconfigpath])
41
- @factory = MessageFactory.new(@pipeline)
50
+ @factory = MessageFactory.new(@pipeline, @pool)
42
51
  @socket = nil
43
52
  @plugin_manager = PluginManager.new(@pipeline)
44
53
  if(@config[:plugin_upgrade] == 'yes')
@@ -60,10 +69,10 @@ module ModSpox
60
69
 
61
70
  # Run the bot
62
71
  def run
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 }
72
+ trap('SIGTERM'){ Logger.warn("Caught SIGTERM"); halt }
73
+ trap('SIGKILL'){ Logger.warn("Caught SIGKILL"); halt }
74
+ trap('SIGINT'){ Logger.warn("Caught SIGINT"); halt }
75
+ trap('SIGQUIT'){ Logger.warn("Caught SIGQUIT"); halt }
67
76
  until @shutdown do
68
77
  @pipeline << Messages::Internal::BotInitialized.new
69
78
  begin
@@ -106,10 +115,28 @@ module ModSpox
106
115
  begin
107
116
  @socket = Sockets.new(self) if @socket.nil?
108
117
  @socket.irc_connect(message.server, message.port)
109
- @pipeline << Messages::Internal::Connected.new(message.server, message.port)
110
118
  rescue Object => boom
111
119
  Logger.warn("Failed connection to server: #{boom}")
112
- @pipeline << Messages::Internal::ConnectionFailed.new(message.server, message.port)
120
+ @pipeline << Messages::Internal::ConnectionFailed.new(@socket.irc_socket.server, @socket.irc_socket.port)
121
+ end
122
+ end
123
+
124
+ # message:: Messages::Internal::Reconnect
125
+ # instructs bot to reconnect to IRC server
126
+ def reconnect(message=nil)
127
+ begin
128
+ @plugin_manager.reload_plugins
129
+ @socket.irc_reconnect
130
+ rescue Object => boom
131
+ Logger.warn("Initial reconnect failed. (#{boom}) Starting timed reconnect process.")
132
+ begin
133
+ @socket.irc_reconnect
134
+ rescue Object => boom
135
+ Logger.warn("Failed to connect to server. Reason: #{boom}")
136
+ Logger.warn("Will retry in 20 seconds")
137
+ sleep(20)
138
+ retry
139
+ end
113
140
  end
114
141
  end
115
142
 
@@ -123,6 +150,7 @@ module ModSpox
123
150
  def bot_stats
124
151
  return {:uptime => Helpers::format_seconds(Time.now - @start_time),
125
152
  :plugins => @plugin_manager.plugins.size,
153
+ :socket_connect => @socket.irc_socket.connected_at,
126
154
  :sent => @socket.irc_socket.sent,
127
155
  :received => @socket.irc_socket.received}
128
156
  end
@@ -154,7 +182,8 @@ module ModSpox
154
182
  :Internal_StatusRequest => :status, :Internal_ChangeNick => :set_nick,
155
183
  :Internal_NickRequest => :get_nick, :Internal_HaltBot => :halt,
156
184
  :Internal_Disconnected => :disconnected, :Internal_TimerClear => :clear_timer,
157
- :Outgoing_Raw => :raw
185
+ :Outgoing_Raw => :raw, :Internal_Reconnect => :reconnect,
186
+ :Incoming_Join => :check_join, :Incoming_Part => :check_part
158
187
  }.each_pair{ |type,method| @pipeline.hook(self, method, type) }
159
188
  end
160
189
 
@@ -167,12 +196,11 @@ module ModSpox
167
196
  # message:: Messages::Internal::Disconnected
168
197
  # Disconnect the bot from the IRC server
169
198
  def disconnected(message)
170
- @socket.shutdown
171
- @socket = nil
199
+ reload
172
200
  end
173
201
 
174
202
  # Stop the bot
175
- def halt(message)
203
+ def halt(message=nil)
176
204
  @shutdown = true
177
205
  reload
178
206
  end
@@ -282,6 +310,7 @@ module ModSpox
282
310
  # message:: Messages::Outgoing::Invite message
283
311
  # Sends INVITE message to server
284
312
  def invite(message)
313
+ okay_to_send(message.channel)
285
314
  nick = message.nick.is_a?(Models::Nick) ? message.nick.nick : message.nick
286
315
  channel = message.channel.is_a?(Models::Channel) ? message.channel.name : message.channel
287
316
  @socket << "INVITE #{nick} #{channel}"
@@ -290,6 +319,7 @@ module ModSpox
290
319
  # message:: Messages::Outgoing::Kick message
291
320
  # Sends KICK message to server
292
321
  def kick(message)
322
+ okay_to_send(message.channel)
293
323
  nick = message.nick.is_a?(Models::Nick) ? message.nick.nick : message.nick
294
324
  channel = message.channel.is_a?(Models::Channel) ? message.channel.name : message.channel
295
325
  @socket << "KICK #{channel} #{nick} :#{message.reason}"
@@ -298,18 +328,19 @@ module ModSpox
298
328
  # message:: Messages::Outgoing::Privmsg message
299
329
  # Sends PRIVMSG message to server
300
330
  def privmsg(message)
331
+ okay_to_send(message.target)
301
332
  target = message.target.name if message.target.is_a?(Models::Channel)
302
333
  target = message.target.nick if message.target.is_a?(Models::Nick)
303
334
  target = message.target unless target
304
335
  messages = message.message.is_a?(Array) ? message.message : [message.message]
305
336
  messages.each do |part|
306
337
  part.split("\n").each do |content|
307
- while(content.size > 450)
308
- output = content[0..450]
309
- content.slice!(0, 451) #(450, content.size)
310
- @socket << "PRIVMSG #{target} :#{message.is_ctcp? ? "\cA#{message.ctcp_type} #{output}\cA" : output}"
338
+ while(content.size > 400)
339
+ output = content[0..400]
340
+ content.slice!(0, 401) #(450, content.size)
341
+ @socket.prioritize_message(target, "PRIVMSG #{target} :#{message.is_ctcp? ? "\cA#{message.ctcp_type} #{output}\cA" : output}")
311
342
  end
312
- @socket << "PRIVMSG #{target} :#{message.is_ctcp? ? "\cA#{message.ctcp_type} #{content}\cA" : content}"
343
+ @socket.prioritize_message(target, "PRIVMSG #{target} :#{message.is_ctcp? ? "\cA#{message.ctcp_type} #{content}\cA" : content}")
313
344
  end
314
345
  end
315
346
  end
@@ -317,6 +348,7 @@ module ModSpox
317
348
  # message:: Messages::Outgoing::Notice message
318
349
  # Sends NOTICE message to server
319
350
  def notice(message)
351
+ okay_to_send(message.target)
320
352
  target = message.target.name if message.target.is_a?(Models::Channel)
321
353
  target = message.target.nick if message.target.is_a?(Models::Nick)
322
354
  @socket << "NOTICE #{target} :#{message}"
@@ -399,14 +431,14 @@ module ModSpox
399
431
  # Sends WHO message to server
400
432
  def who(message)
401
433
  o = message.only_ops? ? 'o' : ''
402
- @socket << "WHO #{message.mask} #{o}"
434
+ @socket.prioritize_message('*who', "WHO #{message.mask} #{o}")
403
435
  end
404
436
 
405
437
  # message:: Messages::Outgoing::Whois message
406
438
  # Sends WHOIS message to server
407
439
  def whois(message)
408
440
  nick = message.nick.is_a?(Models::Nick) ? message.nick.nick : message.nick
409
- @socket << "WHOIS #{message.target_server} #{nick}"
441
+ @socket.prioritize_message('*whois', "WHOIS #{message.target_server} #{nick}")
410
442
  end
411
443
 
412
444
  # message:: Messages::Outgoing::WhoWas message
@@ -502,22 +534,66 @@ module ModSpox
502
534
  @socket << message.message
503
535
  end
504
536
 
537
+ def check_join(m)
538
+ if(m.nick.botnick)
539
+ @channels << m.channel.name.downcase
540
+ end
541
+ end
542
+
543
+ def check_part(m)
544
+ if(m.nick.botnick)
545
+ @channels.delete(m.channel.name.downcase)
546
+ end
547
+ end
548
+
505
549
  private
506
550
 
507
- def start_shutdown
508
- @shutdown = true
509
- @lock.synchronize do
510
- @waiter.signal
551
+ # channel:: channel to check
552
+ # checks if the bot is parked in the given channel
553
+ # and raises an exception (and logs) if the bot
554
+ # is not in the given channel
555
+ def okay_to_send(channel)
556
+ if(channel.is_a?(String) && ['&', '#', '+', '!'].include?(channel[0]))
557
+ channel = Helpers.find_model(channel)
511
558
  end
559
+ return unless channel.is_a?(Models::Channel)
560
+ if(channel.quiet)
561
+ Logger.error("Attempted to send to channel where bot is not allowed to speak: #{channel.name}")
562
+ raise Exceptions::QuietChannel.new(channel)
563
+ end
564
+ unless(in_channel?(channel))
565
+ Logger.error("Attempted to send to channel where bot is not parked: #{channel.name}.")
566
+ raise Exceptions::NotInChannel.new(channel)
567
+ end
568
+ end
569
+
570
+ def in_channel?(channel)
571
+ unless(@channels.include?(channel.name.downcase))
572
+ repopulate_channels
573
+ return @channels.include?(channel.name.downcase)
574
+ else
575
+ return true
576
+ end
577
+ end
578
+
579
+ def repopulate_channels
580
+ bot = Models::Nick.filter(:botnick => true).first
581
+ raise Exceptions::BotException.new("I DON'T KNOW WHO I AM") unless bot
582
+ @channels.clear
583
+ bot.channels.each{|c| @channels << c.name.downcase}
512
584
  end
513
585
 
514
586
  # Cleans information from models to avoid
515
587
  # stale values
516
588
  def clean_models
517
- Models::Nick.clean
518
- Models::Channel.clean
519
- Models::NickChannel.destroy_all
520
- Models::Signature.delete_all
589
+ Models::NickMode.destroy
590
+ Models::Channel.update(:topic => nil)
591
+ Models::Nick.update(:username => nil, :real_name => nil, :address => nil,
592
+ :source => nil, :connected_at => nil, :connected_to => nil,
593
+ :seconds_idle => nil, :away => false, :visible => false, :botnick => false)
594
+ Models::Auth.update(:authed => false)
595
+ Database.db[:auth_masks_nicks].delete
596
+ Database.db[:nick_channels].delete
521
597
  end
522
598
  end
523
599