mod_spox 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/INSTALL +9 -0
- data/README +33 -0
- data/bin/mod_spox +60 -0
- data/data/mod_spox/extras/Tester.rb +14 -0
- data/data/mod_spox/plugins/Authenticator.rb +245 -0
- data/data/mod_spox/plugins/BotNick.rb +18 -0
- data/data/mod_spox/plugins/Initializer.rb +41 -0
- data/data/mod_spox/plugins/Joiner.rb +13 -0
- data/data/mod_spox/plugins/Parter.rb +22 -0
- data/data/mod_spox/plugins/PluginLoader.rb +136 -0
- data/data/mod_spox/plugins/Ponger.rb +14 -0
- data/data/mod_spox/plugins/Quitter.rb +14 -0
- data/lib/mod_spox/Action.rb +73 -0
- data/lib/mod_spox/BaseConfig.rb +48 -0
- data/lib/mod_spox/Bot.rb +472 -0
- data/lib/mod_spox/BotConfig.rb +54 -0
- data/lib/mod_spox/ConfigurationWizard.rb +178 -0
- data/lib/mod_spox/Database.rb +25 -0
- data/lib/mod_spox/Exceptions.rb +35 -0
- data/lib/mod_spox/Helpers.rb +35 -0
- data/lib/mod_spox/Loader.rb +79 -0
- data/lib/mod_spox/Logger.rb +31 -0
- data/lib/mod_spox/MessageFactory.rb +73 -0
- data/lib/mod_spox/Monitors.rb +59 -0
- data/lib/mod_spox/Pipeline.rb +148 -0
- data/lib/mod_spox/Plugin.rb +18 -0
- data/lib/mod_spox/PluginManager.rb +105 -0
- data/lib/mod_spox/Pool.rb +50 -0
- data/lib/mod_spox/Socket.rb +171 -0
- data/lib/mod_spox/Timer.rb +138 -0
- data/lib/mod_spox/handlers/BadNick.rb +16 -0
- data/lib/mod_spox/handlers/Bounce.rb +15 -0
- data/lib/mod_spox/handlers/Created.rb +16 -0
- data/lib/mod_spox/handlers/Handler.rb +31 -0
- data/lib/mod_spox/handlers/Invite.rb +19 -0
- data/lib/mod_spox/handlers/Join.rb +30 -0
- data/lib/mod_spox/handlers/Kick.rb +24 -0
- data/lib/mod_spox/handlers/LuserChannels.rb +16 -0
- data/lib/mod_spox/handlers/LuserClient.rb +16 -0
- data/lib/mod_spox/handlers/LuserMe.rb +14 -0
- data/lib/mod_spox/handlers/LuserOp.rb +16 -0
- data/lib/mod_spox/handlers/LuserUnknown.rb +16 -0
- data/lib/mod_spox/handlers/Mode.rb +47 -0
- data/lib/mod_spox/handlers/Motd.rb +30 -0
- data/lib/mod_spox/handlers/MyInfo.rb +21 -0
- data/lib/mod_spox/handlers/Names.rb +54 -0
- data/lib/mod_spox/handlers/Nick.rb +24 -0
- data/lib/mod_spox/handlers/NickInUse.rb +16 -0
- data/lib/mod_spox/handlers/Notice.rb +32 -0
- data/lib/mod_spox/handlers/Part.rb +19 -0
- data/lib/mod_spox/handlers/Ping.rb +16 -0
- data/lib/mod_spox/handlers/Pong.rb +16 -0
- data/lib/mod_spox/handlers/Privmsg.rb +27 -0
- data/lib/mod_spox/handlers/Quit.rb +21 -0
- data/lib/mod_spox/handlers/Topic.rb +29 -0
- data/lib/mod_spox/handlers/Welcome.rb +34 -0
- data/lib/mod_spox/handlers/Who.rb +60 -0
- data/lib/mod_spox/handlers/Whois.rb +63 -0
- data/lib/mod_spox/handlers/YourHost.rb +17 -0
- data/lib/mod_spox/messages/incoming/BadNick.rb +15 -0
- data/lib/mod_spox/messages/incoming/Bounce.rb +17 -0
- data/lib/mod_spox/messages/incoming/Created.rb +14 -0
- data/lib/mod_spox/messages/incoming/Invite.rb +20 -0
- data/lib/mod_spox/messages/incoming/Join.rb +18 -0
- data/lib/mod_spox/messages/incoming/Kick.rb +25 -0
- data/lib/mod_spox/messages/incoming/LuserChannels.rb +14 -0
- data/lib/mod_spox/messages/incoming/LuserClient.rb +23 -0
- data/lib/mod_spox/messages/incoming/LuserMe.rb +17 -0
- data/lib/mod_spox/messages/incoming/LuserOp.rb +14 -0
- data/lib/mod_spox/messages/incoming/LuserUnknown.rb +14 -0
- data/lib/mod_spox/messages/incoming/Message.rb +22 -0
- data/lib/mod_spox/messages/incoming/Mode.rb +41 -0
- data/lib/mod_spox/messages/incoming/Motd.rb +17 -0
- data/lib/mod_spox/messages/incoming/MyInfo.rb +23 -0
- data/lib/mod_spox/messages/incoming/Names.rb +23 -0
- data/lib/mod_spox/messages/incoming/Nick.rb +25 -0
- data/lib/mod_spox/messages/incoming/NickInUse.rb +14 -0
- data/lib/mod_spox/messages/incoming/Notice.rb +8 -0
- data/lib/mod_spox/messages/incoming/Part.rb +20 -0
- data/lib/mod_spox/messages/incoming/Ping.rb +17 -0
- data/lib/mod_spox/messages/incoming/Pong.rb +8 -0
- data/lib/mod_spox/messages/incoming/Privmsg.rb +64 -0
- data/lib/mod_spox/messages/incoming/Quit.rb +17 -0
- data/lib/mod_spox/messages/incoming/Topic.rb +20 -0
- data/lib/mod_spox/messages/incoming/TopicInfo.rb +20 -0
- data/lib/mod_spox/messages/incoming/Welcome.rb +26 -0
- data/lib/mod_spox/messages/incoming/Who.rb +17 -0
- data/lib/mod_spox/messages/incoming/Whois.rb +47 -0
- data/lib/mod_spox/messages/incoming/YourHost.rb +17 -0
- data/lib/mod_spox/messages/internal/BotInitialized.rb +11 -0
- data/lib/mod_spox/messages/internal/ChangeNick.rb +15 -0
- data/lib/mod_spox/messages/internal/Connected.rb +20 -0
- data/lib/mod_spox/messages/internal/ConnectionFailed.rb +23 -0
- data/lib/mod_spox/messages/internal/Disconnected.rb +8 -0
- data/lib/mod_spox/messages/internal/Disconnecting.rb +8 -0
- data/lib/mod_spox/messages/internal/EstablishConnection.rb +22 -0
- data/lib/mod_spox/messages/internal/HaltBot.rb +8 -0
- data/lib/mod_spox/messages/internal/NickRequest.rb +8 -0
- data/lib/mod_spox/messages/internal/NickResponse.rb +14 -0
- data/lib/mod_spox/messages/internal/PluginLoadRequest.rb +20 -0
- data/lib/mod_spox/messages/internal/PluginLoadResponse.rb +16 -0
- data/lib/mod_spox/messages/internal/PluginModuleRequest.rb +13 -0
- data/lib/mod_spox/messages/internal/PluginModuleResponse.rb +17 -0
- data/lib/mod_spox/messages/internal/PluginReload.rb +8 -0
- data/lib/mod_spox/messages/internal/PluginRequest.rb +17 -0
- data/lib/mod_spox/messages/internal/PluginResponse.rb +20 -0
- data/lib/mod_spox/messages/internal/PluginUnloadRequest.rb +8 -0
- data/lib/mod_spox/messages/internal/PluginUnloadResponse.rb +8 -0
- data/lib/mod_spox/messages/internal/Request.rb +15 -0
- data/lib/mod_spox/messages/internal/Response.rb +15 -0
- data/lib/mod_spox/messages/internal/Shutdown.rb +8 -0
- data/lib/mod_spox/messages/internal/SignaturesUpdate.rb +8 -0
- data/lib/mod_spox/messages/internal/StatusRequest.rb +9 -0
- data/lib/mod_spox/messages/internal/StatusResponse.rb +17 -0
- data/lib/mod_spox/messages/internal/TimerAdd.rb +27 -0
- data/lib/mod_spox/messages/internal/TimerClear.rb +8 -0
- data/lib/mod_spox/messages/internal/TimerRemove.rb +15 -0
- data/lib/mod_spox/messages/internal/TimerResponse.rb +26 -0
- data/lib/mod_spox/messages/internal/TriggersUpdate.rb +8 -0
- data/lib/mod_spox/messages/outgoing/Admin.rb +15 -0
- data/lib/mod_spox/messages/outgoing/Away.rb +10 -0
- data/lib/mod_spox/messages/outgoing/ChannelMode.rb +25 -0
- data/lib/mod_spox/messages/outgoing/Connect.rb +24 -0
- data/lib/mod_spox/messages/outgoing/Die.rb +9 -0
- data/lib/mod_spox/messages/outgoing/Info.rb +15 -0
- data/lib/mod_spox/messages/outgoing/Invite.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Ison.rb +15 -0
- data/lib/mod_spox/messages/outgoing/Join.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Kick.rb +23 -0
- data/lib/mod_spox/messages/outgoing/Kill.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Links.rb +19 -0
- data/lib/mod_spox/messages/outgoing/List.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Lusers.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Motd.rb +16 -0
- data/lib/mod_spox/messages/outgoing/Names.rb +20 -0
- data/lib/mod_spox/messages/outgoing/Nick.rb +16 -0
- data/lib/mod_spox/messages/outgoing/Notice.rb +11 -0
- data/lib/mod_spox/messages/outgoing/Oper.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Part.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Pass.rb +16 -0
- data/lib/mod_spox/messages/outgoing/Ping.rb +10 -0
- data/lib/mod_spox/messages/outgoing/Pong.rb +17 -0
- data/lib/mod_spox/messages/outgoing/Privmsg.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Quit.rb +10 -0
- data/lib/mod_spox/messages/outgoing/Rehash.rb +9 -0
- data/lib/mod_spox/messages/outgoing/Restart.rb +9 -0
- data/lib/mod_spox/messages/outgoing/ServList.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Simple.rb +12 -0
- data/lib/mod_spox/messages/outgoing/Squery.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Squit.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Stats.rb +18 -0
- data/lib/mod_spox/messages/outgoing/Summon.rb +23 -0
- data/lib/mod_spox/messages/outgoing/Time.rb +15 -0
- data/lib/mod_spox/messages/outgoing/Topic.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Trace.rb +15 -0
- data/lib/mod_spox/messages/outgoing/Unaway.rb +9 -0
- data/lib/mod_spox/messages/outgoing/User.rb +23 -0
- data/lib/mod_spox/messages/outgoing/UserHost.rb +15 -0
- data/lib/mod_spox/messages/outgoing/UserMode.rb +19 -0
- data/lib/mod_spox/messages/outgoing/Users.rb +15 -0
- data/lib/mod_spox/messages/outgoing/Version.rb +16 -0
- data/lib/mod_spox/messages/outgoing/Who.rb +19 -0
- data/lib/mod_spox/messages/outgoing/WhoWas.rb +23 -0
- data/lib/mod_spox/messages/outgoing/Whois.rb +19 -0
- data/lib/mod_spox/migration/001_create_auths.rb +13 -0
- data/lib/mod_spox/migration/001_create_channel.rb +13 -0
- data/lib/mod_spox/migration/001_create_channel_modes.rb +13 -0
- data/lib/mod_spox/migration/001_create_config.rb +13 -0
- data/lib/mod_spox/migration/001_create_nick_channels.rb +13 -0
- data/lib/mod_spox/migration/001_create_nick_modes.rb +13 -0
- data/lib/mod_spox/migration/001_create_nicks.rb +13 -0
- data/lib/mod_spox/migration/001_create_servers.rb +13 -0
- data/lib/mod_spox/migration/001_create_settings.rb +13 -0
- data/lib/mod_spox/migration/001_create_signatures.rb +13 -0
- data/lib/mod_spox/migration/001_create_triggers.rb +13 -0
- data/lib/mod_spox/models/Auth.rb +79 -0
- data/lib/mod_spox/models/AuthGroup.rb +15 -0
- data/lib/mod_spox/models/Channel.rb +47 -0
- data/lib/mod_spox/models/ChannelMode.rb +14 -0
- data/lib/mod_spox/models/Config.rb +31 -0
- data/lib/mod_spox/models/Group.rb +13 -0
- data/lib/mod_spox/models/Nick.rb +110 -0
- data/lib/mod_spox/models/NickChannel.rb +43 -0
- data/lib/mod_spox/models/NickMode.rb +18 -0
- data/lib/mod_spox/models/Server.rb +12 -0
- data/lib/mod_spox/models/Setting.rb +40 -0
- data/lib/mod_spox/models/Signature.rb +30 -0
- data/lib/mod_spox/models/Trigger.rb +9 -0
- data/lib/mod_spox/rfc2812.rb +171 -0
- 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
|