Class: SignalWire::Relay::Call

Inherits:
Object
  • Object
show all
Defined in:
lib/signalwire/relay/call.rb

Overview

Represents a live RELAY call.

Created by RelayClient on inbound calling.call.receive events or outbound dial responses.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client, call_id:, node_id:, project_id: '', context: '', tag: '', direction: '', device: {}, state: '', segment_id: '') ⇒ Call

Returns a new instance of Call.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/signalwire/relay/call.rb', line 16

def initialize(client, call_id:, node_id:, project_id: '', context: '',
               tag: '', direction: '', device: {}, state: '', segment_id: '')
  @client     = client
  @call_id    = call_id
  @node_id    = node_id
  @project_id = project_id
  @context    = context
  @tag        = tag
  @direction  = direction
  @device     = device
  @state      = state
  @segment_id = segment_id

  # Event listeners: event_type -> list of handlers
  @listeners = {}
  # Active actions indexed by control_id
  @actions   = {}
  # Mutex for thread-safe state access
  @mutex     = Mutex.new
  @ended_cv  = ConditionVariable.new
  @ended     = false
  @end_event = nil
end

Instance Attribute Details

#call_idObject (readonly)

Returns the value of attribute call_id.



12
13
14
# File 'lib/signalwire/relay/call.rb', line 12

def call_id
  @call_id
end

#contextObject (readonly)

Returns the value of attribute context.



12
13
14
# File 'lib/signalwire/relay/call.rb', line 12

def context
  @context
end

#deviceObject (readonly)

Returns the value of attribute device.



12
13
14
# File 'lib/signalwire/relay/call.rb', line 12

def device
  @device
end

#directionObject (readonly)

Returns the value of attribute direction.



12
13
14
# File 'lib/signalwire/relay/call.rb', line 12

def direction
  @direction
end

#node_idObject (readonly)

Returns the value of attribute node_id.



12
13
14
# File 'lib/signalwire/relay/call.rb', line 12

def node_id
  @node_id
end

#project_idObject (readonly)

Returns the value of attribute project_id.



12
13
14
# File 'lib/signalwire/relay/call.rb', line 12

def project_id
  @project_id
end

#segment_idObject (readonly)

Returns the value of attribute segment_id.



12
13
14
# File 'lib/signalwire/relay/call.rb', line 12

def segment_id
  @segment_id
end

#stateObject

Returns the value of attribute state.



14
15
16
# File 'lib/signalwire/relay/call.rb', line 14

def state
  @state
end

#tagObject (readonly)

Returns the value of attribute tag.



12
13
14
# File 'lib/signalwire/relay/call.rb', line 12

def tag
  @tag
end

Instance Method Details

#_dispatch_event(payload) ⇒ Object

Called by RelayClient when an event arrives for this call.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/signalwire/relay/call.rb', line 76

def _dispatch_event(payload)
  event = Relay.parse_event(payload)
  event_type = event.event_type

  # Update call state
  if event_type == EVENT_CALL_STATE
    @state = event.params['call_state'] || @state
    if @state == CALL_STATE_ENDED
      @mutex.synchronize do
        @ended     = true
        @end_event = event
        @ended_cv.broadcast
      end
      # Resolve any pending actions
      @actions.each_value { |a| a._resolve(event) unless a.done? }
      @actions.clear
    end
  end

  # Route to active actions by control_id
  control_id = event.params['control_id'] || ''
  if !control_id.empty? && @actions.key?(control_id)
    action = @actions[control_id]
    action._check_event(event)
    @actions.delete(control_id) if action.completed
  end

  # Notify registered listeners
  handlers = @mutex.synchronize { (@listeners[event_type] || []).dup }
  handlers.each do |handler|
    begin
      handler.call(event)
    rescue => e
      $stderr.puts "[RELAY] Error in event handler for #{event_type}: #{e.message}"
    end
  end
end

#_execute(method, extra_params = nil) ⇒ Object

Send a calling.<method> JSON-RPC request for this call.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/signalwire/relay/call.rb', line 45

def _execute(method, extra_params = nil)
  rpc_method = "calling.#{method}"
  params = {
    'node_id' => @node_id,
    'call_id' => @call_id
  }
  params.merge!(extra_params) if extra_params
  begin
    @client.execute(rpc_method, params)
  rescue RelayError => e
    code = e.code
    if code && [404, 410, '404', '410'].include?(code)
      $stderr.puts "[RELAY] Call #{@call_id} gone during #{method} (code=#{code})"
      return {}
    end
    raise
  end
end

#_start_action(action, method, params, on_completed: nil) ⇒ Object


Action helper




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
# File 'lib/signalwire/relay/call.rb', line 141

