mod_spox 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +31 -1
- data/LICENSE +674 -0
- data/README.rdoc +73 -0
- data/bin/mod_spox +28 -28
- data/data/mod_spox/extras/AOLSpeak.rb +2 -3
- data/data/mod_spox/extras/AutoKick.rb +10 -23
- data/data/mod_spox/extras/AutoMode.rb +12 -23
- data/data/mod_spox/extras/Bash.rb +55 -0
- data/data/mod_spox/extras/Bouncer.rb +85 -57
- data/data/mod_spox/extras/Bullshit.rb +1 -1
- data/data/mod_spox/extras/Bytes.rb +1 -2
- data/data/mod_spox/extras/Confess.rb +27 -29
- data/data/mod_spox/extras/DCC.rb +11 -20
- data/data/mod_spox/extras/DevWatch.rb +21 -23
- data/data/mod_spox/extras/DownForEveryoneOrJustMe.rb +47 -0
- data/data/mod_spox/extras/EightBall.rb +1 -1
- data/data/mod_spox/extras/FML.rb +35 -0
- data/data/mod_spox/extras/Headers.rb +31 -50
- data/data/mod_spox/extras/Karma.rb +81 -29
- data/data/mod_spox/extras/Logger.rb +2 -2
- data/data/mod_spox/extras/LolSpeak.rb +1 -2
- data/data/mod_spox/extras/PhpCli.rb +138 -8
- data/data/mod_spox/extras/PhpFuncLookup.rb +20 -23
- data/data/mod_spox/extras/Pinger.rb +1 -1
- data/data/mod_spox/extras/Quotes.rb +8 -10
- data/data/mod_spox/extras/RegexTracker.rb +2 -4
- data/data/mod_spox/extras/Roulette.rb +20 -27
- data/data/mod_spox/extras/RubyCli.rb +93 -0
- data/data/mod_spox/extras/Search.rb +17 -3
- data/data/mod_spox/extras/Seen.rb +150 -0
- data/data/mod_spox/extras/SlashdotHeadlineGenerator.rb +500 -0
- data/data/mod_spox/extras/Talk.rb +2 -4
- data/data/mod_spox/extras/Topten.rb +10 -12
- data/data/mod_spox/extras/TracTicket.rb +3 -5
- data/data/mod_spox/extras/Translate.rb +20 -22
- data/data/mod_spox/extras/Twitter.rb +118 -33
- data/data/mod_spox/extras/UrbanDictionary.rb +8 -17
- data/data/mod_spox/extras/Weather.rb +1 -2
- data/data/mod_spox/plugins/Authenticator.rb +93 -98
- data/data/mod_spox/plugins/Banner.rb +26 -56
- data/data/mod_spox/plugins/Helper.rb +5 -6
- data/data/mod_spox/plugins/Initializer.rb +4 -14
- data/data/mod_spox/plugins/Joiner.rb +1 -1
- data/data/mod_spox/plugins/Nicker.rb +13 -0
- data/data/mod_spox/plugins/Parter.rb +2 -2
- data/data/mod_spox/plugins/Permissions.rb +60 -0
- data/data/mod_spox/plugins/PluginLoader.rb +7 -12
- data/data/mod_spox/plugins/Ponger.rb +51 -0
- data/data/mod_spox/plugins/Quitter.rb +1 -2
- data/data/mod_spox/plugins/Servers.rb +57 -0
- data/data/mod_spox/plugins/Status.rb +3 -2
- data/data/mod_spox/plugins/Triggers.rb +9 -9
- data/lib/mod_spox/Bot.rb +109 -33
- data/lib/mod_spox/BotConfig.rb +2 -2
- data/lib/mod_spox/ConfigurationWizard.rb +12 -12
- data/lib/mod_spox/Database.rb +1 -4
- data/lib/mod_spox/Exceptions.rb +26 -0
- data/lib/mod_spox/Helpers.rb +29 -68
- data/lib/mod_spox/Loader.rb +23 -24
- data/lib/mod_spox/Logger.rb +19 -17
- data/lib/mod_spox/MessageFactory.rb +50 -24
- data/lib/mod_spox/Pipeline.rb +21 -7
- data/lib/mod_spox/Plugin.rb +27 -3
- data/lib/mod_spox/PluginManager.rb +28 -15
- data/lib/mod_spox/PriorityQueue.rb +69 -0
- data/lib/mod_spox/Socket.rb +93 -51
- data/lib/mod_spox/Sockets.rb +76 -63
- data/lib/mod_spox/Timer.rb +21 -141
- data/lib/mod_spox/Version.rb +14 -0
- data/lib/mod_spox/handlers/BadNick.rb +1 -1
- data/lib/mod_spox/handlers/Bounce.rb +5 -5
- data/lib/mod_spox/handlers/Created.rb +13 -5
- data/lib/mod_spox/handlers/Handler.rb +12 -3
- data/lib/mod_spox/handlers/Invite.rb +14 -8
- data/lib/mod_spox/handlers/Join.rb +24 -20
- data/lib/mod_spox/handlers/Kick.rb +22 -13
- data/lib/mod_spox/handlers/Mode.rb +42 -36
- data/lib/mod_spox/handlers/Motd.rb +4 -0
- data/lib/mod_spox/handlers/Names.rb +66 -39
- data/lib/mod_spox/handlers/Nick.rb +20 -14
- data/lib/mod_spox/handlers/Part.rb +25 -8
- data/lib/mod_spox/handlers/Ping.rb +11 -5
- data/lib/mod_spox/handlers/Pong.rb +9 -5
- data/lib/mod_spox/handlers/Privmsg.rb +25 -17
- data/lib/mod_spox/handlers/Quit.rb +13 -8
- data/lib/mod_spox/handlers/Topic.rb +4 -0
- data/lib/mod_spox/handlers/Welcome.rb +16 -24
- data/lib/mod_spox/handlers/Who.rb +64 -48
- data/lib/mod_spox/handlers/Whois.rb +92 -60
- data/lib/mod_spox/messages/incoming/Nick.rb +2 -2
- data/lib/mod_spox/messages/incoming/Privmsg.rb +1 -1
- data/lib/mod_spox/messages/incoming/Whois.rb +1 -0
- data/lib/mod_spox/messages/internal/EstablishConnection.rb +1 -1
- data/lib/mod_spox/messages/internal/PluginsReady.rb +10 -0
- data/lib/mod_spox/messages/internal/QueueSocket.rb +8 -0
- data/lib/mod_spox/messages/internal/Reconnect.rb +8 -0
- data/lib/mod_spox/messages/internal/UnqueueSocket.rb +8 -0
- data/lib/mod_spox/migrations/002_persistent_sigs.rb +14 -0
- data/lib/mod_spox/migrations/003_auth_restructure.rb +31 -0
- data/lib/mod_spox/migrations/004_mode_index_fix.rb +18 -0
- data/lib/mod_spox/migrations/005_nick_mode_nopark.rb +18 -0
- data/lib/mod_spox/models/Auth.rb +16 -46
- data/lib/mod_spox/models/AuthMask.rb +13 -0
- data/lib/mod_spox/models/Channel.rb +46 -27
- data/lib/mod_spox/models/Config.rb +10 -19
- data/lib/mod_spox/models/Group.rb +20 -8
- data/lib/mod_spox/models/Models.rb +1 -1
- data/lib/mod_spox/models/Nick.rb +105 -113
- data/lib/mod_spox/models/NickMode.rb +23 -9
- data/lib/mod_spox/models/Server.rb +12 -1
- data/lib/mod_spox/models/Setting.rb +12 -16
- data/lib/mod_spox/models/Signature.rb +28 -8
- data/tests/BotHolder.rb +24 -0
- data/tests/handlers/tc_BadNick.rb +21 -0
- data/tests/handlers/tc_Created.rb +24 -0
- data/tests/handlers/tc_Invite.rb +50 -0
- data/tests/handlers/tc_Join.rb +33 -0
- data/tests/handlers/tc_Kick.rb +32 -0
- data/tests/handlers/tc_Mode.rb +85 -0
- data/tests/handlers/tc_Names.rb +35 -0
- data/tests/handlers/tc_Nick.rb +55 -0
- data/tests/handlers/tc_Part.rb +44 -0
- data/tests/handlers/tc_Ping.rb +40 -0
- data/tests/handlers/tc_Pong.rb +28 -0
- data/tests/handlers/tc_Privmsg.rb +85 -0
- data/tests/handlers/tc_Quit.rb +40 -0
- data/tests/handlers/tc_Who.rb +50 -0
- data/tests/handlers/tc_Whois.rb +61 -0
- data/tests/models/tc_Auth.rb +34 -0
- data/tests/models/tc_Channel.rb +52 -0
- data/tests/models/tc_Config.rb +19 -0
- data/tests/models/tc_Nick.rb +142 -0
- data/tests/models/tc_NickMode.rb +40 -0
- data/tests/models/tc_Setting.rb +21 -0
- data/tests/models/tc_Signature.rb +14 -0
- data/tests/run_tests.rb +4 -0
- metadata +284 -212
- data/README +0 -36
- data/lib/mod_spox/Cache.rb +0 -57
- data/lib/mod_spox/Monitors.rb +0 -84
- data/lib/mod_spox/Pool.rb +0 -164
- data/lib/mod_spox/models/AuthGroup.rb +0 -16
- data/lib/mod_spox/models/ChannelMode.rb +0 -14
- data/lib/mod_spox/models/NickChannel.rb +0 -45
- data/lib/mod_spox/models/NickGroup.rb +0 -16
data/lib/mod_spox/Pipeline.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
['mod_spox/models/Models.rb',
|
2
2
|
'mod_spox/Logger',
|
3
|
-
'mod_spox/Pool',
|
4
3
|
'mod_spox/Exceptions'].each{|f|require f}
|
5
4
|
module ModSpox
|
6
5
|
|
7
6
|
class Pipeline
|
8
7
|
|
9
8
|
# Create a new Pipeline
|
10
|
-
def initialize
|
9
|
+
def initialize(pool)
|
10
|
+
@pool = pool
|
11
11
|
@hooks = Hash.new
|
12
12
|
@plugins = Hash.new
|
13
13
|
@admin = Models::Group.filter(:name => 'admin').first
|
@@ -93,6 +93,9 @@ module ModSpox
|
|
93
93
|
def populate_signatures(m=nil)
|
94
94
|
@populate_lock.synchronize do
|
95
95
|
@signatures = {}
|
96
|
+
a = Models::Signature.filter(:enabled => false)
|
97
|
+
Logger.warn("Killing #{a.count} signatures")
|
98
|
+
a.destroy
|
96
99
|
Models::Signature.all.each do |s|
|
97
100
|
c = s.signature[0].chr.downcase
|
98
101
|
if(c =~ /^[a-z]$/)
|
@@ -117,7 +120,7 @@ module ModSpox
|
|
117
120
|
begin
|
118
121
|
Logger.info("Pipeline is processing a message: #{message}")
|
119
122
|
parse(message)
|
120
|
-
type = message.class.to_s.gsub(/^(ModSpox::Messages::|#<.+?>::)/, '').gsub(
|
123
|
+
type = message.class.to_s.gsub(/^(ModSpox::Messages::|#<.+?>::)/, '').gsub('::', '_').to_sym
|
121
124
|
mod = type.to_s.gsub(/_.+$/, '').to_sym
|
122
125
|
Logger.info("Pipeline determines that #{message} is of type: #{type}")
|
123
126
|
[type, mod, :all].each do |type|
|
@@ -125,7 +128,18 @@ module ModSpox
|
|
125
128
|
@hooks[type].each_value do |objects|
|
126
129
|
begin
|
127
130
|
objects.each do |v|
|
128
|
-
|
131
|
+
@pool.process do
|
132
|
+
begin
|
133
|
+
v[:object].send(v[:method].to_s, message)
|
134
|
+
rescue Object => boom
|
135
|
+
if(boom.class.to_s == 'SQLite3::BusyException')
|
136
|
+
Database.reset_connections
|
137
|
+
retry
|
138
|
+
else
|
139
|
+
raise boom
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
129
143
|
end
|
130
144
|
rescue Object => boom
|
131
145
|
Logger.warn("Plugin threw exception while attempting to process message: #{boom}\n#{boom.backtrace.join("\n")}")
|
@@ -145,7 +159,7 @@ module ModSpox
|
|
145
159
|
def parse(message)
|
146
160
|
return unless message.kind_of?(Messages::Incoming::Privmsg) || message.kind_of?(Messages::Incoming::Notice)
|
147
161
|
trigger = nil
|
148
|
-
@triggers.each{|t| trigger = t if message.message
|
162
|
+
@triggers.each{|t| trigger = t if message.message[0..t.size-1] == t}
|
149
163
|
if(!trigger.nil? || message.addressed?)
|
150
164
|
return if !trigger.nil? && message.message.length == trigger.length
|
151
165
|
Logger.info("Message has matched against a known trigger")
|
@@ -164,7 +178,7 @@ module ModSpox
|
|
164
178
|
esc_trig = trigger.nil? ? '' : Regexp.escape(trigger)
|
165
179
|
res = message.message.scan(/^#{esc_trig}#{sig.signature}$/)
|
166
180
|
if(res.size > 0)
|
167
|
-
next unless message.source.
|
181
|
+
next unless message.source.in_group?(sig.group) || message.source.in_group?(@admin) || sig.group.nil?
|
168
182
|
next if sig.requirement == 'private' && message.is_public?
|
169
183
|
next if sig.requirement == 'public' && message.is_private?
|
170
184
|
params = Hash.new
|
@@ -174,7 +188,7 @@ module ModSpox
|
|
174
188
|
end
|
175
189
|
if(@plugins.has_key?(sig.plugin.to_sym))
|
176
190
|
begin
|
177
|
-
|
191
|
+
@pool.process{ @plugins[sig.plugin.to_sym].send(sig.values[:method], message, params) }
|
178
192
|
rescue Object => boom
|
179
193
|
Logger.warn("Plugin threw exception while attempting to process message: #{boom}\n#{boom.backtrace.join("\n")}")
|
180
194
|
end
|
data/lib/mod_spox/Plugin.rb
CHANGED
@@ -58,15 +58,39 @@ module ModSpox
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
# Adds a new signature for the given plugin
|
62
|
+
# Required args: :sig and :method
|
63
|
+
# Optional args: :group, :req, :desc, and :params
|
61
64
|
def add_sig(args)
|
62
65
|
raise ModSpox::Exceptions::InvalidType.new('You must provide a hash for creating new signatures') unless args.is_a?(Hash)
|
63
|
-
|
66
|
+
args[:params] = nil unless args[:params]
|
67
|
+
sig = Signature.find_or_create(:signature => args[:sig], :plugin => name, :method => args[:method].to_s,
|
68
|
+
:params => args[:params], :group_id => args[:group].nil? ? nil : args[:group].pk)
|
64
69
|
sig.description = args[:desc] if args.has_key?(:desc)
|
65
|
-
sig.group_id = args[:group].pk if args.has_key?(:group)
|
66
70
|
sig.requirement = args[:req] if args.has_key?(:req)
|
67
|
-
sig.params = args[:params] if args.has_key?(:params)
|
68
71
|
sig.save
|
69
72
|
end
|
73
|
+
|
74
|
+
# to:: Where message is going
|
75
|
+
# message:: message
|
76
|
+
# Send an information message to target
|
77
|
+
def information(to, message)
|
78
|
+
reply to, "\2#{name} (info):\2 #{message}"
|
79
|
+
end
|
80
|
+
|
81
|
+
# to:: Where message is going
|
82
|
+
# message:: message
|
83
|
+
# Send an warning message to target
|
84
|
+
def warning(to, message)
|
85
|
+
reply to, "\2#{name} (warn):\2 #{message}"
|
86
|
+
end
|
87
|
+
|
88
|
+
# to:: Where message is going
|
89
|
+
# message:: message
|
90
|
+
# Send an error message to target
|
91
|
+
def error(to, message)
|
92
|
+
reply to, "\2#{name} (error):\2 #{message}"
|
93
|
+
end
|
70
94
|
|
71
95
|
end
|
72
96
|
end
|
@@ -30,18 +30,24 @@ module ModSpox
|
|
30
30
|
# message:: Messages::Internal::PluginReload
|
31
31
|
# Destroys and reinitializes plugins
|
32
32
|
def reload_plugins(message=nil)
|
33
|
-
@
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
33
|
+
@pipeline << Messages::Internal::QueueSocket.new
|
34
|
+
begin
|
35
|
+
@plugin_lock.synchronize do
|
36
|
+
if(!message.nil? && (message.fresh && message.stale))
|
37
|
+
do_unload(message.stale)
|
38
|
+
FileUtils.remove_file(message.stale)
|
39
|
+
FileUtils.copy(message.fresh, BotConfig[:userpluginpath])
|
40
|
+
do_load(message.stale)
|
41
|
+
Logger.info("Completed reload of plugin: #{message.stale}")
|
42
|
+
else
|
43
|
+
unload_plugins
|
44
|
+
load_plugins
|
45
|
+
end
|
43
46
|
end
|
44
|
-
|
47
|
+
rescue Object => boom
|
48
|
+
Logger.error("PluginManager caught error on plugin reload: #{boom}")
|
49
|
+
ensure
|
50
|
+
@pipeline << Messages::Internal::UnqueueSocket.new
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
@@ -53,6 +59,7 @@ module ModSpox
|
|
53
59
|
# message:: Messages::Internal::PluginLoadRequest
|
54
60
|
# Loads a plugin
|
55
61
|
def load_plugin(message)
|
62
|
+
@pipeline << Messages::Internal::QueueSocket.new
|
56
63
|
begin
|
57
64
|
path = !message.name ? "#{BotConfig[:userpluginpath]}/#{message.path.gsub(/^.+\//, '')}" : "#{BotConfig[:userpluginpath]}/#{message.name}"
|
58
65
|
begin
|
@@ -66,13 +73,16 @@ module ModSpox
|
|
66
73
|
rescue Object => boom
|
67
74
|
Logger.warn("Failed to load plugin: #{message.path} Reason: #{boom}")
|
68
75
|
@pipeline << Messages::Internal::PluginLoadResponse.new(message.requester, false)
|
76
|
+
ensure
|
77
|
+
@pipeline << Messages::Internal::SignaturesUpdate.new
|
78
|
+
@pipeline << Messages::Internal::UnqueueSocket.new
|
69
79
|
end
|
70
|
-
@pipeline << Messages::Internal::SignaturesUpdate.new
|
71
80
|
end
|
72
81
|
|
73
82
|
# message:: Messages::Internal::PluginUnloadRequest
|
74
83
|
# Unloads a plugin
|
75
84
|
def unload_plugin(message)
|
85
|
+
@pipeline << Messages::Internal::QueueSocket.new
|
76
86
|
begin
|
77
87
|
do_unload(message.path)
|
78
88
|
unless(File.symlink?(message.path))
|
@@ -86,8 +96,10 @@ module ModSpox
|
|
86
96
|
rescue Object => boom
|
87
97
|
Logger.warn("Failed to unload plugin: #{message.path} Reason: #{boom}")
|
88
98
|
@pipeline << Messages::Internal::PluginUnloadResponse.new(message.requester, false)
|
99
|
+
ensure
|
100
|
+
@pipeline << Messages::Internal::UnqueueSocket.new
|
101
|
+
@pipeline << Messages::Internal::SignaturesUpdate.new
|
89
102
|
end
|
90
|
-
@pipeline << Messages::Internal::SignaturesUpdate.new
|
91
103
|
end
|
92
104
|
|
93
105
|
# message:: Messages::Internal::PluginModuleRequest
|
@@ -116,7 +128,7 @@ module ModSpox
|
|
116
128
|
# Loads and initializes plugins
|
117
129
|
def load_plugins
|
118
130
|
@pipeline << Messages::Internal::TimerClear.new
|
119
|
-
Models::Signature.
|
131
|
+
Models::Signature.set(:enabled => false)
|
120
132
|
[BotConfig[:pluginpath], BotConfig[:userpluginpath]].each do |path|
|
121
133
|
Dir.new(path).each do |file|
|
122
134
|
if(file =~ /^[^\.].+\.rb$/)
|
@@ -129,11 +141,11 @@ module ModSpox
|
|
129
141
|
end
|
130
142
|
end
|
131
143
|
@pipeline << Messages::Internal::SignaturesUpdate.new
|
144
|
+
@pipeline << Messages::Internal::PluginsReady.new
|
132
145
|
end
|
133
146
|
|
134
147
|
# Destroys plugins
|
135
148
|
def unload_plugins
|
136
|
-
Models::Signature.destroy_all
|
137
149
|
@plugins.each_pair do |sym, holder|
|
138
150
|
begin
|
139
151
|
holder.plugin.destroy unless holder.plugin.nil?
|
@@ -162,6 +174,7 @@ module ModSpox
|
|
162
174
|
@plugins[plugin.to_sym] = PluginHolder.new(klass.new({:pipeline => @pipeline, :plugin_module => @plugins_module}))
|
163
175
|
end
|
164
176
|
Logger.info("Properly initialized new plugin: #{plugin}")
|
177
|
+
Database.reset_connections
|
165
178
|
end
|
166
179
|
Logger.info("All plugins found at: #{path} have been loaded")
|
167
180
|
rescue Object => boom
|
@@ -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
|
data/lib/mod_spox/Socket.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
['iconv',
|
2
2
|
'mod_spox/Logger',
|
3
|
-
'mod_spox/Pool',
|
4
3
|
'mod_spox/Exceptions',
|
5
4
|
'mod_spox/messages/Messages',
|
6
5
|
'mod_spox/models/Models',
|
7
|
-
'mod_spox/Pipeline'
|
6
|
+
'mod_spox/Pipeline',
|
7
|
+
'mod_spox/PriorityQueue'].each{|f|require f}
|
8
8
|
|
9
9
|
module ModSpox
|
10
10
|
|
@@ -18,6 +18,7 @@ module ModSpox
|
|
18
18
|
attr_reader :server
|
19
19
|
attr_reader :port
|
20
20
|
attr_reader :socket
|
21
|
+
attr_reader :connected_at
|
21
22
|
|
22
23
|
# factory:: MessageFactory to parse messages
|
23
24
|
# server:: Server to connect to
|
@@ -26,12 +27,13 @@ module ModSpox
|
|
26
27
|
# burst_in:: Number of seconds allowed to burst
|
27
28
|
# burst:: Number of lines allowed to be sent within the burst_in time limit
|
28
29
|
# Create a new Socket
|
29
|
-
def initialize(bot, server, port, delay=2, burst_in=2, burst=4)
|
30
|
+
def initialize(bot, server=nil, port=nil, delay=2, burst_in=2, burst=4)
|
31
|
+
@pool = bot.pool
|
30
32
|
@factory = bot.factory
|
31
33
|
@pipeline = bot.pipeline
|
32
34
|
@dcc = bot.dcc_sockets
|
33
35
|
@server = server
|
34
|
-
@port = port
|
36
|
+
@port = port
|
35
37
|
@sent = 0
|
36
38
|
@received = 0
|
37
39
|
@delay = delay.to_f > 0 ? delay.to_f : 2.0
|
@@ -41,19 +43,35 @@ module ModSpox
|
|
41
43
|
@time_check = nil
|
42
44
|
@check_burst = 0
|
43
45
|
@pause = false
|
44
|
-
@sendq =
|
46
|
+
@sendq = PriorityQueue.new
|
45
47
|
@lock = Mutex.new
|
46
48
|
@ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
|
49
|
+
@connected_at = nil
|
50
|
+
@empty_lines = 0
|
51
|
+
@max_empty = 5
|
52
|
+
@servers = Array.new
|
53
|
+
@connect_locker = Mutex.new
|
47
54
|
end
|
48
55
|
|
49
56
|
# Connects to the IRC server
|
50
57
|
def connect
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
return unless @connect_locker.try_lock
|
59
|
+
begin
|
60
|
+
populate_servers if @servers.empty?
|
61
|
+
s = @servers.pop
|
62
|
+
@server = s.host
|
63
|
+
@port = s.port.to_i
|
64
|
+
Logger.info("Establishing connection to #{@server}:#{@port}")
|
65
|
+
@socket = TCPSocket.new(@server, @port)
|
66
|
+
@socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true)
|
67
|
+
@empty_lines = 0
|
68
|
+
s.connected = true
|
69
|
+
s.save
|
70
|
+
@connected_at = Time.now
|
71
|
+
@pipeline << Messages::Internal::Connected.new(@server, @port)
|
72
|
+
ensure
|
73
|
+
@connect_locker.unlock
|
74
|
+
end
|
57
75
|
end
|
58
76
|
|
59
77
|
# new_delay:: Seconds to delay between bursts
|
@@ -87,65 +105,81 @@ module ModSpox
|
|
87
105
|
# Sends a string to the IRC server
|
88
106
|
def write(message)
|
89
107
|
return if message.nil?
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
108
|
+
begin
|
109
|
+
@socket.puts(message + "\n")
|
110
|
+
@socket.flush
|
111
|
+
Logger.info("<< #{message}")
|
112
|
+
@last_send = Time.new
|
113
|
+
@sent += 1
|
114
|
+
@check_burst += 1
|
115
|
+
@time_check = Time.now.to_i if @time_check.nil?
|
116
|
+
rescue Object => boom
|
117
|
+
Logger.warn("Failed to write message to server. #{boom}")
|
118
|
+
@pipeline << Messages::Internal::Disconnected.new
|
119
|
+
raise Exceptions::Disconnected.new
|
120
|
+
end
|
96
121
|
end
|
97
|
-
|
98
|
-
#
|
99
|
-
|
100
|
-
|
101
|
-
|
122
|
+
|
123
|
+
# string:: string to be processed
|
124
|
+
# Process a string
|
125
|
+
def process(string)
|
126
|
+
string.strip!
|
127
|
+
Logger.info(">> #{string}")
|
128
|
+
if(string[0,5] == 'ERROR')
|
102
129
|
@pipeline << Messages::Internal::Disconnected.new
|
103
|
-
|
104
|
-
server = Models::Server.find_or_create(:host => @server, :port => @port)
|
105
|
-
server.connected = false
|
106
|
-
server.save
|
107
|
-
elsif(tainted_message.length > 0)
|
108
|
-
message = @ic.iconv(tainted_message + ' ')[0..-2]
|
109
|
-
message.strip!
|
110
|
-
Logger.info(">> #{message}")
|
111
|
-
@received += 1
|
112
|
-
begin
|
113
|
-
message.strip!
|
114
|
-
rescue Object => boom
|
115
|
-
#do nothing#
|
116
|
-
ensure
|
117
|
-
@factory << message
|
118
|
-
end
|
130
|
+
raise Exceptions::Disconnected.new
|
119
131
|
end
|
132
|
+
@received += 1
|
133
|
+
@factory << string
|
120
134
|
end
|
121
135
|
|
122
136
|
# message:: String to be sent to server
|
123
137
|
# Queues a message up to be sent to the IRC server
|
124
138
|
def <<(message)
|
125
|
-
@sendq
|
126
|
-
|
139
|
+
@sendq.direct_queue(message)
|
140
|
+
@pool.process{ processor }
|
141
|
+
end
|
142
|
+
|
143
|
+
# target:: Target for outgoing message
|
144
|
+
# message:: Message to send
|
145
|
+
# This queues a message to be sent out of a prioritized
|
146
|
+
# queue. This allows for even message distribution rather
|
147
|
+
# than only on target at a time being flooded.
|
148
|
+
def prioritize_message(target, message)
|
149
|
+
@sendq.priority_queue(target, message)
|
150
|
+
@pool.process{ processor }
|
127
151
|
end
|
128
152
|
|
129
153
|
# Starts the thread for sending messages to the server
|
130
154
|
def processor
|
131
|
-
@lock.
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
@
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
@check_burst
|
155
|
+
return unless @lock.try_lock
|
156
|
+
did_write = false
|
157
|
+
begin
|
158
|
+
loop do
|
159
|
+
write(@sendq.pop)
|
160
|
+
did_write = true
|
161
|
+
if((Time.now.to_i - @time_check) > @burst_in)
|
162
|
+
@time_check = nil
|
163
|
+
@check_burst = 0
|
164
|
+
elsif((Time.now.to_i - @time_check) <= @burst_in && @check_burst >= @burst)
|
165
|
+
Logger.warn("Burst limit hit. Output paused for: #{@delay} seconds")
|
166
|
+
sleep(@delay)
|
167
|
+
@time_check = nil
|
168
|
+
@check_burst = 0
|
169
|
+
end
|
141
170
|
end
|
171
|
+
rescue Exceptions::EmptyQueue => boom
|
172
|
+
Logger.info('Socket reached an empty queue.')
|
173
|
+
ensure
|
174
|
+
@lock.unlock
|
175
|
+
@pool.process{ processor } if did_write
|
142
176
|
end
|
143
177
|
end
|
144
178
|
|
145
179
|
# restart:: Reconnect after closing connection
|
146
180
|
# Closes connection to IRC server
|
147
181
|
def shutdown(restart=false)
|
148
|
-
@socket.close unless @socket.closed?
|
182
|
+
@socket.close unless @socket.nil? || @socket.closed?
|
149
183
|
@kill = true
|
150
184
|
server = Models::Server.find_or_create(:host => @server, :port => @port)
|
151
185
|
server.connected = false
|
@@ -154,6 +188,14 @@ module ModSpox
|
|
154
188
|
connect if restart
|
155
189
|
end
|
156
190
|
|
191
|
+
private
|
192
|
+
|
193
|
+
def populate_servers
|
194
|
+
Models::Server.reverse_order(:priority).each{|s|
|
195
|
+
@servers << s
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
157
199
|
end
|
158
200
|
|
159
201
|
end
|