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
@@ -1,6 +1,18 @@
1
+ require 'mod_spox/Pool'
2
+
1
3
  module ModSpox
2
4
 
3
5
  class Logger
6
+
7
+ def self.do_initialization
8
+ Logger.fd(nil) unless Logger.class_variable_defined?(:@@fd)
9
+ Logger.severity unless Logger.class_variable_defined?(:@@severity)
10
+ @@lock = Mutex.new unless Logger.class_variable_defined?(:@@lock)
11
+ @@lock.synchronize do
12
+ @@writer = LogWriter.new(@@fd) unless Logger.class_variable_defined?(:@@writer)
13
+ @@initialized = true
14
+ end
15
+ end
4
16
 
5
17
  # severity:: minimum severity for visible logging
6
18
  # Sets the maximum level of visible logs
@@ -21,9 +33,43 @@ module ModSpox
21
33
  # severity level of a message, the better chance it has of
22
34
  # being outputted
23
35
  def self.log(message, severity=1)
24
- Logger.fd(nil) unless Logger.class_variable_defined?(:@@fd)
25
- Logger.severity unless Logger.class_variable_defined?(:@@severity)
26
- @@fd.puts("LOGGER [#{Time.now}]: #{message}") unless @@severity < severity
36
+ do_initialization unless Logger.class_variable_defined?(:@@initialized)
37
+ @@writer.log(message) unless @@severity < severity
38
+ end
39
+
40
+ def self.kill
41
+ @@writer.kill
42
+ end
43
+
44
+ end
45
+
46
+ class LogWriter
47
+
48
+ attr_reader :fd
49
+
50
+ def initialize(fd)
51
+ @fd = fd
52
+ @kill = false
53
+ @queue = Queue.new
54
+ @thread = Thread.new do
55
+ until(@kill)
56
+ processor
57
+ end
58
+ end
59
+ end
60
+
61
+ def kill
62
+ @kill = true
63
+ @queue << "Logger has been told to shut down"
64
+ end
65
+
66
+ def log(message)
67
+ @queue << message
68
+ end
69
+
70
+ def processor
71
+ message = @queue.pop
72
+ @fd.puts("LOGGER [#{Time.now}]: #{message}")
27
73
  end
28
74
 
29
75
  end
@@ -1,3 +1,8 @@
1
+ ['mod_spox/handlers/Handler',
2
+ 'mod_spox/Logger',
3
+ 'mod_spox/Pipeline',
4
+ 'mod_spox/Pool'].each{|f|require f}
5
+
1
6
  module ModSpox
2
7
 
3
8
  class MessageFactory < Pool
@@ -12,7 +12,13 @@ module ModSpox
12
12
 
13
13
  # Force the monitor to wake everyone up
14
14
  def wakeup
15
- @threads.each{|t|t.wakeup}
15
+ @threads.each do |t|
16
+ begin
17
+ t.wakeup
18
+ rescue Object => boom
19
+ # thread was dead #
20
+ end
21
+ end
16
22
  @threads.clear
17
23
  end
18
24
 
@@ -40,24 +46,20 @@ module ModSpox
40
46
  # Stop waiting
41
47
  def wakeup
42
48
  return if @threads.empty?
43
- @threads.each do |thread|
44
- if(thread.status == 'sleep')
45
- Logger.log("Sending wakeup for thread: #{thread}", 5)
46
- thread.run
47
- Logger.log("Status of thread to wakeup: #{thread.status}", 5)
48
- Logger.log("Calling thread is: #{Thread.current}", 5)
49
- else
50
- Logger.log("Thread to wakeup has been killed: #{thread}")
49
+ @threads.each do |t|
50
+ begin
51
+ t.wakeup
52
+ rescue Object => boom
53
+ # thread was dead #
51
54
  end
52
- @threads.delete(thread)
53
55
  end
56
+ @threads.clear
54
57
  end
55
58
 
56
59
  # Start waiting
57
60
  def wait
58
61
  @threads << Thread.current
59
- Logger.log("Stopping execution of thread: #{Thread.current}", 5)
60
- Thread.stop
62
+ sleep
61
63
  end
62
64
 
63
65
  # Returns if a thread is currently waiting in this monitor
@@ -1,3 +1,7 @@
1
+ ['mod_spox/models/Models.rb',
2
+ 'mod_spox/Logger',
3
+ 'mod_spox/Pool',
4
+ 'mod_spox/Exceptions'].each{|f|require f}
1
5
  module ModSpox
