adhearsion 2.4.0.beta1 → 2.4.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -3
- data/README.markdown +1 -1
- data/lib/adhearsion/call.rb +33 -8
- data/lib/adhearsion/call_controller.rb +9 -0
- data/lib/adhearsion/call_controller/dial.rb +145 -32
- data/lib/adhearsion/console.rb +1 -1
- data/lib/adhearsion/generators/app/templates/README.md +20 -1
- data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +4 -2
- data/lib/adhearsion/initializer.rb +2 -0
- data/lib/adhearsion/process.rb +2 -0
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/dial_spec.rb +808 -6
- data/spec/adhearsion/call_controller_spec.rb +7 -0
- data/spec/adhearsion/process_spec.rb +14 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 954ba804ad541b402e9b92a8bc467cfe6d37bd5d
|
4
|
+
data.tar.gz: 220bcee6bb1362c837b7a1f652135aaf279d7ae9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c21bfd97fb6181317bc98cf881b893b84d69ca2028fd20da89f1acf192390305a48b89677cacc40181f07bcdd0580c20de961abf446d7dd273d73b7fb6b20b59
|
7
|
+
data.tar.gz: 07321554930f529a73ea01aa6c8f79ebb45782272a8a023e3b6610957297cd202fbe333694071357ef72aabb25be0e8c8cecd7531b34c14169ffc5c23bc81d44
|
data/CHANGELOG.md
CHANGED
@@ -1,18 +1,28 @@
|
|
1
1
|
# [develop](https://github.com/adhearsion/adhearsion)
|
2
2
|
|
3
|
-
# [2.4.0.
|
3
|
+
# [2.4.0.beta2](https://github.com/adhearsion/adhearsion/compare/v2.3.5...v2.4.0.beta2) - [2013-08-23](https://rubygems.org/gems/adhearsion/versions/2.4.0.beta2)
|
4
|
+
* Deprecation: Ruby 1.9.2 support is deprecated and will be dropped in a future version of Adhearsion
|
5
|
+
* Feature: Update to Punchblock 2.0 for JRuby compatibility, better performance and Rayo spec compliance
|
4
6
|
* Feature: Add Call#wait_for_end, which blocks until the call ends and returns its end reason
|
5
7
|
* Feature: Add joined call attribute to dial status
|
6
8
|
* Feature: Track call start/end time and duration
|
7
9
|
* Feature: Add per-call join duration and disposition to DialStatus
|
8
|
-
* Feature: Add `CallController#dial_and_confirm` which allows parallel confirmation of outbound calls
|
10
|
+
* Feature: Add `CallController#dial_and_confirm` which allows parallel confirmation of outbound calls and apologies to losing parties
|
11
|
+
* Feature: Add `CallController::Dial::Dial#split`, `#rejoin`, `#merge` and `#skip_cleanup` to support conferencing and transfer use cases
|
9
12
|
* Feature: Add `#originate` method to console as alias for `Adhearsion::OutboundCall.originate`
|
10
13
|
* Feature: Allow the console to be disabled using `--no-console`
|
11
14
|
* Feature: Add CLI options to generate hooks for a plugin to register on [ahnhub.com](http://www.ahnhub.com)
|
12
15
|
* Bugfix: Removed unnecessary Mocha reference from generated plugin
|
13
|
-
* Bugfix: Call loggers
|
16
|
+
* Bugfix: Call loggers are deleted after a call finishes, fixing a memory leak
|
14
17
|
* Bugfix: A menu definition's block context is now available
|
15
18
|
* Bugfix: Ensure call's command registry is not leaked outside the actor
|
19
|
+
* Bugfix: An application's lib/ directory is now in the load path so dependencies may be reasolved more simply. You may now require 'foo.rb' instead of "#{Adhearsion.root}/lib/foo.rb"
|
20
|
+
* Bugfix: Resolve hostname to some local name when experiencing network issues
|
21
|
+
* Bugfix: Ensure smooth shutdown of console and XMPP connections
|
22
|
+
* Bugfix: Warn when booting Adhearsion with an invalid environment name
|
23
|
+
* Bugfix: `CallController#reject` now terminates a call controller in the same way as `#hangup`
|
24
|
+
* Bugfix: `Call` actors can now process other queries while waiting for responses to commands from a VoIP engine
|
25
|
+
* Bugfix: Allow the CLI to function on JRuby
|
16
26
|
|
17
27
|
# [2.3.5](https://github.com/adhearsion/adhearsion/compare/v2.3.4...v2.3.5) - [2013-06-06](https://rubygems.org/gems/adhearsion/versions/2.3.5)
|
18
28
|
* Bugfix: Fix race conditions in barging in before output start is acknowledged
|
data/README.markdown
CHANGED
@@ -27,7 +27,7 @@ Adhearsion rests above a lower-level telephony platform, for example [Asterisk](
|
|
27
27
|
* A VoIP platform:
|
28
28
|
* Asterisk 1.8+
|
29
29
|
* FreeSWITCH
|
30
|
-
* Prism 11+ with rayo-server
|
30
|
+
* A Rayo server (Prism 11+ with rayo-server, or FreeSWITCH with mod_rayo)
|
31
31
|
* An interest in building cool new things
|
32
32
|
|
33
33
|
\* Support for Ruby 1.9.2 is deprecated, and requires locking your application to ActiveSupport 3.x as follows:
|
data/lib/adhearsion/call.rb
CHANGED
@@ -30,7 +30,20 @@ module Adhearsion
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
# @return [Symbol] the reason for the call ending
|
34
|
+
attr_reader :end_reason
|
35
|
+
|
36
|
+
# @return [Array<Adhearsion::CallController>] the set of call controllers executing on the call
|
37
|
+
attr_reader :controllers
|
38
|
+
|
39
|
+
# @return [Hash<String => String>] a collection of SIP headers set during the call
|
40
|
+
attr_reader :variables
|
41
|
+
|
42
|
+
# @return [Time] the time at which the call began. For inbound calls this is the time at which the call was offered to Adhearsion. For outbound calls it is the time at which the remote party answered.
|
43
|
+
attr_reader :start_time
|
44
|
+
|
45
|
+
# @return [Time] the time at which the call began. For inbound calls this is the time at which the call was offered to Adhearsion. For outbound calls it is the time at which the remote party answered.
|
46
|
+
attr_reader :end_time
|
34
47
|
|
35
48
|
delegate :[], :[]=, :to => :variables
|
36
49
|
delegate :to, :from, :to => :offer, :allow_nil => true
|
@@ -57,6 +70,7 @@ module Adhearsion
|
|
57
70
|
def id
|
58
71
|
offer.target_call_id if offer
|
59
72
|
end
|
73
|
+
alias :to_s :id
|
60
74
|
|
61
75
|
#
|
62
76
|
# @return [String, nil] The domain on which the call resides
|
@@ -161,13 +175,27 @@ module Adhearsion
|
|
161
175
|
end
|
162
176
|
|
163
177
|
on_joined do |event|
|
164
|
-
|
178
|
+
if event.call_uri
|
179
|
+
target = event.call_uri
|
180
|
+
type = :call
|
181
|
+
else
|
182
|
+
target = event.mixer_name
|
183
|
+
type = :mixer
|
184
|
+
end
|
185
|
+
logger.info "Joined to #{type} #{target}"
|
165
186
|
@peers[target] = Adhearsion.active_calls[target]
|
166
187
|
signal :joined, target
|
167
188
|
end
|
168
189
|
|
169
190
|
on_unjoined do |event|
|
170
|
-
|
191
|
+
if event.call_uri
|
192
|
+
target = event.call_uri
|
193
|
+
type = :call
|
194
|
+
else
|
195
|
+
target = event.mixer_name
|
196
|
+
type = :mixer
|
197
|
+
end
|
198
|
+
logger.info "Unjoined from #{type} #{target}"
|
171
199
|
@peers.delete target
|
172
200
|
signal :unjoined, target
|
173
201
|
end
|
@@ -277,6 +305,7 @@ module Adhearsion
|
|
277
305
|
# @param [Hash, Optional] options further options to be joined with
|
278
306
|
#
|
279
307
|
def join(target, options = {})
|
308
|
+
logger.info "Joining to #{target}"
|
280
309
|
command = Punchblock::Command::Join.new options.merge(join_options_with_target(target))
|
281
310
|
write_and_await_response command
|
282
311
|
end
|
@@ -289,6 +318,7 @@ module Adhearsion
|
|
289
318
|
# @option target [String] mixer_name The mixer to unjoin from
|
290
319
|
#
|
291
320
|
def unjoin(target)
|
321
|
+
logger.info "Unjoining from #{target}"
|
292
322
|
command = Punchblock::Command::Unjoin.new join_options_with_target(target)
|
293
323
|
write_and_await_response command
|
294
324
|
end
|
@@ -366,11 +396,6 @@ module Adhearsion
|
|
366
396
|
def logger_id
|
367
397
|
"#{self.class}: #{id}@#{domain}"
|
368
398
|
end
|
369
|
-
# @private
|
370
|
-
def to_ary
|
371
|
-
[current_actor]
|
372
|
-
end
|
373
|
-
|
374
399
|
# @private
|
375
400
|
def inspect
|
376
401
|
attrs = [:offer, :end_reason, :commands, :variables, :controllers, :to, :from].map do |attr|
|
@@ -77,6 +77,15 @@ module Adhearsion
|
|
77
77
|
@call, @metadata, @block = call, metadata || {}, block
|
78
78
|
end
|
79
79
|
|
80
|
+
def method_missing(method_name, *args, &block)
|
81
|
+
if @block
|
82
|
+
block_context = eval "self", @block.binding
|
83
|
+
block_context.send method_name, *args, &block
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
80
89
|
#
|
81
90
|
# Execute the controller, allowing passing control to another controller
|
82
91
|
#
|
@@ -76,33 +76,36 @@ module Adhearsion
|
|
76
76
|
raise Call::Hangup unless call.alive? && call.active?
|
77
77
|
@options, @call = options, call
|
78
78
|
@targets = to.respond_to?(:has_key?) ? to : Array(to)
|
79
|
+
@call_targets = {}
|
79
80
|
set_defaults
|
80
81
|
end
|
81
82
|
|
82
|
-
def
|
83
|
-
@
|
84
|
-
|
85
|
-
@latch = CountDownLatch.new @targets.size
|
86
|
-
|
87
|
-
@options[:from] ||= @call.from
|
88
|
-
|
89
|
-
_for = @options.delete :for
|
90
|
-
@options[:timeout] ||= _for if _for
|
91
|
-
|
92
|
-
@confirmation_controller = @options.delete :confirm
|
93
|
-
@confirmation_metadata = @options.delete :confirm_metadata
|
83
|
+
def inspect
|
84
|
+
"#<#{self.class} to=#{@to.inspect} options=#{@options.inspect}>"
|
94
85
|
end
|
95
86
|
|
87
|
+
# Prep outbound calls, link call lifecycles and place outbound calls
|
96
88
|
def run
|
97
89
|
track_originating_call
|
98
90
|
prep_calls
|
99
91
|
place_calls
|
100
92
|
end
|
101
93
|
|
94
|
+
#
|
95
|
+
# Links the lifecycle of the originating call to the Dial operation such that the Dial is unblocked when the originating call ends
|
102
96
|
def track_originating_call
|
103
|
-
@call.on_end
|
97
|
+
@call.on_end do |_|
|
98
|
+
logger.info "Root call ended, unblocking everything..."
|
99
|
+
@waiters.each do |latch|
|
100
|
+
latch.countdown! until latch.count == 0
|
101
|
+
end
|
102
|
+
end
|
104
103
|
end
|
105
104
|
|
105
|
+
#
|
106
|
+
# Prepares a set of OutboundCall actors to be dialed and links their lifecycles to the Dial operation
|
107
|
+
#
|
108
|
+
# @yield Each call to the passed block for further setup operations
|
106
109
|
def prep_calls
|
107
110
|
@calls = @targets.map do |target, specific_options|
|
108
111
|
new_call = OutboundCall.new
|
@@ -122,9 +125,11 @@ module Adhearsion
|
|
122
125
|
pre_confirmation_tasks new_call
|
123
126
|
|
124
127
|
new_call.on_unjoined @call do |unjoined|
|
125
|
-
new_call["dial_countdown_#{@call.id}"] = true
|
126
128
|
join_status.ended
|
127
|
-
@
|
129
|
+
unless @splitting
|
130
|
+
new_call["dial_countdown_#{@call.id}"] = true
|
131
|
+
@latch.countdown!
|
132
|
+
end
|
128
133
|
end
|
129
134
|
|
130
135
|
if @confirmation_controller
|
@@ -141,31 +146,107 @@ module Adhearsion
|
|
141
146
|
@call.answer
|
142
147
|
join_status.started
|
143
148
|
new_call.join @call
|
144
|
-
status.answer!
|
149
|
+
status.answer!
|
145
150
|
elsif status.result == :answer
|
146
151
|
join_status.lost_confirmation!
|
147
152
|
end
|
148
153
|
end
|
149
154
|
|
150
|
-
[new_call
|
155
|
+
@call_targets[new_call] = [target, specific_options]
|
156
|
+
|
157
|
+
yield new_call if block_given?
|
158
|
+
|
159
|
+
new_call
|
151
160
|
end
|
152
161
|
|
153
162
|
status.calls = @calls
|
154
163
|
end
|
155
164
|
|
165
|
+
#
|
166
|
+
# Dials the set of outbound calls
|
156
167
|
def place_calls
|
157
|
-
@calls.
|
168
|
+
@calls.each do |call|
|
169
|
+
target, specific_options = @call_targets[call]
|
158
170
|
local_options = @options.dup.deep_merge specific_options if specific_options
|
159
171
|
call.dial target, (local_options || @options)
|
160
|
-
call
|
161
172
|
end
|
162
173
|
end
|
163
174
|
|
175
|
+
# Split calls party to the dial
|
176
|
+
# Marks the end time in the status of each join, but does not unblock #dial until one of the calls ends
|
177
|
+
# Optionally executes call controllers on calls once split, where 'current_dial' is available in controller metadata in order to perform further operations on the Dial, including rejoining and termination.
|
178
|
+
# @param [Hash] targets Target call controllers to execute on call legs once split
|
179
|
+
# @option options [Adhearsion::CallController] :main The call controller class to execute on the 'main' call leg (the one who initiated the #dial)
|
180
|
+
# @option options [Proc] :main_callback A block to call when the :main controller completes
|
181
|
+
# @option options [Adhearsion::CallController] :others The call controller class to execute on the 'other' call legs (the ones created as a result of the #dial)
|
182
|
+
# @option options [Proc] :others_callback A block to call when the :others controller completes on an individual call
|
183
|
+
def split(targets = {})
|
184
|
+
logger.info "Splitting calls apart"
|
185
|
+
@splitting = true
|
186
|
+
@calls.each do |call|
|
187
|
+
logger.info "Unjoining peer #{call.id}"
|
188
|
+
ignoring_missing_joins { @call.unjoin call.id }
|
189
|
+
if split_controller = targets[:others]
|
190
|
+
logger.info "Executing split controller #{split_controller} on #{call.id}"
|
191
|
+
call.execute_controller split_controller.new(call, 'current_dial' => self), targets[:others_callback]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
if split_controller = targets[:main]
|
195
|
+
logger.info "Executing split controller #{split_controller} on main call"
|
196
|
+
@call.execute_controller split_controller.new(@call, 'current_dial' => self), targets[:main_callback]
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Rejoin parties that were previously split
|
201
|
+
# @param [Call, String, Hash] target The target to join calls to. See Call#join for details.
|
202
|
+
def rejoin(target = @call)
|
203
|
+
logger.info "Rejoining to #{target}"
|
204
|
+
unless target == @call
|
205
|
+
@call.join target
|
206
|
+
end
|
207
|
+
@calls.each do |call|
|
208
|
+
call.join target
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Merge another Dial into this one, joining all calls to a mixer
|
213
|
+
# @param [Dial] other the other dial operation to merge calls from
|
214
|
+
def merge(other)
|
215
|
+
logger.info "Merging with #{other.inspect}"
|
216
|
+
mixer_name = SecureRandom.uuid
|
217
|
+
|
218
|
+
split
|
219
|
+
other.split
|
220
|
+
|
221
|
+
rejoin mixer_name: mixer_name
|
222
|
+
other.rejoin mixer_name: mixer_name
|
223
|
+
|
224
|
+
calls_to_merge = other.status.calls + [other.root_call]
|
225
|
+
@calls.concat calls_to_merge
|
226
|
+
|
227
|
+
latch = CountDownLatch.new calls_to_merge.size
|
228
|
+
calls_to_merge.each do |call|
|
229
|
+
call.on_end { |event| latch.countdown! }
|
230
|
+
end
|
231
|
+
@waiters << latch
|
232
|
+
end
|
233
|
+
|
234
|
+
#
|
235
|
+
# Block until the dial operation is completed by an appropriate quorum of the involved calls ending
|
164
236
|
def await_completion
|
165
237
|
@latch.wait(@options[:timeout]) || status.timeout!
|
166
|
-
|
238
|
+
return unless status.result == :answer
|
239
|
+
@waiters.each(&:wait)
|
240
|
+
end
|
241
|
+
|
242
|
+
#
|
243
|
+
# Do not hangup outbound calls when the Dial operation finishes. This allows outbound calls to continue with other processing once they are unjoined.
|
244
|
+
def skip_cleanup
|
245
|
+
@skip_cleanup = true
|
167
246
|
end
|
168
247
|
|
248
|
+
#
|
249
|
+
# Hangup any remaining calls
|
169
250
|
def cleanup_calls
|
170
251
|
calls_to_hangup = @calls.map do |call|
|
171
252
|
begin
|
@@ -177,18 +258,45 @@ module Adhearsion
|
|
177
258
|
logger.info "#dial finished with no remaining outbound calls"
|
178
259
|
return
|
179
260
|
end
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
261
|
+
if @skip_cleanup
|
262
|
+
logger.info "#dial finished. Leaving #{calls_to_hangup.size} outbound calls going which are still active: #{calls_to_hangup.map(&:first).join ", "}."
|
263
|
+
else
|
264
|
+
logger.info "#dial finished. Hanging up #{calls_to_hangup.size} outbound calls which are still active: #{calls_to_hangup.map(&:first).join ", "}."
|
265
|
+
calls_to_hangup.each do |id, outbound_call|
|
266
|
+
begin
|
267
|
+
outbound_call.hangup
|
268
|
+
rescue Celluloid::DeadActorError
|
269
|
+
# This actor may previously have been shut down due to the call ending
|
270
|
+
end
|
186
271
|
end
|
187
272
|
end
|
188
273
|
end
|
189
274
|
|
275
|
+
protected
|
276
|
+
|
277
|
+
def root_call
|
278
|
+
@call
|
279
|
+
end
|
280
|
+
|
190
281
|
private
|
191
282
|
|
283
|
+
def set_defaults
|
284
|
+
@status = DialStatus.new
|
285
|
+
|
286
|
+
@latch = CountDownLatch.new @targets.size
|
287
|
+
@waiters = [@latch]
|
288
|
+
|
289
|
+
@options[:from] ||= @call.from
|
290
|
+
|
291
|
+
_for = @options.delete :for
|
292
|
+
@options[:timeout] ||= _for if _for
|
293
|
+
|
294
|
+
@confirmation_controller = @options.delete :confirm
|
295
|
+
@confirmation_metadata = @options.delete :confirm_metadata
|
296
|
+
|
297
|
+
@skip_cleanup = false
|
298
|
+
end
|
299
|
+
|
192
300
|
def pre_confirmation_tasks(call)
|
193
301
|
on_all_except call do |target_call|
|
194
302
|
logger.info "#dial hanging up call #{target_call.id} because this call has been answered by another channel"
|
@@ -200,7 +308,7 @@ module Adhearsion
|
|
200
308
|
end
|
201
309
|
|
202
310
|
def on_all_except(call)
|
203
|
-
@calls.each do |target_call
|
311
|
+
@calls.each do |target_call|
|
204
312
|
begin
|
205
313
|
next if target_call.id == call.id
|
206
314
|
yield target_call
|
@@ -209,16 +317,22 @@ module Adhearsion
|
|
209
317
|
end
|
210
318
|
end
|
211
319
|
end
|
320
|
+
|
321
|
+
def ignoring_missing_joins
|
322
|
+
yield
|
323
|
+
rescue Punchblock::ProtocolError => e
|
324
|
+
raise unless e.name == :service_unavailable
|
325
|
+
end
|
212
326
|
end
|
213
327
|
|
214
328
|
class ParallelConfirmationDial < Dial
|
329
|
+
private
|
330
|
+
|
215
331
|
def set_defaults
|
216
332
|
super
|
217
333
|
@apology_controller = @options.delete :apology
|
218
334
|
end
|
219
335
|
|
220
|
-
private
|
221
|
-
|
222
336
|
def pre_confirmation_tasks(call)
|
223
337
|
end
|
224
338
|
|
@@ -237,7 +351,7 @@ module Adhearsion
|
|
237
351
|
|
238
352
|
class DialStatus
|
239
353
|
# The collection of calls created during the dial operation
|
240
|
-
attr_accessor :calls
|
354
|
+
attr_accessor :calls
|
241
355
|
|
242
356
|
# A collection of status objects indexed by call. Provides status on the joins such as duration
|
243
357
|
attr_accessor :joins
|
@@ -257,8 +371,7 @@ module Adhearsion
|
|
257
371
|
end
|
258
372
|
|
259
373
|
# @private
|
260
|
-
def answer!
|
261
|
-
@joined_call = call
|
374
|
+
def answer!
|
262
375
|
@result = :answer
|
263
376
|
end
|
264
377
|
|
data/lib/adhearsion/console.rb
CHANGED
@@ -43,7 +43,7 @@ module Adhearsion
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def stop
|
46
|
-
return unless instance_variable_defined?(:@pry_thread)
|
46
|
+
return unless instance_variable_defined?(:@pry_thread) && @pry_thread
|
47
47
|
@pry_thread.kill
|
48
48
|
@pry_thread = nil
|
49
49
|
logger.info "Adhearsion Console shutting down"
|
@@ -17,6 +17,23 @@ If you are using Asterisk 1.8, you will need to add an additional context with t
|
|
17
17
|
|
18
18
|
## FreeSWITCH
|
19
19
|
|
20
|
+
### With mod_rayo (recommended)
|
21
|
+
|
22
|
+
* Ensure that mod_rayo is installed and configured according to its' documentation.
|
23
|
+
* Add an extension to your dialplan like so:
|
24
|
+
|
25
|
+
```xml
|
26
|
+
<extension name='Adhearsion'>
|
27
|
+
<condition field="destination_number" expression="^10$">
|
28
|
+
<action application='rayo'/>
|
29
|
+
</condition>
|
30
|
+
</extension>
|
31
|
+
```
|
32
|
+
|
33
|
+
* Connect Adhearsion via XMPP using the Rayo protocol, as per the sample config.
|
34
|
+
|
35
|
+
### With Punchblock's FreeSWITCH mode (deprecated)
|
36
|
+
|
20
37
|
* Ensure that mod_event_socket is installed, and configure it in autoload_configs/event_socket.conf.xml to taste
|
21
38
|
* Add an extension to your dialplan like so:
|
22
39
|
|
@@ -25,11 +42,13 @@ If you are using Asterisk 1.8, you will need to add an additional context with t
|
|
25
42
|
<condition field="destination_number" expression="^10$">
|
26
43
|
<action application="set" data="hangup_after_bridge=false"/>
|
27
44
|
<action application="set" data="park_after_bridge=true"/>
|
28
|
-
<action application='
|
45
|
+
<action application='rayo'/>
|
29
46
|
</condition>
|
30
47
|
</extension>
|
31
48
|
```
|
32
49
|
|
50
|
+
* Connect Adhearsion via EventSocket, as per the sample config.
|
51
|
+
|
33
52
|
## Voxeo PRISM
|
34
53
|
|
35
54
|
Install the [rayo-server](https://github.com/rayo/rayo-server) app into PRISM 11 and follow the [configuration guide](https://github.com/rayo/rayo-server/wiki/Single-node-and-cluster-configuration-reference).
|