Class: Adhearsion::CallController::Dial::Dial

Inherits:
Object
  • Object
show all
Defined in:
lib/adhearsion/call_controller/dial.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(to, options, call) ⇒ Dial

Returns a new instance of Dial.

Raises:


79
80
81
82
83
84
85
86
# File 'lib/adhearsion/call_controller/dial.rb', line 79

def initialize(to, options, call)
  raise Call::Hangup unless call.active?
  @id = SecureRandom.uuid
  @options, @call = options, call
  @targets = to.respond_to?(:has_key?) ? to : Array(to)
  @call_targets = {}
  set_defaults
end

Instance Attribute Details

#statusObject

Returns the value of attribute status.


77
78
79
# File 'lib/adhearsion/call_controller/dial.rb', line 77

def status
  @status
end

Instance Method Details

#await_completionObject

Block until the dial operation is completed by an appropriate quorum of the involved calls ending


287
288
289
290
291
292
293
# File 'lib/adhearsion/call_controller/dial.rb', line 287

def await_completion
  @latch.wait(@options[:timeout]) || status.timeout!
  return unless status.result == :answer
  logger.debug "Main calls were completed, waiting for any added calls: #{@waiters.inspect}"
  @waiters.each(&:wait)
  logger.debug "All calls were completed, unblocking."
end

#cleanup_callsObject

Hangup any remaining calls


303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/adhearsion/call_controller/dial.rb', line 303

def cleanup_calls
  calls_to_hangup = @calls.map do |call|
    ignoring_ended_calls do
      [call.id, call] if call.active?
    end
  end.compact
  if calls_to_hangup.size.zero?
    logger.info "#dial finished with no remaining outbound calls"
    return
  end
  if @skip_cleanup
    logger.info "#dial finished. Leaving #{calls_to_hangup.size} outbound calls going which are still active: #{calls_to_hangup.map(&:first).join ", "}."
  else
    logger.info "#dial finished. Hanging up #{calls_to_hangup.size} outbound calls which are still active: #{calls_to_hangup.map(&:first).join ", "}."
    calls_to_hangup.each do |id, outbound_call|
      ignoring_ended_calls do
        if @cleanup_controller
          logger.info "#dial running #{@cleanup_controller.class.name} on #{outbound_call.id}"
          outbound_call.execute_controller @cleanup_controller.new(outbound_call, @cleanup_metadata), ->(call) { call.hangup }
        else
          logger.info "#dial hanging up #{outbound_call.id}"
          outbound_call.hangup
        end
      end
    end
  end
end

#delete_loggerObject


331
332
333
# File 'lib/adhearsion/call_controller/dial.rb', line 331

def delete_logger
  ::Logging::Repository.instance.delete logger_id
end

#inspectObject


88
89
90
# File 'lib/adhearsion/call_controller/dial.rb', line 88

def inspect
  "#<#{self.class}[#{@id}] to=#{@to.inspect} options=#{@options.inspect}>"
end

#merge(other) ⇒ Object

Merge another Dial into this one, joining all calls to a mixer

Parameters:

  • other (Dial)

    the other dial operation to merge calls from


266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/adhearsion/call_controller/dial.rb', line 266

def merge(other)
  logger.info "Merging with #{other.inspect}"

  split
  other.split

  rejoin({mixer_name: @id}, {})
  other.rejoin({mixer_name: @id}, {})

  calls_to_merge = other.status.calls + [other.root_call]
  @calls.merge calls_to_merge

  latch = CountDownLatch.new calls_to_merge.size
  calls_to_merge.each do |call|
    call.on_end { |event| latch.countdown! }
  end
  @waiters << latch
end

#place_callsObject

Dials the set of outbound calls


200
201
202
203
204
205
206
# File 'lib/adhearsion/call_controller/dial.rb', line 200

def place_calls
  @calls.each do |call|
    target, specific_options = @call_targets[call]
    local_options = @options.dup.deep_merge specific_options if specific_options
    call.dial target, (local_options || @options)
  end
end

#prep_calls { ... } ⇒ Object

Prepares a set of OutboundCall actors to be dialed and links their lifecycles to the Dial operation

Yields:

  • Each call to the passed block for further setup operations


137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/adhearsion/call_controller/dial.rb', line 137

