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:



76
77
78
79
80
81
82
83
# File 'lib/adhearsion/call_controller/dial.rb', line 76

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



74
75
76
# File 'lib/adhearsion/call_controller/dial.rb', line 74

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



284
285
286
287
288
289
290
# File 'lib/adhearsion/call_controller/dial.rb', line 284

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



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

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



328
329
330
# File 'lib/adhearsion/call_controller/dial.rb', line 328

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

#inspectObject



85
86
87
# File 'lib/adhearsion/call_controller/dial.rb', line 85

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



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

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



197
198
199
200
201
202
203
# File 'lib/adhearsion/call_controller/dial.rb', line 197

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



134
135
136
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
# File 'lib/adhearsion/call_controller/dial.rb', line 134

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.



246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/adhearsion/call_controller/dial.rb', line 246

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



90
91
92
93
94
95
# File 'lib/adhearsion/call_controller/dial.rb', line 90

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.



294
295
296
# File 'lib/adhearsion/call_controller/dial.rb', line 294

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



213
214
215
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
# File 'lib/adhearsion/call_controller/dial.rb', line 213

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:



112
113
114
115
116
117
118
119
# File 'lib/adhearsion/call_controller/dial.rb', line 112

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



124
125
126
127
128
# File 'lib/adhearsion/call_controller/dial.rb', line 124

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



99
100
101
102
103
104
105
106
# File 'lib/adhearsion/call_controller/dial.rb', line 99

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