mod_spox 0.0.1

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