punchblock 0.9.2 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG.md +34 -21
- data/Rakefile +20 -0
- data/lib/punchblock/client/component_registry.rb +2 -0
- data/lib/punchblock/client.rb +2 -2
- data/lib/punchblock/command/accept.rb +2 -0
- data/lib/punchblock/command/answer.rb +2 -0
- data/lib/punchblock/command/dial.rb +2 -0
- data/lib/punchblock/command/hangup.rb +2 -0
- data/lib/punchblock/command/join.rb +2 -0
- data/lib/punchblock/command/mute.rb +2 -0
- data/lib/punchblock/command/redirect.rb +2 -0
- data/lib/punchblock/command/reject.rb +3 -1
- data/lib/punchblock/command/unjoin.rb +2 -0
- data/lib/punchblock/command/unmute.rb +2 -0
- data/lib/punchblock/command.rb +2 -0
- data/lib/punchblock/command_node.rb +2 -0
- data/lib/punchblock/component/asterisk/agi/command.rb +4 -2
- data/lib/punchblock/component/asterisk/agi.rb +2 -0
- data/lib/punchblock/component/asterisk/ami/action.rb +4 -2
- data/lib/punchblock/component/asterisk/ami.rb +2 -0
- data/lib/punchblock/component/asterisk.rb +2 -0
- data/lib/punchblock/component/component_node.rb +2 -0
- data/lib/punchblock/component/input.rb +2 -0
- data/lib/punchblock/component/output.rb +2 -0
- data/lib/punchblock/component/record.rb +2 -0
- data/lib/punchblock/component/stop.rb +2 -0
- data/lib/punchblock/component.rb +2 -0
- data/lib/punchblock/connection/asterisk.rb +4 -1
- data/lib/punchblock/connection/connected.rb +2 -0
- data/lib/punchblock/connection/generic_connection.rb +5 -0
- data/lib/punchblock/connection/xmpp.rb +5 -6
- data/lib/punchblock/connection.rb +2 -0
- data/lib/punchblock/core_ext/blather/stanza/presence.rb +2 -0
- data/lib/punchblock/core_ext/blather/stanza.rb +2 -0
- data/lib/punchblock/core_ext/ruby.rb +1 -12
- data/lib/punchblock/disconnected_error.rb +2 -0
- data/lib/punchblock/event/answered.rb +2 -0
- data/lib/punchblock/event/asterisk/ami/event.rb +3 -1
- data/lib/punchblock/event/asterisk/ami.rb +2 -0
- data/lib/punchblock/event/asterisk.rb +2 -0
- data/lib/punchblock/event/complete.rb +3 -1
- data/lib/punchblock/event/dtmf.rb +2 -0
- data/lib/punchblock/event/end.rb +2 -0
- data/lib/punchblock/event/joined.rb +2 -0
- data/lib/punchblock/event/offer.rb +6 -0
- data/lib/punchblock/event/ringing.rb +2 -0
- data/lib/punchblock/event/unjoined.rb +2 -0
- data/lib/punchblock/event.rb +2 -0
- data/lib/punchblock/has_headers.rb +3 -1
- data/lib/punchblock/header.rb +2 -0
- data/lib/punchblock/key_value_pair_node.rb +2 -0
- data/lib/punchblock/media_container.rb +2 -0
- data/lib/punchblock/media_node.rb +2 -0
- data/lib/punchblock/protocol_error.rb +2 -0
- data/lib/punchblock/rayo_node.rb +4 -3
- data/lib/punchblock/ref.rb +2 -0
- data/lib/punchblock/translator/asterisk/call.rb +80 -26
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +3 -1
- data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +3 -1
- data/lib/punchblock/translator/asterisk/component/asterisk.rb +2 -0
- data/lib/punchblock/translator/asterisk/component/input.rb +4 -2
- data/lib/punchblock/translator/asterisk/component/output.rb +21 -3
- data/lib/punchblock/translator/asterisk/component.rb +2 -0
- data/lib/punchblock/translator/asterisk.rb +50 -20
- data/lib/punchblock/translator.rb +2 -0
- data/lib/punchblock/version.rb +3 -1
- data/lib/punchblock.rb +2 -0
- data/punchblock.gemspec +2 -2
- data/spec/capture_warnings.rb +32 -0
- data/spec/punchblock/client/component_registry_spec.rb +2 -0
- data/spec/punchblock/client_spec.rb +3 -1
- data/spec/punchblock/command/accept_spec.rb +3 -1
- data/spec/punchblock/command/answer_spec.rb +3 -1
- data/spec/punchblock/command/dial_spec.rb +12 -10
- data/spec/punchblock/command/hangup_spec.rb +3 -1
- data/spec/punchblock/command/join_spec.rb +11 -9
- data/spec/punchblock/command/mute_spec.rb +3 -1
- data/spec/punchblock/command/redirect_spec.rb +5 -3
- data/spec/punchblock/command/reject_spec.rb +7 -5
- data/spec/punchblock/command/unjoin_spec.rb +7 -5
- data/spec/punchblock/command/unmute_spec.rb +3 -1
- data/spec/punchblock/command_node_spec.rb +9 -7
- data/spec/punchblock/component/asterisk/agi/command_spec.rb +21 -19
- data/spec/punchblock/component/asterisk/ami/action_spec.rb +19 -17
- data/spec/punchblock/component/component_node_spec.rb +5 -3
- data/spec/punchblock/component/input_spec.rb +51 -49
- data/spec/punchblock/component/output_spec.rb +60 -58
- data/spec/punchblock/component/record_spec.rb +36 -34
- data/spec/punchblock/connection/asterisk_spec.rb +9 -4
- data/spec/punchblock/connection/xmpp_spec.rb +40 -39
- data/spec/punchblock/event/answered_spec.rb +4 -2
- data/spec/punchblock/event/asterisk/ami/event_spec.rb +9 -7
- data/spec/punchblock/event/complete_spec.rb +12 -10
- data/spec/punchblock/event/dtmf_spec.rb +6 -4
- data/spec/punchblock/event/end_spec.rb +6 -4
- data/spec/punchblock/event/joined_spec.rb +8 -6
- data/spec/punchblock/event/offer_spec.rb +7 -5
- data/spec/punchblock/event/ringing_spec.rb +4 -2
- data/spec/punchblock/event/unjoined_spec.rb +8 -6
- data/spec/punchblock/header_spec.rb +13 -11
- data/spec/punchblock/protocol_error_spec.rb +8 -6
- data/spec/punchblock/ref_spec.rb +5 -3
- data/spec/punchblock/translator/asterisk/call_spec.rb +261 -14
- data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +13 -11
- data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +4 -2
- data/spec/punchblock/translator/asterisk/component/input_spec.rb +10 -8
- data/spec/punchblock/translator/asterisk/component/output_spec.rb +111 -20
- data/spec/punchblock/translator/asterisk/component_spec.rb +3 -1
- data/spec/punchblock/translator/asterisk_spec.rb +107 -10
- data/spec/spec_helper.rb +23 -17
- data/spec/support/mock_connection_with_event_handler.rb +2 -0
- metadata +43 -41
data/lib/punchblock/event.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Punchblock
|
2
4
|
module HasHeaders
|
3
5
|
##
|
@@ -23,7 +25,7 @@ module Punchblock
|
|
23
25
|
# @param [Hash, Array] headers A hash of key-value header pairs, or an array of Header objects
|
24
26
|
#
|
25
27
|
def headers=(headers)
|
26
|
-
find('//ns:header', :ns => self.class.registered_ns).each
|
28
|
+
find('//ns:header', :ns => self.class.registered_ns).each(&:remove)
|
27
29
|
if headers.is_a? Hash
|
28
30
|
headers.each_pair { |k,v| self << Header.new(k, v) }
|
29
31
|
elsif headers.is_a? Array
|
data/lib/punchblock/header.rb
CHANGED
data/lib/punchblock/rayo_node.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'active_support/core_ext/class/attribute'
|
2
4
|
require 'niceogiri'
|
3
5
|
|
@@ -41,12 +43,11 @@ module Punchblock
|
|
41
43
|
def self.import(node, call_id = nil, component_id = nil)
|
42
44
|
ns = (node.namespace.href if node.namespace)
|
43
45
|
klass = class_from_registration(node.element_name, ns)
|
44
|
-
|
46
|
+
if klass && klass != self
|
45
47
|
klass.import node, call_id, component_id
|
46
48
|
else
|
47
49
|
new.inherit node
|
48
|
-
end
|
49
|
-
event.tap do |event|
|
50
|
+
end.tap do |event|
|
50
51
|
event.call_id = call_id
|
51
52
|
event.component_id = component_id
|
52
53
|
end
|
data/lib/punchblock/ref.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
|
3
5
|
module Punchblock
|
@@ -7,9 +9,10 @@ module Punchblock
|
|
7
9
|
include HasGuardedHandlers
|
8
10
|
include Celluloid
|
9
11
|
|
10
|
-
attr_reader :id, :channel, :translator, :agi_env, :direction
|
12
|
+
attr_reader :id, :channel, :translator, :agi_env, :direction, :pending_joins
|
11
13
|
|
12
14
|
HANGUP_CAUSE_TO_END_REASON = Hash.new { :error }
|
15
|
+
HANGUP_CAUSE_TO_END_REASON[0] = :hangup
|
13
16
|
HANGUP_CAUSE_TO_END_REASON[16] = :hangup
|
14
17
|
HANGUP_CAUSE_TO_END_REASON[17] = :busy
|
15
18
|
HANGUP_CAUSE_TO_END_REASON[18] = :timeout
|
@@ -18,11 +21,25 @@ module Punchblock
|
|
18
21
|
HANGUP_CAUSE_TO_END_REASON[22] = :reject
|
19
22
|
HANGUP_CAUSE_TO_END_REASON[102] = :timeout
|
20
23
|
|
21
|
-
|
24
|
+
class << self
|
25
|
+
def parse_environment(agi_env)
|
26
|
+
agi_env_as_array(agi_env).inject({}) do |accumulator, element|
|
27
|
+
accumulator[element[0].to_sym] = element[1] || ''
|
28
|
+
accumulator
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def agi_env_as_array(agi_env)
|
33
|
+
URI::Parser.new.unescape(agi_env).encode.split("\n").map { |p| p.split ': ' }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(channel, translator, agi_env = nil)
|
22
38
|
@channel, @translator = channel, translator
|
23
|
-
@agi_env =
|
39
|
+
@agi_env = agi_env || {}
|
24
40
|
@id, @components = UUIDTools::UUID.random_create.to_s, {}
|
25
41
|
@answered = false
|
42
|
+
@pending_joins = {}
|
26
43
|
pb_logger.debug "Starting up call with channel #{channel}, id #{@id}"
|
27
44
|
end
|
28
45
|
|
@@ -89,21 +106,19 @@ module Punchblock
|
|
89
106
|
end
|
90
107
|
|
91
108
|
def process_ami_event(ami_event)
|
92
|
-
pb_logger.trace "Processing AMI event #{ami_event.inspect}"
|
93
109
|
case ami_event.name
|
94
110
|
when 'Hangup'
|
95
|
-
pb_logger.
|
111
|
+
pb_logger.trace "Received a Hangup AMI event. Sending End event."
|
96
112
|
send_end_event HANGUP_CAUSE_TO_END_REASON[ami_event['Cause'].to_i]
|
97
113
|
when 'AsyncAGI'
|
98
|
-
pb_logger.
|
114
|
+
pb_logger.trace "Received an AsyncAGI event. Looking for matching AGICommand component."
|
99
115
|
if component = component_with_id(ami_event['CommandID'])
|
100
|
-
pb_logger.debug "Found component #{component.id} for event. Forwarding event..."
|
101
116
|
component.handle_ami_event! ami_event
|
102
117
|
else
|
103
|
-
pb_logger.
|
118
|
+
pb_logger.warn "Could not find component for AMI event: #{ami_event.inspect}"
|
104
119
|
end
|
105
120
|
when 'Newstate'
|
106
|
-
pb_logger.
|
121
|
+
pb_logger.trace "Received a Newstate AMI event with state #{ami_event['ChannelState']}: #{ami_event['ChannelStateDesc']}"
|
107
122
|
case ami_event['ChannelState']
|
108
123
|
when '5'
|
109
124
|
send_pb_event Event::Ringing.new
|
@@ -111,6 +126,33 @@ module Punchblock
|
|
111
126
|
@answered = true
|
112
127
|
send_pb_event Event::Answered.new
|
113
128
|
end
|
129
|
+
when 'BridgeExec'
|
130
|
+
if join_command = pending_joins[ami_event['Channel2']]
|
131
|
+
join_command.response = true
|
132
|
+
end
|
133
|
+
when 'Bridge'
|
134
|
+
other_call_channel = ([ami_event['Channel1'], ami_event['Channel2']] - [channel]).first
|
135
|
+
if other_call = translator.call_for_channel(other_call_channel)
|
136
|
+
event = case ami_event['Bridgestate']
|
137
|
+
when 'Link'
|
138
|
+
Event::Joined.new.tap do |e|
|
139
|
+
e.other_call_id = other_call.id
|
140
|
+
end
|
141
|
+
when 'Unlink'
|
142
|
+
Event::Unjoined.new.tap do |e|
|
143
|
+
e.other_call_id = other_call.id
|
144
|
+
end
|
145
|
+
end
|
146
|
+
send_pb_event event
|
147
|
+
end
|
148
|
+
when 'Unlink'
|
149
|
+
other_call_channel = ([ami_event['Channel1'], ami_event['Channel2']] - [channel]).first
|
150
|
+
if other_call = translator.call_for_channel(other_call_channel)
|
151
|
+
event = Event::Unjoined.new.tap do |e|
|
152
|
+
e.other_call_id = other_call.id
|
153
|
+
end
|
154
|
+
send_pb_event event
|
155
|
+
end
|
114
156
|
end
|
115
157
|
trigger_handler :ami, ami_event
|
116
158
|
end
|
@@ -143,6 +185,13 @@ module Punchblock
|
|
143
185
|
send_ami_action 'Hangup', 'Channel' => channel do |response|
|
144
186
|
command.response = true
|
145
187
|
end
|
188
|
+
when Command::Join
|
189
|
+
other_call = translator.call_with_id command.other_call_id
|
190
|
+
pending_joins[other_call.channel] = command
|
191
|
+
send_agi_action 'EXEC Bridge', other_call.channel
|
192
|
+
when Command::Unjoin
|
193
|
+
other_call = translator.call_with_id command.other_call_id
|
194
|
+
redirect_back other_call
|
146
195
|
when Punchblock::Component::Asterisk::AGI::Command
|
147
196
|
execute_component Component::Asterisk::AGICommand, command
|
148
197
|
when Punchblock::Component::Output
|
@@ -155,12 +204,12 @@ module Punchblock
|
|
155
204
|
end
|
156
205
|
|
157
206
|
def send_agi_action(command, *params, &block)
|
158
|
-
pb_logger.
|
207
|
+
pb_logger.trace "Sending AGI action #{command}"
|
159
208
|
@current_agi_command = Punchblock::Component::Asterisk::AGI::Command.new :name => command, :params => params, :call_id => id
|
160
209
|
@current_agi_command.request!
|
161
210
|
@current_agi_command.register_handler :internal, Punchblock::Event::Complete do |e|
|
162
|
-
pb_logger.
|
163
|
-
block.call e
|
211
|
+
pb_logger.trace "AGI action received complete event #{e.inspect}"
|
212
|
+
block.call e if block
|
164
213
|
end
|
165
214
|
execute_component Component::Asterisk::AGICommand, @current_agi_command, :internal => true
|
166
215
|
end
|
@@ -168,7 +217,7 @@ module Punchblock
|
|
168
217
|
def send_ami_action(name, headers = {}, &block)
|
169
218
|
(name.is_a?(RubyAMI::Action) ? name : RubyAMI::Action.new(name, headers, &block)).tap do |action|
|
170
219
|
@current_ami_action = action
|
171
|
-
pb_logger.
|
220
|
+
pb_logger.trace "Sending AMI action #{action.inspect}"
|
172
221
|
translator.send_ami_action! action
|
173
222
|
end
|
174
223
|
end
|
@@ -179,9 +228,25 @@ module Punchblock
|
|
179
228
|
|
180
229
|
private
|
181
230
|
|
231
|
+
def redirect_back(other_call = nil)
|
232
|
+
redirect_options = {
|
233
|
+
'Channel' => channel,
|
234
|
+
'Exten' => Asterisk::REDIRECT_EXTENSION,
|
235
|
+
'Priority' => Asterisk::REDIRECT_PRIORITY,
|
236
|
+
'Context' => Asterisk::REDIRECT_CONTEXT
|
237
|
+
}
|
238
|
+
redirect_options.merge!({
|
239
|
+
'ExtraChannel' => other_call.channel,
|
240
|
+
'ExtraExten' => Asterisk::REDIRECT_EXTENSION,
|
241
|
+
'ExtraPriority' => Asterisk::REDIRECT_PRIORITY,
|
242
|
+
'ExtraContext' => Asterisk::REDIRECT_CONTEXT
|
243
|
+
}) if other_call
|
244
|
+
send_ami_action 'Redirect', redirect_options
|
245
|
+
end
|
246
|
+
|
182
247
|
def send_end_event(reason)
|
183
248
|
send_pb_event Event::End.new(:reason => reason)
|
184
|
-
|
249
|
+
after(5) { shutdown }
|
185
250
|
end
|
186
251
|
|
187
252
|
def execute_component(type, command, options = {})
|
@@ -194,7 +259,7 @@ module Punchblock
|
|
194
259
|
|
195
260
|
def send_pb_event(event)
|
196
261
|
event.call_id = id
|
197
|
-
pb_logger.
|
262
|
+
pb_logger.trace "Sending Punchblock event: #{event.inspect}"
|
198
263
|
translator.handle_pb_event! event
|
199
264
|
end
|
200
265
|
|
@@ -210,17 +275,6 @@ module Punchblock
|
|
210
275
|
accumulator
|
211
276
|
end
|
212
277
|
end
|
213
|
-
|
214
|
-
def parse_environment(agi_env)
|
215
|
-
agi_env_as_array(agi_env).inject({}) do |accumulator, element|
|
216
|
-
accumulator[element[0].to_sym] = element[1] || ''
|
217
|
-
accumulator
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def agi_env_as_array(agi_env)
|
222
|
-
URI.unescape(agi_env).encode.split("\n").map { |p| p.split ': ' }
|
223
|
-
end
|
224
278
|
end
|
225
279
|
end
|
226
280
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
require 'active_support/core_ext/string/filters'
|
3
5
|
|
@@ -28,7 +30,7 @@ module Punchblock
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def parse_agi_result(result)
|
31
|
-
match = URI.
|
33
|
+
match = URI::Parser.new.unescape(result).chomp.match(/^(\d{3}) result=(-?\d*) ?(\(?.*\)?)?$/)
|
32
34
|
if match
|
33
35
|
data = match[3] ? match[3].gsub(/(^\()|(\)$)/, '') : nil
|
34
36
|
[match[1].to_i, match[2].to_i, data]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Punchblock
|
2
4
|
module Translator
|
3
5
|
class Asterisk
|
@@ -55,7 +57,7 @@ module Punchblock
|
|
55
57
|
|
56
58
|
def success_reason(response)
|
57
59
|
headers = response.headers
|
58
|
-
headers.merge! @extra_complete_attributes if
|
60
|
+
headers.merge! @extra_complete_attributes if instance_variable_defined?(:@extra_complete_attributes)
|
59
61
|
headers.delete 'ActionID'
|
60
62
|
Punchblock::Component::Asterisk::AMI::Action::Complete::Success.new :message => headers.delete('Message'), :attributes => headers
|
61
63
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Punchblock
|
2
4
|
module Translator
|
3
5
|
class Asterisk
|
@@ -71,7 +73,7 @@ module Punchblock
|
|
71
73
|
end
|
72
74
|
|
73
75
|
def cancel_initial_timer
|
74
|
-
return unless @initial_timer
|
76
|
+
return unless instance_variable_defined?(:@initial_timer) && @initial_timer
|
75
77
|
@initial_timer.cancel
|
76
78
|
@initial_timer = nil
|
77
79
|
end
|
@@ -90,7 +92,7 @@ module Punchblock
|
|
90
92
|
end
|
91
93
|
|
92
94
|
def cancel_inter_digit_timer
|
93
|
-
return unless @inter_digit_timer
|
95
|
+
return unless instance_variable_defined?(:@inter_digit_timer) && @inter_digit_timer
|
94
96
|
@inter_digit_timer.cancel
|
95
97
|
@inter_digit_timer = nil
|
96
98
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'active_support/core_ext/string/filters'
|
2
4
|
|
3
5
|
module Punchblock
|
@@ -38,7 +40,11 @@ module Punchblock
|
|
38
40
|
|
39
41
|
send_ref
|
40
42
|
|
41
|
-
@interrupt_digits =
|
43
|
+
@interrupt_digits = if [:any, :dtmf].include? @component_node.interrupt_on
|
44
|
+
'0123456789*#'
|
45
|
+
else
|
46
|
+
nil
|
47
|
+
end
|
42
48
|
|
43
49
|
@execution_elements.each do |element|
|
44
50
|
element.call
|
@@ -46,12 +52,20 @@ module Punchblock
|
|
46
52
|
process_playback_completion
|
47
53
|
end
|
48
54
|
when :unimrcp
|
49
|
-
doc = @component_node.ssml.to_s.squish.gsub(/["\\]/) { |m| "\\#{m}" }
|
50
55
|
send_ref
|
51
|
-
@call.send_agi_action! 'EXEC MRCPSynth',
|
56
|
+
@call.send_agi_action! 'EXEC MRCPSynth', escaped_doc, mrcpsynth_options do |complete_event|
|
52
57
|
pb_logger.debug "MRCPSynth completed with #{complete_event}."
|
53
58
|
send_complete_event success_reason
|
54
59
|
end
|
60
|
+
when :swift
|
61
|
+
doc = escaped_doc
|
62
|
+
doc << "|1|1" if [:any, :dtmf].include? @component_node.interrupt_on
|
63
|
+
doc.insert 0, "#{@component_node.voice}^" if @component_node.voice
|
64
|
+
send_ref
|
65
|
+
@call.send_agi_action! 'EXEC Swift', doc do |complete_event|
|
66
|
+
pb_logger.debug "Swift completed with #{complete_event}."
|
67
|
+
send_complete_event success_reason
|
68
|
+
end
|
55
69
|
end
|
56
70
|
end
|
57
71
|
|
@@ -79,6 +93,10 @@ module Punchblock
|
|
79
93
|
|
80
94
|
private
|
81
95
|
|
96
|
+
def escaped_doc
|
97
|
+
@component_node.ssml.to_s.squish.gsub(/["\\]/) { |m| "\\#{m}" }
|
98
|
+
end
|
99
|
+
|
82
100
|
def mrcpsynth_options
|
83
101
|
[].tap do |opts|
|
84
102
|
opts << 'i=any' if [:any, :dtmf].include? @component_node.interrupt_on
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'celluloid'
|
2
4
|
require 'ruby_ami'
|
3
5
|
|
@@ -13,6 +15,10 @@ module Punchblock
|
|
13
15
|
|
14
16
|
attr_reader :ami_client, :connection, :media_engine, :calls
|
15
17
|
|
18
|
+
REDIRECT_CONTEXT = 'adhearsion-redirect'
|
19
|
+
REDIRECT_EXTENSION = '1'
|
20
|
+
REDIRECT_PRIORITY = '1'
|
21
|
+
|
16
22
|
def initialize(ami_client, connection, media_engine = nil)
|
17
23
|
pb_logger.debug "Starting up..."
|
18
24
|
@ami_client, @connection, @media_engine = ami_client, connection, media_engine
|
@@ -30,9 +36,7 @@ module Punchblock
|
|
30
36
|
end
|
31
37
|
|
32
38
|
def call_for_channel(channel)
|
33
|
-
|
34
|
-
pb_logger.trace "Looking up call for channel #{channel} from #{@channel_to_call_id}. Found: #{call || 'none'}"
|
35
|
-
call
|
39
|
+
call_with_id @channel_to_call_id[channel]
|
36
40
|
end
|
37
41
|
|
38
42
|
def register_component(component)
|
@@ -45,37 +49,27 @@ module Punchblock
|
|
45
49
|
|
46
50
|
def shutdown
|
47
51
|
pb_logger.debug "Shutting down"
|
48
|
-
@calls.values.each
|
52
|
+
@calls.values.each(&:shutdown!)
|
49
53
|
current_actor.terminate!
|
50
54
|
end
|
51
55
|
|
52
56
|
def handle_ami_event(event)
|
53
57
|
return unless event.is_a? RubyAMI::Event
|
54
|
-
|
58
|
+
|
55
59
|
if event.name.downcase == "fullybooted"
|
56
60
|
pb_logger.trace "Counting FullyBooted event"
|
57
61
|
@fully_booted_count += 1
|
58
62
|
if @fully_booted_count >= 2
|
59
63
|
handle_pb_event Connection::Connected.new
|
60
64
|
@fully_booted_count = 0
|
65
|
+
run_at_fully_booted
|
61
66
|
end
|
62
67
|
return
|
63
68
|
end
|
64
69
|
|
65
|
-
|
66
|
-
pb_logger.trace "Received a VarSet event indicating the full channel for call #{call}"
|
67
|
-
@channel_to_call_id.delete call.channel
|
68
|
-
pb_logger.trace "Removed call with old channel from channel map: #{@channel_to_call_id}"
|
69
|
-
call.channel = event['Channel']
|
70
|
-
register_call call
|
71
|
-
end
|
70
|
+
handle_varset_ami_event event
|
72
71
|
|
73
|
-
|
74
|
-
pb_logger.trace "Found call by channel matching this event. Sending to call #{call.id}"
|
75
|
-
call.process_ami_event! event
|
76
|
-
elsif event.name.downcase == "asyncagi" && event['SubEvent'] == "Start"
|
77
|
-
handle_async_agi_start_event event
|
78
|
-
end
|
72
|
+
ami_dispatch_to_or_create_call event
|
79
73
|
|
80
74
|
handle_pb_event Event::Asterisk::AMI::Event.new(:name => event.name, :attributes => event.headers)
|
81
75
|
end
|
@@ -85,7 +79,7 @@ module Punchblock
|
|
85
79
|
end
|
86
80
|
|
87
81
|
def execute_command(command, options = {})
|
88
|
-
pb_logger.
|
82
|
+
pb_logger.trace "Executing command #{command.inspect}"
|
89
83
|
command.request!
|
90
84
|
|
91
85
|
command.call_id ||= options[:call_id]
|
@@ -135,10 +129,46 @@ module Punchblock
|
|
135
129
|
ami_client.send_action name, headers, &block
|
136
130
|
end
|
137
131
|
|
132
|
+
def run_at_fully_booted
|
133
|
+
send_ami_action('Command', {
|
134
|
+
'Command' => "dialplan add extension #{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY},AGI,agi:async into #{REDIRECT_CONTEXT}"
|
135
|
+
})
|
136
|
+
pb_logger.trace "Added extension extension #{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY},AGI,agi:async into #{REDIRECT_CONTEXT}"
|
137
|
+
end
|
138
|
+
|
138
139
|
private
|
139
140
|
|
141
|
+
def handle_varset_ami_event(event)
|
142
|
+
return unless event.name == 'VarSet' && event['Variable'] == 'punchblock_call_id' && (call = call_with_id event['Value'])
|
143
|
+
|
144
|
+
pb_logger.trace "Received a VarSet event indicating the full channel for call #{call}"
|
145
|
+
@channel_to_call_id.delete call.channel
|
146
|
+
pb_logger.trace "Removed call with old channel from channel map: #{@channel_to_call_id}"
|
147
|
+
call.channel = event['Channel']
|
148
|
+
register_call call
|
149
|
+
end
|
150
|
+
|
151
|
+
def ami_dispatch_to_or_create_call(event)
|
152
|
+
if (event['Channel'] && call_for_channel(event['Channel'])) ||
|
153
|
+
(event['Channel1'] && call_for_channel(event['Channel1'])) ||
|
154
|
+
(event['Channel2'] && call_for_channel(event['Channel2']))
|
155
|
+
[event['Channel'], event['Channel1'], event['Channel2']].compact.each do |channel|
|
156
|
+
call = call_for_channel channel
|
157
|
+
call.process_ami_event! event if call
|
158
|
+
end
|
159
|
+
elsif event.name.downcase == "asyncagi" && event['SubEvent'] == "Start"
|
160
|
+
handle_async_agi_start_event event
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
140
164
|
def handle_async_agi_start_event(event)
|
141
|
-
|
165
|
+
env = Call.parse_environment event['Env']
|
166
|
+
|
167
|
+
return pb_logger.warn "Ignoring AsyncAGI Start event because it is for an 'h' extension" if env[:agi_extension] == 'h'
|
168
|
+
return pb_logger.warn "Ignoring AsyncAGI Start event because it is for an 'Kill' type" if env[:agi_type] == 'Kill'
|
169
|
+
|
170
|
+
pb_logger.trace "Handling AsyncAGI Start event by creating a new call"
|
171
|
+
call = Call.new event['Channel'], current_actor, env
|
142
172
|
register_call call
|
143
173
|
call.send_offer!
|
144
174
|
end
|
data/lib/punchblock/version.rb
CHANGED
data/lib/punchblock.rb
CHANGED
data/punchblock.gemspec
CHANGED
@@ -28,11 +28,11 @@ Gem::Specification.new do |s|
|
|
28
28
|
s.add_runtime_dependency %q<state_machine>, [">= 1.0.1"]
|
29
29
|
s.add_runtime_dependency %q<future-resource>, [">= 0.0.2"]
|
30
30
|
s.add_runtime_dependency %q<has-guarded-handlers>, [">= 0.1.0"]
|
31
|
-
s.add_runtime_dependency %q<celluloid>, [">= 0.
|
31
|
+
s.add_runtime_dependency %q<celluloid>, [">= 0.9.0"]
|
32
32
|
s.add_runtime_dependency %q<ruby_ami>, [">= 0.1.3"]
|
33
33
|
s.add_runtime_dependency %q<ruby_speech>, [">= 0.5.1"]
|
34
34
|
|
35
|
-
s.add_development_dependency %q<bundler>, ["
|
35
|
+
s.add_development_dependency %q<bundler>, [">= 1.0.0"]
|
36
36
|
s.add_development_dependency %q<rspec>, ["~> 2.7.0"]
|
37
37
|
s.add_development_dependency %q<ci_reporter>, [">= 1.6.3"]
|
38
38
|
s.add_development_dependency %q<yard>, ["~> 0.6.0"]
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
stderr_file = Tempfile.new "ahn.stderr"
|
3
|
+
$stderr.reopen stderr_file.path
|
4
|
+
current_dir = Dir.pwd
|
5
|
+
|
6
|
+
at_exit do
|
7
|
+
stderr_file.rewind
|
8
|
+
lines = stderr_file.read.split("\n").uniq
|
9
|
+
stderr_file.close!
|
10
|
+
|
11
|
+
pb_warnings, other_warnings = lines.partition { |line| line.include?(current_dir) && !line.include?('vendor') && line.include?('warning') }
|
12
|
+
|
13
|
+
if pb_warnings.any?
|
14
|
+
puts
|
15
|
+
puts "-" * 30 + " PB Warnings: " + "-" * 30
|
16
|
+
puts
|
17
|
+
puts pb_warnings.join("\n")
|
18
|
+
puts
|
19
|
+
puts "-" * 75
|
20
|
+
puts
|
21
|
+
end
|
22
|
+
|
23
|
+
if other_warnings.any?
|
24
|
+
Dir.mkdir 'tmp' unless Dir.exists? 'tmp'
|
25
|
+
File.open('tmp/warnings.txt', 'w') { |f| f.write other_warnings.join("\n") }
|
26
|
+
puts
|
27
|
+
puts "Non-PB warnings written to tmp/warnings.txt"
|
28
|
+
puts
|
29
|
+
end
|
30
|
+
|
31
|
+
exit 1 if pb_warnings.any? # fail the build...
|
32
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
module Punchblock
|
@@ -81,7 +83,7 @@ module Punchblock
|
|
81
83
|
context 'if event handlers have not been set' do
|
82
84
|
it 'should queue up the event' do
|
83
85
|
subject.handle_event mock_event
|
84
|
-
subject.event_queue.pop(true).should == mock_event
|
86
|
+
subject.event_queue.pop(true).should be == mock_event
|
85
87
|
end
|
86
88
|
end
|
87
89
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
module Punchblock
|
4
6
|
module Command
|
5
7
|
describe Accept do
|
6
8
|
it 'registers itself' do
|
7
|
-
RayoNode.class_from_registration(:accept, 'urn:xmpp:rayo:1').should == Accept
|
9
|
+
RayoNode.class_from_registration(:accept, 'urn:xmpp:rayo:1').should be == Accept
|
8
10
|
end
|
9
11
|
|
10
12
|
it_should_behave_like 'command_headers'
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
module Punchblock
|
4
6
|
module Command
|
5
7
|
describe Answer do
|
6
8
|
it 'registers itself' do
|
7
|
-
RayoNode.class_from_registration(:answer, 'urn:xmpp:rayo:1').should == Answer
|
9
|
+
RayoNode.class_from_registration(:answer, 'urn:xmpp:rayo:1').should be == Answer
|
8
10
|
end
|
9
11
|
|
10
12
|
it_should_behave_like 'command_headers'
|