mod_spox 0.1.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +36 -0
- data/INSTALL +2 -2
- data/README +0 -1
- data/bin/mod_spox +51 -12
- data/data/mod_spox/extras/AOLSpeak.rb +5 -18
- data/data/mod_spox/extras/AutoKick.rb +44 -23
- data/data/mod_spox/extras/AutoMode.rb +2 -5
- data/data/mod_spox/extras/AutoRejoin.rb +21 -0
- data/data/mod_spox/extras/Bouncer.rb +10 -10
- data/data/mod_spox/extras/Bytes.rb +12 -0
- data/data/mod_spox/extras/Confess.rb +131 -52
- data/data/mod_spox/extras/DCC.rb +189 -0
- data/data/mod_spox/extras/DevWatch.rb +32 -33
- data/data/mod_spox/extras/FloodKicker.rb +129 -0
- data/data/mod_spox/extras/GoogleIt.rb +13 -0
- data/data/mod_spox/extras/Headers.rb +31 -4
- data/data/mod_spox/extras/Karma.rb +103 -49
- data/data/mod_spox/extras/Logger.rb +45 -30
- data/data/mod_spox/extras/LolSpeak.rb +1 -1
- data/data/mod_spox/extras/NickServ.rb +83 -0
- data/data/mod_spox/extras/PhpCli.rb +12 -15
- data/data/mod_spox/extras/PhpFuncLookup.rb +57 -25
- data/data/mod_spox/extras/Quotes.rb +5 -4
- data/data/mod_spox/extras/RegexTracker.rb +160 -0
- data/data/mod_spox/extras/Roulette.rb +22 -23
- data/data/mod_spox/extras/Search.rb +3 -2
- data/data/mod_spox/extras/Slashdot.rb +35 -0
- data/data/mod_spox/extras/Topten.rb +5 -5
- data/data/mod_spox/extras/TracTicket.rb +68 -0
- data/data/mod_spox/extras/Translate.rb +69 -30
- data/data/mod_spox/extras/Twitter.rb +372 -0
- data/data/mod_spox/extras/UrbanDictionary.rb +21 -12
- data/data/mod_spox/extras/Weather.rb +1 -1
- data/data/mod_spox/plugins/Authenticator.rb +63 -30
- data/data/mod_spox/plugins/Banner.rb +164 -151
- data/data/mod_spox/plugins/Helper.rb +18 -7
- data/data/mod_spox/plugins/PluginLoader.rb +46 -22
- data/data/mod_spox/plugins/PoolConfig.rb +52 -0
- data/data/mod_spox/plugins/Quitter.rb +1 -1
- data/data/mod_spox/plugins/Status.rb +28 -0
- data/lib/mod_spox/Action.rb +20 -3
- data/lib/mod_spox/BaseConfig.rb +1 -0
- data/lib/mod_spox/Bot.rb +98 -75
- data/lib/mod_spox/BotConfig.rb +14 -6
- data/lib/mod_spox/ConfigurationWizard.rb +94 -105
- data/lib/mod_spox/Database.rb +33 -13
- data/lib/mod_spox/Helpers.rb +67 -38
- data/lib/mod_spox/Loader.rb +25 -5
- data/lib/mod_spox/Logger.rb +20 -62
- data/lib/mod_spox/MessageFactory.rb +34 -25
- data/lib/mod_spox/Monitors.rb +5 -0
- data/lib/mod_spox/Pipeline.rb +40 -51
- data/lib/mod_spox/Plugin.rb +40 -9
- data/lib/mod_spox/PluginManager.rb +46 -38
- data/lib/mod_spox/Pool.rb +129 -143
- data/lib/mod_spox/Socket.rb +41 -50
- data/lib/mod_spox/Sockets.rb +211 -0
- data/lib/mod_spox/Timer.rb +86 -69
- data/lib/mod_spox/handlers/BadNick.rb +1 -1
- data/lib/mod_spox/handlers/Created.rb +1 -1
- data/lib/mod_spox/handlers/Handler.rb +9 -0
- data/lib/mod_spox/handlers/Invite.rb +1 -1
- data/lib/mod_spox/handlers/Join.rb +2 -2
- data/lib/mod_spox/handlers/Kick.rb +1 -1
- data/lib/mod_spox/handlers/LuserChannels.rb +1 -1
- data/lib/mod_spox/handlers/LuserOp.rb +1 -1
- data/lib/mod_spox/handlers/LuserUnknown.rb +1 -1
- data/lib/mod_spox/handlers/Mode.rb +2 -2
- data/lib/mod_spox/handlers/MyInfo.rb +1 -1
- data/lib/mod_spox/handlers/Names.rb +1 -1
- data/lib/mod_spox/handlers/Nick.rb +20 -3
- data/lib/mod_spox/handlers/NickInUse.rb +3 -3
- data/lib/mod_spox/handlers/Notice.rb +5 -15
- data/lib/mod_spox/handlers/Part.rb +1 -1
- data/lib/mod_spox/handlers/Ping.rb +1 -1
- data/lib/mod_spox/handlers/Pong.rb +1 -1
- data/lib/mod_spox/handlers/Privmsg.rb +2 -2
- data/lib/mod_spox/handlers/Quit.rb +1 -1
- data/lib/mod_spox/handlers/Topic.rb +2 -1
- data/lib/mod_spox/handlers/Welcome.rb +3 -3
- data/lib/mod_spox/handlers/Who.rb +9 -7
- data/lib/mod_spox/handlers/Whois.rb +29 -16
- data/lib/mod_spox/handlers/YourHost.rb +1 -1
- data/lib/mod_spox/messages/incoming/Privmsg.rb +38 -19
- data/lib/mod_spox/messages/internal/DCCListener.rb +12 -0
- data/lib/mod_spox/messages/internal/DCCRequest.rb +12 -0
- data/lib/mod_spox/messages/internal/DCCSocket.rb +19 -0
- data/lib/mod_spox/messages/internal/StatusRequest.rb +2 -1
- data/lib/mod_spox/messages/outgoing/Privmsg.rb +21 -5
- data/lib/mod_spox/migrations/001_initialize_models.rb +115 -0
- data/lib/mod_spox/models/Auth.rb +24 -16
- data/lib/mod_spox/models/AuthGroup.rb +4 -3
- data/lib/mod_spox/models/Channel.rb +20 -12
- data/lib/mod_spox/models/ChannelMode.rb +2 -2
- data/lib/mod_spox/models/Config.rb +11 -3
- data/lib/mod_spox/models/Group.rb +6 -1
- data/lib/mod_spox/models/Nick.rb +93 -33
- data/lib/mod_spox/models/NickChannel.rb +8 -6
- data/lib/mod_spox/models/NickGroup.rb +16 -0
- data/lib/mod_spox/models/NickMode.rb +3 -3
- data/lib/mod_spox/models/Server.rb +6 -2
- data/lib/mod_spox/models/Setting.rb +12 -6
- data/lib/mod_spox/models/Signature.rb +7 -13
- data/lib/mod_spox/models/Trigger.rb +1 -1
- metadata +125 -100
data/lib/mod_spox/Socket.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
['
|
1
|
+
['iconv',
|
2
|
+
'mod_spox/Logger',
|
2
3
|
'mod_spox/Pool',
|
3
4
|
'mod_spox/Exceptions',
|
4
5
|
'mod_spox/messages/Messages',
|
@@ -7,8 +8,8 @@
|
|
7
8
|
|
8
9
|
module ModSpox
|
9
10
|
|
10
|
-
class Socket
|
11
|
-
|
11
|
+
class Socket
|
12
|
+
|
12
13
|
attr_reader :sent
|
13
14
|
attr_reader :received
|
14
15
|
attr_reader :burst
|
@@ -16,7 +17,8 @@ module ModSpox
|
|
16
17
|
attr_reader :delay
|
17
18
|
attr_reader :server
|
18
19
|
attr_reader :port
|
19
|
-
|
20
|
+
attr_reader :socket
|
21
|
+
|
20
22
|
# factory:: MessageFactory to parse messages
|
21
23
|
# server:: Server to connect to
|
22
24
|
# port:: Port number to connect to
|
@@ -25,9 +27,9 @@ module ModSpox
|
|
25
27
|
# burst:: Number of lines allowed to be sent within the burst_in time limit
|
26
28
|
# Create a new Socket
|
27
29
|
def initialize(bot, server, port, delay=2, burst_in=2, burst=4)
|
28
|
-
super()
|
29
30
|
@factory = bot.factory
|
30
31
|
@pipeline = bot.pipeline
|
32
|
+
@dcc = bot.dcc_sockets
|
31
33
|
@server = server
|
32
34
|
@port = port.to_i
|
33
35
|
@sent = 0
|
@@ -36,120 +38,110 @@ module ModSpox
|
|
36
38
|
@burst = burst.to_i > 0 ? burst.to_i : 4
|
37
39
|
@burst_in = 2
|
38
40
|
@kill = false
|
39
|
-
@reader_thread = nil
|
40
41
|
@time_check = nil
|
41
42
|
@check_burst = 0
|
42
43
|
@pause = false
|
43
|
-
@sendq =
|
44
|
+
@sendq = Queue.new
|
44
45
|
@lock = Mutex.new
|
45
|
-
|
46
|
+
@ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
|
46
47
|
end
|
47
|
-
|
48
|
+
|
48
49
|
# Connects to the IRC server
|
49
50
|
def connect
|
50
|
-
Logger.
|
51
|
+
Logger.info("Establishing connection to #{@server}:#{@port}")
|
51
52
|
@socket = TCPSocket.new(@server, @port)
|
52
|
-
@socket.sync = true
|
53
53
|
server = Models::Server.find_or_create(:host => @server, :port => @port)
|
54
54
|
server.connected = true
|
55
55
|
server.save
|
56
|
-
Logger.
|
57
|
-
reader
|
56
|
+
Logger.info("Created new send queue: #{@sendq}")
|
58
57
|
end
|
59
|
-
|
58
|
+
|
60
59
|
# new_delay:: Seconds to delay between bursts
|
61
60
|
# Resets the delay
|
62
61
|
def delay=(new_delay)
|
63
62
|
raise Exceptions::InvalidValue('Send delay must be a positive number') unless new_delay.to_f > 0
|
64
63
|
@delay = new_delay.to_f
|
65
64
|
end
|
66
|
-
|
65
|
+
|
67
66
|
# new_burst:: Number of lines allowed in burst
|
68
67
|
# Resets the burst
|
69
68
|
def burst=(new_burst)
|
70
69
|
raise Exceptions::InvalidValue('Burst value must be a positive number') unless new_burst.to_i > 0
|
71
70
|
@burst = new_burst
|
72
71
|
end
|
73
|
-
|
72
|
+
|
74
73
|
# new_burst_in:: Number of seconds allowed to burst
|
75
74
|
# Resets the burst_in
|
76
75
|
def burst_in=(new_burst_in)
|
77
76
|
raise Exceptions::InvalidValue('Burst in value must be positive') unless new_burst_in.to_i > 0
|
78
77
|
@burst_in = new_burst_in
|
79
78
|
end
|
80
|
-
|
79
|
+
|
81
80
|
# message:: String to send to server
|
82
81
|
# Sends a string to the IRC server
|
83
82
|
def puts(message)
|
84
83
|
write(message)
|
85
84
|
end
|
86
|
-
|
85
|
+
|
87
86
|
# message:: String to send to server
|
88
87
|
# Sends a string to the IRC server
|
89
88
|
def write(message)
|
90
89
|
return if message.nil?
|
91
|
-
@socket.puts(message + "\n")
|
92
|
-
Logger.
|
90
|
+
@socket.puts(message + "\n")
|
91
|
+
Logger.info("<< #{message}")
|
93
92
|
@last_send = Time.new
|
94
93
|
@sent += 1
|
95
94
|
@check_burst += 1
|
96
95
|
@time_check = Time.now.to_i if @time_check.nil?
|
97
96
|
end
|
98
|
-
|
99
|
-
# Retrieves a string from the server
|
100
|
-
def gets
|
101
|
-
read
|
102
|
-
end
|
103
|
-
|
97
|
+
|
104
98
|
# Retrieves a string from the server
|
105
99
|
def read
|
106
|
-
|
107
|
-
if(
|
100
|
+
tainted_message = @socket.gets
|
101
|
+
if(tainted_message.nil? || @socket.closed?) # || message =~ /^ERROR/)
|
108
102
|
@pipeline << Messages::Internal::Disconnected.new
|
109
103
|
shutdown
|
110
104
|
server = Models::Server.find_or_create(:host => @server, :port => @port)
|
111
105
|
server.connected = false
|
112
106
|
server.save
|
113
|
-
|
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
|
114
119
|
end
|
115
|
-
Logger.log(">> #{message}", 5)
|
116
|
-
@received += 1
|
117
|
-
message.strip!
|
118
|
-
return message
|
119
120
|
end
|
120
121
|
|
121
122
|
# message:: String to be sent to server
|
122
123
|
# Queues a message up to be sent to the IRC server
|
123
124
|
def <<(message)
|
124
125
|
@sendq << message
|
126
|
+
Pool << lambda{ processor }
|
125
127
|
end
|
126
|
-
|
128
|
+
|
127
129
|
# Starts the thread for sending messages to the server
|
128
130
|
def processor
|
129
131
|
@lock.synchronize do
|
130
|
-
write(@sendq.pop)
|
132
|
+
write(@sendq.pop(true))
|
131
133
|
if((Time.now.to_i - @time_check) > @burst_in)
|
132
134
|
@time_check = nil
|
133
135
|
@check_burst = 0
|
134
136
|
elsif((Time.now.to_i - @time_check) <= @burst_in && @check_burst >= @burst)
|
135
|
-
Logger.
|
137
|
+
Logger.warn("Burst limit hit. Output paused for: #{@delay} seconds")
|
136
138
|
sleep(@delay)
|
137
139
|
@time_check = nil
|
138
140
|
@check_burst = 0
|
139
141
|
end
|
140
142
|
end
|
141
143
|
end
|
142
|
-
|
143
|
-
# Starts the thread for reading messages from the server
|
144
|
-
def reader
|
145
|
-
@reader_thread = Thread.new do
|
146
|
-
until @kill do
|
147
|
-
Kernel.select([@socket], nil, nil, nil)
|
148
|
-
@factory << read
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
144
|
+
|
153
145
|
# restart:: Reconnect after closing connection
|
154
146
|
# Closes connection to IRC server
|
155
147
|
def shutdown(restart=false)
|
@@ -158,11 +150,10 @@ module ModSpox
|
|
158
150
|
server = Models::Server.find_or_create(:host => @server, :port => @port)
|
159
151
|
server.connected = false
|
160
152
|
server.save
|
161
|
-
sleep(0.1)
|
162
|
-
@reader_thread.kill if @reader_thread.alive?
|
153
|
+
sleep(0.1)
|
163
154
|
connect if restart
|
164
155
|
end
|
165
|
-
|
156
|
+
|
166
157
|
end
|
167
158
|
|
168
159
|
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
['ipaddr',
|
2
|
+
'iconv',
|
3
|
+
'mod_spox/Bot',
|
4
|
+
'mod_spox/Pipeline',
|
5
|
+
'mod_spox/MessageFactory',
|
6
|
+
'mod_spox/Socket'
|
7
|
+
].each{|f| require f}
|
8
|
+
|
9
|
+
module ModSpox
|
10
|
+
|
11
|
+
class Sockets
|
12
|
+
|
13
|
+
attr_reader :irc_socket
|
14
|
+
|
15
|
+
def initialize(bot)
|
16
|
+
@bot = bot
|
17
|
+
@irc_socket = nil
|
18
|
+
@dcc_sockets = []
|
19
|
+
@mapped_sockets = {}
|
20
|
+
@read_sockets = []
|
21
|
+
@listening_dcc = []
|
22
|
+
@dcc_ports = {:start => 49152, :end => 65535}
|
23
|
+
@dcc_wait = 30
|
24
|
+
@read_thread = nil
|
25
|
+
@pipeline = bot.pipeline
|
26
|
+
@factory = bot.factory
|
27
|
+
@pipeline.hook(self, :check_dcc, :Incoming_Privmsg)
|
28
|
+
@pipeline.hook(self, :return_socket, :Internal_DCCRequest)
|
29
|
+
@pipeline.hook(self, :dcc_listener, :Internal_DCCListener)
|
30
|
+
@kill = false
|
31
|
+
@ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
|
32
|
+
end
|
33
|
+
|
34
|
+
# server:: IRC server string
|
35
|
+
# port:: IRC port
|
36
|
+
# Connect to the given IRC server
|
37
|
+
def irc_connect(server, port)
|
38
|
+
@irc_socket = Socket.new(@bot, server, port)
|
39
|
+
@irc_socket.connect
|
40
|
+
@read_sockets << @irc_socket.socket
|
41
|
+
restart_reader
|
42
|
+
end
|
43
|
+
|
44
|
+
def <<(message)
|
45
|
+
if(message =~ /::(\S+)::\s:(.+)$/)
|
46
|
+
id = $1.to_i
|
47
|
+
message = $2 + "\r\n"
|
48
|
+
sock_info = @mapped_sockets[id]
|
49
|
+
socket = sock_info[:socket]
|
50
|
+
else
|
51
|
+
socket = @irc_socket
|
52
|
+
end
|
53
|
+
socket << message
|
54
|
+
end
|
55
|
+
|
56
|
+
# message:: ModSpox::Messages::Incoming::Privmsg
|
57
|
+
# Checks if incoming message is a request to
|
58
|
+
# start a DCC chat session and builds the connection
|
59
|
+
def check_dcc(message)
|
60
|
+
if(message.is_ctcp? && message.ctcp_type == 'DCC')
|
61
|
+
if(message.message =~ /^CHAT chat (\S+) (\S+)/)
|
62
|
+
if(message.source.in_group?('dcc') || message.source.in_group?('admin'))
|
63
|
+
ip = IPAddr.new($1.to_i, Object::Socket::AF_INET).to_s
|
64
|
+
port = $2.to_i
|
65
|
+
build_connection(ip, port, message.source)
|
66
|
+
else
|
67
|
+
Logger.warn("Error: #{message.source.nick} is attempting to establish DCC connection without permission.")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# message:: ModSpox::Messages::Internal::DCCRequest
|
74
|
+
# Returns the DCC Socket requested in a ModSpox::Messages::Internal::DCCSocket
|
75
|
+
# message.
|
76
|
+
def return_socket(message)
|
77
|
+
socket = nick = nil
|
78
|
+
if(@mapped_socks.has_key?(message.socket_id))
|
79
|
+
socket = @mapped_socks[message.socket_id][:socket]
|
80
|
+
nick = @mapped_socks[message.socket_id][:nick]
|
81
|
+
end
|
82
|
+
@pipeline << Messages::Internal::DCCSocket.new(message.socket_id, nick, socket)
|
83
|
+
end
|
84
|
+
|
85
|
+
# message:: ModSpox::Messages::Internal::DCCListener
|
86
|
+
# Sets up a new socket for a user to connect to. Helpful
|
87
|
+
# if the user wants a DCC chat session but is firewalled
|
88
|
+
def dcc_listener(message)
|
89
|
+
Thread.new do
|
90
|
+
me = Models::Nick.filter(:botnick => true).first
|
91
|
+
return if me.nil?
|
92
|
+
port = rand(@dcc_ports[:end] - @dcc_ports[:start]) + @dcc_ports[:start]
|
93
|
+
socket = Object::Socket.new(Object::Socket::AF_INET, Object::Socket::SOCK_STREAM, 0)
|
94
|
+
addr = Object::Socket.pack_sockaddr_in(port, me.address)
|
95
|
+
socket.bind(addr)
|
96
|
+
client = nil
|
97
|
+
addrinfo = nil
|
98
|
+
cport = nil
|
99
|
+
cip = nil
|
100
|
+
begin
|
101
|
+
Timeout::timeout(@dcc_wait) do
|
102
|
+
@pipeline << Messages::Outgoing::Privmsg.new(message.nick, "CHAT chat #{IPAddr.new(me.address).to_i} #{port}", false, true, 'DCC')
|
103
|
+
socket.listen(5)
|
104
|
+
client, addrinfo = socket.accept
|
105
|
+
cport, cip = Object::Socket.unpack_sockaddr_in(addrinfo)
|
106
|
+
end
|
107
|
+
Logger.info("New DCC socket created for #{message.nick.nick} has connected from: #{cip}:#{cport}")
|
108
|
+
stop_reader
|
109
|
+
@dcc_sockets << client
|
110
|
+
@mapped_sockets[client.object_id] = {:socket => client, :nick => message.nick}
|
111
|
+
@read_sockets << client
|
112
|
+
start_reader
|
113
|
+
rescue Timeout::Error => boom
|
114
|
+
Logger.warn("Timeout reached waiting for #{message.nick.nick} to connect to DCC socket. Closing.")
|
115
|
+
client.close
|
116
|
+
rescue Object => boom
|
117
|
+
Logger.warn("Unknown error encountered while building DCC listener for: #{message.nick.nick}. Error: #{boom}")
|
118
|
+
client.close
|
119
|
+
ensure
|
120
|
+
socket.close
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Shuts down all active sockets
|
126
|
+
def shutdown
|
127
|
+
stop_reader
|
128
|
+
@irc_socket.shutdown
|
129
|
+
@dcc_sockets.each do |sock|
|
130
|
+
close_dcc(sock)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# ip:: IP address to connect to
|
137
|
+
# port:: Port to connect to
|
138
|
+
# nick:: Nick this connection is associated with
|
139
|
+
# Builds a DCC connection to given location
|
140
|
+
def build_connection(ip, port, nick)
|
141
|
+
begin
|
142
|
+
socket = TCPSocket.new(ip, port)
|
143
|
+
stop_reader
|
144
|
+
@read_sockets << socket
|
145
|
+
@mapped_sockets[socket.object_id] = {:socket => socket, :nick => nick}
|
146
|
+
@dcc_sockets << socket
|
147
|
+
start_reader
|
148
|
+
Logger.info("New DCC connection established to #{nick.nick} on #{ip}:#{port}")
|
149
|
+
rescue Object => boom
|
150
|
+
Logger.warn("DCC connection to #{nick.nick} on #{ip}:#{port} failed. #{boom}")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def close_dcc(sock)
|
155
|
+
@read_sockets.delete(sock)
|
156
|
+
@dcc_sockets.delete(sock)
|
157
|
+
@mapped_sockets.delete(sock.object_id)
|
158
|
+
end
|
159
|
+
|
160
|
+
def stop_reader
|
161
|
+
Logger.info('Stopping reader thread for sockets')
|
162
|
+
if(!@thread_read.nil? && @thread_read.alive?)
|
163
|
+
@kill = true
|
164
|
+
@thread_read.join(0.2)
|
165
|
+
@thread_read.kill if @thread_read.alive?
|
166
|
+
@kill = false
|
167
|
+
end
|
168
|
+
Logger.info('Reader thread for sockets has been stopped')
|
169
|
+
end
|
170
|
+
|
171
|
+
def restart_reader
|
172
|
+
stop_reader
|
173
|
+
start_reader
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
def start_reader
|
178
|
+
Logger.info('Starting reader thread for sockets')
|
179
|
+
if(!@thread_read.nil? && @thread_read.alive?)
|
180
|
+
Logger.warn('ERROR: Cannot start reader. Already running.')
|
181
|
+
else
|
182
|
+
@thread_read = Thread.new do
|
183
|
+
until @kill do
|
184
|
+
begin
|
185
|
+
result = Kernel.select(@read_sockets, nil, nil, nil)
|
186
|
+
for sock in result[0] do
|
187
|
+
unless(sock == @irc_socket.socket)
|
188
|
+
tainted_string = sock.gets
|
189
|
+
string = @ic.iconv(tainted_string + ' ')[0..-2]
|
190
|
+
Logger.info("DCC >> #{string}", false)
|
191
|
+
if(sock.closed? || string.nil?)
|
192
|
+
sock.close
|
193
|
+
close_dcc(sock)
|
194
|
+
else
|
195
|
+
@pipeline << Messages::Incoming::Privmsg.new(string, @mapped_sockets[sock.object_id][:nick], "::#{sock.object_id}::", string)
|
196
|
+
end
|
197
|
+
else
|
198
|
+
@irc_socket.read
|
199
|
+
end
|
200
|
+
end
|
201
|
+
rescue Object => boom
|
202
|
+
Logger.warn("Socket error detected: #{boom}\n#{boom.backtrace}", false)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
Logger.info('Reader thread for sockets has been started')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
data/lib/mod_spox/Timer.rb
CHANGED
@@ -2,66 +2,57 @@
|
|
2
2
|
'mod_spox/Pipeline',
|
3
3
|
'mod_spox/Pool',
|
4
4
|
'mod_spox/Action',
|
5
|
-
'mod_spox/Exceptions'
|
6
|
-
'mod_spox/Monitors'].each{|f|require f}
|
5
|
+
'mod_spox/Exceptions'].each{|f|require f}
|
7
6
|
|
8
7
|
module ModSpox
|
9
8
|
|
10
|
-
class Timer
|
11
|
-
|
9
|
+
class Timer
|
10
|
+
|
12
11
|
# pipeline:: message pipeline
|
13
12
|
# Create a new Timer
|
14
13
|
def initialize(pipeline)
|
15
|
-
super()
|
16
14
|
@pipeline = pipeline
|
17
15
|
@timers = Array.new
|
18
|
-
|
19
|
-
@monitor = Monitors::Timer.new
|
20
|
-
@thread = nil
|
16
|
+
@timer_thread = nil
|
21
17
|
@stop_timer = false
|
22
|
-
@
|
23
|
-
@
|
18
|
+
@awake_lock = Mutex.new
|
19
|
+
@add_lock = Mutex.new
|
20
|
+
@new_actions = Queue.new
|
24
21
|
{:Internal_TimerAdd => :add_message,
|
25
22
|
:Internal_TimerRemove => :remove_message,
|
26
23
|
:Internal_TimerClear => :clear}.each_pair do |type,method|
|
27
24
|
@pipeline.hook(self, method, type)
|
28
25
|
end
|
29
|
-
start_pool
|
30
26
|
end
|
31
|
-
|
27
|
+
|
32
28
|
# Wakes the timer up early
|
33
29
|
def wakeup
|
34
|
-
|
35
|
-
|
30
|
+
@awake_lock.synchronize do
|
31
|
+
if(@timer_thread.status == 'sleep')
|
32
|
+
Logger.info('Timer has been explicitly told to wakeup')
|
33
|
+
@timer_thread.wakeup
|
34
|
+
end
|
35
|
+
end
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
# message:: TimerAdd message
|
39
39
|
# Add a recurring code block
|
40
40
|
def add_message(message)
|
41
|
-
Logger.
|
41
|
+
Logger.info("New block is being added to the timer")
|
42
42
|
action = nil
|
43
|
-
@
|
44
|
-
|
45
|
-
|
46
|
-
@owners[message.requester.name.to_sym] << action
|
47
|
-
end
|
48
|
-
begin
|
49
|
-
@pipeline << Messages::Internal::TimerResponse.new(message.requester, action, true, message.id)
|
50
|
-
Logger.log("New block was successfully added to the timer", 15)
|
51
|
-
rescue Object => boom
|
52
|
-
Logger.log("Failed to add block to timer: #{boom}", 10)
|
53
|
-
@pipeline << Messages::Internal::TimerResponse.new(message.requester, action, false, message.id)
|
54
|
-
end
|
43
|
+
@new_actions << {:period => message.period, :once => message.once, :data => message.data,
|
44
|
+
:block => message.block, :requester => message.requester, :m_id => message.id}
|
45
|
+
wakeup
|
55
46
|
end
|
56
|
-
|
47
|
+
|
57
48
|
# message:: TimerRemove message
|
58
49
|
# Remove an action from the timer
|
59
50
|
def remove_message(message)
|
60
51
|
remove(message.action)
|
61
|
-
Logger.
|
52
|
+
Logger.info("Action has been removed from the Timer")
|
62
53
|
@pipeline << Messages::Internal::TimerResponse.new(nil, message.action, false, message.id)
|
63
54
|
end
|
64
|
-
|
55
|
+
|
65
56
|
# period:: seconds between running action
|
66
57
|
# once:: only run action once
|
67
58
|
# data:: data to be available
|
@@ -70,18 +61,17 @@ module ModSpox
|
|
70
61
|
def add(period, once=false, data=nil, &func)
|
71
62
|
action = Action.new(self, period, data, once, &func)
|
72
63
|
@timers << action
|
73
|
-
wakeup
|
74
64
|
return action
|
75
65
|
end
|
76
|
-
|
66
|
+
|
77
67
|
# action:: Action to add to timer's queue
|
78
|
-
# Adds a new action to the timer
|
68
|
+
# Adds a new action to the timer
|
79
69
|
def add_action(action)
|
80
70
|
raise Exceptions::InvalidType.new('An Action object must be supplied') unless action.is_a?(Action)
|
81
71
|
@timers << action
|
82
72
|
wakeup
|
83
73
|
end
|
84
|
-
|
74
|
+
|
85
75
|
# action:: Action to remove from timer's queue
|
86
76
|
# Removes and action from the timer
|
87
77
|
def remove(action)
|
@@ -89,74 +79,101 @@ module ModSpox
|
|
89
79
|
@timers.delete(action)
|
90
80
|
wakeup
|
91
81
|
end
|
92
|
-
|
82
|
+
|
93
83
|
# Starts the timer
|
94
84
|
def start
|
95
|
-
raise Exceptions::AlreadyRunning.new('Timer is already running') unless @
|
96
|
-
@
|
85
|
+
raise Exceptions::AlreadyRunning.new('Timer is already running') unless @timer_thread.nil?
|
86
|
+
@timer_thread = Thread.new do
|
87
|
+
begin
|
97
88
|
until @stop_timer do
|
98
|
-
to_sleep =
|
99
|
-
|
100
|
-
|
101
|
-
|
89
|
+
to_sleep = get_min_sleep
|
90
|
+
Logger.info("Timer is set to sleep for #{to_sleep.nil? ? 'forever' : "#{to_sleep} seconds"}")
|
91
|
+
if((to_sleep.nil? || to_sleep > 0) && @new_actions.empty?)
|
92
|
+
actual_sleep = to_sleep.nil? ? sleep : sleep(to_sleep)
|
93
|
+
else
|
94
|
+
actual_sleep = 0
|
102
95
|
end
|
103
|
-
Logger.
|
104
|
-
actual_sleep = @monitor.wait(to_sleep)
|
96
|
+
Logger.info("Timer was set to sleep for #{to_sleep.nil? ? 'forever' : "#{to_sleep} seconds"}. Actual sleep: #{actual_sleep} seconds")
|
105
97
|
tick(actual_sleep)
|
106
|
-
|
98
|
+
add_waiting_actions
|
107
99
|
end
|
108
|
-
|
100
|
+
rescue Object => boom
|
101
|
+
Logger.warn("Timer error encountered: #{boom}")
|
102
|
+
end
|
103
|
+
Logger.warn("Timer has completed running.")
|
104
|
+
end
|
109
105
|
end
|
110
|
-
|
106
|
+
|
111
107
|
# Stops the timer
|
112
108
|
def stop
|
113
|
-
raise Exceptions::NotRunning.new('Timer is not running') if @
|
109
|
+
raise Exceptions::NotRunning.new('Timer is not running') if @timer_thread.nil?
|
114
110
|
@stop_timer = true
|
115
111
|
wakeup
|
116
|
-
@
|
112
|
+
@timer_thread.join
|
117
113
|
end
|
118
|
-
|
114
|
+
|
119
115
|
# Clears all actions in the timer's queue
|
120
116
|
def clear(message=nil)
|
121
117
|
if(message.nil? || message.plugin.nil?)
|
122
|
-
@queue.clear
|
123
118
|
@timers.clear
|
124
|
-
@
|
119
|
+
@new_actions.clear
|
120
|
+
wakeup
|
125
121
|
else
|
126
|
-
@
|
127
|
-
|
128
|
-
@owners[message.plugin].each do |action|
|
129
|
-
remove(action)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
122
|
+
@timers.each{ |action| @timers.delete(action) if action.owner == message.plugin}
|
123
|
+
wakeup
|
133
124
|
end
|
134
125
|
end
|
135
|
-
|
126
|
+
|
136
127
|
private
|
137
|
-
|
128
|
+
|
129
|
+
def get_min_sleep
|
130
|
+
min = @timers.map{|t| t.remaining}.sort[0]
|
131
|
+
unless(min.nil? || min > 0)
|
132
|
+
@timers.each{|t| @timers.delete(t) if t.remaining == 0} # kill stuck actions
|
133
|
+
min = get_min_sleep
|
134
|
+
end
|
135
|
+
Logger.info("Total number of actions in timer: #{@timers.size}")
|
136
|
+
Logger.info("Actions belong to: #{@timers.map{|a| a.owner}.join(', ')}")
|
137
|
+
return min
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_waiting_actions
|
141
|
+
until(@new_actions.empty?) do
|
142
|
+
a = @new_actions.pop
|
143
|
+
action = add(a[:period], a[:once], a[:data], &a[:block])
|
144
|
+
action.owner = a[:requester]
|
145
|
+
begin
|
146
|
+
@pipeline << Messages::Internal::TimerResponse.new(a[:requester], action, true, a[:m_id])
|
147
|
+
Logger.info("New block was successfully added to the timer")
|
148
|
+
rescue Object => boom
|
149
|
+
Logger.warn("Failed to add block to timer: #{boom}")
|
150
|
+
@pipeline << Messages::Internal::TimerResponse.new(a[:requester], action, false, a[:m_id])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
138
155
|
# time_passed:: time passed since last tick
|
139
156
|
# Decrements all Actions the given amount of time
|
140
157
|
def tick(time_passed)
|
141
158
|
for action in @timers do
|
142
159
|
action.tick(time_passed)
|
143
160
|
if(action.due?)
|
144
|
-
|
161
|
+
remove(action) if action.is_complete?
|
162
|
+
block = action.schedule
|
163
|
+
Pool << lambda{processor(block)}
|
145
164
|
end
|
146
165
|
end
|
147
166
|
end
|
148
|
-
|
167
|
+
|
149
168
|
# Process the actions
|
150
|
-
def processor
|
151
|
-
action = @queue.pop
|
169
|
+
def processor(action)
|
152
170
|
begin
|
153
171
|
action.run
|
154
|
-
remove(action) if action.is_complete?
|
155
172
|
rescue Object => boom
|
156
|
-
Logger.
|
173
|
+
Logger.warn("Timer block generated an exception: #{boom}\n#{boom.backtrace.join("\n")}")
|
157
174
|
end
|
158
175
|
end
|
159
|
-
|
176
|
+
|
160
177
|
end
|
161
|
-
|
178
|
+
|
162
179
|
end
|
@@ -9,7 +9,7 @@ module ModSpox
|
|
9
9
|
if(string =~ /#{RPL_ERRORNEOUSNICK}\s\S+\s(\S+)\s:/)
|
10
10
|
return Messages::Incoming::BadNick.new(string, $1)
|
11
11
|
else
|
12
|
-
Logger.
|
12
|
+
Logger.warn('Failed to process RPL_ERRORONEOUSNICK message')
|
13
13
|
return nil
|
14
14
|
end
|
15
15
|
end
|
@@ -9,7 +9,7 @@ module ModSpox
|
|
9
9
|
if(string =~ /#{RPL_CREATED.to_s}.+?:created\s(.+)$/)
|
10
10
|
return Messages::Incoming::Created.new(string, $1)
|
11
11
|
else
|
12
|
-
Logger.
|
12
|
+
Logger.warn('Failed to parse RPL_CREATED message')
|
13
13
|
return nil
|
14
14
|
end
|
15
15
|
end
|