def prep_calls
  @calls = Set.new
  @targets.map do |target, specific_options|
    new_call = OutboundCall.new

    join_status = JoinStatus.new
    status.joins[new_call] = join_status

    new_call.on_end do |event|
      @latch.countdown! unless new_call["dial_countdown_#{@id}"]
      if event.reason == :error
        status.error!
        join_status.errored!
      end
    end

    new_call.on_answer do |event|
      new_call.on_joined @call do |joined|
        join_status.started joined.timestamp.to_time
      end

      new_call.on_unjoined @call do |unjoined|
        join_status.ended unjoined.timestamp.to_time
        unless @splitting
          new_call["dial_countdown_#{@id}"] = true
          @latch.countdown!
        end
      end

      if @confirmation_controller
        status.unconfirmed!
        join_status.unconfirmed!
        condition = Celluloid::Condition.new
        new_call.execute_controller @confirmation_controller.new(new_call, @confirmation_metadata), lambda { |call| condition.broadcast }
        condition.wait
      end

      if new_call.active? && status.result != :answer
        logger.info "#dial joining call #{new_call.id} to #{@call.id}"
        pre_join_tasks new_call
        @call.answer
        new_call.join @join_target, @join_options
        unless @join_target == @call
          @call.join @join_target, @join_options
        end
        status.answer!
      elsif status.result == :answer
        join_status.lost_confirmation!
      end
    end

    @call_targets[new_call] = [target, specific_options]

    yield new_call if block_given?

    @calls << new_call
  end

  status.calls = @calls
end

#rejoin(target = nil, join_options = nil) ⇒ Object

Rejoin parties that were previously split

Parameters:

  • target (Call, String, Hash) (defaults to: nil)

    The target to join calls to. See Call#join for details.

  • join_options (Hash) (defaults to: nil)

    Options to specify the kind of join operation to perform. See `Call#join` for details.


249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/adhearsion/call_controller/dial.rb', line 249

def rejoin(target = nil, join_options = nil)
  target ||= join_target
  join_options ||= @join_options
  logger.info "Rejoining to #{target}"
  ignoring_ended_calls do
    unless target == @call
      @join_target = target
      @call.join target, join_options
    end
  end
  @calls.each do |call|
    ignoring_ended_calls { call.join target, join_options }
  end
end

#run(controller) ⇒ Object

Prep outbound calls, link call lifecycles and place outbound calls


93
94
95
96
97
98
# File 'lib/adhearsion/call_controller/dial.rb', line 93

def run(controller)
  track_originating_call
  start_ringback controller
  prep_calls
  place_calls
end

#skip_cleanupObject

Do not hangup outbound calls when the Dial operation finishes. This allows outbound calls to continue with other processing once they are unjoined.


297
298
299
# File 'lib/adhearsion/call_controller/dial.rb', line 297

def skip_cleanup
  @skip_cleanup = true
end

#split(targets = {}) ⇒ Object

Split calls party to the dial Marks the end time in the status of each join, but does not unblock #dial until one of the calls ends 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.

Parameters:

  • targets (Hash) (defaults to: {})

    Target call controllers to execute on call legs once split

  • options (Hash)

    a customizable set of options


216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/adhearsion/call_controller/dial.rb', line 216

def split(targets = {})
  @splitting = true
  calls_to_split = @calls.map do |call|
    ignoring_ended_calls do
      [call.id, call] if call.active?
    end
  end.compact
  logger.info "Splitting off peer calls #{calls_to_split.map(&:first).join ", "}"
  calls_to_split.each do |id, call|
    ignoring_ended_calls do
      logger.debug "Unjoining peer #{call.id} from #{join_target}"
      ignoring_missing_joins { call.unjoin join_target }
      if split_controller = targets[:others]
        logger.info "Executing controller #{split_controller} on split call #{call.id}"
        call.execute_controller split_controller.new(call, 'current_dial' => self), targets[:others_callback]
      end
    end
  end
  ignoring_ended_calls do
    if join_target != @call
      logger.debug "Unjoining main call #{@call.id} from #{join_target}"
      @call.unjoin join_target
    end
    if split_controller = targets[:main]
      logger.info "Executing controller #{split_controller} on main call"
      @call.execute_controller split_controller.new(@call, 'current_dial' => self), targets[:main_callback]
    end
  end
end

#start_ringback(controller) ⇒ Object

Starts ringback on the specified controller

Parameters:


115
116
117
118
119
120
121
122
# File 'lib/adhearsion/call_controller/dial.rb', line 115

def start_ringback(controller)
  return unless @ringback
  @ringback_component = if @ringback.respond_to?(:call)
    @ringback.call
  else
    controller.play! @ringback, repeat_times: 0
  end
end

#terminate_ringbackObject

Terminates any ringback that might be playing


127
128
129
130
131
# File 'lib/adhearsion/call_controller/dial.rb', line 127

def terminate_ringback
  return unless @ringback_component
  return unless @ringback_component.executing?
  @ringback_component.stop!
end

#track_originating_callObject

Links the lifecycle of the originating call to the Dial operation such that the Dial is unblocked when the originating call ends


102
103
104
105
106
107
108
109
# File 'lib/adhearsion/call_controller/dial.rb', line 102

def track_originating_call
  @call.on_end do |_|
    logger.debug "Root call ended, unblocking connected calls"
    @waiters.each do |latch|
      latch.countdown! until latch.count == 0
    end
  end
end