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.
Files changed (105) hide show
  1. data/CHANGELOG +36 -0
  2. data/INSTALL +2 -2
  3. data/README +0 -1
  4. data/bin/mod_spox +51 -12
  5. data/data/mod_spox/extras/AOLSpeak.rb +5 -18
  6. data/data/mod_spox/extras/AutoKick.rb +44 -23
  7. data/data/mod_spox/extras/AutoMode.rb +2 -5
  8. data/data/mod_spox/extras/AutoRejoin.rb +21 -0
  9. data/data/mod_spox/extras/Bouncer.rb +10 -10
  10. data/data/mod_spox/extras/Bytes.rb +12 -0
  11. data/data/mod_spox/extras/Confess.rb +131 -52
  12. data/data/mod_spox/extras/DCC.rb +189 -0
  13. data/data/mod_spox/extras/DevWatch.rb +32 -33
  14. data/data/mod_spox/extras/FloodKicker.rb +129 -0
  15. data/data/mod_spox/extras/GoogleIt.rb +13 -0
  16. data/data/mod_spox/extras/Headers.rb +31 -4
  17. data/data/mod_spox/extras/Karma.rb +103 -49
  18. data/data/mod_spox/extras/Logger.rb +45 -30
  19. data/data/mod_spox/extras/LolSpeak.rb +1 -1
  20. data/data/mod_spox/extras/NickServ.rb +83 -0
  21. data/data/mod_spox/extras/PhpCli.rb +12 -15
  22. data/data/mod_spox/extras/PhpFuncLookup.rb +57 -25
  23. data/data/mod_spox/extras/Quotes.rb +5 -4
  24. data/data/mod_spox/extras/RegexTracker.rb +160 -0
  25. data/data/mod_spox/extras/Roulette.rb +22 -23
  26. data/data/mod_spox/extras/Search.rb +3 -2
  27. data/data/mod_spox/extras/Slashdot.rb +35 -0
  28. data/data/mod_spox/extras/Topten.rb +5 -5
  29. data/data/mod_spox/extras/TracTicket.rb +68 -0
  30. data/data/mod_spox/extras/Translate.rb +69 -30
  31. data/data/mod_spox/extras/Twitter.rb +372 -0
  32. data/data/mod_spox/extras/UrbanDictionary.rb +21 -12
  33. data/data/mod_spox/extras/Weather.rb +1 -1
  34. data/data/mod_spox/plugins/Authenticator.rb +63 -30
  35. data/data/mod_spox/plugins/Banner.rb +164 -151
  36. data/data/mod_spox/plugins/Helper.rb +18 -7
  37. data/data/mod_spox/plugins/PluginLoader.rb +46 -22
  38. data/data/mod_spox/plugins/PoolConfig.rb +52 -0
  39. data/data/mod_spox/plugins/Quitter.rb +1 -1
  40. data/data/mod_spox/plugins/Status.rb +28 -0
  41. data/lib/mod_spox/Action.rb +20 -3
  42. data/lib/mod_spox/BaseConfig.rb +1 -0
  43. data/lib/mod_spox/Bot.rb +98 -75
  44. data/lib/mod_spox/BotConfig.rb +14 -6
  45. data/lib/mod_spox/ConfigurationWizard.rb +94 -105
  46. data/lib/mod_spox/Database.rb +33 -13
  47. data/lib/mod_spox/Helpers.rb +67 -38
  48. data/lib/mod_spox/Loader.rb +25 -5
  49. data/lib/mod_spox/Logger.rb +20 -62
  50. data/lib/mod_spox/MessageFactory.rb +34 -25
  51. data/lib/mod_spox/Monitors.rb +5 -0
  52. data/lib/mod_spox/Pipeline.rb +40 -51
  53. data/lib/mod_spox/Plugin.rb +40 -9
  54. data/lib/mod_spox/PluginManager.rb +46 -38
  55. data/lib/mod_spox/Pool.rb +129 -143
  56. data/lib/mod_spox/Socket.rb +41 -50
  57. data/lib/mod_spox/Sockets.rb +211 -0
  58. data/lib/mod_spox/Timer.rb +86 -69
  59. data/lib/mod_spox/handlers/BadNick.rb +1 -1
  60. data/lib/mod_spox/handlers/Created.rb +1 -1
  61. data/lib/mod_spox/handlers/Handler.rb +9 -0
  62. data/lib/mod_spox/handlers/Invite.rb +1 -1
  63. data/lib/mod_spox/handlers/Join.rb +2 -2
  64. data/lib/mod_spox/handlers/Kick.rb +1 -1
  65. data/lib/mod_spox/handlers/LuserChannels.rb +1 -1
  66. data/lib/mod_spox/handlers/LuserOp.rb +1 -1
  67. data/lib/mod_spox/handlers/LuserUnknown.rb +1 -1
  68. data/lib/mod_spox/handlers/Mode.rb +2 -2
  69. data/lib/mod_spox/handlers/MyInfo.rb +1 -1
  70. data/lib/mod_spox/handlers/Names.rb +1 -1
  71. data/lib/mod_spox/handlers/Nick.rb +20 -3
  72. data/lib/mod_spox/handlers/NickInUse.rb +3 -3
  73. data/lib/mod_spox/handlers/Notice.rb +5 -15
  74. data/lib/mod_spox/handlers/Part.rb +1 -1
  75. data/lib/mod_spox/handlers/Ping.rb +1 -1
  76. data/lib/mod_spox/handlers/Pong.rb +1 -1
  77. data/lib/mod_spox/handlers/Privmsg.rb +2 -2
  78. data/lib/mod_spox/handlers/Quit.rb +1 -1
  79. data/lib/mod_spox/handlers/Topic.rb +2 -1
  80. data/lib/mod_spox/handlers/Welcome.rb +3 -3
  81. data/lib/mod_spox/handlers/Who.rb +9 -7
  82. data/lib/mod_spox/handlers/Whois.rb +29 -16
  83. data/lib/mod_spox/handlers/YourHost.rb +1 -1
  84. data/lib/mod_spox/messages/incoming/Privmsg.rb +38 -19
  85. data/lib/mod_spox/messages/internal/DCCListener.rb +12 -0
  86. data/lib/mod_spox/messages/internal/DCCRequest.rb +12 -0
  87. data/lib/mod_spox/messages/internal/DCCSocket.rb +19 -0
  88. data/lib/mod_spox/messages/internal/StatusRequest.rb +2 -1
  89. data/lib/mod_spox/messages/outgoing/Privmsg.rb +21 -5
  90. data/lib/mod_spox/migrations/001_initialize_models.rb +115 -0
  91. data/lib/mod_spox/models/Auth.rb +24 -16
  92. data/lib/mod_spox/models/AuthGroup.rb +4 -3
  93. data/lib/mod_spox/models/Channel.rb +20 -12
  94. data/lib/mod_spox/models/ChannelMode.rb +2 -2
  95. data/lib/mod_spox/models/Config.rb +11 -3
  96. data/lib/mod_spox/models/Group.rb +6 -1
  97. data/lib/mod_spox/models/Nick.rb +93 -33
  98. data/lib/mod_spox/models/NickChannel.rb +8 -6
  99. data/lib/mod_spox/models/NickGroup.rb +16 -0
  100. data/lib/mod_spox/models/NickMode.rb +3 -3
  101. data/lib/mod_spox/models/Server.rb +6 -2
  102. data/lib/mod_spox/models/Setting.rb +12 -6
  103. data/lib/mod_spox/models/Signature.rb +7 -13
  104. data/lib/mod_spox/models/Trigger.rb +1 -1
  105. metadata +125 -100
