spox-mod_spox 0.3.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 (268) hide show
  1. data/CHANGELOG +174 -0
  2. data/INSTALL +15 -0
  3. data/LICENSE +674 -0
  4. data/README.rdoc +73 -0
  5. data/bin/mod_spox +112 -0
  6. data/data/mod_spox/extras/AOLSpeak.rb +257 -0
  7. data/data/mod_spox/extras/AutoKick.rb +152 -0
  8. data/data/mod_spox/extras/AutoMode.rb +122 -0
  9. data/data/mod_spox/extras/AutoRejoin.rb +37 -0
  10. data/data/mod_spox/extras/Bash.rb +55 -0
  11. data/data/mod_spox/extras/Bouncer.rb +220 -0
  12. data/data/mod_spox/extras/Bullshit.rb +19 -0
  13. data/data/mod_spox/extras/Bytes.rb +11 -0
  14. data/data/mod_spox/extras/Confess.rb +244 -0
  15. data/data/mod_spox/extras/DCC.rb +182 -0
  16. data/data/mod_spox/extras/DevWatch.rb +153 -0
  17. data/data/mod_spox/extras/DownForEveryoneOrJustMe.rb +47 -0
  18. data/data/mod_spox/extras/EightBall.rb +33 -0
  19. data/data/mod_spox/extras/FML.rb +35 -0
  20. data/data/mod_spox/extras/FloodKicker.rb +129 -0
  21. data/data/mod_spox/extras/Fortune.rb +76 -0
  22. data/data/mod_spox/extras/GoogleIt.rb +13 -0
  23. data/data/mod_spox/extras/Headers.rb +59 -0
  24. data/data/mod_spox/extras/Karma.rb +302 -0
  25. data/data/mod_spox/extras/Locator.rb +44 -0
  26. data/data/mod_spox/extras/Logger.rb +182 -0
  27. data/data/mod_spox/extras/LolSpeak.rb +21 -0
  28. data/data/mod_spox/extras/NickServ.rb +84 -0
  29. data/data/mod_spox/extras/PhpCli.rb +308 -0
  30. data/data/mod_spox/extras/PhpFuncLookup.rb +297 -0
  31. data/data/mod_spox/extras/Pinger.rb +11 -0
  32. data/data/mod_spox/extras/Quotes.rb +79 -0
  33. data/data/mod_spox/extras/RegexTracker.rb +158 -0
  34. data/data/mod_spox/extras/Roulette.rb +267 -0
  35. data/data/mod_spox/extras/RubyCli.rb +93 -0
  36. data/data/mod_spox/extras/Search.rb +49 -0
  37. data/data/mod_spox/extras/Seen.rb +150 -0
  38. data/data/mod_spox/extras/Slashdot.rb +35 -0
  39. data/data/mod_spox/extras/SlashdotHeadlineGenerator.rb +500 -0
  40. data/data/mod_spox/extras/Talk.rb +32 -0
  41. data/data/mod_spox/extras/Topten.rb +103 -0
  42. data/data/mod_spox/extras/TracTicket.rb +66 -0
  43. data/data/mod_spox/extras/Translate.rb +132 -0
  44. data/data/mod_spox/extras/Twitter.rb +458 -0
  45. data/data/mod_spox/extras/UrbanDictionary.rb +55 -0
  46. data/data/mod_spox/extras/Weather.rb +55 -0
  47. data/data/mod_spox/plugins/Authenticator.rb +289 -0
  48. data/data/mod_spox/plugins/Banner.rb +585 -0
  49. data/data/mod_spox/plugins/BotNick.rb +18 -0
  50. data/data/mod_spox/plugins/Helper.rb +49 -0
  51. data/data/mod_spox/plugins/Initializer.rb +35 -0
  52. data/data/mod_spox/plugins/Joiner.rb +23 -0
  53. data/data/mod_spox/plugins/Nicker.rb +14 -0
  54. data/data/mod_spox/plugins/Parter.rb +23 -0
  55. data/data/mod_spox/plugins/Permissions.rb +60 -0
  56. data/data/mod_spox/plugins/PluginLoader.rb +180 -0
  57. data/data/mod_spox/plugins/Ponger.rb +70 -0
  58. data/data/mod_spox/plugins/PoolConfig.rb +52 -0
  59. data/data/mod_spox/plugins/Quitter.rb +15 -0
  60. data/data/mod_spox/plugins/Servers.rb +57 -0
  61. data/data/mod_spox/plugins/Status.rb +31 -0
  62. data/data/mod_spox/plugins/Triggers.rb +85 -0
  63. data/lib/mod_spox/BaseConfig.rb +51 -0
  64. data/lib/mod_spox/Bot.rb +604 -0
  65. data/lib/mod_spox/BotConfig.rb +65 -0
  66. data/lib/mod_spox/ConfigurationWizard.rb +180 -0
  67. data/lib/mod_spox/Database.rb +51 -0
  68. data/lib/mod_spox/Exceptions.rb +84 -0
  69. data/lib/mod_spox/Helpers.rb +122 -0
  70. data/lib/mod_spox/Loader.rb +60 -0
  71. data/lib/mod_spox/Logger.rb +37 -0
  72. data/lib/mod_spox/MessageFactory.rb +112 -0
  73. data/lib/mod_spox/Pipeline.rb +207 -0
  74. data/lib/mod_spox/Plugin.rb +97 -0
  75. data/lib/mod_spox/PluginHolder.rb +22 -0
  76. data/lib/mod_spox/PluginManager.rb +257 -0
  77. data/lib/mod_spox/PriorityQueue.rb +69 -0
  78. data/lib/mod_spox/Socket.rb +201 -0
  79. data/lib/mod_spox/Sockets.rb +226 -0
  80. data/lib/mod_spox/Timer.rb +60 -0
  81. data/lib/mod_spox/Version.rb +14 -0
  82. data/lib/mod_spox/handlers/BadNick.rb +19 -0
  83. data/lib/mod_spox/handlers/Bounce.rb +24 -0
  84. data/lib/mod_spox/handlers/Created.rb +27 -0
  85. data/lib/mod_spox/handlers/Handler.rb +39 -0
  86. data/lib/mod_spox/handlers/Invite.rb +28 -0
  87. data/lib/mod_spox/handlers/Join.rb +38 -0
  88. data/lib/mod_spox/handlers/Kick.rb +36 -0
  89. data/lib/mod_spox/handlers/LuserChannels.rb +19 -0
  90. data/lib/mod_spox/handlers/LuserClient.rb +18 -0
  91. data/lib/mod_spox/handlers/LuserMe.rb +16 -0
  92. data/lib/mod_spox/handlers/LuserOp.rb +19 -0
  93. data/lib/mod_spox/handlers/LuserUnknown.rb +19 -0
  94. data/lib/mod_spox/handlers/Mode.rb +62 -0
  95. data/lib/mod_spox/handlers/Motd.rb +38 -0
  96. data/lib/mod_spox/handlers/MyInfo.rb +24 -0
  97. data/lib/mod_spox/handlers/Names.rb +86 -0
  98. data/lib/mod_spox/handlers/Nick.rb +50 -0
  99. data/lib/mod_spox/handlers/NickInUse.rb +19 -0
  100. data/lib/mod_spox/handlers/Notice.rb +35 -0
  101. data/lib/mod_spox/handlers/Part.rb +39 -0
  102. data/lib/mod_spox/handlers/Ping.rb +25 -0
  103. data/lib/mod_spox/handlers/Pong.rb +23 -0
  104. data/lib/mod_spox/handlers/Privmsg.rb +39 -0
  105. data/lib/mod_spox/handlers/Quit.rb +29 -0
  106. data/lib/mod_spox/handlers/Topic.rb +38 -0
  107. data/lib/mod_spox/handlers/Welcome.rb +30 -0
  108. data/lib/mod_spox/handlers/Who.rb +83 -0
  109. data/lib/mod_spox/handlers/Whois.rb +117 -0
  110. data/lib/mod_spox/handlers/YourHost.rb +20 -0
  111. data/lib/mod_spox/messages/Messages.rb +6 -0
  112. data/lib/mod_spox/messages/incoming/BadNick.rb +16 -0
  113. data/lib/mod_spox/messages/incoming/Bounce.rb +18 -0
  114. data/lib/mod_spox/messages/incoming/Created.rb +15 -0
  115. data/lib/mod_spox/messages/incoming/Invite.rb +21 -0
  116. data/lib/mod_spox/messages/incoming/Join.rb +19 -0
  117. data/lib/mod_spox/messages/incoming/Kick.rb +26 -0
  118. data/lib/mod_spox/messages/incoming/LuserChannels.rb +15 -0
  119. data/lib/mod_spox/messages/incoming/LuserClient.rb +24 -0
  120. data/lib/mod_spox/messages/incoming/LuserMe.rb +18 -0
  121. data/lib/mod_spox/messages/incoming/LuserOp.rb +15 -0
  122. data/lib/mod_spox/messages/incoming/LuserUnknown.rb +15 -0
  123. data/lib/mod_spox/messages/incoming/Message.rb +22 -0
  124. data/lib/mod_spox/messages/incoming/Mode.rb +42 -0
  125. data/lib/mod_spox/messages/incoming/Motd.rb +18 -0
  126. data/lib/mod_spox/messages/incoming/MyInfo.rb +24 -0
  127. data/lib/mod_spox/messages/incoming/Names.rb +24 -0
  128. data/lib/mod_spox/messages/incoming/Nick.rb +26 -0
  129. data/lib/mod_spox/messages/incoming/NickInUse.rb +15 -0
  130. data/lib/mod_spox/messages/incoming/Notice.rb +9 -0
  131. data/lib/mod_spox/messages/incoming/Part.rb +21 -0
  132. data/lib/mod_spox/messages/incoming/Ping.rb +18 -0
  133. data/lib/mod_spox/messages/incoming/Pong.rb +9 -0
  134. data/lib/mod_spox/messages/incoming/Privmsg.rb +90 -0
  135. data/lib/mod_spox/messages/incoming/Quit.rb +18 -0
  136. data/lib/mod_spox/messages/incoming/Topic.rb +21 -0
  137. data/lib/mod_spox/messages/incoming/TopicInfo.rb +21 -0
  138. data/lib/mod_spox/messages/incoming/Welcome.rb +27 -0
  139. data/lib/mod_spox/messages/incoming/Who.rb +18 -0
  140. data/lib/mod_spox/messages/incoming/Whois.rb +49 -0
  141. data/lib/mod_spox/messages/incoming/YourHost.rb +18 -0
  142. data/lib/mod_spox/messages/internal/BotInitialized.rb +11 -0
  143. data/lib/mod_spox/messages/internal/ChangeNick.rb +15 -0
  144. data/lib/mod_spox/messages/internal/Connected.rb +20 -0
  145. data/lib/mod_spox/messages/internal/ConnectionFailed.rb +23 -0
  146. data/lib/mod_spox/messages/internal/DCCListener.rb +12 -0
  147. data/lib/mod_spox/messages/internal/DCCRequest.rb +12 -0
  148. data/lib/mod_spox/messages/internal/DCCSocket.rb +19 -0
  149. data/lib/mod_spox/messages/internal/Disconnected.rb +8 -0
  150. data/lib/mod_spox/messages/internal/Disconnecting.rb +8 -0
  151. data/lib/mod_spox/messages/internal/EstablishConnection.rb +22 -0
  152. data/lib/mod_spox/messages/internal/HaltBot.rb +8 -0
  153. data/lib/mod_spox/messages/internal/NickRequest.rb +9 -0
  154. data/lib/mod_spox/messages/internal/NickResponse.rb +15 -0
  155. data/lib/mod_spox/messages/internal/PluginLoadRequest.rb +21 -0
  156. data/lib/mod_spox/messages/internal/PluginLoadResponse.rb +17 -0
  157. data/lib/mod_spox/messages/internal/PluginModuleRequest.rb +14 -0
  158. data/lib/mod_spox/messages/internal/PluginModuleResponse.rb +18 -0
  159. data/lib/mod_spox/messages/internal/PluginReload.rb +18 -0
  160. data/lib/mod_spox/messages/internal/PluginRequest.rb +18 -0
  161. data/lib/mod_spox/messages/internal/PluginResponse.rb +21 -0
  162. data/lib/mod_spox/messages/internal/PluginUnloadRequest.rb +9 -0
  163. data/lib/mod_spox/messages/internal/PluginUnloadResponse.rb +9 -0
  164. data/lib/mod_spox/messages/internal/PluginsReady.rb +10 -0
  165. data/lib/mod_spox/messages/internal/QueueSocket.rb +8 -0
  166. data/lib/mod_spox/messages/internal/Reconnect.rb +8 -0
  167. data/lib/mod_spox/messages/internal/Request.rb +15 -0
  168. data/lib/mod_spox/messages/internal/Response.rb +15 -0
  169. data/lib/mod_spox/messages/internal/Shutdown.rb +8 -0
  170. data/lib/mod_spox/messages/internal/SignaturesUpdate.rb +8 -0
  171. data/lib/mod_spox/messages/internal/StatusRequest.rb +10 -0
  172. data/lib/mod_spox/messages/internal/StatusResponse.rb +18 -0
  173. data/lib/mod_spox/messages/internal/TimerAdd.rb +36 -0
  174. data/lib/mod_spox/messages/internal/TimerClear.rb +16 -0
  175. data/lib/mod_spox/messages/internal/TimerRemove.rb +23 -0
  176. data/lib/mod_spox/messages/internal/TimerResponse.rb +34 -0
  177. data/lib/mod_spox/messages/internal/TriggersUpdate.rb +8 -0
  178. data/lib/mod_spox/messages/internal/UnqueueSocket.rb +8 -0
  179. data/lib/mod_spox/messages/outgoing/Admin.rb +15 -0
  180. data/lib/mod_spox/messages/outgoing/Away.rb +11 -0
  181. data/lib/mod_spox/messages/outgoing/ChannelMode.rb +25 -0
  182. data/lib/mod_spox/messages/outgoing/Connect.rb +24 -0
  183. data/lib/mod_spox/messages/outgoing/Die.rb +9 -0
  184. data/lib/mod_spox/messages/outgoing/Info.rb +15 -0
  185. data/lib/mod_spox/messages/outgoing/Invite.rb +19 -0
  186. data/lib/mod_spox/messages/outgoing/Ison.rb +15 -0
  187. data/lib/mod_spox/messages/outgoing/Join.rb +19 -0
  188. data/lib/mod_spox/messages/outgoing/Kick.rb +23 -0
  189. data/lib/mod_spox/messages/outgoing/Kill.rb +19 -0
  190. data/lib/mod_spox/messages/outgoing/Links.rb +19 -0
  191. data/lib/mod_spox/messages/outgoing/List.rb +19 -0
  192. data/lib/mod_spox/messages/outgoing/Lusers.rb +19 -0
  193. data/lib/mod_spox/messages/outgoing/Motd.rb +16 -0
  194. data/lib/mod_spox/messages/outgoing/Names.rb +20 -0
  195. data/lib/mod_spox/messages/outgoing/Nick.rb +16 -0
  196. data/lib/mod_spox/messages/outgoing/Notice.rb +12 -0
  197. data/lib/mod_spox/messages/outgoing/Oper.rb +19 -0
  198. data/lib/mod_spox/messages/outgoing/Part.rb +19 -0
  199. data/lib/mod_spox/messages/outgoing/Pass.rb +16 -0
  200. data/lib/mod_spox/messages/outgoing/Ping.rb +11 -0
  201. data/lib/mod_spox/messages/outgoing/Pong.rb +17 -0
  202. data/lib/mod_spox/messages/outgoing/Privmsg.rb +43 -0
  203. data/lib/mod_spox/messages/outgoing/Quit.rb +11 -0
  204. data/lib/mod_spox/messages/outgoing/Raw.rb +16 -0
  205. data/lib/mod_spox/messages/outgoing/Rehash.rb +9 -0
  206. data/lib/mod_spox/messages/outgoing/Restart.rb +9 -0
  207. data/lib/mod_spox/messages/outgoing/ServList.rb +19 -0
  208. data/lib/mod_spox/messages/outgoing/Simple.rb +12 -0
  209. data/lib/mod_spox/messages/outgoing/Squery.rb +19 -0
  210. data/lib/mod_spox/messages/outgoing/Squit.rb +19 -0
  211. data/lib/mod_spox/messages/outgoing/Stats.rb +18 -0
  212. data/lib/mod_spox/messages/outgoing/Summon.rb +23 -0
  213. data/lib/mod_spox/messages/outgoing/Time.rb +15 -0
  214. data/lib/mod_spox/messages/outgoing/Topic.rb +19 -0
  215. data/lib/mod_spox/messages/outgoing/Trace.rb +15 -0
  216. data/lib/mod_spox/messages/outgoing/Unaway.rb +9 -0
  217. data/lib/mod_spox/messages/outgoing/User.rb +23 -0
  218. data/lib/mod_spox/messages/outgoing/UserHost.rb +15 -0
  219. data/lib/mod_spox/messages/outgoing/UserMode.rb +19 -0
  220. data/lib/mod_spox/messages/outgoing/Users.rb +15 -0
  221. data/lib/mod_spox/messages/outgoing/Version.rb +16 -0
  222. data/lib/mod_spox/messages/outgoing/Who.rb +23 -0
  223. data/lib/mod_spox/messages/outgoing/WhoWas.rb +23 -0
  224. data/lib/mod_spox/messages/outgoing/Whois.rb +19 -0
  225. data/lib/mod_spox/migrations/001_initialize_models.rb +115 -0
  226. data/lib/mod_spox/migrations/002_persistent_sigs.rb +14 -0
  227. data/lib/mod_spox/migrations/003_auth_restructure.rb +31 -0
  228. data/lib/mod_spox/migrations/004_mode_index_fix.rb +18 -0
  229. data/lib/mod_spox/migrations/005_nick_mode_nopark.rb +18 -0
  230. data/lib/mod_spox/models/Auth.rb +65 -0
  231. data/lib/mod_spox/models/AuthMask.rb +13 -0
  232. data/lib/mod_spox/models/Channel.rb +89 -0
  233. data/lib/mod_spox/models/Config.rb +30 -0
  234. data/lib/mod_spox/models/Group.rb +30 -0
  235. data/lib/mod_spox/models/Models.rb +4 -0
  236. data/lib/mod_spox/models/Nick.rb +195 -0
  237. data/lib/mod_spox/models/NickMode.rb +32 -0
  238. data/lib/mod_spox/models/Server.rb +27 -0
  239. data/lib/mod_spox/models/Setting.rb +52 -0
  240. data/lib/mod_spox/models/Signature.rb +52 -0
  241. data/lib/mod_spox/models/Trigger.rb +9 -0
  242. data/lib/mod_spox/rfc2812.rb +49 -0
  243. data/populate_gemspec.rb +15 -0
  244. data/tests/BotHolder.rb +24 -0
  245. data/tests/handlers/tc_BadNick.rb +21 -0
  246. data/tests/handlers/tc_Created.rb +24 -0
  247. data/tests/handlers/tc_Invite.rb +50 -0
  248. data/tests/handlers/tc_Join.rb +33 -0
  249. data/tests/handlers/tc_Kick.rb +32 -0
  250. data/tests/handlers/tc_Mode.rb +85 -0
  251. data/tests/handlers/tc_Names.rb +35 -0
  252. data/tests/handlers/tc_Nick.rb +55 -0
  253. data/tests/handlers/tc_Part.rb +44 -0
  254. data/tests/handlers/tc_Ping.rb +40 -0
  255. data/tests/handlers/tc_Pong.rb +28 -0
  256. data/tests/handlers/tc_Privmsg.rb +85 -0
  257. data/tests/handlers/tc_Quit.rb +40 -0
  258. data/tests/handlers/tc_Who.rb +50 -0
  259. data/tests/handlers/tc_Whois.rb +61 -0
  260. data/tests/models/tc_Auth.rb +39 -0
  261. data/tests/models/tc_Channel.rb +52 -0
  262. data/tests/models/tc_Config.rb +19 -0
  263. data/tests/models/tc_Nick.rb +144 -0
  264. data/tests/models/tc_NickMode.rb +40 -0
  265. data/tests/models/tc_Setting.rb +21 -0
  266. data/tests/models/tc_Signature.rb +14 -0
  267. data/tests/run_tests.rb +6 -0
  268. metadata +362 -0