2
6
 
3
7
  class Pipeline < Pool
@@ -10,6 +14,7 @@ module ModSpox
10
14
  @hooks = Hash.new
11
15
  @plugins = Hash.new
12
16
  @admin = Models::Group.filter(:name => 'admin').first
17
+ @populate_lock = Mutex.new
13
18
  populate_triggers
14
19
  populate_signatures
15
20
  hook(self, :populate_triggers, :Internal_TriggersUpdate)
@@ -36,7 +41,7 @@ module ModSpox
36
41
  # Unhooks a plugin from the pipeline (This does not unhook
37
42
  # it from the standard hooks)
38
43
  def unhook_plugin(plugin)
39
- Logger.log("Plugin #{plugin.name} unhooking from plugin", 10)
44
+ Logger.log("Plugin #{plugin.name} unhooking from pipeline", 10)
40
45
  @plugins.delete(plugin.name.to_sym)
41
46
  @hooks.each_pair do |type, things|
42
47
  things.delete(plugin.name.to_sym) if things.has_key?(plugin.name.to_sym)
@@ -82,14 +87,32 @@ module ModSpox
82
87
 
83
88
  # Repopulate the active trigger list
84
89
  def populate_triggers(m=nil)
85
- @triggers = []
86
- Models::Trigger.filter(:active => true).each{|t|@triggers << t.trigger}
90
+ @populate_lock.synchronize do
91
+ @triggers = []
92
+ Models::Trigger.filter(:active => true).each{|t|@triggers << t.trigger}
93
+ end
87
94
  end
88
95
 
89
96
  # Repopulate the active signatures list
90
97
  def populate_signatures(m=nil)
91
- @signatures = []
92
- Models::Signature.all.each{|s|@signatures << s}
98
+ @populate_lock.synchronize do
99
+ @signatures = {}
100
+ Models::Signature.all.each do |s|
101
+ Logger.log("Signature being processed: #{s.signature}")
102
+ c = s.signature[0].chr.downcase
103
+ if(c =~ /^[a-z]$/)
104
+ type = c.to_sym
105
+ elsif(c =~ /^[0-9]$/)
106
+ type = :digit
107
+ else
108
+ type = :other
109
+ end
110
+ @signatures[type] = [] unless @signatures[type]
111
+ unless @signatures.include?(s)
112
+ @signatures[type] << s
113
+ end
114
+ end
115
+ end
93
116
  end
94
117
 
95
118
  private
