punchblock 0.9.2 → 0.10.0
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/.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'
|