@@ -0,0 +1,207 @@
1
+ ['mod_spox/models/Models.rb',
2
+ 'mod_spox/Logger',
3
+ 'mod_spox/Exceptions',
4
+ 'mod_spox/messages/incoming/Privmsg',
5
+ 'mod_spox/messages/incoming/Notice'].each{|f|require f}
6
+ module ModSpox
7
+
8
+ class Pipeline
9
+
10
+ # Create a new Pipeline
11
+ def initialize(pool)
12
+ @pool = pool
13
+ @hooks = Hash.new
14
+ @plugins = Hash.new
15
+ @admin = Models::Group.filter(:name => 'admin').first
16
+ @populate_lock = Mutex.new
17
+ populate_triggers
18
+ populate_signatures
19
+ hook(self, :populate_triggers, :Internal_TriggersUpdate)
20
+ hook(self, :populate_signatures, :Internal_SignaturesUpdate)
21
+ end
22
+
23
+ # message:: Message to send down pipeline
24
+ # Queues a message to send down pipeline
25
+ def <<(message)
26
+ Logger.info("Message added to pipeline queue: #{message}")
27
+ message_processor(message)
28
+ end
29
+
30
+ # plugin:: Plugin to hook to pipeline
31
+ # Hooks a plugin into the pipeline so it can be called
32
+ # directly when it matches a trigger
33
+ def hook_plugin(plugin)
34
+ Logger.info("Plugin #{plugin.name} hooking into pipeline")
35
+ @plugins[plugin.name.to_sym] = plugin
36
+ end
37
+
38
+ # plugin:: Plugin to unhook from pipeline
39
+ # Unhooks a plugin from the pipeline (This does not unhook
40
+ # it from the standard hooks)
41
+ def unhook_plugin(plugin)
42
+ Logger.info("Plugin #{plugin.name} unhooking from pipeline")
43
+ @plugins.delete(plugin.name.to_sym)
44
+ @hooks.each_pair do |type, things|
45
+ things.delete(plugin.name.to_sym) if things.has_key?(plugin.name.to_sym)
46
+ end
47
+ end
48
+
49
+ # plugin:: Plugin to hook to pipeline
50
+ # method:: Plugin method pipeline should call to process message
51
+ # type:: Type of message the plugin wants to process
52
+ # Hooks a plugin into the pipeline for a specific type of message
53
+ def hook(object, method, type)
54
+ Logger.info("Object #{object.class.to_s} hooking into messages of type: #{type}")
55
+ type = type.gsub(/::/, '_').to_sym unless type.is_a?(Symbol)
56
+ method = method.to_sym unless method.is_a?(Symbol)
57
+ name = object.class.to_s.gsub(/^.+:/, '')
58
+ @hooks[type] = Hash.new unless @hooks.has_key?(type)
59
+ @hooks[type][name.to_sym] = Array.new unless @hooks[type][name.to_sym].is_a?(Array)
60
+ @hooks[type][name.to_sym] << {:object => object, :method => method}
61
+ end
62
+
63
+ # plugin:: Plugin to unhook from pipeline
64
+ # type:: Type of message the plugin no longer wants to process
65
+ # This will remove the hook a plugin has for a specific message type
66
+ def unhook(object, method, type)
67
+ Logger.info("Object #{object.class.to_s} unhooking from messages of type: #{type}")
68
+ type = type.gsub(/::/, '_').to_sym unless type.is_a?(Symbol)
69
+ name = object.class.to_s.gsub(/^.+:/, '').to_sym
70
+ raise Exceptions::InvalidValue.new("Unknown hook type given: #{type.to_s}") unless @hooks.has_key?(type)
71
+ raise Exceptions::InvalidValue.new("Unknown object hooked: #{name.to_s}") unless @hooks[type].has_key?(name)
72
+ @hooks[type][name].each{|hook|
73
+ @hooks[type][name].delete(hook) if hook[:method] == method
74
+ }
75
+ @hooks[type].delete(name) if @hooks[type][name].empty
76
+ @hooks.delete(type) if @hooks[type].empty?
77
+ end
78
+
79
+ # Clears all hooks from the pipeline (Commonly used when reloading plugins)
80
+ def clear
81
+ Logger.info("All hooks have been cleared from pipeline")
82
+ @hooks.clear
83
+ @plugins.clear
84
+ end
85
+
86
+ # Repopulate the active trigger list
87
+ def populate_triggers(m=nil)
88
+ @populate_lock.synchronize do
89
+ @triggers = []
90
+ Models::Trigger.filter(:active => true).each{|t|@triggers << t.trigger}
91
+ end
92
+ end
93
+
94
+ # Repopulate the active signatures list
95
+ def populate_signatures(m=nil)
96
+ @populate_lock.synchronize do
97
+ @signatures = {}
98
+ a = Models::Signature.filter(:enabled => false)
99
+ Logger.warn("Killing #{a.count} signatures")
100
+ a.destroy
101
+ Models::Signature.all.each do |s|
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
116
+ end
117
+
118
+ private
119
+
120
+ # Processes messages
121
+ def message_processor(message)
122
+ begin
123
+ Logger.info("Pipeline is processing a message: #{message}")
124
+ parse(message)
125
+ type = message.class.to_s.gsub(/^(ModSpox::Messages::|#<.+?>::)/, '').gsub('::', '_').to_sym
126
+ mod = type.to_s.gsub(/_.+$/, '').to_sym
127
+ Logger.info("Pipeline determines that #{message} is of type: #{type}")
128
+ [type, mod, :all].each do |type|
129
+ if(@hooks.has_key?(type))
130
+ @hooks[type].each_value do |objects|
131
+ begin
132
+ objects.each do |v|
133
+ @pool.process do
134
+ begin
135
+ v[:object].send(v[:method].to_s, message)
136
+ rescue Object => boom
137
+ if(boom.class.to_s == 'SQLite3::BusyException')
138
+ Database.reset_connections
139
+ retry
140
+ else
141
+ raise boom
142
+ end
143
+ end
144
+ end
145
+ end
146
+ rescue Object => boom
147
+ Logger.warn("Plugin threw exception while attempting to process message: #{boom}\n#{boom.backtrace.join("\n")}")
148
+ end
149
+ end
150
+ end
151
+ end
152
+ rescue Object => boom
153
+ Logger.warn("Pipeline encountered an exception while processing a message: #{boom}\n#{boom.backtrace.join("\n")}")
154
+ end
155
+ end
156
+
157
+ # message:: Message to parse
158
+ # This will parse a message to see if it matches any valid
159
+ # trigger signatures. If matches are found, they will be sent
160
+ # to the proper plugin for processing
161
+ def parse(message)
162
+ return unless message.kind_of?(Messages::Incoming::Privmsg) || message.kind_of?(Messages::Incoming::Notice)
163
+ trigger = nil
164
+ @triggers.each{|t| trigger = t if message.message[0..t.size-1] == t}
165
+ if(!trigger.nil? || message.addressed?)
166
+ return if !trigger.nil? && message.message.length == trigger.length
167
+ Logger.info("Message has matched against a known trigger")
168
+ c = (message.addressed? && trigger.nil?) ? message.message[0].chr.downcase : message.message[trigger.length].chr.downcase
169
+ if(c =~ /^[a-z]$/)
170
+ type = c.to_sym
171
+ elsif(c =~ /^[0-9]$/)
172
+ type = :digit
173
+ else
174
+ type = :other
175
+ end
176
+ sig_check = @signatures.has_key?(type) ? @signatures[type] : []
177
+ sig_check = sig_check + @signatures[:other] if type != :other && @signatures.has_key?(:other)
178
+ sig_check.each do |sig|
179
+ Logger.info("Matching against: #{trigger}#{sig.signature}")
180
+ esc_trig = trigger.nil? ? '' : Regexp.escape(trigger)
181
+ res = message.message.scan(/^#{esc_trig}#{sig.signature}$/)
182
+ if(res.size > 0)
183
+ next unless message.source.in_group?(sig.group) || message.source.in_group?(@admin) || sig.group.nil?
184
+ next if sig.requirement == 'private' && message.is_public?
185
+ next if sig.requirement == 'public' && message.is_private?
186
+ params = Hash.new
187
+ sig.params.size.times do |i|
188
+ params[sig.params[i].to_sym] = res[0][i]
189
+ Logger.info("Signature params: #{sig.params[i].to_sym} = #{res[0][i]}")
190
+ end
191
+ if(@plugins.has_key?(sig.plugin.to_sym))
192
+ begin
193
+ @pool.process{ @plugins[sig.plugin.to_sym].send(sig.values[:method], message, params) }
194
+ rescue Object => boom
195
+ Logger.warn("Plugin threw exception while attempting to process message: #{boom}\n#{boom.backtrace.join("\n")}")
196
+ end
197
+ end
198
+ end
199
+ end
200
+ else
201
+ Logger.info("Message failed to match any known trigger")
202
+ end
203
+ end
204
+
205
+ end
206
+
207
+ end
@@ -0,0 +1,97 @@
1
+ ['mod_spox/Pipeline',
2
+ 'mod_spox/Logger',
3
+ 'mod_spox/Exceptions',
4
+ 'mod_spox/messages/outgoing/Privmsg'].each{|f|require f}
5
+ module ModSpox
6
+ class Plugin
7
+ include Models
8
+ def initialize(args)
9
+ @pipeline = args[:pipeline]
10
+ @plugin_module = args[:plugin_module]
11
+ raise Exceptions::BotException.new('Plugin creation failed to supply message pipeline') unless @pipeline.is_a?(Pipeline)
12
+ @pipeline.hook_plugin(self)
13
+ end
14
+
15
+ # Called before the object is destroyed by the ModSpox::PluginManager
16
+ def destroy
17
+ Logger.info("Destroy method for plugin #{name} has not been defined.")
18
+ end
19
+
20
+ # Returns the name of the class
21
+ def name
22
+ self.class.name.to_s.gsub(/^.+:/, '')
23
+ end
24
+
25
+ # target:: target for message
26
+ # message:: string message
27
+ # This is a helper method that will send an outgoing Privmsg
28
+ # to the given target
29
+ def reply(target, message)
30
+ @pipeline << Messages::Outgoing::Privmsg.new(target, message)
31
+ end
32
+
33
+ # Returns the nick model of the bot
34
+ def me
35
+ nick = Models::Nick.filter(:botnick => true).first
36
+ if(nick)
37
+ return nick
38
+ else
39
+ raise Exception.new("Fatal Error: I don't know who I am.")
40
+ end
41
+ end
42
+
43
+ # Returns constant given from plugin module. Raises ModSpox::Exceptions::InvalidType
44
+ # exception if constant is not found within the module
45
+ # Note: Use _ for depth (ie: Foo::Bar::Fee if Fee is wanted give: :Foo_Bar_Fee)
46
+ def plugin_const(const)
47
+ klass = @plugin_module
48
+ begin
49
+ const.to_s.split('_').each do |part|
50
+ klass = klass.const_get(part.to_sym)
51
+ end
52
+ rescue NameError => boom
53
+ raise ModSpox::Exceptions::InvalidType.new("Requested constant has not been defined within the plugins module (#{const}): #{boom}")
54
+ end
55
+ if(klass.nil?)
56
+ raise ModSpox::Exceptions::InvalidType.new("Requested constant has not been defined within the plugins module (#{const})")
57
+ else
58
+ return klass
59
+ end
60
+ end
61
+
62
+ # Adds a new signature for the given plugin
63
+ # Required args: :sig and :method
64
+ # Optional args: :group, :req, :desc, and :params
65
+ def add_sig(args)
66
+ raise ModSpox::Exceptions::InvalidType.new('You must provide a hash for creating new signatures') unless args.is_a?(Hash)
67
+ args[:params] = nil unless args[:params]
68
+ sig = Signature.find_or_create(:signature => args[:sig], :plugin => name, :method => args[:method].to_s,
69
+ :params => args[:params], :group_id => args[:group].nil? ? nil : args[:group].pk)
70
+ sig.description = args[:desc] if args.has_key?(:desc)
71
+ sig.requirement = args[:req] if args.has_key?(:req)
72
+ sig.save
73
+ end
74
+
75
+ # to:: Where message is going
76
+ # message:: message
77
+ # Send an information message to target
78
+ def information(to, message)
79
+ reply to, "\2#{name} (info):\2 #{message}"
80
+ end
81
+
82
+ # to:: Where message is going
83
+ # message:: message
84
+ # Send an warning message to target
85
+ def warning(to, message)
86
+ reply to, "\2#{name} (warn):\2 #{message}"
87
+ end
88
+
89
+ # to:: Where message is going
90
+ # message:: message
91
+ # Send an error message to target
92
+ def error(to, message)
93
+ reply to, "\2#{name} (error):\2 #{message}"
94
+ end
95
+
96
+ end
97
+ end
@@ -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
@@ -0,0 +1,257 @@
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',
8
+ 'mod_spox/Exceptions',
9
+ 'mod_spox/messages/internal/QueueSocket',
10
+ 'mod_spox/messages/internal/UnqueueSocket',
11
+ 'mod_spox/messages/internal/PluginLoadResponse',
12
+ 'mod_spox/messages/internal/SignaturesUpdate',
13
+ 'mod_spox/messages/internal/PluginUnloadResponse',
14
+ 'mod_spox/messages/internal/PluginModuleResponse',
15
+ 'mod_spox/messages/internal/PluginResponse',
16
+ 'mod_spox/messages/internal/TimerClear',
17
+ 'mod_spox/messages/internal/PluginsReady'].each{|f|require f}
18
+
19
+ module ModSpox
20
+
21
+ class PluginManager
22
+
23
+ include Exceptions
24
+
25
+ # Hash of plugins. Defined by class name symbol (i.e. Trivia class: plugins[:Trivia])
26
+ attr_reader :plugins
27
+
28
+ # pipeline:: Pipeline for messages
29
+ # Create new PluginManager
30
+ def initialize(pipeline)
31
+ @plugins = Hash.new
32
+ @pipeline = pipeline
33
+ @pipeline.hook(self, :load_plugin, :Internal_PluginLoadRequest)
34
+ @pipeline.hook(self, :unload_plugin, :Internal_PluginUnloadRequest)
35
+ @pipeline.hook(self, :reload_plugins, :Internal_PluginReload)
36
+ @pipeline.hook(self, :send_modules, :Internal_PluginModuleRequest)
37
+ @pipeline.hook(self, :plugin_request, :Internal_PluginRequest)
38
+ @plugins_module = Module.new
39
+ @plugin_lock = Mutex.new
40
+ load_plugins
41
+ end
42
+
43
+ # message:: Messages::Internal::PluginReload
44
+ # Destroys and reinitializes plugins
45
+ def reload_plugins(message=nil)
46
+ @pipeline << Messages::Internal::QueueSocket.new
47
+ begin
48
+ @plugin_lock.synchronize do
49
+ if(!message.nil? && (message.fresh && message.stale))
50
+ do_unload(message.stale)
51
+ FileUtils.remove_file(message.stale)
52
+ FileUtils.copy(message.fresh, BotConfig[:userpluginpath])
53
+ do_load(message.stale)
54
+ Logger.info("Completed reload of plugin: #{message.stale}")
55
+ else
56
+ unload_plugins
57
+ load_plugins
58
+ end
59
+ end
60
+ rescue Object => boom
61
+ Logger.error("PluginManager caught error on plugin reload: #{boom}")
62
+ ensure
63
+ @pipeline << Messages::Internal::UnqueueSocket.new
64
+ end
65
+ end
66
+
67
+ # Destroys plugins
68
+ def destroy_plugins
69
+ unload_plugins
70
+ end
71
+
72
+ # message:: Messages::Internal::PluginLoadRequest
73
+ # Loads a plugin
74
+ def load_plugin(message)
75
+ @pipeline << Messages::Internal::QueueSocket.new
76
+ begin
77
+ path = !message.name ? "#{BotConfig[:userpluginpath]}/#{message.path.gsub(/^.+\//, '')}" : "#{BotConfig[:userpluginpath]}/#{message.name}"
78
+ begin
79
+ File.symlink(message.path, path)
80
+ rescue NotImplementedError => boom
81
+ FileUtils.copy(message.path, path)
82
+ end
83
+ do_load(path)
84
+ @pipeline << Messages::Internal::PluginLoadResponse.new(message.requester, true)
85
+ Logger.info("Loaded new plugin: #{message.path}")
86
+ rescue Object => boom
87
+ Logger.warn("Failed to load plugin: #{message.path} Reason: #{boom}")
88
+ @pipeline << Messages::Internal::PluginLoadResponse.new(message.requester, false)
89
+ ensure
90
+ @pipeline << Messages::Internal::SignaturesUpdate.new
91
+ @pipeline << Messages::Internal::UnqueueSocket.new
92
+ end
93
+ end
94
+
95
+ # message:: Messages::Internal::PluginUnloadRequest
96
+ # Unloads a plugin
97
+ def unload_plugin(message)
98
+ @pipeline << Messages::Internal::QueueSocket.new
99
+ begin
100
+ do_unload(message.path)
101
+ unless(File.symlink?(message.path))
102
+ unless(message.name.nil?)
103
+ FileUtils.copy(message.path, "#{BotConfig[:userpluginpath]}/#{message.name}")
104
+ end
105
+ end
106
+ File.delete(message.path)
107
+ @pipeline << Messages::Internal::PluginUnloadResponse.new(message.requester, true)
108
+ Logger.info("Unloaded plugin: #{message.path}")
109
+ rescue Object => boom
110
+ Logger.warn("Failed to unload plugin: #{message.path} Reason: #{boom}")
111
+ @pipeline << Messages::Internal::PluginUnloadResponse.new(message.requester, false)
112
+ ensure
113
+ @pipeline << Messages::Internal::UnqueueSocket.new
114
+ @pipeline << Messages::Internal::SignaturesUpdate.new
115
+ end
116
+ end
117
+
118
+ # message:: Messages::Internal::PluginModuleRequest
119
+ # Sends the plugins module to the requester
120
+ def send_modules(message)
121
+ @pipeline << Messages::Internal::PluginModuleResponse.new(message.requester, @plugins_module)
122
+ end
123
+
124
+ # message:: Messages::Internal::PluginRequest
125
+ # Returns a plugin to requesting object
126
+ def plugin_request(message)
127
+ if(@plugins.has_key?(message.plugin))
128
+ response = Messages::Internal::PluginResponse.new(message.requester, @plugins[message.plugin])
129
+ else
130
+ response = Messages::Internal::PluginResponse.new(message.requester, nil)
131
+ end
132
+ @pipeline << response
133
+ end
134
+
135
+ def upgrade_plugins
136
+ @plugins[:PluginLoader].plugin.extras_upgrade
137
+ end
138
+
139
+ private
140
+
141
+ # Loads and initializes plugins
142
+ def load_plugins
143
+ @pipeline << Messages::Internal::TimerClear.new
144
+ Models::Signature.set(:enabled => false)
145
+ [BotConfig[:pluginpath], BotConfig[:userpluginpath]].each do |path|
146
+ Dir.new(path).each do |file|
147
+ if(file =~ /^[^\.].+\.rb$/)
148
+ begin
149
+ do_load("#{path}/#{file}")
150
+ rescue Object => boom
151
+ Logger.warn("Failed to load file: #{path}/#{file}. Reason: #{boom}")
152
+ end
153
+ end
154
+ end
155
+ end
156
+ @pipeline << Messages::Internal::SignaturesUpdate.new
157
+ @pipeline << Messages::Internal::PluginsReady.new
158
+ end
159
+
160
+ # Destroys plugins
161
+ def unload_plugins
162
+ @plugins.each_pair do |sym, holder|
163
+ begin
164
+ holder.plugin.destroy unless holder.plugin.nil?
165
+ @pipeline.unhook_plugin(holder.plugin)
166
+ rescue Object => boom
167
+ Logger.warn("Plugin destruction error: #{boom}")
168
+ end
169
+ end
170
+ @plugins_module = Module.new
171
+ @pipeline << Messages::Internal::TimerClear.new
172
+ end
173
+
174
+ # path:: path to plugin file
175
+ # Loads a plugin into the plugin module
176
+ def do_load(path)
177
+ if(File.exists?(path))
178
+ plugins = discover_plugins(path)
179
+ raise PluginMissing.new("Plugin file at: #{path} does not contain a plugin class") if plugins.nil?
180
+ @plugins_module.module_eval(IO.readlines(path).join("\n"))
181
+ begin
182
+ plugins.each do |plugin|
183
+ klass = @plugins_module.const_get(plugin)
184
+ if(@plugins.has_key?(plugin.to_sym))
185
+ @plugins[plugin.to_sym].set_plugin(klass.new({:pipeline => @pipeline, :plugin_module => @plugins_module}))
186
+ else
187
+ @plugins[plugin.to_sym] = PluginHolder.new(klass.new({:pipeline => @pipeline, :plugin_module => @plugins_module}))
188
+ end
189
+ Logger.info("Properly initialized new plugin: #{plugin}")
190
+ Database.reset_connections
191
+ end
192
+ Logger.info("All plugins found at: #{path} have been loaded")
193
+ rescue Object => boom
194
+ Logger.warn("Plugin loading failed: #{boom}\n#{boom.backtrace.join("\n")}")
195
+ Logger.warn("All constants loaded from file: #{path} will now be unloaded")
196
+ do_unload(path)
197
+ end
198
+ else
199
+ raise PluginFileNotFound.new("Failed to find file at: #{path}")
200
+ end
201
+ end
202
+
203
+ # path:: path to plugin file
204
+ # Unloads a plugin and all constants from the plugin module
205
+ def do_unload(path)
206
+ if(File.exists?(path))
207
+ discover_plugins(path).each do |plugin|
208
+ if(@plugins.has_key?(plugin.to_sym))
209
+ @plugins[plugin.to_sym].plugin.destroy unless @plugins[plugin.to_sym].plugin.nil?
210
+ @pipeline.unhook_plugin(@plugins[plugin.to_sym].plugin)
211
+ @plugins[plugin.to_sym].set_plugin(nil)
212
+ @pipeline << Messages::Internal::TimerClear.new(plugin.to_sym)
213
+ end
214
+ Models::Signature.filter(:plugin => plugin.to_s).destroy
215
+ end
216
+ discover_consts(path).each do |const|
217
+ Logger.info("Removing constant: #{const}")
218
+ @plugins_module.send(:remove_const, const)
219
+ end
220
+ Logger.info("Removed all constants found in file: #{path}")
221
+ else
222
+ raise PluginFileNotFound.new("Failed to find file at: #{path}")
223
+ end
224
+ end
225
+
226
+ # path:: path to plugin
227
+ # Find class names of any plugins within the file at given path
228
+ def discover_plugins(path)
229
+ temp = Module.new
230
+ begin
231
+ temp.module_eval(IO.readlines(path).join("\n"))
232
+ klasses = []
233
+ temp.constants.each do |const|
234
+ klass = temp.const_get(const)
235
+ klasses << const if klass < Plugin
236
+ end
237
+ return klasses
238
+ rescue Object => boom
239
+ return nil
240
+ end
241
+ end
242
+
243
+ # path:: path to plugin
244
+ # Find all constant names in given path
245
+ def discover_consts(path)
246
+ temp = Module.new
247
+ begin
248
+ temp.module_eval(IO.readlines(path).join("\n"))
249
+ return temp.constants
250
+ rescue Object => boom
251
+ return nil
252
+ end
253
+ end
254
+
255
+ end
256
+
257
+ end
@@ -0,0 +1,69 @@
1
+ module ModSpox
2
+ # This class provides some simple logic for message output. It
3
+ # is basically a priority based queue with some round robin
4
+ # thrown in, just to keep things interesting. This queue provides
5
+ # protection on message output from extreme lag to one target when
6
+ # another target is expecting large quantities out output
7
+ # NOTE: Design help from the great Ryan "pizza_" Flynn
8
+ class PriorityQueue
9
+
10
+ # Create a priority queue
11
+ def initialize
12
+ @target_queues = {}
13
+ @queues = {:PRIORITY => Queue.new, :NEW => Queue.new, :NORMAL => Queue.new, :WHOCARES => Queue.new}
14
+ @lock = Mutex.new
15
+ end
16
+
17
+ # target:: message target (targets starting with * will be labelled WHOCARES
18
+ # message:: message to send
19
+ # This prioritizes output to help reduce lag when lots of output
20
+ # is being sent to another target. This will automatically decide
21
+ # how to queue the message based on the target
22
+ def priority_queue(target, message)
23
+ @lock.synchronize do
24
+ target.downcase!
25
+ @target_queues[target] = Queue.new unless @target_queues[target]
26
+ if(target[0].chr == '*')
27
+ @target_queues[target] << message
28
+ @queues[:WHOCARES] << @target_queues[target]
29
+ else
30
+ @target_queues[target] << message
31
+ if(@target_queues[target].size < 2)
32
+ @queues[:NEW] << @target_queues[target]
33
+ else
34
+ @queues[:NORMAL] << @target_queues[target]
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ # message:: message to send
41
+ # This will add messages to the PRIORITY queue which gets
42
+ # sent before all other messages.
43
+ def direct_queue(message)
44
+ @lock.synchronize do
45
+ @target_queues[:general] = Queue.new unless @target_queues[:general]
46
+ @target_queues[:general] << message
47
+ @queues[:PRIORITY] << @target_queues[:general]
48
+ end
49
+ end
50
+
51
+ # Returns the next message to send. This method decides what
52
+ # message to send based on the priority of the message. It
53
+ # will throw an Exceptions::EmptyQueue when there are no messages
54
+ # left.
55
+ def pop
56
+ m = nil
57
+ @lock.synchronize do
58
+ [:PRIORITY, :NEW, :NORMAL, :WHOCARES].each do |k|
59
+ unless(@queues[k].empty?)
60
+ m = @queues[k].pop.pop
61
+ break
62
+ end
63
+ end
64
+ end
65
+ raise Exceptions::EmptyQueue.new if m.nil?
66
+ return m
67
+ end
68
+ end
69
+ end