mod_spox 0.0.1

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 (191) hide show
  1. data/CHANGELOG +2 -0
  2. data/INSTALL +9 -0
  3. data/README +33 -0
  4. data/bin/mod_spox +60 -0
  5. data/data/mod_spox/extras/Tester.rb +14 -0
  6. data/data/mod_spox/plugins/Authenticator.rb +245 -0
  7. data/data/mod_spox/plugins/BotNick.rb +18 -0
  8. data/data/mod_spox/plugins/Initializer.rb +41 -0
  9. data/data/mod_spox/plugins/Joiner.rb +13 -0
  10. data/data/mod_spox/plugins/Parter.rb +22 -0
  11. data/data/mod_spox/plugins/PluginLoader.rb +136 -0
  12. data/data/mod_spox/plugins/Ponger.rb +14 -0
  13. data/data/mod_spox/plugins/Quitter.rb +14 -0
  14. data/lib/mod_spox/Action.rb +73 -0
  15. data/lib/mod_spox/BaseConfig.rb +48 -0
  16. data/lib/mod_spox/Bot.rb +472 -0
  17. data/lib/mod_spox/BotConfig.rb +54 -0
  18. data/lib/mod_spox/ConfigurationWizard.rb +178 -0
  19. data/lib/mod_spox/Database.rb +25 -0
  20. data/lib/mod_spox/Exceptions.rb +35 -0
  21. data/lib/mod_spox/Helpers.rb +35 -0
  22. data/lib/mod_spox/Loader.rb +79 -0
  23. data/lib/mod_spox/Logger.rb +31 -0
  24. data/lib/mod_spox/MessageFactory.rb +73 -0
  25. data/lib/mod_spox/Monitors.rb +59 -0
  26. data/lib/mod_spox/Pipeline.rb +148 -0
  27. data/lib/mod_spox/Plugin.rb +18 -0
  28. data/lib/mod_spox/PluginManager.rb +105 -0
  29. data/lib/mod_spox/Pool.rb +50 -0
  30. data/lib/mod_spox/Socket.rb +171 -0
  31. data/lib/mod_spox/Timer.rb +138 -0
  32. data/lib/mod_spox/handlers/BadNick.rb +16 -0
  33. data/lib/mod_spox/handlers/Bounce.rb +15 -0
  34. data/lib/mod_spox/handlers/Created.rb +16 -0
  35. data/lib/mod_spox/handlers/Handler.rb +31 -0
  36. data/lib/mod_spox/handlers/Invite.rb +19 -0
  37. data/lib/mod_spox/handlers/Join.rb +30 -0
  38. data/lib/mod_spox/handlers/Kick.rb +24 -0
  39. data/lib/mod_spox/handlers/LuserChannels.rb +16 -0
  40. data/lib/mod_spox/handlers/LuserClient.rb +16 -0
  41. data/lib/mod_spox/handlers/LuserMe.rb +14 -0
  42. data/lib/mod_spox/handlers/LuserOp.rb +16 -0
  43. data/lib/mod_spox/handlers/LuserUnknown.rb +16 -0
  44. data/lib/mod_spox/handlers/Mode.rb +47 -0
  45. data/lib/mod_spox/handlers/Motd.rb +30 -0
  46. data/lib/mod_spox/handlers/MyInfo.rb +21 -0
  47. data/lib/mod_spox/handlers/Names.rb +54 -0
  48. data/lib/mod_spox/handlers/Nick.rb +24 -0
  49. data/lib/mod_spox/handlers/NickInUse.rb +16 -0
  50. data/lib/mod_spox/handlers/Notice.rb +32 -0
  51. data/lib/mod_spox/handlers/Part.rb +19 -0
  52. data/lib/mod_spox/handlers/Ping.rb +16 -0
  53. data/lib/mod_spox/handlers/Pong.rb +16 -0
  54. data/lib/mod_spox/handlers/Privmsg.rb +27 -0
  55. data/lib/mod_spox/handlers/Quit.rb +21 -0
  56. data/lib/mod_spox/handlers/Topic.rb +29 -0
  57. data/lib/mod_spox/handlers/Welcome.rb +34 -0
  58. data/lib/mod_spox/handlers/Who.rb +60 -0
  59. data/lib/mod_spox/handlers/Whois.rb +63 -0
  60. data/lib/mod_spox/handlers/YourHost.rb +17 -0
  61. data/lib/mod_spox/messages/incoming/BadNick.rb +15 -0
  62. data/lib/mod_spox/messages/incoming/Bounce.rb +17 -0
  63. data/lib/mod_spox/messages/incoming/Created.rb +14 -0
  64. data/lib/mod_spox/messages/incoming/Invite.rb +20 -0
  65. data/lib/mod_spox/messages/incoming/Join.rb +18 -0
  66. data/lib/mod_spox/messages/incoming/Kick.rb +25 -0
  67. data/lib/mod_spox/messages/incoming/LuserChannels.rb +14 -0
  68. data/lib/mod_spox/messages/incoming/LuserClient.rb +23 -0
  69. data/lib/mod_spox/messages/incoming/LuserMe.rb +17 -0
  70. data/lib/mod_spox/messages/incoming/LuserOp.rb +14 -0
  71. data/lib/mod_spox/messages/incoming/LuserUnknown.rb +14 -0
  72. data/lib/mod_spox/messages/incoming/Message.rb +22 -0
  73. data/lib/mod_spox/messages/incoming/Mode.rb +41 -0
  74. data/lib/mod_spox/messages/incoming/Motd.rb +17 -0
  75. data/lib/mod_spox/messages/incoming/MyInfo.rb +23 -0
  76. data/lib/mod_spox/messages/incoming/Names.rb +23 -0
  77. data/lib/mod_spox/messages/incoming/Nick.rb +25 -0
  78. data/lib/mod_spox/messages/incoming/NickInUse.rb +14 -0
  79. data/lib/mod_spox/messages/incoming/Notice.rb +8 -0
  80. data/lib/mod_spox/messages/incoming/Part.rb +20 -0
  81. data/lib/mod_spox/messages/incoming/Ping.rb +17 -0
  82. data/lib/mod_spox/messages/incoming/Pong.rb +8 -0
  83. data/lib/mod_spox/messages/incoming/Privmsg.rb +64 -0
  84. data/lib/mod_spox/messages/incoming/Quit.rb +17 -0
  85. data/lib/mod_spox/messages/incoming/Topic.rb +20 -0
  86. data/lib/mod_spox/messages/incoming/TopicInfo.rb +20 -0
  87. data/lib/mod_spox/messages/incoming/Welcome.rb +26 -0
  88. data/lib/mod_spox/messages/incoming/Who.rb +17 -0
  89. data/lib/mod_spox/messages/incoming/Whois.rb +47 -0
  90. data/lib/mod_spox/messages/incoming/YourHost.rb +17 -0
  91. data/lib/mod_spox/messages/internal/BotInitialized.rb +11 -0
  92. data/lib/mod_spox/messages/internal/ChangeNick.rb +15 -0
  93. data/lib/mod_spox/messages/internal/Connected.rb +20 -0
  94. data/lib/mod_spox/messages/internal/ConnectionFailed.rb +23 -0
  95. data/lib/mod_spox/messages/internal/Disconnected.rb +8 -0
  96. data/lib/mod_spox/messages/internal/Disconnecting.rb +8 -0
  97. data/lib/mod_spox/messages/internal/EstablishConnection.rb +22 -0
  98. data/lib/mod_spox/messages/internal/HaltBot.rb +8 -0
  99. data/lib/mod_spox/messages/internal/NickRequest.rb +8 -0
  100. data/lib/mod_spox/messages/internal/NickResponse.rb +14 -0
  101. data/lib/mod_spox/messages/internal/PluginLoadRequest.rb +20 -0
  102. data/lib/mod_spox/messages/internal/PluginLoadResponse.rb +16 -0
  103. data/lib/mod_spox/messages/internal/PluginModuleRequest.rb +13 -0
  104. data/lib/mod_spox/messages/internal/PluginModuleResponse.rb +17 -0
  105. data/lib/mod_spox/messages/internal/PluginReload.rb +8 -0
  106. data/lib/mod_spox/messages/internal/PluginRequest.rb +17 -0
  107. data/lib/mod_spox/messages/internal/PluginResponse.rb +20 -0
  108. data/lib/mod_spox/messages/internal/PluginUnloadRequest.rb +8 -0
  109. data/lib/mod_spox/messages/internal/PluginUnloadResponse.rb +8 -0
  110. data/lib/mod_spox/messages/internal/Request.rb +15 -0
  111. data/lib/mod_spox/messages/internal/Response.rb +15 -0
  112. data/lib/mod_spox/messages/internal/Shutdown.rb +8 -0
  113. data/lib/mod_spox/messages/internal/SignaturesUpdate.rb +8 -0
  114. data/lib/mod_spox/messages/internal/StatusRequest.rb +9 -0
  115. data/lib/mod_spox/messages/internal/StatusResponse.rb +17 -0
  116. data/lib/mod_spox/messages/internal/TimerAdd.rb +27 -0
  117. data/lib/mod_spox/messages/internal/TimerClear.rb +8 -0
  118. data/lib/mod_spox/messages/internal/TimerRemove.rb +15 -0
  119. data/lib/mod_spox/messages/internal/TimerResponse.rb +26 -0
  120. data/lib/mod_spox/messages/internal/TriggersUpdate.rb +8 -0
  121. data/lib/mod_spox/messages/outgoing/Admin.rb +15 -0
  122. data/lib/mod_spox/messages/outgoing/Away.rb +10 -0
  123. data/lib/mod_spox/messages/outgoing/ChannelMode.rb +25 -0
  124. data/lib/mod_spox/messages/outgoing/Connect.rb +24 -0
  125. data/lib/mod_spox/messages/outgoing/Die.rb +9 -0
  126. data/lib/mod_spox/messages/outgoing/Info.rb +15 -0
  127. data/lib/mod_spox/messages/outgoing/Invite.rb +19 -0
  128. data/lib/mod_spox/messages/outgoing/Ison.rb +15 -0
  129. data/lib/mod_spox/messages/outgoing/Join.rb +19 -0
  130. data/lib/mod_spox/messages/outgoing/Kick.rb +23 -0
  131. data/lib/mod_spox/messages/outgoing/Kill.rb +19 -0
  132. data/lib/mod_spox/messages/outgoing/Links.rb +19 -0
  133. data/lib/mod_spox/messages/outgoing/List.rb +19 -0
  134. data/lib/mod_spox/messages/outgoing/Lusers.rb +19 -0
  135. data/lib/mod_spox/messages/outgoing/Motd.rb +16 -0
  136. data/lib/mod_spox/messages/outgoing/Names.rb +20 -0
  137. data/lib/mod_spox/messages/outgoing/Nick.rb +16 -0
  138. data/lib/mod_spox/messages/outgoing/Notice.rb +11 -0
  139. data/lib/mod_spox/messages/outgoing/Oper.rb +19 -0
  140. data/lib/mod_spox/messages/outgoing/Part.rb +19 -0
  141. data/lib/mod_spox/messages/outgoing/Pass.rb +16 -0
  142. data/lib/mod_spox/messages/outgoing/Ping.rb +10 -0
  143. data/lib/mod_spox/messages/outgoing/Pong.rb +17 -0
  144. data/lib/mod_spox/messages/outgoing/Privmsg.rb +19 -0
  145. data/lib/mod_spox/messages/outgoing/Quit.rb +10 -0
  146. data/lib/mod_spox/messages/outgoing/Rehash.rb +9 -0
  147. data/lib/mod_spox/messages/outgoing/Restart.rb +9 -0
  148. data/lib/mod_spox/messages/outgoing/ServList.rb +19 -0
  149. data/lib/mod_spox/messages/outgoing/Simple.rb +12 -0
  150. data/lib/mod_spox/messages/outgoing/Squery.rb +19 -0
  151. data/lib/mod_spox/messages/outgoing/Squit.rb +19 -0
  152. data/lib/mod_spox/messages/outgoing/Stats.rb +18 -0
  153. data/lib/mod_spox/messages/outgoing/Summon.rb +23 -0
  154. data/lib/mod_spox/messages/outgoing/Time.rb +15 -0
  155. data/lib/mod_spox/messages/outgoing/Topic.rb +19 -0
  156. data/lib/mod_spox/messages/outgoing/Trace.rb +15 -0
  157. data/lib/mod_spox/messages/outgoing/Unaway.rb +9 -0
  158. data/lib/mod_spox/messages/outgoing/User.rb +23 -0
  159. data/lib/mod_spox/messages/outgoing/UserHost.rb +15 -0
  160. data/lib/mod_spox/messages/outgoing/UserMode.rb +19 -0
  161. data/lib/mod_spox/messages/outgoing/Users.rb +15 -0
  162. data/lib/mod_spox/messages/outgoing/Version.rb +16 -0
  163. data/lib/mod_spox/messages/outgoing/Who.rb +19 -0
  164. data/lib/mod_spox/messages/outgoing/WhoWas.rb +23 -0
  165. data/lib/mod_spox/messages/outgoing/Whois.rb +19 -0
  166. data/lib/mod_spox/migration/001_create_auths.rb +13 -0
  167. data/lib/mod_spox/migration/001_create_channel.rb +13 -0
  168. data/lib/mod_spox/migration/001_create_channel_modes.rb +13 -0
  169. data/lib/mod_spox/migration/001_create_config.rb +13 -0
  170. data/lib/mod_spox/migration/001_create_nick_channels.rb +13 -0
  171. data/lib/mod_spox/migration/001_create_nick_modes.rb +13 -0
  172. data/lib/mod_spox/migration/001_create_nicks.rb +13 -0
  173. data/lib/mod_spox/migration/001_create_servers.rb +13 -0
  174. data/lib/mod_spox/migration/001_create_settings.rb +13 -0
  175. data/lib/mod_spox/migration/001_create_signatures.rb +13 -0
  176. data/lib/mod_spox/migration/001_create_triggers.rb +13 -0
  177. data/lib/mod_spox/models/Auth.rb +79 -0
  178. data/lib/mod_spox/models/AuthGroup.rb +15 -0
  179. data/lib/mod_spox/models/Channel.rb +47 -0
  180. data/lib/mod_spox/models/ChannelMode.rb +14 -0
  181. data/lib/mod_spox/models/Config.rb +31 -0
  182. data/lib/mod_spox/models/Group.rb +13 -0
  183. data/lib/mod_spox/models/Nick.rb +110 -0
  184. data/lib/mod_spox/models/NickChannel.rb +43 -0
  185. data/lib/mod_spox/models/NickMode.rb +18 -0
  186. data/lib/mod_spox/models/Server.rb +12 -0
  187. data/lib/mod_spox/models/Setting.rb +40 -0
  188. data/lib/mod_spox/models/Signature.rb +30 -0
  189. data/lib/mod_spox/models/Trigger.rb +9 -0
  190. data/lib/mod_spox/rfc2812.rb +171 -0
  191. metadata +261 -0