@@ -1,4 +1,5 @@
1
- ['mod_spox/Logger',
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 < Pool
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 = @queue
44
+ @sendq = Queue.new
44
45
  @lock = Mutex.new
45
- start_pool
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.log("Establishing connection to #{@server}:#{@port}", 10)
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.log("Created new send queue: #{@sendq}", 10)
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") #send(message + "\n", 0)
92
- Logger.log("<< #{message}", 5)
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
- message = @socket.gets
107
- if(message.nil? || @socket.closed?) # || message =~ /^ERROR/)
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
- return
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.log("Burst limit hit. Output paused for: #{@delay} seconds", 70)
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
@@ -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 < Pool
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
- Logger.log("Created queue: #{@queue} in timer", 10)
19
- @monitor = Monitors::Timer.new
20
- @thread = nil
16
+ @timer_thread = nil
21
17
  @stop_timer = false
22
- @owners = {}
23
- @owners_lock = Mutex.new
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
- Logger.log("Timer has been explicitly told to wakeup", 15)
35
- @monitor.wakeup unless @thread.nil?
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.log("New block is being added to the timer", 15)
41
+ Logger.info("New block is being added to the timer")
42
42
  action = nil
43
- @owners_lock.synchronize do
44
- action = add(message.period, message.once, message.data, &message.block)
45
- @owners[message.requester.name.to_sym] = [] unless @owners.has_key?(message.requester.name.to_sym)
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.log("Action has been removed from the Timer", 15)
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 @thread.nil?
96
- @thread = Thread.new{
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 = nil
99
- @timers.each do |a|
100
- to_sleep = a.remaining if to_sleep.nil?
101
- to_sleep = a.remaining if !a.remaining.nil? && a.remaining < to_sleep
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.log("Timer is set to sleep for #{to_sleep.nil? ? 'forever' : "#{to_sleep} seconds"}", 15)
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
- Logger.log("Timer was set to sleep for #{to_sleep.nil? ? 'forever' : "#{to_sleep} seconds"} seconds. Actual sleep time: #{actual_sleep} seconds", 15)
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 @thread.nil?
109
+ raise Exceptions::NotRunning.new('Timer is not running') if @timer_thread.nil?
114
110
  @stop_timer = true
115
111
  wakeup
116
- @thread.join
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
- @owners.clear
119
+ @new_actions.clear
120
+ wakeup
125
121
  else
126
- @owners_lock.synchronize do
127
- if(@owners.has_key?(message.plugin))
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
- @queue << action.schedule
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.log("Timer block generated an exception: #{boom}\n#{boom.backtrace.join("\n")}", 5)
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.log('Failed to process RPL_ERRORONEOUSNICK message')
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.log('Failed to parse RPL_CREATED message')
12
+ Logger.warn('Failed to parse RPL_CREATED message')
13
13
  return nil
14
14
  end
15
15
  end