def _start_action(action, method, params, on_completed: nil)
  if @state == CALL_STATE_ENDED
    $stderr.puts "[RELAY] Call #{@call_id} already ended, skipping #{method}"
    gone_event = RelayEvent.new(event_type: '', params: {})
    action._resolve(gone_event)
    return action
  end
  action._set_on_completed(on_completed) if on_completed
  @actions[action.control_id] = action
  begin
    result = _execute(method, params)
  rescue => exc
    @actions.delete(action.control_id)
    action._resolve(RelayEvent.new(event_type: '', params: {}))
    raise
  end
  # _execute returns {} when the call is gone (404/410)
  if result.nil? || result.empty?
    @actions.delete(action.control_id)
    unless action.done?
      gone_event = RelayEvent.new(event_type: '', params: {})
      action._resolve(gone_event)
    end
  end
  action
end

#ai(control_id: nil, on_completed: nil, **kwargs) ⇒ Object


AI (returns AIAction)




506
507
508
509
510
511
512
# File 'lib/signalwire/relay/call.rb', line 506

def ai(control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid }
  kwargs.each { |k, v| params[k.to_s] = v }
  action = AIAction.new(self, cid)
  _start_action(action, 'ai', params, on_completed: on_completed)
end

#ai_hold(**kwargs) ⇒ Object



356
357
358
# File 'lib/signalwire/relay/call.rb', line 356