@@ -0,0 +1,148 @@
1
+ module ModSpox
2
+
3
+ class Pipeline < Pool
4
+
5
+ # procs:: number of threads running in Pool
6
+ # Create a new Pipeline
7
+ def initialize(procs=3)
8
+ super(procs)
9
+ @messageq = Queue.new
10
+ Logger.log("Created queue #{@messageq} in pipeline", 10)
11
+ @hooks = Hash.new
12
+ @plugins = Hash.new
13
+ populate_triggers
14
+ populate_signatures
15
+ hook(self, :populate_triggers, :Internal_TriggersUpdate)
16
+ hook(self, :populate_signatures, :Internal_SignaturesUpdate)
17
+ start_pool
18
+ end
19
+
20
+ # message:: Message to send down pipeline
21
+ # Queues a message to send down pipeline
22
+ def <<(message)
23
+ Logger.log("Message added to pipeline queue: #{message}", 5)
24
+ @messageq << message
25
+ end
26
+
27
+ # plugin:: Plugin to hook to pipeline
28
+ # Hooks a plugin into the pipeline so it can be called
29
+ # directly when it matches a trigger
30
+ def hook_plugin(plugin)
31
+ Logger.log("Plugin #{plugin.name} hooking into pipeline", 10)
32
+ @plugins[plugin.name.to_sym] = plugin
33
+ end
34
+
35
+ # plugin:: Plugin to unhook from pipeline
36
+ # Unhooks a plugin from the pipeline (This does not unhook
37
+ # it from the standard hooks)
38
+ def unhook_plugin(plugin)
39
+ Logger.log("Plugin #{plugin.name} unhooking from plugin", 10)
40
+ @plugins.delete(plugin.name.to_sym)
41
+ @hooks.each_pair do |type, things|
42
+ things.delete(plugin.name.to_sym) if things.has_key?(plugin.name.to_sym)
43
+ end
44
+ end
45
+
46
+ # plugin:: Plugin to hook to pipeline
47
+ # method:: Plugin method pipeline should call to process message
48
+ # type:: Type of message the plugin wants to process
49
+ # Hooks a plugin into the pipeline for a specific type of message
50
+ def hook(object, method, type)
51
+ Logger.log("Object #{object.class.to_s} hooking into messages of type: #{type}", 10)
52
+ type = type.gsub(/::/, '_').to_sym unless type.is_a?(Symbol)
53
+ method = method.to_sym unless method.is_a?(Symbol)
54
+ name = object.class.to_s.gsub(/^.+:/, '')
55
+ @hooks[type] = Hash.new unless @hooks.has_key?(type)
56
+ @hooks[type][name.to_sym] = Array.new unless @hooks[type][name.to_sym].is_a?(Array)
57
+ @hooks[type][name.to_sym] << {:object => object, :method => method}
58
+ end
59
+
60
+ # plugin:: Plugin to unhook from pipeline
61
+ # type:: Type of message the plugin no longer wants to process
62
+ # This will remove the hook a plugin has for a specific message type
63
+ def unhook(object, method, type)
64
+ Logger.log("Object #{object.class.to_s} unhooking from messages of type: #{type}", 10)
65
+ type = type.gsub(/::/, '_').to_sym unless type.is_a?(Symbol)
66
+ name = object.class.gsub(/^.+:/, '').to_sym
67
+ raise Exception::InvalidValue.new("Unknown hook type given: #{type.to_s}") unless @hooks.has_key?(type)
68
+ raise Exception::InvalidValue.new("Unknown object hooked: #{name.to_s}") unless @hooks[type].has_key?(name)
69
+ @hooks[type][name].each{|hook|
70
+ @hooks[type][name].delete(hook) if hook[:method] == method
71
+ }
72
+ @hooks[type].delete(name) if @hooks[type][name].empty
73
+ @hooks.delete(type) if @hooks[type].empty?
74
+ end
75
+
76
+ # Clears all hooks from the pipeline (Commonly used when reloading plugins)
77
+ def clear
78
+ Logger.log("All hooks have been cleared from pipeline", 10)
79
+ @hooks.clear
80
+ @plugins.clear
81
+ end
82
+
83
+ def populate_triggers(m=nil)
84
+ @triggers = []
85
+ Models::Trigger.filter(:active => true).each{|t|@triggers << t.trigger}
86
+ end
87
+
88
+ def populate_signatures(m=nil)
89
+ @signatures = []
90
+ Models::Signature.all.each{|s|@signatures << s}
91
+ end
92
+
93
+ private
94
+
95
+ # Processes messages
96
+ def processor
97
+ begin
98
+ message = @messageq.pop
99
+ Logger.log("Pipeline is processing a message: #{message}", 10)
100
+ parse(message)
101
+ type = message.class.to_s.gsub(/^ModSpox::Messages::/, '').gsub(/::/, '_').to_sym
102
+ mod = type.to_s.gsub(/_.+$/, '').to_sym
103
+ Logger.log("Pipeline determines that #{message} is of type: #{type}", 10)
104
+ [type, mod, :all].each{|type|
105
+ if(@hooks.has_key?(type))
106
+ @hooks[type].each_value{|objects|
107
+ objects.each{|v| v[:object].send(v[:method].to_s, message) }
108
+ }
109
+ end
110
+ }
111
+ rescue Object => boom
112
+ Logger.log("Pipeline encountered an exception while processing a message: #{boom}\n#{boom.backtrace.join("\n")}", 10)
113
+ end
114
+ end
115
+
116
+ # message:: Message to parse
117
+ # This will parse a message to see if it matches any valid
118
+ # trigger signatures. If matches are found, they will be sent
119
+ # to the proper plugin for processing
120
+ def parse(message)
121
+ return unless message.kind_of?(Messages::Incoming::Privmsg) || message.kind_of?(Messages::Incoming::Notice)
122
+ trigger = nil
123
+ @triggers.each{|t| trigger = t if message.message =~ /^#{t}/}
124
+ if(!trigger.nil? || message.addressed?)
125
+ Logger.log("Message has matched against a known trigger", 15)
126
+ @signatures.each{|sig|
127
+ Logger.log("Matching against: #{trigger}#{sig.signature}")
128
+ res = message.message.scan(/^#{trigger}#{sig.signature}$/)
129
+ if(res.size > 0)
130
+ next unless message.source.auth_groups.include?(sig.group) || sig.group.nil?
131
+ params = Hash.new
132
+ sig.params.size.times do |i|
133
+ params[sig.params[i - 1].to_sym] = res[0][i]
134
+ Logger.log("Signature params: #{sig.params[i - 1].to_sym} = #{res[0][i]}")
135
+ end
136
+ if(@plugins.has_key?(sig.plugin.to_sym))
137
+ @plugins[sig.plugin.to_sym].send(sig.values[:method], message, params)
138
+ end
139
+ end
140
+ }
141
+ else
142
+ Logger.log("Message failed to match any known trigger", 15)
143
+ end
144
+ end
145
+
146
+ end
147
+
148
+ end
@@ -0,0 +1,18 @@
1
+ module ModSpox
2
+ class Plugin
3
+ def initialize(pipeline)
4
+ @pipeline = pipeline
5
+ @pipeline.hook_plugin(self)
6
+ end
7
+
8
+ # Called before the object is destroyed by the ModSpox::PluginManager
9
+ def destroy
10
+ Logger.log("Destroy method for plugin #{name} has not been defined.", 15)
11
+ end
12
+
13
+ # Returns the name of the class
14
+ def name
15
+ self.class.name.to_s.gsub(/^.+:/, '')
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,105 @@
1
+ module ModSpox
2
+
3
+ class PluginManager
4
+
5
+ # Hash of plugins. Defined by class name symbol (i.e. Trivia class: plugins[:Trivia])
6
+ attr_reader :plugins
7
+
8
+ # pipeline:: Pipeline for messages
9
+ # Create new PluginManager
10
+ def initialize(pipeline)
11
+ @plugins = Hash.new
12
+ @pipeline = pipeline
13
+ @pipeline.hook(self, :load_plugin, :Internal_PluginLoadRequest)
14
+ @pipeline.hook(self, :unload_plugin, :Internal_PluginUnloadRequest)
15
+ @pipeline.hook(self, :reload_plugins, :Internal_PluginReload)
16
+ @pipeline.hook(self, :send_modules, :Internal_PluginModuleRequest)
17
+ @plugins_module = Module.new
18
+ load_plugins
19
+ end
20
+
21
+ # message:: Messages::Internal::PluginReload
22
+ # Destroys and reinitializes plugins
23
+ def reload_plugins(mesasge=nil)
24
+ unload_plugins
25
+ load_plugins
26
+ end
27
+
28
+ # Destroys plugins
29
+ def destroy_plugins
30
+ unload_plugins
31
+ end
32
+
33
+ # message:: Messages::Internal::PluginLoadRequest
34
+ # Loads a plugin
35
+ def load_plugin(message)
36
+ begin
37
+ Logger.log("THE MESSAGE NAME IS: #{message.name}")
38
+ path = message.name.nil? ? BotConfig[:userpluginpath] : "#{BotConfig[:userpluginpath]}/#{message.name}"
39
+ File.copy(message.path, path)
40
+ reload_plugins
41
+ @pipeline << Messages::Internal::PluginLoadResponse.new(message.requester, true)
42
+ Logger.log("Loaded new plugin: #{message.path}", 10)
43
+ rescue Object => boom
44
+ Logger.log("Failed to load plugin: #{message.path}", 10)
45
+ @pipeline << Messages::Internal::PluginLoadResponse.new(message.requester, false)
46
+ end
47
+ end
48
+
49
+ # message:: Messages::Internal::PluginUnloadRequest
50
+ # Unloads a plugin
51
+ def unload_plugin(message)
52
+ begin
53
+ unless(message.name.nil?)
54
+ File.copy(message.path, "#{BotConfig[:userpluginpath]}/#{message.name}")
55
+ end
56
+ File.unlink(message.path)
57
+ reload_plugins
58
+ @pipeline << Messages::Internal::PluginUnloadResponse.new(message.requester, true)
59
+ Logger.log("Unloaded plugin: #{message.path}", 10)
60
+ rescue Object => boom
61
+ Logger.log("Failed to unload plugin: #{message.path} Reason: #{boom}", 10)
62
+ @pipeline << Messages::Internal::PluginUnloadResponse.new(message.requester, false)
63
+ end
64
+ end
65
+
66
+ # message:: Messages::Internal::PluginModuleRequest
67
+ # Sends the plugins module to the requester
68
+ def send_modules(message)
69
+ @pipeline << Messages::Internal::PluginModuleResponse.new(message.requester, @plugins_module)
70
+ end
71
+
72
+ private
73
+
74
+ # Loads and initializes plugins
75
+ def load_plugins
76
+ @pipeline << Messages::Internal::TimerClear.new
77
+ [BotConfig[:pluginpath], BotConfig[:userpluginpath]].each{|path|
78
+ Dir.new(path).each{|file|
79
+ if(file =~ /^[^\.].+\.rb$/)
80
+ @plugins_module.module_eval(IO.readlines("#{path}/#{file}").join("\n"))
81
+ end
82
+ }
83
+ }
84
+ @plugins_module.constants.each{|const|
85
+ klass = @plugins_module.const_get(const)
86
+ if(klass < Plugin)
87
+ @plugins[const.to_sym] = klass.new(@pipeline)
88
+ Logger.log("Initialized new plugin: #{const}", 15)
89
+ end
90
+ }
91
+ @pipeline << Messages::Internal::SignaturesUpdate.new
92
+ end
93
+
94
+ # Destroys plugins
95
+ def unload_plugins
96
+ @plugins.each_pair{|sym, plugin| plugin.destroy; @pipeline.unhook_plugin(plugin)}
97
+ @plugins.clear
98
+ Models::Signature.delete_all
99
+ @plugins_module = Module.new
100
+ @pipeline << Messages::Internal::TimerClear.new
101
+ end
102
+
103
+ end
104
+
105
+ end
@@ -0,0 +1,50 @@
1
+ module ModSpox
2
+
3
+ # The Pool class is used to reduce thread creation. When
4
+ # used in conjuntion with a PoolQueue, it provides an easy
5
+ # way to process many objects in an asynchronise manner
6
+ class Pool
7
+
8
+ # num_procs:: Number of threads to use
9
+ # Create a new Pool
10
+ def initialize(num_procs=2)
11
+ @num_threads = num_procs
12
+ @threads = Array.new
13
+ @kill = false
14
+ end
15
+
16
+ # Stop all the running threads
17
+ def destroy
18
+ @kill = true
19
+ @threads.each{|t|
20
+ Logger.log("Shutting down thread: #{t} in #{self.class.to_s}", 10)
21
+ t.exit
22
+ }
23
+ sleep(0.1)
24
+ end
25
+
26
+ # Starts the pool
27
+ def start_pool
28
+ @num_threads.times do
29
+ @threads << Thread.new{
30
+ until @kill do
31
+ processor
32
+ end
33
+ }
34
+ end
35
+ end
36
+
37
+ # Method the pool uses to do stuff.
38
+ # (It is important to note that using this can
39
+ # very easily eat up all your CPU time. The Processor
40
+ # method must yield at some point, otherwise it will
41
+ # just continue to loop, even if it is doing nothing.
42
+ # This is the reason for the PoolQueue as this Pool
43
+ # was created as a way to quickly process messages)
44
+ def processor
45
+ raise Exceptions::NotImplemented.new('Processor method has not been implemented')
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,171 @@
1
+ module ModSpox
2
+
3
+ class Socket
4
+
5
+ attr_reader :sent
6
+ attr_reader :received
7
+ attr_reader :burst
8
+ attr_reader :burst_in
9
+ attr_reader :delay
10
+ attr_reader :server
11
+ attr_reader :port
12
+
13
+ # factory:: MessageFactory to parse messages
14
+ # server:: Server to connect to
15
+ # port:: Port number to connect to
16
+ # delay:: Number of seconds to delay between bursts
17
+ # burst_in:: Number of seconds allowed to burst
18
+ # burst:: Number of lines allowed to be sent within the burst_in time limit
19
+ # Create a new Socket
20
+ def initialize(bot, server, port, delay=2, burst_in=2, burst=4)
21
+ @factory = bot.factory
22
+ @pipeline = bot.pipeline
23
+ @server = server
24
+ @port = port.to_i
25
+ @sent = 0
26
+ @received = 0
27
+ @delay = delay.to_f > 0 ? delay.to_f : 2.0
28
+ @burst = burst.to_i > 0 ? burst.to_i : 4
29
+ @burst_in = 2
30
+ @kill = false
31
+ @reader_thread = nil
32
+ @writer_thread = nil
33
+ @time_check = nil
34
+ @check_burst = 0
35
+ @pause = false
36
+ end
37
+
38
+ # Connects to the IRC server
39
+ def connect
40
+ Logger.log("Establishing connection to #{@server}:#{@port}", 10)
41
+ @socket = TCPSocket.new(@server, @port)
42
+ server = Models::Server.find_or_create(:host => @server, :port => @port)
43
+ server.connected = true
44
+ server.save
45
+ @sendq = Queue.new
46
+ Logger.log("Created new send queue: #{@sendq}", 10)
47
+ spooler
48
+ reader
49
+ end
50
+
51
+ # new_delay:: Seconds to delay between bursts
52
+ # Resets the delay
53
+ def delay=(new_delay)
54
+ raise Exceptions::InvalidValue('Send delay must be a positive number') unless new_delay.to_f > 0
55
+ @delay = new_delay.to_f
56
+ end
57
+
58
+ # new_burst:: Number of lines allowed in burst
59
+ # Resets the burst
60
+ def burst=(new_burst)
61
+ raise Exceptions::InvalidValue('Burst value must be a positive number') unless new_busrt.to_i > 0
62
+ @burst = new_burst
63
+ end
64
+
65
+ # new_burst_in:: Number of seconds allowed to burst
66
+ # Resets the burst_in
67
+ def burst_in=(new_burst_in)
68
+ raise Exceptions::InvalidValue('Burst in value must be positive') unless new_burst_in.to_i > 0
69
+ @burst_in = new_burst_in
70
+ end
71
+
72
+ # message:: String to send to server
73
+ # Sends a string to the IRC server
74
+ def puts(message)
75
+ write(message)
76
+ end
77
+
78
+ # message:: String to send to server
79
+ # Sends a string to the IRC server
80
+ def write(message)
81
+ return if message.nil?
82
+ Logger.log("<< #{message}", 5)
83
+ @socket.send(message + "\n", 0)
84
+ @last_send = Time.new
85
+ @sent += 1
86
+ @check_burst += 1
87
+ @check_time = Time.now if @check_time.nil?
88
+ end
89
+
90
+ # Retrieves a string from the server
91
+ def gets
92
+ read
93
+ end
94
+
95
+ # Retrieves a string from the server
96
+ def read
97
+ message = @socket.gets
98
+ if(message.nil?)
99
+ @pipeline << Messages::Internal::Disconnected.new
100
+ shutdown
101
+ server = Models::Server.find_or_create(:host => @server, :port => @port)
102
+ server.connected = false
103
+ server.save
104
+ return
105
+ end
106
+ Logger.log(">> #{message}", 5)
107
+ @received += 1
108
+ message.strip!
109
+ return message
110
+ end
111
+
112
+ # message:: String to be sent to server
113
+ # Queues a message up to be sent to the IRC server
114
+ def <<(message)
115
+ queue(message)
116
+ end
117
+
118
+ # message:: String to be sent to server
119
+ # Queues a message up to be sent to the IRC server
120
+ def queue(message)
121
+ @sendq << message
122
+ end
123
+
124
+ # Starts the thread for sending messages to the server
125
+ def spooler
126
+ @writer_thread = Thread.new{
127
+ until @kill do
128
+ write(@sendq.pop)
129
+ if((Time.now - @check_time) > @burst_in && @check_burst > @burst)
130
+ sleep(@delay)
131
+ @check_time = nil
132
+ @check_burst = 0
133
+ elsif((Time.now - @check_time) > @burst_in && @check_burst < @burst)
134
+ @check_time = nil
135
+ @check_burst = 0
136
+ end
137
+ end
138
+ }
139
+ end
140
+
141
+ # Starts the thread for reading messages from the server
142
+ def reader
143
+ @reader_thread = Thread.new{
144
+ until @kill do
145
+ Kernel.select([@socket], nil, nil, nil)
146
+ @factory << read
147
+ end
148
+ @sendq.clear
149
+ }
150
+ end
151
+
152
+ # restart:: Reconnect after closing connection
153
+ # Closes connection to IRC server
154
+ def shutdown(restart=false)
155
+ @kill = true
156
+ @reader_thread.join(0.1)
157
+ @reader_thread.kill if @reader_thread.alive?
158
+ @writer_thread.join(0.1)
159
+ @writer_thread.kill if @writer_thread.alive?
160
+ @reader_thread = nil
161
+ @writer_thread = nil
162
+ @socket.close
163
+ server = Models::Server.find_or_create(:host => @server, :port => @port)
164
+ server.connected = false
165
+ server.save
166
+ connect if restart
167
+ end
168
+
169
+ end
170
+
171
+ end
@@ -0,0 +1,138 @@
1
+ module ModSpox
2
+
3
+ class Timer < Pool
4
+
5
+ # pipeline:: message pipeline
6
+ # procs:: number of threads in the pool
7
+ # Create a new Timer
8
+ def initialize(pipeline, procs=2)
9
+ super(procs)
10
+ @pipeline = pipeline
11
+ @timers = Array.new
12
+ @actions = Queue.new
13
+ Logger.log("Created queue: #{@actions} in timer", 10)
14
+ @monitor = Monitors::Timer.new
15
+ @thread = nil
16
+ @stop_timer = false
17
+ {:Internal_TimerAdd => :add_message,
18
+ :Internal_TimerRemove => :remove_message}.each_pair{|type,method|
19
+ @pipeline.hook(self, method, type)
20
+ }
21
+ start_pool
22
+ end
23
+
24
+ # Wakes the timer up early
25
+ def wakeup
26
+ Logger.log("Timer has been explicitly told to wakeup", 15)
27
+ @monitor.wakeup unless @thread.nil?
28
+ end
29
+
30
+ # message:: TimerAdd message
31
+ # Add a recurring code block
32
+ def add_message(message)
33
+ Logger.log("New block is being added to the timer", 15)
34
+ action = add(message.period, message.once, message.data, &message.block)
35
+ begin
36
+ @pipeline << Messages::Internal::TimerResponse.new(message.requester, action, true)
37
+ Logger.log("New block was successfully added to the timer", 15)
38
+ rescue Object => boom
39
+ Logger.log("Failed to add block to timer: #{boom}", 10)
40
+ @pipeline << Messages::Internal::TimerResponse.new(message.requester, action, false)
41
+ end
42
+ end
43
+
44
+ # message:: TimerRemove message
45
+ # Remove an action from the timer
46
+ def remove_message(message)
47
+ remove(message.action)
48
+ Logger.log("Action has been removed from the Timer", 15)
49
+ @pipeline << Messages::Internal::TimerResponse.new(nil, message.action, false)
50
+ end
51
+
52
+ # period:: seconds between running action
53
+ # once:: only run action once
54
+ # data:: data to be available
55
+ # &func:: data block to run
56
+ # Adds a new action to the timer
57
+ def add(period, once=false, data=nil, &func)
58
+ action = Action.new(self, period, data, once, &func)
59
+ @timers << action
60
+ wakeup
61
+ return action
62
+ end
63
+
64
+ # action:: Action to add to timer's queue
65
+ # Adds a new action to the timer
66
+ def add_action(action)
67
+ raise Exceptions::InvalidType.new('An Action object must be supplied') unless action.is_a?(Action)
68
+ @timers << action
69
+ wakeup
70
+ end
71
+
72
+ # action:: Action to remove from timer's queue
73
+ # Removes and action from the timer
74
+ def remove(action)
75
+ raise Exceptions::InvalidType.new('An Action object must be supplied') unless action.is_a?(Action)
76
+ @timers.delete(action)
77
+ wakeup
78
+ end
79
+
80
+ # Starts the timer
81
+ def start
82
+ raise Exceptions::AlreadyRunning.new('Timer is already running') unless @thread.nil?
83
+ @thread = Thread.new{
84
+ until @stop_timer do
85
+ to_sleep = nil
86
+ @timers.each do |a|
87
+ to_sleep = a.remaining if to_sleep.nil?
88
+ to_sleep = a.remaining if !a.remaining.nil? && a.remaining < to_sleep
89
+ end
90
+ Logger.log("Timer is set to sleep for #{to_sleep.nil? ? 'forever' : "#{to_sleep} seconds"}", 15)
91
+ actual_sleep = @monitor.wait(to_sleep)
92
+ tick(actual_sleep)
93
+ Logger.log("Timer was set to sleep for #{to_sleep.nil? ? 'forever' : "#{to_sleep} seconds"} seconds. Actual sleep time: #{actual_sleep} seconds", 15)
94
+ end
95
+ }
96
+ end
97
+
98
+ # Stops the timer
99
+ def stop
100
+ raise Exceptions::NotRunning.new('Timer is not running') if @thread.nil?
101
+ @stop_timer = true
102
+ wakeup
103
+ @thread.join
104
+ end
105
+
106
+ # Clears all actions in the timer's queue
107
+ def clear
108
+ @actions.clear
109
+ @timers.clear
110
+ end
111
+
112
+ private
113
+
114
+ # time_passed:: time passed since last tick
115
+ # Decrements all Actions the given amount of time
116
+ def tick(time_passed)
117
+ for action in @timers do
118
+ action.tick(time_passed)
119
+ if(action.due?)
120
+ @actions << action.schedule
121
+ end
122
+ end
123
+ end
124
+
125
+ # Process the actions
126
+ def processor
127
+ action = @actions.pop
128
+ begin
129
+ action.run
130
+ remove(action) if action.is_complete?
131
+ rescue Object => boom
132
+ Logger.log("Timer block generated an exception: #{boom}\n#{boom.backtrace.join("\n")}", 5)
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -0,0 +1,16 @@
1
+ module ModSpox
2
+ module Handlers
3
+ class BadNick < Handler
4
+ def initialize(handlers)
5
+ handlers[ERR_ERRONEOUSNICKNAME] = self
6
+ end
7
+ def process(string)
8
+ if(string =~ /#{RPL_ERRORNEOUSNICK}\s\S+\s(\S+)\s:/)
9
+ return Messages::Incoming::BadNick.new(string, $1)
10
+ else
11
+ Logger.log('Failed to process RPL_ERRORONEOUSNICK message')
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ module ModSpox
2
+ module Handlers
3
+ class Bounce < Handler
4
+ def initialize(handlers)
5
+ handlers[RPL_BOUNCE] = self
6
+ end
7
+
8
+ def process(string)
9
+ if(string =~ /:Try server (\S+), port (.+)$/)
10
+ return Messages::Incoming::Bounce.new(string, $1, $2)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module ModSpox
2
+ module Handlers
3
+ class Created < Handler
4
+ def initialize(handlers)
5
+ handlers[RPL_CREATED] = self
6
+ end
7
+ def process(string)
8
+ if(string =~ /#{RPL_CREATED.to_s}.+?:created\s(.+)$/)
9
+ return Messages::Incoming::Created(string, $1)
10
+ else
11
+ Logger.log('Failed to parse RPL_CREATED message')
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end