mod_spox 0.0.2 → 0.0.3
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.
- data/CHANGELOG +1 -1
- data/README +6 -2
- data/bin/mod_spox +15 -9
- data/data/mod_spox/extras/AOLSpeak.rb +271 -0
- data/data/mod_spox/extras/AutoKick.rb +119 -0
- data/data/mod_spox/extras/AutoRejoin.rb +14 -0
- data/data/mod_spox/extras/Bullshit.rb +19 -0
- data/data/mod_spox/extras/Confess.rb +166 -0
- data/data/mod_spox/extras/DevWatch.rb +154 -0
- data/data/mod_spox/extras/EightBall.rb +31 -0
- data/data/mod_spox/extras/Headers.rb +50 -0
- data/data/mod_spox/extras/Karma.rb +16 -17
- data/data/mod_spox/extras/LolSpeak.rb +22 -0
- data/data/mod_spox/extras/PhpCli.rb +146 -0
- data/data/mod_spox/extras/PhpFuncLookup.rb +261 -0
- data/data/mod_spox/extras/Quotes.rb +80 -0
- data/data/mod_spox/extras/Roulette.rb +24 -3
- data/data/mod_spox/extras/Talk.rb +41 -0
- data/data/mod_spox/extras/Translate.rb +95 -0
- data/data/mod_spox/extras/Weather.rb +55 -0
- data/data/mod_spox/plugins/Authenticator.rb +12 -0
- data/data/mod_spox/plugins/Banner.rb +3 -3
- data/data/mod_spox/plugins/Helper.rb +37 -0
- data/lib/mod_spox/Bot.rb +1 -1
- data/lib/mod_spox/ConfigurationWizard.rb +12 -5
- data/lib/mod_spox/MessageFactory.rb +1 -2
- data/lib/mod_spox/Monitors.rb +26 -8
- data/lib/mod_spox/Pipeline.rb +13 -13
- data/lib/mod_spox/PluginManager.rb +12 -1
- data/lib/mod_spox/Pool.rb +212 -29
- data/lib/mod_spox/Socket.rb +6 -6
- data/lib/mod_spox/Timer.rb +6 -8
- data/lib/mod_spox/handlers/Handler.rb +1 -1
- data/lib/mod_spox/handlers/Privmsg.rb +1 -0
- data/lib/mod_spox/handlers/Who.rb +1 -0
- data/lib/mod_spox/messages/internal/PluginRequest.rb +1 -1
- data/lib/mod_spox/messages/outgoing/Privmsg.rb +9 -1
- data/lib/mod_spox/models/Setting.rb +3 -3
- data/lib/mod_spox/models/Signature.rb +1 -1
- metadata +18 -20
@@ -0,0 +1,37 @@
|
|
1
|
+
class Helper < ModSpox::Plugin
|
2
|
+
|
3
|
+
include Models
|
4
|
+
|
5
|
+
def initialize(pipeline)
|
6
|
+
super(pipeline)
|
7
|
+
Signature.find_or_create(:signature => 'help', :plugin => name, :method => 'default_help',
|
8
|
+
:description => 'Display default help information')
|
9
|
+
Signature.find_or_create(:signature => 'help (\S+)', :plugin => name, :method => 'plugin_help',
|
10
|
+
:description => 'Display help information from given plugin').params = [:plugin]
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_help(message, params)
|
14
|
+
plugins = Signature.map(:plugin)
|
15
|
+
plugins.uniq!
|
16
|
+
reply message.replyto, "Plugins currently available for help: #{plugins.join(', ')}"
|
17
|
+
reply message.replyto, "Request help on a plugin: !help Plugin"
|
18
|
+
end
|
19
|
+
|
20
|
+
def plugin_help(message, params)
|
21
|
+
sigs = Signature.filter(:plugin => params[:plugin])
|
22
|
+
if(sigs.count > 0)
|
23
|
+
reply message.source, "Available triggers for plugin: \2#{params[:plugin]}\2"
|
24
|
+
sigs.each do |sig|
|
25
|
+
output = []
|
26
|
+
output << "\2Pattern:\2 #{sig.signature}"
|
27
|
+
output << "\2Parameters:\2 [#{sig.params.join(' | ')}]" if sig.params
|
28
|
+
output << "\2Auth Group:\2 #{Group[sig.group_id].name}" if sig.group_id
|
29
|
+
output << "\2Description:\2 #{sig.description}" if sig.description
|
30
|
+
reply message.source, output.join(' ')
|
31
|
+
end
|
32
|
+
else
|
33
|
+
reply message.replyto, "\2Error:\2 No triggers found for plugin named: #{params[:plugin]}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/mod_spox/Bot.rb
CHANGED
@@ -272,7 +272,7 @@ module ModSpox
|
|
272
272
|
target = message.target.name if message.target.is_a?(Models::Channel)
|
273
273
|
target = message.target.nick if message.target.is_a?(Models::Nick)
|
274
274
|
target = message.target unless target
|
275
|
-
@socket << "PRIVMSG #{target} :#{message.message}"
|
275
|
+
@socket << "PRIVMSG #{target} :#{message.is_action? ? "\cAACTION #{message.message}\cA" : message.message}"
|
276
276
|
end
|
277
277
|
|
278
278
|
# message:: Messages::Outgoing::Notice message
|
@@ -1,4 +1,4 @@
|
|
1
|
-
['etc', '
|
1
|
+
['etc', 'mod_spox/Loader',
|
2
2
|
'mod_spox/BotConfig', 'mod_spox/BaseConfig'].each{|f|require f}
|
3
3
|
|
4
4
|
|
@@ -28,6 +28,12 @@ module ModSpox
|
|
28
28
|
@config << {:id => :admin_password, :string => 'Administrator password: ', :regex => '.+', :default => nil, :value => nil, :echo => false}
|
29
29
|
@config << {:id => :plugin_directory, :string => 'Plugin directory (bot must have write priviliges): ', :regex => '.+', :default => nil, :echo => true}
|
30
30
|
@config << {:id => :trigger, :string => 'Trigger character for plugins: ', :regex => '.', :default => '!', :value => nil, :echo => true}
|
31
|
+
@stuck_visible = true
|
32
|
+
begin
|
33
|
+
require 'termios'
|
34
|
+
@stuck_visible = false
|
35
|
+
rescue Object => boom
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
# Run the configuration wizard
|
@@ -83,7 +89,7 @@ module ModSpox
|
|
83
89
|
# echo:: echo user input
|
84
90
|
# Turns echoing of user input on or off
|
85
91
|
def input_echo(echo=true)
|
86
|
-
return if echo == @echo
|
92
|
+
return if echo == @echo || @stuck_visible
|
87
93
|
term = Termios::getattr($stdin)
|
88
94
|
term.c_lflag |= Termios::ECHO if echo
|
89
95
|
term.c_lflag &= ~Termios::ECHO unless echo
|
@@ -137,11 +143,12 @@ module ModSpox
|
|
137
143
|
Database.db << "CREATE TABLE IF NOT EXISTS nick_modes (id integer primary key auto_increment not null, mode varchar(255) not null, nick_id int not null references nicks, channel_id int references channels, unique index nick_modes_nick_id_channel_id_index (nick_id, channel_id))"
|
138
144
|
Database.db << "CREATE TABLE IF NOT EXISTS servers (id int not null auto_increment primary key, host varchar(255) not null, port int not null default 6667, priority int not null default 0, connected boolean not null default false, unique index servers_server_port_index (server, port))"
|
139
145
|
Database.db << "CREATE TABLE IF NOT EXISTS settings (id int not null auto_increment primary key, name varchar(255) not null unique, value text)"
|
140
|
-
Database.db << "CREATE TABLE IF NOT EXISTS signatures (id int not null auto_increment primary key, signature varchar(255) not null, params varchar(255), group_id int default null references groups, method varchar(255) not null, plugin varchar(255) not null, description text)"
|
146
|
+
Database.db << "CREATE TABLE IF NOT EXISTS signatures (id int not null auto_increment primary key, signature varchar(255) not null, params varchar(255), group_id int default null references groups, method varchar(255) not null, plugin varchar(255) not null, description text, requirement enum('public', 'private', 'both') default 'both' not null)"
|
141
147
|
Database.db << "CREATE TABLE IF NOT EXISTS triggers (id int not null auto_increment primary key, `trigger` varchar(255) unique not null, active boolean not null default false)"
|
142
148
|
Database.db << "CREATE TABLE IF NOT EXISTS groups (id int not null auto_increment primary key, name varchar(255) not null unique)"
|
143
149
|
Database.db << "CREATE TABLE IF NOT EXISTS auth_groups (auth_id int not null references auths, group_id int not null references groups, primary key(auth_id, group_id))"
|
144
150
|
when :pgsql
|
151
|
+
Database.db << "CREATE TYPE sig_requirement AS ENUM ('public', 'private', 'both')"
|
145
152
|
Database.db << "CREATE TABLE nicks (id serial not null primary key, nick text unique not null, username text, real_name text, address text, source text, connected_at timestamp, connected_to text, seconds_idle integer, visible boolean not null default false, away boolean not null default false, botnick boolean not null default false)"
|
146
153
|
Database.db << "CREATE TABLE channels (id serial not null primary key, name text unique not null, password text, autojoin boolean not null default false, topic text, quiet boolean not null default false, parked boolean not null default false)"
|
147
154
|
Database.db << "CREATE TABLE auths (id serial not null primary key, password text, services boolean not null default false, mask text unique, authed boolean not null default false, nick_id integer unique references nicks)"
|
@@ -151,7 +158,7 @@ module ModSpox
|
|
151
158
|
Database.db << "CREATE TABLE nick_channels (channel_id integer not null references channels, nick_id integer not null references nicks, primary key(nick_id, channel_id))"
|
152
159
|
Database.db << "CREATE TABLE nick_modes (id serial not null primary key, mode text not null, nick_id integer not null references nicks, channel_id integer references channels, unique (nick_id, channel_id))"
|
153
160
|
Database.db << "CREATE TABLE servers (id serial not null primary key, host text not null, port integer not null default 6667, priority integer not null default 0, connected boolean not null default false, unique (host, port))"
|
154
|
-
Database.db << "CREATE TABLE signatures (id serial not null primary key, signature text not null, params text, group_id integer default null references groups, method text not null, plugin text not null, description text)"
|
161
|
+
Database.db << "CREATE TABLE signatures (id serial not null primary key, signature text not null, params text, group_id integer default null references groups, method text not null, plugin text not null, description text, requirement sig_requirement default 'both' not null)"
|
155
162
|
Database.db << "CREATE TABLE settings (id serial not null primary key, name text unique not null, value text)"
|
156
163
|
Database.db << "CREATE TABLE triggers (id serial not null primary key, trigger text unique not null, active boolean not null default false)"
|
157
164
|
Database.db << "CREATE TABLE auth_groups (auth_id integer not null references auths, group_id integer not null references groups, primary key (auth_id, group_id))"
|
@@ -167,7 +174,7 @@ module ModSpox
|
|
167
174
|
Database.db << "CREATE TABLE if not exists servers (id integer primary key autoincrement, host string NOT NULL, port integer NOT NULL DEFAULT 6667, priority integer NOT NULL DEFAULT 0, connected boolean NOT NULL DEFAULT 'f')"
|
168
175
|
Database.db << "CREATE UNIQUE INDEX if not exists servers_host_port_index on servers (host, port)"
|
169
176
|
Database.db << "CREATE TABLE if not exists settings (id integer PRIMARY KEY AUTOINCREMENT, name string UNIQUE NOT NULL, value text)"
|
170
|
-
Database.db << "CREATE TABLE if not exists signatures (id integer PRIMARY KEY AUTOINCREMENT, signature string NOT NULL UNIQUE, params string, group_id integer DEFAULT NULL REFERENCES groups, method string NOT NULL, plugin string NOT NULL, description text)"
|
177
|
+
Database.db << "CREATE TABLE if not exists signatures (id integer PRIMARY KEY AUTOINCREMENT, signature string NOT NULL UNIQUE, params string, group_id integer DEFAULT NULL REFERENCES groups, method string NOT NULL, plugin string NOT NULL, description text, requirement text not null default 'both')"
|
171
178
|
Database.db << "CREATE TABLE if not exists triggers (id integer PRIMARY KEY AUTOINCREMENT, trigger string UNIQUE NOT NULL, active boolean NOT NULL DEFAULT 'f')"
|
172
179
|
Database.db << "CREATE TABLE if not exists groups (id integer PRIMARY KEY AUTOINCREMENT, name string UNIQUE NOT NULL COLLATE NOCASE)"
|
173
180
|
Database.db << "CREATE TABLE if not exists auth_groups(auth_id integer REFERENCES auths, group_id integer REFERENCES groups)"
|
@@ -5,10 +5,9 @@ module ModSpox
|
|
5
5
|
# pipeline:: Message pipeline
|
6
6
|
# Create a new MessageFactory
|
7
7
|
def initialize(pipeline)
|
8
|
-
super(
|
8
|
+
super()
|
9
9
|
@pipeline = pipeline
|
10
10
|
@handlers = Hash.new
|
11
|
-
@queue = Queue.new
|
12
11
|
Logger.log("Created new factory queue: #{@queue}", 15)
|
13
12
|
build_handlers
|
14
13
|
start_pool
|
data/lib/mod_spox/Monitors.rb
CHANGED
@@ -34,24 +34,42 @@ module ModSpox
|
|
34
34
|
|
35
35
|
# Create a new Boolean Monitor
|
36
36
|
def initialize
|
37
|
-
@
|
37
|
+
@threads = []
|
38
38
|
end
|
39
39
|
|
40
40
|
# Stop waiting
|
41
41
|
def wakeup
|
42
|
-
|
43
|
-
@
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
return if @threads.empty?
|
43
|
+
@threads.each do |thread|
|
44
|
+
if(thread.status == 'sleep')
|
45
|
+
Logger.log("Sending wakeup for thread: #{thread}", 5)
|
46
|
+
thread.run
|
47
|
+
Logger.log("Status of thread to wakeup: #{thread.status}", 5)
|
48
|
+
Logger.log("Calling thread is: #{Thread.current}", 5)
|
49
|
+
else
|
50
|
+
Logger.log("Thread to wakeup has been killed: #{thread}")
|
51
|
+
end
|
52
|
+
@threads.delete(thread)
|
53
|
+
end
|
47
54
|
end
|
48
55
|
|
49
56
|
# Start waiting
|
50
57
|
def wait
|
51
|
-
@
|
52
|
-
Logger.log("Stopping execution of thread: #{
|
58
|
+
@threads << Thread.current
|
59
|
+
Logger.log("Stopping execution of thread: #{Thread.current}", 5)
|
53
60
|
Thread.stop
|
54
61
|
end
|
62
|
+
|
63
|
+
# Returns if a thread is currently waiting in this monitor
|
64
|
+
def thread_waiting?(thread)
|
65
|
+
return @threads.include?(thread)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Removes a thread from the list of waiting. Will be removed automatically
|
69
|
+
# if thread has been killed on next call to wakeup
|
70
|
+
def remove_thread(thread)
|
71
|
+
@threads.delete(thread) if @threads.include?(thread)
|
72
|
+
end
|
55
73
|
|
56
74
|
end
|
57
75
|
end
|
data/lib/mod_spox/Pipeline.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
module ModSpox
|
2
2
|
|
3
3
|
class Pipeline < Pool
|
4
|
-
|
5
|
-
# procs:: number of threads running in Pool
|
4
|
+
|
6
5
|
# Create a new Pipeline
|
7
|
-
def initialize
|
8
|
-
super(
|
6
|
+
def initialize
|
7
|
+
super()
|
9
8
|
@timeout = 20 # Anything over 20 seconds and we assume a plugin locked up the thread
|
10
|
-
@
|
11
|
-
Logger.log("Created queue #{@messageq} in pipeline", 10)
|
9
|
+
Logger.log("Created queue #{@queue} in pipeline", 10)
|
12
10
|
@hooks = Hash.new
|
13
11
|
@plugins = Hash.new
|
14
12
|
@admin = Models::Group.filter(:name => 'admin').first
|
@@ -23,7 +21,7 @@ module ModSpox
|
|
23
21
|
# Queues a message to send down pipeline
|
24
22
|
def <<(message)
|
25
23
|
Logger.log("Message added to pipeline queue: #{message}", 5)
|
26
|
-
@
|
24
|
+
@queue << message
|
27
25
|
end
|
28
26
|
|
29
27
|
# plugin:: Plugin to hook to pipeline
|
@@ -65,9 +63,9 @@ module ModSpox
|
|
65
63
|
def unhook(object, method, type)
|
66
64
|
Logger.log("Object #{object.class.to_s} unhooking from messages of type: #{type}", 10)
|
67
65
|
type = type.gsub(/::/, '_').to_sym unless type.is_a?(Symbol)
|
68
|
-
name = object.class.gsub(/^.+:/, '').to_sym
|
69
|
-
raise
|
70
|
-
raise
|
66
|
+
name = object.class.to_s.gsub(/^.+:/, '').to_sym
|
67
|
+
raise Exceptions::InvalidValue.new("Unknown hook type given: #{type.to_s}") unless @hooks.has_key?(type)
|
68
|
+
raise Exceptions::InvalidValue.new("Unknown object hooked: #{name.to_s}") unless @hooks[type].has_key?(name)
|
71
69
|
@hooks[type][name].each{|hook|
|
72
70
|
@hooks[type][name].delete(hook) if hook[:method] == method
|
73
71
|
}
|
@@ -98,8 +96,8 @@ module ModSpox
|
|
98
96
|
|
99
97
|
# Processes messages
|
100
98
|
def processor
|
99
|
+
message = @queue.pop
|
101
100
|
begin
|
102
|
-
message = @messageq.pop
|
103
101
|
Logger.log("Pipeline is processing a message: #{message}", 10)
|
104
102
|
parse(message)
|
105
103
|
type = message.class.to_s.gsub(/^ModSpox::Messages::/, '').gsub(/::/, '_').to_sym
|
@@ -140,10 +138,12 @@ module ModSpox
|
|
140
138
|
res = message.message.scan(/^#{trigger}#{sig.signature}$/)
|
141
139
|
if(res.size > 0)
|
142
140
|
next unless message.source.auth_groups.include?(sig.group) || message.source.auth_groups.include?(@admin) ||sig.group.nil?
|
141
|
+
next if sig.requirement == 'private' && message.is_public?
|
142
|
+
next if sig.requirement == 'public' && message.is_private?
|
143
143
|
params = Hash.new
|
144
144
|
sig.params.size.times do |i|
|
145
|
-
params[sig.params[i
|
146
|
-
Logger.log("Signature params: #{sig.params[i
|
145
|
+
params[sig.params[i].to_sym] = res[0][i]
|
146
|
+
Logger.log("Signature params: #{sig.params[i].to_sym} = #{res[0][i]}")
|
147
147
|
end
|
148
148
|
if(@plugins.has_key?(sig.plugin.to_sym))
|
149
149
|
begin
|
@@ -14,6 +14,7 @@ module ModSpox
|
|
14
14
|
@pipeline.hook(self, :unload_plugin, :Internal_PluginUnloadRequest)
|
15
15
|
@pipeline.hook(self, :reload_plugins, :Internal_PluginReload)
|
16
16
|
@pipeline.hook(self, :send_modules, :Internal_PluginModuleRequest)
|
17
|
+
@pipeline.hook(self, :plugin_request, :Internal_PluginRequest)
|
17
18
|
@plugins_module = Module.new
|
18
19
|
load_plugins
|
19
20
|
end
|
@@ -34,7 +35,6 @@ module ModSpox
|
|
34
35
|
# Loads a plugin
|
35
36
|
def load_plugin(message)
|
36
37
|
begin
|
37
|
-
Logger.log("THE MESSAGE NAME IS: #{message.name}")
|
38
38
|
path = !message.name ? BotConfig[:userpluginpath] : "#{BotConfig[:userpluginpath]}/#{message.name}"
|
39
39
|
FileUtils.copy(message.path, path)
|
40
40
|
reload_plugins
|
@@ -69,6 +69,17 @@ module ModSpox
|
|
69
69
|
@pipeline << Messages::Internal::PluginModuleResponse.new(message.requester, @plugins_module)
|
70
70
|
end
|
71
71
|
|
72
|
+
# message:: Messages::Internal::PluginRequest
|
73
|
+
# Returns a plugin to requesting object
|
74
|
+
def plugin_request(message)
|
75
|
+
if(@plugins.has_key?(message.plugin))
|
76
|
+
response = Messages::Internal::PluginResponse.new(message.requester, @plugins[message.plugin])
|
77
|
+
else
|
78
|
+
response = Messages::Internal::PluginResponse.new(message.requester, nil)
|
79
|
+
end
|
80
|
+
@pipeline << response
|
81
|
+
end
|
82
|
+
|
72
83
|
private
|
73
84
|
|
74
85
|
# Loads and initializes plugins
|
data/lib/mod_spox/Pool.rb
CHANGED
@@ -1,49 +1,232 @@
|
|
1
1
|
module ModSpox
|
2
2
|
|
3
|
-
# The Pool class is used to reduce thread creation.
|
4
|
-
#
|
5
|
-
# way to process many objects in an asynchronous manner
|
3
|
+
# The Pool class is used to reduce thread creation. It provides
|
4
|
+
# an easy way to process many objects in an asynchronous manner.
|
6
5
|
class Pool
|
7
6
|
|
8
|
-
#
|
7
|
+
#
|
8
|
+
def Pool.max_threads(num=nil)
|
9
|
+
if(num.nil?)
|
10
|
+
return @@max_threads
|
11
|
+
else
|
12
|
+
num = num.to_i
|
13
|
+
raise Exceptions::InvalidValue.new('Maximum threads setting must be a positive integer') if num < 1
|
14
|
+
@@max_threads = num
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def Pool.max_thread_life(num=nil)
|
19
|
+
if(num.nil?)
|
20
|
+
return @@max_thread_life
|
21
|
+
else
|
22
|
+
num = num.to_i
|
23
|
+
raise Exceptions::InvalidValue.new('Maximum thread life setting must be a positive integer') if num < 1
|
24
|
+
@@max_thread_life = num
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Action thread is to perform
|
29
|
+
attr_reader :proc
|
30
|
+
# Storage space that the pool will be processing
|
31
|
+
attr_reader :queue
|
32
|
+
|
9
33
|
# Create a new Pool
|
10
|
-
def initialize
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@kill = false
|
34
|
+
def initialize
|
35
|
+
@proc = Proc.new{ run_processor }
|
36
|
+
@queue = PoolQueue.new
|
14
37
|
end
|
15
38
|
|
16
|
-
#
|
39
|
+
# Destroys this pool
|
17
40
|
def destroy
|
18
|
-
|
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)
|
41
|
+
Pool.remove_pool(self)
|
24
42
|
end
|
25
43
|
|
26
44
|
# Starts the pool
|
27
45
|
def start_pool
|
28
|
-
|
29
|
-
@threads << Thread.new{
|
30
|
-
until @kill do
|
31
|
-
processor
|
32
|
-
end
|
33
|
-
}
|
34
|
-
end
|
46
|
+
Pool.add_pool(self)
|
35
47
|
end
|
36
48
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
49
|
+
private
|
50
|
+
|
51
|
+
def run_processor
|
52
|
+
begin
|
53
|
+
processor
|
54
|
+
rescue Object => boom
|
55
|
+
Logger.log("Pool encountered an error processing code block: #{boom}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
44
59
|
def processor
|
45
60
|
raise Exceptions::NotImplemented.new('Processor method has not been implemented')
|
46
61
|
end
|
62
|
+
|
63
|
+
# Running pools
|
64
|
+
@@pools = Array.new
|
65
|
+
# Threads running in pool
|
66
|
+
@@threads = Hash.new
|
67
|
+
# Lock for thread safety
|
68
|
+
@@lock = Mutex.new
|
69
|
+
# Maximum number of threads in pool
|
70
|
+
@@max_threads = 10
|
71
|
+
# Maximum number of seconds a thread may spend waiting for an action
|
72
|
+
@@max_thread_life = 60
|
73
|
+
# Thread to tend to pool actions
|
74
|
+
@@pool_thread = nil
|
75
|
+
# Informs threads to halt
|
76
|
+
@@kill = false
|
77
|
+
# Place for threads to wait for actions
|
78
|
+
@@thread_stopper = Monitors::Boolean.new
|
79
|
+
# Timer for the Pool thread
|
80
|
+
@@watcher_timer = Monitors::Timer.new
|
81
|
+
|
82
|
+
# Adds a new thread to the pool
|
83
|
+
def Pool.add_thread
|
84
|
+
if(@@threads.size < @@max_threads)
|
85
|
+
thread = Thread.new do
|
86
|
+
sleep(0.01)
|
87
|
+
until(@@kill) do
|
88
|
+
Pool.schedule_thread
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@@threads[thread] = Time.now
|
92
|
+
Logger.log("New thread added to pool: #{thread}")
|
93
|
+
else
|
94
|
+
raise Exceptions::BotException.new("Reached maximum thread pool size: #{@@max_threads} threads")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# thread:: Thread to delete
|
99
|
+
# Removes thread from pool
|
100
|
+
def Pool.delete_thread(thread)
|
101
|
+
@@lock.synchronize do
|
102
|
+
@@threads.delete(thread) if @@threads.has_key?(thread)
|
103
|
+
@@thread_stopper.remove_thread(thread)
|
104
|
+
thread.kill
|
105
|
+
Logger.log("Thread removed from thread pool: #{thread}")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the largest queue size of all available pools
|
110
|
+
def Pool.max_queue_size
|
111
|
+
size = 0
|
112
|
+
@@pools.each do |pool|
|
113
|
+
size = pool.queue.size if pool.queue.size > size
|
114
|
+
end
|
115
|
+
return size
|
116
|
+
end
|
117
|
+
|
118
|
+
# Schedules a thread within the pool
|
119
|
+
def Pool.schedule_thread
|
120
|
+
run_pool = nil
|
121
|
+
@@lock.synchronize do
|
122
|
+
@@pools.each do |pool|
|
123
|
+
run_pool = pool if (run_pool.nil? && pool.queue.size > 0) || (!run_pool.nil? && (run_pool.queue.size < pool.queue.size))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
unless(run_pool.nil?)
|
127
|
+
@@threads[Thread.current] = Time.now
|
128
|
+
run_pool.proc.call
|
129
|
+
@@threads[Thread.current] = Time.now
|
130
|
+
else
|
131
|
+
@@thread_stopper.wait
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Starts the master thread for Pool maintanence
|
136
|
+
def Pool.start_watcher
|
137
|
+
@@pool_thread = Thread.new{
|
138
|
+
until(@@kill) do
|
139
|
+
sleep_time = 0
|
140
|
+
begin
|
141
|
+
waiters = Pool.waiting_threads
|
142
|
+
unless(waiters.empty?)
|
143
|
+
waiters.each do |thread|
|
144
|
+
time = Time.now.to_i - @@threads[thread].to_i
|
145
|
+
sleep_time = (@@max_thread_life - time).to_i if sleep_time = 0 || (@@max_thread_life - time).to_i < sleep_time
|
146
|
+
if(time > @@max_thread_life)
|
147
|
+
Pool.delete_thread(thread)
|
148
|
+
elsif(!['sleep', 'run'].include?(thread.status))
|
149
|
+
Pool.delete_thread(thread)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
else
|
153
|
+
if(Pool.max_queue_size > 0)
|
154
|
+
Pool.add_thread
|
155
|
+
end
|
156
|
+
end
|
157
|
+
rescue Object => boom
|
158
|
+
Logger.log("Pool watcher caught an error (ignoring): #{boom}")
|
159
|
+
end
|
160
|
+
sleep_time = 1 if sleep_time == 0
|
161
|
+
sleep_time = nil if sleep_time < 0
|
162
|
+
Logger.log("Pool watcher thread is now sleeping for: #{sleep_time.nil? ? 'forever' : "#{sleep_time} seconds"}")
|
163
|
+
@@watcher_timer.wait(sleep_time)
|
164
|
+
end
|
165
|
+
} if @@pool_thread.nil?
|
166
|
+
end
|
167
|
+
|
168
|
+
# Forces sleeping threads to wake up
|
169
|
+
def Pool.wakeup_threads
|
170
|
+
@@watcher_timer.wakeup
|
171
|
+
@@thread_stopper.wakeup
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns list of threads currently waiting for an action to process
|
175
|
+
def Pool.waiting_threads
|
176
|
+
waiting = []
|
177
|
+
@@threads.each_pair do |thread,time|
|
178
|
+
waiting << thread if @@thread_stopper.thread_waiting?(thread)
|
179
|
+
end
|
180
|
+
return waiting
|
181
|
+
end
|
182
|
+
|
183
|
+
# Adds a Pool to the master Pool list
|
184
|
+
def Pool.add_pool(pool)
|
185
|
+
@@pools << pool
|
186
|
+
@@lock.synchronize do
|
187
|
+
Pool.start_watcher
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Removes a Pool from the master Pool list
|
192
|
+
def Pool.remove_pool(pool)
|
193
|
+
@@pools.delete(pool)
|
194
|
+
@@kill = true if @@pools.empty?
|
195
|
+
end
|
196
|
+
|
197
|
+
# Modified Queue to properly interact with Pool
|
198
|
+
class PoolQueue < Queue
|
199
|
+
|
200
|
+
def initialize
|
201
|
+
super
|
202
|
+
@lock = Mutex.new
|
203
|
+
end
|
204
|
+
|
205
|
+
def <<(val)
|
206
|
+
@lock.synchronize do
|
207
|
+
super
|
208
|
+
Pool.wakeup_threads
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def push(val)
|
213
|
+
@lock.synchronize do
|
214
|
+
super
|
215
|
+
Pool.wakeup_threads
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def pop
|
220
|
+
@lock.synchronize do
|
221
|
+
if(size > 0)
|
222
|
+
super
|
223
|
+
else
|
224
|
+
raise Exceptions::BotException.new("Queue is currently empty")
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
47
230
|
|
48
231
|
end
|
49
232
|
|
data/lib/mod_spox/Socket.rb
CHANGED
@@ -39,6 +39,7 @@ module ModSpox
|
|
39
39
|
def connect
|
40
40
|
Logger.log("Establishing connection to #{@server}:#{@port}", 10)
|
41
41
|
@socket = TCPSocket.new(@server, @port)
|
42
|
+
@socket.sync = true
|
42
43
|
server = Models::Server.find_or_create(:host => @server, :port => @port)
|
43
44
|
server.connected = true
|
44
45
|
server.save
|
@@ -58,7 +59,7 @@ module ModSpox
|
|
58
59
|
# new_burst:: Number of lines allowed in burst
|
59
60
|
# Resets the burst
|
60
61
|
def burst=(new_burst)
|
61
|
-
raise Exceptions::InvalidValue('Burst value must be a positive number') unless
|
62
|
+
raise Exceptions::InvalidValue('Burst value must be a positive number') unless new_burst.to_i > 0
|
62
63
|
@burst = new_burst
|
63
64
|
end
|
64
65
|
|
@@ -84,7 +85,7 @@ module ModSpox
|
|
84
85
|
@last_send = Time.new
|
85
86
|
@sent += 1
|
86
87
|
@check_burst += 1
|
87
|
-
@check_time = Time.now if @check_time.nil?
|
88
|
+
@check_time = Time.now.to_i if @check_time.nil?
|
88
89
|
end
|
89
90
|
|
90
91
|
# Retrieves a string from the server
|
@@ -126,11 +127,11 @@ module ModSpox
|
|
126
127
|
@writer_thread = Thread.new{
|
127
128
|
until @kill do
|
128
129
|
write(@sendq.pop)
|
129
|
-
if((Time.now - @check_time) > @burst_in
|
130
|
-
sleep(@delay)
|
130
|
+
if((Time.now.to_i - @check_time) > @burst_in)
|
131
131
|
@check_time = nil
|
132
132
|
@check_burst = 0
|
133
|
-
elsif((Time.now - @check_time)
|
133
|
+
elsif((Time.now.to_i - @check_time) >= @burst_in && @check_burst >= @burst)
|
134
|
+
sleep(@delay)
|
134
135
|
@check_time = nil
|
135
136
|
@check_burst = 0
|
136
137
|
end
|
@@ -145,7 +146,6 @@ module ModSpox
|
|
145
146
|
Kernel.select([@socket], nil, nil, nil)
|
146
147
|
@factory << read
|
147
148
|
end
|
148
|
-
@sendq.clear
|
149
149
|
}
|
150
150
|
end
|
151
151
|
|
data/lib/mod_spox/Timer.rb
CHANGED
@@ -3,14 +3,12 @@ module ModSpox
|
|
3
3
|
class Timer < Pool
|
4
4
|
|
5
5
|
# pipeline:: message pipeline
|
6
|
-
# procs:: number of threads in the pool
|
7
6
|
# Create a new Timer
|
8
|
-
def initialize(pipeline
|
9
|
-
super(
|
7
|
+
def initialize(pipeline)
|
8
|
+
super()
|
10
9
|
@pipeline = pipeline
|
11
10
|
@timers = Array.new
|
12
|
-
@
|
13
|
-
Logger.log("Created queue: #{@actions} in timer", 10)
|
11
|
+
Logger.log("Created queue: #{@queue} in timer", 10)
|
14
12
|
@monitor = Monitors::Timer.new
|
15
13
|
@thread = nil
|
16
14
|
@stop_timer = false
|
@@ -105,7 +103,7 @@ module ModSpox
|
|
105
103
|
|
106
104
|
# Clears all actions in the timer's queue
|
107
105
|
def clear
|
108
|
-
@
|
106
|
+
@queue.clear
|
109
107
|
@timers.clear
|
110
108
|
end
|
111
109
|
|
@@ -117,14 +115,14 @@ module ModSpox
|
|
117
115
|
for action in @timers do
|
118
116
|
action.tick(time_passed)
|
119
117
|
if(action.due?)
|
120
|
-
@
|
118
|
+
@queue << action.schedule
|
121
119
|
end
|
122
120
|
end
|
123
121
|
end
|
124
122
|
|
125
123
|
# Process the actions
|
126
124
|
def processor
|
127
|
-
action = @
|
125
|
+
action = @queue.pop
|
128
126
|
begin
|
129
127
|
action.run
|
130
128
|
remove(action) if action.is_complete?
|
@@ -10,7 +10,7 @@ module ModSpox
|
|
10
10
|
protected
|
11
11
|
|
12
12
|
def find_model(string)
|
13
|
-
if(string =~ /^[A-Za-z\|\\\{\}\[\]\^\`~\_\-]
|
13
|
+
if(string =~ /^[A-Za-z\|\\\{\}\[\]\^\`~\_\-]+[A-Za-z0-9\|\\\{\}\[\]\^\`~\_\-]*$/)
|
14
14
|
Logger.log("Model: #{string} -> Nick")
|
15
15
|
return Models::Nick.find_or_create(:nick => string)
|
16
16
|
elsif(string =~ /^[&#+!]/)
|
@@ -17,6 +17,7 @@ module ModSpox
|
|
17
17
|
source.source = base_source
|
18
18
|
source.save
|
19
19
|
end
|
20
|
+
Models::NickChannel.find_or_create(:channel_id => target.pk, :nick_id => source.pk) if target.is_a?(ModSpox::Models::Channel)
|
20
21
|
return Messages::Incoming::Privmsg.new(string, source, target, message)
|
21
22
|
else
|
22
23
|
Logger.log('Failed to match PRIVMSG message')
|
@@ -35,6 +35,7 @@ module ModSpox
|
|
35
35
|
@raw_cache[location] << string
|
36
36
|
if(location[0].chr !~ /[A-Za-z]/)
|
37
37
|
channel = find_model(location)
|
38
|
+
Models::NickChannel.find_or_create(:channel_id => channel.pk, :nick_id => nick.pk)
|
38
39
|
if(info.include?('+'))
|
39
40
|
Models::NickMode.find_or_create(:channel_id => channel.pk, :nick_id => nick.pk, :mode => 'v')
|
40
41
|
elsif(info.include?('@'))
|