def ai_hold(**kwargs)
  _execute('ai_hold', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
end

#ai_message(**kwargs) ⇒ Object


AI




352
353
354
# File 'lib/signalwire/relay/call.rb', line 352

def ai_message(**kwargs)
  _execute('ai_message', kwargs.transform_keys(&:to_s))
end

#ai_unhold(**kwargs) ⇒ Object



360
361
362
# File 'lib/signalwire/relay/call.rb', line 360

def ai_unhold(**kwargs)
  _execute('ai_unhold', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
end

#amazon_bedrock(**kwargs) ⇒ Object



364
365
366
# File 'lib/signalwire/relay/call.rb', line 364

def amazon_bedrock(**kwargs)
  _execute('amazon_bedrock', kwargs.transform_keys(&:to_s))
end

#answer(**kwargs) ⇒ Object


Call lifecycle methods




172
173
174
# File 'lib/signalwire/relay/call.rb', line 172

def answer(**kwargs)
  _execute('answer', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
end

#bind_digit(digits:, bind_method:, **kwargs) ⇒ Object


Digit binding




258
259
260
261
262
# File 'lib/signalwire/relay/call.rb', line 258

def bind_digit(digits:, bind_method:, **kwargs)
  params = { 'digits' => digits, 'bind_method' => bind_method }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('bind_digit', params)
end

#clear_digit_bindings(**kwargs) ⇒ Object



264
265
266
# File 'lib/signalwire/relay/call.rb', line 264

def clear_digit_bindings(**kwargs)
  _execute('clear_digit_bindings', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
end

#collect(collect_opts, control_id: nil, on_completed: nil, **kwargs) ⇒ Object



411
412
413
414
415
416
417
418
# File 'lib/signalwire/relay/call.rb', line 411

def collect(collect_opts, control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid }
  params.merge!(collect_opts.transform_keys(&:to_s)) if collect_opts.is_a?(Hash)
  kwargs.each { |k, v| params[k.to_s] = v }
  action = StandaloneCollectAction.new(self, cid)
  _start_action(action, 'collect', params, on_completed: on_completed)
end

#connect(devices:, **kwargs) ⇒ Object


Connect




188
189
190
191
192
# File 'lib/signalwire/relay/call.rb', line 188

def connect(devices:, **kwargs)
  params = { 'devices' => devices }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('connect', params)
end

#denoiseObject


Denoise




214
215
216
# File 'lib/signalwire/relay/call.rb', line 214

def denoise
  _execute('denoise')
end

#denoise_stopObject



218
219
220
# File 'lib/signalwire/relay/call.rb', line 218

def denoise_stop
  _execute('denoise.stop')
end

#detect(detect_opts, timeout: nil, control_id: nil, on_completed: nil, **kwargs) ⇒ Object


Detect




424
425
426
427
428
429
430
431
432
# File 'lib/signalwire/relay/call.rb', line 424

def detect(detect_opts, timeout: nil, control_id: nil,
           on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'detect' => detect_opts }
  params['timeout'] = timeout if timeout
  kwargs.each { |k, v| params[k.to_s] = v }
  action = DetectAction.new(self, cid)
  _start_action(action, 'detect', params, on_completed: on_completed)
end

#disconnectObject



194
195
196
# File 'lib/signalwire/relay/call.rb', line 194

def disconnect
  _execute('disconnect')
end

#echo(**kwargs) ⇒ Object


Echo




250
251
252
# File 'lib/signalwire/relay/call.rb', line 250

def echo(**kwargs)
  _execute('echo', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
end

#ended?Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/signalwire/relay/call.rb', line 133

def ended?
  @ended
end

#hangup(reason: 'hangup') ⇒ Object



176
177
178
# File 'lib/signalwire/relay/call.rb', line 176

def hangup(reason: 'hangup')
  _execute('end', { 'reason' => reason })
end

#holdObject


Hold / Unhold




202
203
204
# File 'lib/signalwire/relay/call.rb', line 202

def hold
  _execute('hold')
end

#inspectObject



518
519
520
# File 'lib/signalwire/relay/call.rb', line 518

def inspect
  to_s
end

#join_conference(name:, **kwargs) ⇒ Object


Conference




236
237
238
239
240
# File 'lib/signalwire/relay/call.rb', line 236

def join_conference(name:, **kwargs)
  params = { 'name' => name }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('join_conference', params)
end

#join_room(name:, **kwargs) ⇒ Object


Room




327
328
329
330
331
# File 'lib/signalwire/relay/call.rb', line 327

def join_room(name:, **kwargs)
  params = { 'name' => name }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('join_room', params)
end

#leave_conference(conference_id:) ⇒ Object



242
243
244
# File 'lib/signalwire/relay/call.rb', line 242

def leave_conference(conference_id:)
  _execute('leave_conference', { 'conference_id' => conference_id })
end

#leave_roomObject



333
334
335
# File 'lib/signalwire/relay/call.rb', line 333

def leave_room
  _execute('leave_room')
end

#live_transcribe(action:, **kwargs) ⇒ Object


Live transcribe / translate




311
312
313
314
315
# File 'lib/signalwire/relay/call.rb', line 311

def live_transcribe(action:, **kwargs)
  params = { 'action' => action }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('live_transcribe', params)
end

#live_translate(action:, **kwargs) ⇒ Object



317
318
319
320
321
# File 'lib/signalwire/relay/call.rb', line 317

def live_translate(action:, **kwargs)
  params = { 'action' => action }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('live_translate', params)
end

#on(event_type, &handler) ⇒ Object

Register an event listener for this call.



69
70
71
72
73
# File 'lib/signalwire/relay/call.rb', line 69

def on(event_type, &handler)
  @mutex.synchronize do
    (@listeners[event_type] ||= []) << handler
  end
end

#pass_callObject



180
181
182
# File 'lib/signalwire/relay/call.rb', line 180

def pass_call
  _execute('pass')
end

#pay(payment_connector_url:, control_id: nil, on_completed: nil, **kwargs) ⇒ Object


Pay




494
495
496
497
498
499
500
# File 'lib/signalwire/relay/call.rb', line 494

def pay(payment_connector_url:, control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'payment_connector_url' => payment_connector_url }
  kwargs.each { |k, v| params[k.to_s] = v }
  action = PayAction.new(self, cid)
  _start_action(action, 'pay', params, on_completed: on_completed)
end

#play(media, volume: nil, direction: nil, loop_count: nil, control_id: nil, on_completed: nil, **kwargs) ⇒ Object


Audio playback (returns PlayAction)




372
373
374
375
376
377
378
379
380
381
382
# File 'lib/signalwire/relay/call.rb', line 372

def play(media, volume: nil, direction: nil, loop_count: nil,
         control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'play' => media }
  params['volume']    = volume if volume
  params['direction'] = direction if direction
  params['loop']      = loop_count if loop_count
  kwargs.each { |k, v| params[k.to_s] = v }
  action = PlayAction.new(self, cid)
  _start_action(action, 'play', params, on_completed: on_completed)
end

#play_and_collect(media, collect, volume: nil, control_id: nil, on_completed: nil, **kwargs) ⇒ Object


Input collection




401
402
403
404
405
406
407
408
409
# File 'lib/signalwire/relay/call.rb', line 401

def play_and_collect(media, collect, volume: nil, control_id: nil,
                     on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'play' => media, 'collect' => collect }
  params['volume'] = volume if volume
  kwargs.each { |k, v| params[k.to_s] = v }
  action = CollectAction.new(self, cid)
  _start_action(action, 'play_and_collect', params, on_completed: on_completed)
end

#queue_enter(queue_name:, control_id: nil, **kwargs) ⇒ Object


Queue




272
273
274
275
276
277
# File 'lib/signalwire/relay/call.rb', line 272

def queue_enter(queue_name:, control_id: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'queue_name' => queue_name }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('queue.enter', params)
end

#queue_leave(queue_name:, control_id: nil, **kwargs) ⇒ Object



279
280
281
282
283
284
# File 'lib/signalwire/relay/call.rb', line 279

def queue_leave(queue_name:, control_id: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'queue_name' => queue_name }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('queue.leave', params)
end

#receive_fax(control_id: nil, on_completed: nil, **kwargs) ⇒ Object



446
447
448
449
450
451
452
# File 'lib/signalwire/relay/call.rb', line 446

def receive_fax(control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid }
  kwargs.each { |k, v| params[k.to_s] = v }
  action = FaxAction.new(self, cid, 'receive_fax')
  _start_action(action, 'receive_fax', params, on_completed: on_completed)
end

#record(audio: nil, control_id: nil, on_completed: nil, **kwargs) ⇒ Object


Recording (returns RecordAction)




388
389
390
391
392
393
394
395
# File 'lib/signalwire/relay/call.rb', line 388

def record(audio: nil, control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  record_obj = { 'audio' => audio || {} }
  params = { 'control_id' => cid, 'record' => record_obj }
  kwargs.each { |k, v| params[k.to_s] = v }
  action = RecordAction.new(self, cid)
  _start_action(action, 'record', params, on_completed: on_completed)
end

#refer(device:, **kwargs) ⇒ Object


Refer (SIP REFER)




290
291
292
293
294
# File 'lib/signalwire/relay/call.rb', line 290

def refer(device:, **kwargs)
  params = { 'device' => device }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('refer', params)
end

#send_digits(digits:, control_id: nil, **kwargs) ⇒ Object


Send digits




300
301
302
303
304
305
# File 'lib/signalwire/relay/call.rb', line 300

def send_digits(digits:, control_id: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'digits' => digits }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('send_digits', params)
end

#send_fax(document:, control_id: nil, on_completed: nil, **kwargs) ⇒ Object


Fax




438
439
440
441
442
443
444
# File 'lib/signalwire/relay/call.rb', line 438

def send_fax(document:, control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'document' => document }
  kwargs.each { |k, v| params[k.to_s] = v }
  action = FaxAction.new(self, cid, 'send_fax')
  _start_action(action, 'send_fax', params, on_completed: on_completed)
end

#stream(url:, control_id: nil, on_completed: nil, **kwargs) ⇒ Object


Stream




470
471
472
473
474
475
476
# File 'lib/signalwire/relay/call.rb', line 470

def stream(url:, control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'url' => url }
  kwargs.each { |k, v| params[k.to_s] = v }
  action = StreamAction.new(self, cid)
  _start_action(action, 'stream', params, on_completed: on_completed)
end

#tap_audio(tap_opts, device:, control_id: nil, on_completed: nil, **kwargs) ⇒ Object


Tap




458
459
460
461
462
463
464
# File 'lib/signalwire/relay/call.rb', line 458

def tap_audio(tap_opts, device:, control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid, 'tap' => tap_opts, 'device' => device }
  kwargs.each { |k, v| params[k.to_s] = v }
  action = TapAction.new(self, cid)
  _start_action(action, 'tap', params, on_completed: on_completed)
end

#to_sObject



514
515
516
# File 'lib/signalwire/relay/call.rb', line 514

def to_s
  "Call(id=#{@call_id}, state=#{@state}, direction=#{@direction})"
end

#transcribe(control_id: nil, on_completed: nil, **kwargs) ⇒ Object


Transcribe




482
483
484
485
486
487
488
# File 'lib/signalwire/relay/call.rb', line 482

def transcribe(control_id: nil, on_completed: nil, **kwargs)
  cid = control_id || SecureRandom.uuid
  params = { 'control_id' => cid }
  kwargs.each { |k, v| params[k.to_s] = v }
  action = TranscribeAction.new(self, cid)
  _start_action(action, 'transcribe', params, on_completed: on_completed)
end

#transfer(dest:, **kwargs) ⇒ Object


Transfer




226
227
228
229
230
# File 'lib/signalwire/relay/call.rb', line 226

def transfer(dest:, **kwargs)
  params = { 'dest' => dest }
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('transfer', params)
end

#unholdObject



206
207
208
# File 'lib/signalwire/relay/call.rb', line 206

def unhold
  _execute('unhold')
end

#user_event(event: nil, **kwargs) ⇒ Object


User events




341
342
343
344
345
346
# File 'lib/signalwire/relay/call.rb', line 341

def user_event(event: nil, **kwargs)
  params = {}
  params['event'] = event if event
  kwargs.each { |k, v| params[k.to_s] = v }
  _execute('user_event', params.empty? ? nil : params)
end

#wait_for_ended(timeout: nil) ⇒ Object

Wait for the call to reach the ended state.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/signalwire/relay/call.rb', line 115

def wait_for_ended(timeout: nil)
  @mutex.synchronize do
    return @end_event if @ended

    if timeout
      deadline = Time.now + timeout
      while !@ended
        remaining = deadline - Time.now
        break if remaining <= 0
        @ended_cv.wait(@mutex, remaining)
      end
    else
      @ended_cv.wait(@mutex) until @ended
    end
    @end_event
  end
end