mod_spox 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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