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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e1b64f1b1643fc319f4203fbf8e6cf2452c444d
4
- data.tar.gz: f533f739a9536421599852a4f049407b287b30b1
3
+ metadata.gz: 954ba804ad541b402e9b92a8bc467cfe6d37bd5d
4
+ data.tar.gz: 220bcee6bb1362c837b7a1f652135aaf279d7ae9
5
5
  SHA512:
6
- metadata.gz: bb6cad9f60e451a309f4e98b5cdb61e19835032e44f053cba58598b9dc4b188abc1d089802d9c596566c3ebdc107e2fec1d61e5a4e26c5f0bfca24bb6f6e5ed8
7
- data.tar.gz: 803b943cbebf981da00a580e46151dad0fc86bb188ccb89171e759461cbb03f889355c159e9b9ad67f77a2d5bb149717e85ae10ff96428128bdbff43aff528f5
6
+ metadata.gz: c21bfd97fb6181317bc98cf881b893b84d69ca2028fd20da89f1acf192390305a48b89677cacc40181f07bcdd0580c20de961abf446d7dd273d73b7fb6b20b59
7
+ data.tar.gz: 07321554930f529a73ea01aa6c8f79ebb45782272a8a023e3b6610957297cd202fbe333694071357ef72aabb25be0e8c8cecd7531b34c14169ffc5c23bc81d44
@@ -1,18 +1,28 @@
1
1
  # [develop](https://github.com/adhearsion/adhearsion)
2
2
 
3
- # [2.4.0.beta1](https://github.com/adhearsion/adhearsion/compare/v2.3.5...v2.4.0.beta1) - [2013-08-20](https://rubygems.org/gems/adhearsion/versions/2.4.0.beta1)
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 should be deleted after a call finishes
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
@@ -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:
@@ -30,7 +30,20 @@ module Adhearsion
30
30
  end
31
31
  end
32
32
 
33
- attr_reader :end_reason, :controllers, :variables, :start_time, :end_time
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
- target = event.call_uri || event.mixer_name
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
- target = event.call_uri || event.mixer_name
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 set_defaults
83
- @status = DialStatus.new
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 { |_| @latch.countdown! until @latch.count == 0 }
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
- @latch.countdown!
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!(new_call)
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, target, specific_options]
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.map! do |call, target, specific_options|
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
- @latch.wait if status.result == :answer
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
- logger.info "#dial finished. Hanging up #{calls_to_hangup.size} outbound calls which are still active: #{calls_to_hangup.map(&:first).join ", "}."
181
- calls_to_hangup.each do |id, outbound_call|
182
- begin
183
- outbound_call.hangup
184
- rescue Celluloid::DeadActorError
185
- # This actor may previously have been shut down due to the call ending
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, :joined_call
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!(call)
261
- @joined_call = call
374
+ def answer!
262
375
  @result = :answer
263
376
  end
264
377
 
@@ -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='park'/>
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).