@@ -133,7 +156,16 @@ module ModSpox
133
156
  @triggers.each{|t| trigger = t if message.message =~ /^#{t}/}
134
157
  if(!trigger.nil? || message.addressed?)
135
158
  Logger.log("Message has matched against a known trigger", 15)
136
- @signatures.each do |sig|
159
+ c = message.addressed? ? message.message[0].chr.downcase : message.message[1].chr.downcase
160
+ if(c =~ /^[a-z]$/)
161
+ type = c.to_sym
162
+ elsif(c =~ /^[0-9]$/)
163
+ type = :digit
164
+ else
165
+ type = :other
166
+ end
167
+ return unless @signatures[type]
168
+ @signatures[type].each do |sig|
137
169
  Logger.log("Matching against: #{trigger}#{sig.signature}")
138
170
  res = message.message.scan(/^#{trigger}#{sig.signature}$/)
139
171
  if(res.size > 0)
@@ -1,3 +1,6 @@
1
+ ['mod_spox/Pipeline',
2
+ 'mod_spox/Logger',
3
+ 'mod_spox/Exceptions'].each{|f|require f}
1
4
  module ModSpox
2
5
  class Plugin
3
6
  def initialize(pipeline)
@@ -0,0 +1,22 @@
1
+ module ModSpox
2
+ # Holder for plugin
3
+ class PluginHolder
4
+ # Create a new holder with a plugin
5
+ def initialize(plugin)
6
+ @plugin = plugin
7
+ @lock = Mutex.new
8
+ end
9
+ # plugin:: Plugin to set into this holder
10
+ def set_plugin(plugin)
11
+ @lock.synchronize do
12
+ @plugin = plugin
13
+ end
14
+ end
15
+ # Returns the plugin this holder contains
16
+ def plugin
17
+ @lock.synchronize do
18
+ return @plugin
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,4 +1,10 @@
1
- require 'fileutils'
1
+ ['fileutils',
2
+ 'mod_spox/Logger',
3
+ 'mod_spox/Pipeline',
4
+ 'mod_spox/models/Models',
5
+ 'mod_spox/messages/Messages',
6
+ 'mod_spox/Plugin',
7
+ 'mod_spox/PluginHolder'].each{|f|require f}
2
8
  module ModSpox
3
9
 
4
10
  class PluginManager
@@ -17,14 +23,26 @@ module ModSpox
17
23
  @pipeline.hook(self, :send_modules, :Internal_PluginModuleRequest)
18
24
  @pipeline.hook(self, :plugin_request, :Internal_PluginRequest)
19
25
  @plugins_module = Module.new
26
+ @plugin_lock = Mutex.new
20
27
  load_plugins
21
28
  end
22
29
 
23
30
  # message:: Messages::Internal::PluginReload
24
31
  # Destroys and reinitializes plugins
25
- def reload_plugins(mesasge=nil)
26
- unload_plugins
27
- load_plugins
32
+ def reload_plugins(message=nil)
33
+ @plugin_lock.synchronize do
34
+ if(message.fresh && message.stale)
35
+ do_unload(message.stale)
36
+ FileUtils.remove_file(message.stale)
37
+ FileUtils.copy(message.fresh, BotConfig[:userpluginpath])
38
+ do_load(message.stale)
39
+ Logger.log("Completed reload of plugin: #{message.stale}")
40
+ else
41
+ unload_plugins
42
+ load_plugins
43
+ end
44
+ @pipeline << Messages::Internal::SignaturesUpdate.new
45
+ end
28
46
  end
29
47
 
30
48
  # Destroys plugins
@@ -36,32 +54,34 @@ module ModSpox
36
54
  # Loads a plugin
37
55
  def load_plugin(message)
38
56
  begin
39
- path = !message.name ? BotConfig[:userpluginpath] : "#{BotConfig[:userpluginpath]}/#{message.name}"
57
+ path = !message.name ? "#{BotConfig[:userpluginpath]}/#{message.path.gsub(/^.+\//, '')}" : "#{BotConfig[:userpluginpath]}/#{message.name}"
40
58
  FileUtils.copy(message.path, path)
41
- reload_plugins
59
+ do_load(path)
42
60
  @pipeline << Messages::Internal::PluginLoadResponse.new(message.requester, true)
43
61
  Logger.log("Loaded new plugin: #{message.path}", 10)
44
62
  rescue Object => boom
45
63
  Logger.log("Failed to load plugin: #{message.path} Reason: #{boom}", 10)
46
64
  @pipeline << Messages::Internal::PluginLoadResponse.new(message.requester, false)
47
65
  end
66
+ @pipeline << Messages::Internal::SignaturesUpdate.new
48
67
  end
49
68
 
50
69
  # message:: Messages::Internal::PluginUnloadRequest
51
70
  # Unloads a plugin
52
71
  def unload_plugin(message)
53
72
  begin
73
+ do_unload(message.path)
54
74
  unless(message.name.nil?)
55
75
  FileUtils.copy(message.path, "#{BotConfig[:userpluginpath]}/#{message.name}")
56
76
  end
57
77
  FileUtils.remove_file(message.path)
58
- reload_plugins
59
78
  @pipeline << Messages::Internal::PluginUnloadResponse.new(message.requester, true)
60
79
  Logger.log("Unloaded plugin: #{message.path}", 10)
61
80
  rescue Object => boom
62
81
  Logger.log("Failed to unload plugin: #{message.path} Reason: #{boom}", 10)
63
82
  @pipeline << Messages::Internal::PluginUnloadResponse.new(message.requester, false)
64
83
  end
84
+ @pipeline << Messages::Internal::SignaturesUpdate.new
65
85
  end
66
86
 
67
87
  # message:: Messages::Internal::PluginModuleRequest
@@ -86,39 +106,117 @@ module ModSpox
86
106
  # Loads and initializes plugins
87
107
  def load_plugins
88
108
  @pipeline << Messages::Internal::TimerClear.new
89
- [BotConfig[:pluginpath], BotConfig[:userpluginpath]].each{|path|
90
- Dir.new(path).each{|file|
109
+ Models::Signature.destroy_all
110
+ [BotConfig[:pluginpath], BotConfig[:userpluginpath]].each do |path|
111
+ Dir.new(path).each do |file|
91
112
  if(file =~ /^[^\.].+\.rb$/)
92
113
  begin
93
- @plugins_module.module_eval(IO.readlines("#{path}/#{file}").join("\n"))
114
+ do_load("#{path}/#{file}")
94
115
  rescue Object => boom
95
116
  Logger.log("Failed to load file: #{path}/#{file}. Reason: #{boom}")
96
117
  end
97
118
  end
98
- }
99
- }
100
- @plugins_module.constants.each{|const|
101
- klass = @plugins_module.const_get(const)
102
- if(klass < Plugin)
103
- begin
104
- @plugins[const.to_sym] = klass.new(@pipeline)
105
- Logger.log("Initialized new plugin: #{const}", 15)
106
- rescue Object => boom
107
- Logger.log("Failed to initialize plugin #{const}. Reason: #{boom}")
108
- end
109
119
  end
110
- }
120
+ end
111
121
  @pipeline << Messages::Internal::SignaturesUpdate.new
112
122
  end
113
123
 
114
124
  # Destroys plugins
115
125
  def unload_plugins
116
- @plugins.each_pair{|sym, plugin| plugin.destroy; @pipeline.unhook_plugin(plugin)}
117
- @plugins.clear
118
- Models::Signature.delete_all
126
+ @plugins.each_pair do |sym, holder|
127
+ holder.plugin.destroy
128
+ @pipeline.unhook_plugin(holder.plugin)
129
+ end
130
+ Models::Signature.destroy_all
119
131
  @plugins_module = Module.new
120
132
  @pipeline << Messages::Internal::TimerClear.new
121
133
  end
134
+
135
+ # path:: path to plugin file
136
+ # Loads a plugin into the plugin module
137
+ def do_load(path)
138
+ if(File.exists?(path))
139
+ plugins = discover_plugins(path)
140
+ raise PluginMissing.new("Plugin file at: #{path} does not contain a plugin class") if plugins.nil?
141
+ @plugins_module.module_eval(IO.readlines(path).join("\n"))
142
+ begin
143
+ plugins.each do |plugin|
144
+ klass = @plugins_module.const_get(plugin)
145
+ if(@plugins.has_key?(plugin.to_sym))
146
+ @plugins[plugin.to_sym].set_plugin(klass.new(@pipeline))
147
+ else
148
+ @plugins[plugin.to_sym] = PluginHolder.new(klass.new(@pipeline))
149
+ end
150
+ Logger.log("Properly initialized new plugin: #{plugin}", 25)
151
+ end
152
+ Logger.log("All plugins found at: #{path} have been loaded")
153
+ rescue Object => boom
154
+ Logger.log("Plugin loading failed: #{boom}\n#{boom.backtrace.join("\n")}", 25)
155
+ Logger.log("All constants loaded from file: #{path} will now be unloaded")
156
+ do_unload(path)
157
+ end
158
+ else
159
+ raise PluginFileNotFound.new("Failed to find file at: #{path}")
160
+ end
161
+ end
162
+
163
+ # path:: path to plugin file
164
+ # Unloads a plugin and all constants from the plugin module
165
+ def do_unload(path)
166
+ if(File.exists?(path))
167
+ discover_plugins(path).each do |plugin|
168
+ if(@plugins.has_key?(plugin.to_sym))
169
+ @plugins[plugin.to_sym].plugin.destroy
170
+ @pipeline.unhook_plugin(@plugins[plugin.to_sym].plugin)
171
+ @plugins[plugin.to_sym].set_plugin(nil)
172
+ @pipeline << Messages::Internal::TimerClear.new(plugin.to_sym)
173
+ end
174
+ Models::Signature.filter(:plugin => plugin).destroy
175
+ end
176
+ discover_consts(path).each do |const|
177
+ Logger.log("Removing constant: #{const}")
178
+ @plugins_module.send(:remove_const, const)
179
+ end
180
+ Logger.log("Removed all constants found in file: #{path}")
181
+ else
182
+ raise PluginFileNotFound.new("Failed to find file at: #{path}")
183
+ end
184
+ end
185
+
186
+ # path:: path to plugin
187
+ # Find class names of any plugins within the file at given path
188
+ def discover_plugins(path)
189
+ temp = Module.new
190
+ begin
191
+ temp.module_eval(IO.readlines(path).join("\n"))
192
+ klasses = []
193
+ temp.constants.each do |const|
194
+ klass = temp.const_get(const)
195
+ klasses << const if klass < Plugin
196
+ end
197
+ return klasses
198
+ rescue Object => boom
199
+ return nil
200
+ end
201
+ end
202
+
203
+ # path:: path to plugin
204
+ # Find all constant names in given path
205
+ def discover_consts(path)
206
+ temp = Module.new
207
+ begin
208
+ temp.module_eval(IO.readlines(path).join("\n"))
209
+ return temp.constants
210
+ rescue Object => boom
211
+ return nil
212
+ end
213
+ end
214
+
215
+ class PluginMissing < Exceptions::BotException
216
+ end
217
+
218
+ class PluginFileNotFound < Exceptions::BotException
219
+ end
122
220
 
123
221
  end
124
222
 
data/lib/mod_spox/Pool.rb CHANGED
@@ -1,15 +1,20 @@
1
+ ['mod_spox/Logger',
2
+ 'mod_spox/Exceptions',
3
+ 'mod_spox/Monitors',
4
+ 'timeout'].each{|f|require f}
5
+
1
6
  module ModSpox
2
7
 
3
8
  # The Pool class is used to reduce thread creation. It provides
4
- # an easy way to process many objects in an asynchronous manner.
9
+ # an easy way to process many actions in an asynchronous manner.
5
10
  class Pool
6
-
11
+
7
12
  # Action thread is to perform
8
13
  attr_reader :proc
9
14
  # Storage space that the pool will be processing
10
15
  attr_reader :queue
11
16
 
12
- # Create a new Pool
17
+ # Create a new pool
13
18
  def initialize
14
19
  @proc = Proc.new{ run_processor }
15
20
  @queue = PoolQueue.new
@@ -31,10 +36,12 @@ module ModSpox
31
36
  begin
32
37
  processor
33
38
  rescue Object => boom
34
- Logger.log("Pool encountered an error processing code block: #{boom}")
39
+ Logger.log("Pool encountered an error processing code block: #{boom}", 99)
35
40
  end
36
41
  end
37
42
 
43
+ # Action the pool is responsible for. This is the method
44
+ # the child class should override
38
45
  def processor
39
46
  raise Exceptions::NotImplemented.new('Processor method has not been implemented')
40
47
  end
@@ -43,21 +50,30 @@ module ModSpox
43
50
  @@pools = Array.new
44
51
  # Threads running in pool
45
52
  @@threads = Array.new
46
- # Lock for thread safety
47
- @@lock = Mutex.new
53
+ # Schedule lock for thread safety
54
+ @@schedule_lock = Mutex.new
55
+ # Thread creation lock
56
+ @@thread_lock = Mutex.new
48
57
  # Maximum number of seconds a thread may spend waiting for an action
49
58
  @@max_exec_time = 60
59
+ # Maximum number of threads to process Pool
60
+ @@max_threads = 10
61
+ # Maxium wait time (max time for threads to wait for new action to process)
62
+ @@max_wait_time = 20
63
+ # Monitor for threads to wait in
64
+ @@stop_point = Monitors::Timer.new
50
65
  # Informs threads to halt
51
66
  @@kill = false
52
67
 
53
68
  # Adds a new thread to the pool
54
- def Pool.add_thread
55
- @@threads << Thread.new do
56
- until(Pool.max_queue_size < 1 || @@kill) do
69
+ def Pool.add_thread(force=false)
70
+ if(force || @@threads.size < @@max_threads)
71
+ thr = Thread.new do
57
72
  Pool.schedule_thread
58
73
  end
59
- @@threads.delete(Thread.current)
74
+ @@threads << thr
60
75
  end
76
+ @@stop_point.wakeup
61
77
  end
62
78
 
63
79
  # Returns the largest queue size of all available pools
@@ -72,12 +88,15 @@ module ModSpox
72
88
  # Schedules a thread within the pool
73
89
  def Pool.schedule_thread
74
90
  run_pool = nil
75
- @@lock.synchronize do
91
+ @@schedule_lock.synchronize do
76
92
  @@pools.each do |pool|
77
93
  run_pool = pool if (run_pool.nil? && pool.queue.size > 0) || (!run_pool.nil? && (run_pool.queue.size < pool.queue.size))
78
94
  end
79
95
  end
80
- unless(run_pool.nil?)
96
+ if(run_pool.nil?)
97
+ @@threads.delete(Thread.current)
98
+ return
99
+ else
81
100
  begin
82
101
  Timeout::timeout(@@max_exec_time) do
83
102
  run_pool.proc.call
@@ -88,14 +107,23 @@ module ModSpox
88
107
  Logger.log("Thread encountered error processing pool item: #{boom}")
89
108
  end
90
109
  end
110
+ start = Time.now
111
+ @@stop_point.wait(@@max_wait_time) if Pool.max_queue_size < 1
112
+ if((Time.now - start).to_i < @@max_wait_time)
113
+ Pool.schedule_thread
114
+ else
115
+ @@threads.delete(Thread.current)
116
+ @@threads.each do |thread|
117
+ @@threads.delete(thread) unless thread.alive?
118
+ end
119
+ end
91
120
  end
92
121
 
93
122
  # Forces sleeping threads to wake up
94
123
  def Pool.process
95
- sleep(0.1)
96
- Pool.add_thread if Pool.max_queue_size > 0
97
- Logger.log("Current number of threads: #{@@threads.size}")
98
- Logger.log("Total number of threads in use system-wide: #{Thread.list.size}")
124
+ @@thread_lock.synchronize do
125
+ Pool.add_thread if Pool.max_queue_size > 0
126
+ end
99
127
  end
100
128
 
101
129
  # Adds a Pool to the master Pool list
@@ -106,8 +134,12 @@ module ModSpox
106
134
  # Removes a Pool from the master Pool list
107
135
  def Pool.remove_pool(pool)
108
136
  @@pools.delete(pool)
109
- sleep(0.1)
110
- @@threads.each{|t| t.kill} if @@pools.empty?
137
+ if(@@pools.empty?)
138
+ @@kill = true
139
+ @@stop_point.wakeup
140
+ sleep(0.1)
141
+ @@threads.each{|t| t.kill if t.alive?}
142
+ end
111
143
  end
112
144
 
113
145
  # Modified Queue to properly interact with Pool
@@ -119,17 +151,14 @@ module ModSpox
119
151
  end
120
152
 
121
153
  def <<(val)
122
- @lock.synchronize do
123
- super
124
- Pool.process
125
- end
154
+ push(val)
126
155
  end
127
156
 
128
157
  def push(val)
129
158
  @lock.synchronize do
130
159
  super
131
- Pool.process
132
160
  end
161
+ Pool.process
133
162
  end
134
163
 
135
164
  def pop
@@ -1,3 +1,10 @@
1
+ ['mod_spox/Logger',
2
+ 'mod_spox/Pool',
3
+ 'mod_spox/Exceptions',
4
+ 'mod_spox/messages/Messages',
5
+ 'mod_spox/models/Models',
6
+ 'mod_spox/Pipeline'].each{|f|require f}
7
+
1
8
  module ModSpox
2
9
 
3
10
  class Socket < Pool
@@ -34,6 +41,7 @@ module ModSpox
34
41
  @check_burst = 0
35
42
  @pause = false
36
43
  @sendq = @queue
44
+ @lock = Mutex.new
37
45
  start_pool
38
46
  end
39
47
 
@@ -96,7 +104,7 @@ module ModSpox
96
104
  # Retrieves a string from the server
97
105
  def read
98
106
  message = @socket.gets
99
- if(message.nil?) # || message =~ /^ERROR/)
107
+ if(message.nil? || @socket.closed?) # || message =~ /^ERROR/)
100
108
  @pipeline << Messages::Internal::Disconnected.new
101
109
  shutdown
102
110
  server = Models::Server.find_or_create(:host => @server, :port => @port)
@@ -118,15 +126,18 @@ module ModSpox
118
126
 
119
127
  # Starts the thread for sending messages to the server
120
128
  def processor
121
- write(@sendq.pop)
122
- if((Time.now.to_i - @time_check) > @burst_in)
123
- @time_check = nil
124
- @check_burst = 0
125
- elsif((Time.now.to_i - @time_check) >= @burst_in && @check_burst >= @burst)
126
- sleep(@delay)
127
- @time_check = nil
128
- @check_burst = 0
129
- end
129
+ @lock.synchronize do
130
+ write(@sendq.pop)
131
+ if((Time.now.to_i - @time_check) > @burst_in)
132
+ @time_check = nil
133
+ @check_burst = 0
134
+ elsif((Time.now.to_i - @time_check) <= @burst_in && @check_burst >= @burst)
135
+ Logger.log("Burst limit hit. Output paused for: #{@delay} seconds", 70)
136
+ sleep(@delay)
137
+ @time_check = nil
138
+ @check_burst = 0
139
+ end
140
+ end
130
141
  end
131
142
 
132
143
  # Starts the thread for reading messages from the server