signalwire-sdk 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +259 -0
  4. data/bin/swaig-test +872 -0
  5. data/lib/signalwire/agent/agent_base.rb +2134 -0
  6. data/lib/signalwire/contexts/context_builder.rb +861 -0
  7. data/lib/signalwire/core/logging_config.rb +54 -0
  8. data/lib/signalwire/datamap/data_map.rb +315 -0
  9. data/lib/signalwire/logging.rb +92 -0
  10. data/lib/signalwire/pom/prompt_object_model.rb +269 -0
  11. data/lib/signalwire/pom/section.rb +202 -0
  12. data/lib/signalwire/prefabs/concierge.rb +92 -0
  13. data/lib/signalwire/prefabs/faq_bot.rb +67 -0
  14. data/lib/signalwire/prefabs/info_gatherer.rb +79 -0
  15. data/lib/signalwire/prefabs/receptionist.rb +74 -0
  16. data/lib/signalwire/prefabs/survey.rb +75 -0
  17. data/lib/signalwire/relay/action.rb +291 -0
  18. data/lib/signalwire/relay/call.rb +523 -0
  19. data/lib/signalwire/relay/client.rb +789 -0
  20. data/lib/signalwire/relay/constants.rb +124 -0
  21. data/lib/signalwire/relay/message.rb +137 -0
  22. data/lib/signalwire/relay/relay_event.rb +670 -0
  23. data/lib/signalwire/rest/http_client.rb +159 -0
  24. data/lib/signalwire/rest/namespaces/addresses.rb +19 -0
  25. data/lib/signalwire/rest/namespaces/calling.rb +179 -0
  26. data/lib/signalwire/rest/namespaces/chat.rb +18 -0
  27. data/lib/signalwire/rest/namespaces/compat.rb +229 -0
  28. data/lib/signalwire/rest/namespaces/datasphere.rb +39 -0
  29. data/lib/signalwire/rest/namespaces/fabric.rb +235 -0
  30. data/lib/signalwire/rest/namespaces/imported_numbers.rb +18 -0
  31. data/lib/signalwire/rest/namespaces/logs.rb +46 -0
  32. data/lib/signalwire/rest/namespaces/lookup.rb +18 -0
  33. data/lib/signalwire/rest/namespaces/mfa.rb +26 -0
  34. data/lib/signalwire/rest/namespaces/number_groups.rb +32 -0
  35. data/lib/signalwire/rest/namespaces/phone_numbers.rb +124 -0
  36. data/lib/signalwire/rest/namespaces/project.rb +33 -0
  37. data/lib/signalwire/rest/namespaces/pubsub.rb +18 -0
  38. data/lib/signalwire/rest/namespaces/queues.rb +28 -0
  39. data/lib/signalwire/rest/namespaces/recordings.rb +18 -0
  40. data/lib/signalwire/rest/namespaces/registry.rb +67 -0
  41. data/lib/signalwire/rest/namespaces/short_codes.rb +26 -0
  42. data/lib/signalwire/rest/namespaces/sip_profile.rb +22 -0
  43. data/lib/signalwire/rest/namespaces/verified_callers.rb +24 -0
  44. data/lib/signalwire/rest/namespaces/video.rb +129 -0
  45. data/lib/signalwire/rest/pagination.rb +89 -0
  46. data/lib/signalwire/rest/phone_call_handler.rb +56 -0
  47. data/lib/signalwire/rest/rest_client.rb +114 -0
  48. data/lib/signalwire/runtime.rb +98 -0
  49. data/lib/signalwire/security/session_manager.rb +124 -0
  50. data/lib/signalwire/security/webhook_middleware.rb +191 -0
  51. data/lib/signalwire/security/webhook_validator.rb +327 -0
  52. data/lib/signalwire/server/agent_server.rb +413 -0
  53. data/lib/signalwire/serverless/lambda_handler.rb +251 -0
  54. data/lib/signalwire/skills/builtin/api_ninjas_trivia.rb +99 -0
  55. data/lib/signalwire/skills/builtin/claude_skills.rb +92 -0
  56. data/lib/signalwire/skills/builtin/custom_skills.rb +54 -0
  57. data/lib/signalwire/skills/builtin/datasphere.rb +153 -0
  58. data/lib/signalwire/skills/builtin/datasphere_serverless.rb +107 -0
  59. data/lib/signalwire/skills/builtin/datetime.rb +97 -0
  60. data/lib/signalwire/skills/builtin/google_maps.rb +168 -0
  61. data/lib/signalwire/skills/builtin/info_gatherer.rb +189 -0
  62. data/lib/signalwire/skills/builtin/joke.rb +65 -0
  63. data/lib/signalwire/skills/builtin/math.rb +176 -0
  64. data/lib/signalwire/skills/builtin/mcp_gateway.rb +121 -0
  65. data/lib/signalwire/skills/builtin/native_vector_search.rb +116 -0
  66. data/lib/signalwire/skills/builtin/play_background_file.rb +86 -0
  67. data/lib/signalwire/skills/builtin/spider.rb +169 -0
  68. data/lib/signalwire/skills/builtin/swml_transfer.rb +118 -0
  69. data/lib/signalwire/skills/builtin/weather_api.rb +92 -0
  70. data/lib/signalwire/skills/builtin/web_search.rb +141 -0
  71. data/lib/signalwire/skills/builtin/wikipedia_search.rb +125 -0
  72. data/lib/signalwire/skills/skill_base.rb +82 -0
  73. data/lib/signalwire/skills/skill_manager.rb +97 -0
  74. data/lib/signalwire/skills/skill_registry.rb +258 -0
  75. data/lib/signalwire/swaig/function_result.rb +777 -0
  76. data/lib/signalwire/swml/document.rb +84 -0
  77. data/lib/signalwire/swml/schema.json +12250 -0
  78. data/lib/signalwire/swml/schema.rb +81 -0
  79. data/lib/signalwire/swml/service.rb +650 -0
  80. data/lib/signalwire/utils/schema_utils.rb +298 -0
  81. data/lib/signalwire/utils/serverless.rb +19 -0
  82. data/lib/signalwire/utils/url_validator.rb +138 -0
  83. data/lib/signalwire/version.rb +5 -0
  84. data/lib/signalwire.rb +114 -0
  85. metadata +225 -0
@@ -0,0 +1,523 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module SignalWire
6
+ module Relay
7
+ # Represents a live RELAY call.
8
+ #
9
+ # Created by RelayClient on inbound calling.call.receive events or
10
+ # outbound dial responses.
11
+ class Call
12
+ attr_reader :call_id, :node_id, :project_id, :context, :tag,
13
+ :direction, :device, :segment_id
14
+ attr_accessor :state
15
+
16
+ def initialize(client, call_id:, node_id:, project_id: '', context: '',
17
+ tag: '', direction: '', device: {}, state: '', segment_id: '')
18
+ @client = client
19
+ @call_id = call_id
20
+ @node_id = node_id
21
+ @project_id = project_id
22
+ @context = context
23
+ @tag = tag
24
+ @direction = direction
25
+ @device = device
26
+ @state = state
27
+ @segment_id = segment_id
28
+
29
+ # Event listeners: event_type -> list of handlers
30
+ @listeners = {}
31
+ # Active actions indexed by control_id
32
+ @actions = {}
33
+ # Mutex for thread-safe state access
34
+ @mutex = Mutex.new
35
+ @ended_cv = ConditionVariable.new
36
+ @ended = false
37
+ @end_event = nil
38
+ end
39
+
40
+ # ------------------------------------------------------------------
41
+ # Internal RPC primitive
42
+ # ------------------------------------------------------------------
43
+
44
+ # Send a calling.<method> JSON-RPC request for this call.
45
+ def _execute(method, extra_params = nil)
46
+ rpc_method = "calling.#{method}"
47
+ params = {
48
+ 'node_id' => @node_id,
49
+ 'call_id' => @call_id
50
+ }
51
+ params.merge!(extra_params) if extra_params
52
+ begin
53
+ @client.execute(rpc_method, params)
54
+ rescue RelayError => e
55
+ code = e.code
56
+ if code && [404, 410, '404', '410'].include?(code)
57
+ $stderr.puts "[RELAY] Call #{@call_id} gone during #{method} (code=#{code})"
58
+ return {}
59
+ end
60
+ raise
61
+ end
62
+ end
63
+
64
+ # ------------------------------------------------------------------
65
+ # Event plumbing
66
+ # ------------------------------------------------------------------
67
+
68
+ # Register an event listener for this call.
69
+ def on(event_type, &handler)
70
+ @mutex.synchronize do
71
+ (@listeners[event_type] ||= []) << handler
72
+ end
73
+ end
74
+
75
+ # Called by RelayClient when an event arrives for this call.
76
+ def _dispatch_event(payload)
77
+ event = Relay.parse_event(payload)
78
+ event_type = event.event_type
79
+
80
+ # Update call state
81
+ if event_type == EVENT_CALL_STATE
82
+ @state = event.params['call_state'] || @state
83
+ if @state == CALL_STATE_ENDED
84
+ @mutex.synchronize do
85
+ @ended = true
86
+ @end_event = event
87
+ @ended_cv.broadcast
88
+ end
89
+ # Resolve any pending actions
90
+ @actions.each_value { |a| a._resolve(event) unless a.done? }
91
+ @actions.clear
92
+ end
93
+ end
94
+
95
+ # Route to active actions by control_id
96
+ control_id = event.params['control_id'] || ''
97
+ if !control_id.empty? && @actions.key?(control_id)
98
+ action = @actions[control_id]
99
+ action._check_event(event)
100
+ @actions.delete(control_id) if action.completed
101
+ end
102
+
103
+ # Notify registered listeners
104
+ handlers = @mutex.synchronize { (@listeners[event_type] || []).dup }
105
+ handlers.each do |handler|
106
+ begin
107
+ handler.call(event)
108
+ rescue => e
109
+ $stderr.puts "[RELAY] Error in event handler for #{event_type}: #{e.message}"
110
+ end
111
+ end
112
+ end
113
+
114
+ # Wait for the call to reach the ended state.
115
+ def wait_for_ended(timeout: nil)
116
+ @mutex.synchronize do
117
+ return @end_event if @ended
118
+
119
+ if timeout
120
+ deadline = Time.now + timeout
121
+ while !@ended
122
+ remaining = deadline - Time.now
123
+ break if remaining <= 0
124
+ @ended_cv.wait(@mutex, remaining)
125
+ end
126
+ else
127
+ @ended_cv.wait(@mutex) until @ended
128
+ end
129
+ @end_event
130
+ end
131
+ end
132
+
133
+ def ended?
134
+ @ended
135
+ end
136
+
137
+ # ------------------------------------------------------------------
138
+ # Action helper
139
+ # ------------------------------------------------------------------
140
+
141
+ def _start_action(action, method, params, on_completed: nil)
142
+ if @state == CALL_STATE_ENDED
143
+ $stderr.puts "[RELAY] Call #{@call_id} already ended, skipping #{method}"
144
+ gone_event = RelayEvent.new(event_type: '', params: {})
145
+ action._resolve(gone_event)
146
+ return action
147
+ end
148
+ action._set_on_completed(on_completed) if on_completed
149
+ @actions[action.control_id] = action
150
+ begin
151
+ result = _execute(method, params)
152
+ rescue => exc
153
+ @actions.delete(action.control_id)
154
+ action._resolve(RelayEvent.new(event_type: '', params: {}))
155
+ raise
156
+ end
157
+ # _execute returns {} when the call is gone (404/410)
158
+ if result.nil? || result.empty?
159
+ @actions.delete(action.control_id)
160
+ unless action.done?
161
+ gone_event = RelayEvent.new(event_type: '', params: {})
162
+ action._resolve(gone_event)
163
+ end
164
+ end
165
+ action
166
+ end
167
+
168
+ # ------------------------------------------------------------------
169
+ # Call lifecycle methods
170
+ # ------------------------------------------------------------------
171
+
172
+ def answer(**kwargs)
173
+ _execute('answer', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
174
+ end
175
+
176
+ def hangup(reason: 'hangup')
177
+ _execute('end', { 'reason' => reason })
178
+ end
179
+
180
+ def pass_call
181
+ _execute('pass')
182
+ end
183
+
184
+ # ------------------------------------------------------------------
185
+ # Connect
186
+ # ------------------------------------------------------------------
187
+
188
+ def connect(devices:, **kwargs)
189
+ params = { 'devices' => devices }
190
+ kwargs.each { |k, v| params[k.to_s] = v }
191
+ _execute('connect', params)
192
+ end
193
+
194
+ def disconnect
195
+ _execute('disconnect')
196
+ end
197
+
198
+ # ------------------------------------------------------------------
199
+ # Hold / Unhold
200
+ # ------------------------------------------------------------------
201
+
202
+ def hold
203
+ _execute('hold')
204
+ end
205
+
206
+ def unhold
207
+ _execute('unhold')
208
+ end
209
+
210
+ # ------------------------------------------------------------------
211
+ # Denoise
212
+ # ------------------------------------------------------------------
213
+
214
+ def denoise
215
+ _execute('denoise')
216
+ end
217
+
218
+ def denoise_stop
219
+ _execute('denoise.stop')
220
+ end
221
+
222
+ # ------------------------------------------------------------------
223
+ # Transfer
224
+ # ------------------------------------------------------------------
225
+
226
+ def transfer(dest:, **kwargs)
227
+ params = { 'dest' => dest }
228
+ kwargs.each { |k, v| params[k.to_s] = v }
229
+ _execute('transfer', params)
230
+ end
231
+
232
+ # ------------------------------------------------------------------
233
+ # Conference
234
+ # ------------------------------------------------------------------
235
+
236
+ def join_conference(name:, **kwargs)
237
+ params = { 'name' => name }
238
+ kwargs.each { |k, v| params[k.to_s] = v }
239
+ _execute('join_conference', params)
240
+ end
241
+
242
+ def leave_conference(conference_id:)
243
+ _execute('leave_conference', { 'conference_id' => conference_id })
244
+ end
245
+
246
+ # ------------------------------------------------------------------
247
+ # Echo
248
+ # ------------------------------------------------------------------
249
+
250
+ def echo(**kwargs)
251
+ _execute('echo', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
252
+ end
253
+
254
+ # ------------------------------------------------------------------
255
+ # Digit binding
256
+ # ------------------------------------------------------------------
257
+
258
+ def bind_digit(digits:, bind_method:, **kwargs)
259
+ params = { 'digits' => digits, 'bind_method' => bind_method }
260
+ kwargs.each { |k, v| params[k.to_s] = v }
261
+ _execute('bind_digit', params)
262
+ end
263
+
264
+ def clear_digit_bindings(**kwargs)
265
+ _execute('clear_digit_bindings', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
266
+ end
267
+
268
+ # ------------------------------------------------------------------
269
+ # Queue
270
+ # ------------------------------------------------------------------
271
+
272
+ def queue_enter(queue_name:, control_id: nil, **kwargs)
273
+ cid = control_id || SecureRandom.uuid
274
+ params = { 'control_id' => cid, 'queue_name' => queue_name }
275
+ kwargs.each { |k, v| params[k.to_s] = v }
276
+ _execute('queue.enter', params)
277
+ end
278
+
279
+ def queue_leave(queue_name:, control_id: nil, **kwargs)
280
+ cid = control_id || SecureRandom.uuid
281
+ params = { 'control_id' => cid, 'queue_name' => queue_name }
282
+ kwargs.each { |k, v| params[k.to_s] = v }
283
+ _execute('queue.leave', params)
284
+ end
285
+
286
+ # ------------------------------------------------------------------
287
+ # Refer (SIP REFER)
288
+ # ------------------------------------------------------------------
289
+
290
+ def refer(device:, **kwargs)
291
+ params = { 'device' => device }
292
+ kwargs.each { |k, v| params[k.to_s] = v }
293
+ _execute('refer', params)
294
+ end
295
+
296
+ # ------------------------------------------------------------------
297
+ # Send digits
298
+ # ------------------------------------------------------------------
299
+
300
+ def send_digits(digits:, control_id: nil, **kwargs)
301
+ cid = control_id || SecureRandom.uuid
302
+ params = { 'control_id' => cid, 'digits' => digits }
303
+ kwargs.each { |k, v| params[k.to_s] = v }
304
+ _execute('send_digits', params)
305
+ end
306
+
307
+ # ------------------------------------------------------------------
308
+ # Live transcribe / translate
309
+ # ------------------------------------------------------------------
310
+
311
+ def live_transcribe(action:, **kwargs)
312
+ params = { 'action' => action }
313
+ kwargs.each { |k, v| params[k.to_s] = v }
314
+ _execute('live_transcribe', params)
315
+ end
316
+
317
+ def live_translate(action:, **kwargs)
318
+ params = { 'action' => action }
319
+ kwargs.each { |k, v| params[k.to_s] = v }
320
+ _execute('live_translate', params)
321
+ end
322
+
323
+ # ------------------------------------------------------------------
324
+ # Room
325
+ # ------------------------------------------------------------------
326
+
327
+ def join_room(name:, **kwargs)
328
+ params = { 'name' => name }
329
+ kwargs.each { |k, v| params[k.to_s] = v }
330
+ _execute('join_room', params)
331
+ end
332
+
333
+ def leave_room
334
+ _execute('leave_room')
335
+ end
336
+
337
+ # ------------------------------------------------------------------
338
+ # User events
339
+ # ------------------------------------------------------------------
340
+
341
+ def user_event(event: nil, **kwargs)
342
+ params = {}
343
+ params['event'] = event if event
344
+ kwargs.each { |k, v| params[k.to_s] = v }
345
+ _execute('user_event', params.empty? ? nil : params)
346
+ end
347
+
348
+ # ------------------------------------------------------------------
349
+ # AI
350
+ # ------------------------------------------------------------------
351
+
352
+ def ai_message(**kwargs)
353
+ _execute('ai_message', kwargs.transform_keys(&:to_s))
354
+ end
355
+
356
+ def ai_hold(**kwargs)
357
+ _execute('ai_hold', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
358
+ end
359
+
360
+ def ai_unhold(**kwargs)
361
+ _execute('ai_unhold', kwargs.empty? ? nil : kwargs.transform_keys(&:to_s))
362
+ end
363
+
364
+ def amazon_bedrock(**kwargs)
365
+ _execute('amazon_bedrock', kwargs.transform_keys(&:to_s))
366
+ end
367
+
368
+ # ------------------------------------------------------------------
369
+ # Audio playback (returns PlayAction)
370
+ # ------------------------------------------------------------------
371
+
372
+ def play(media, volume: nil, direction: nil, loop_count: nil,
373
+ control_id: nil, on_completed: nil, **kwargs)
374
+ cid = control_id || SecureRandom.uuid
375
+ params = { 'control_id' => cid, 'play' => media }
376
+ params['volume'] = volume if volume
377
+ params['direction'] = direction if direction
378
+ params['loop'] = loop_count if loop_count
379
+ kwargs.each { |k, v| params[k.to_s] = v }
380
+ action = PlayAction.new(self, cid)
381
+ _start_action(action, 'play', params, on_completed: on_completed)
382
+ end
383
+
384
+ # ------------------------------------------------------------------
385
+ # Recording (returns RecordAction)
386
+ # ------------------------------------------------------------------
387
+
388
+ def record(audio: nil, control_id: nil, on_completed: nil, **kwargs)
389
+ cid = control_id || SecureRandom.uuid
390
+ record_obj = { 'audio' => audio || {} }
391
+ params = { 'control_id' => cid, 'record' => record_obj }
392
+ kwargs.each { |k, v| params[k.to_s] = v }
393
+ action = RecordAction.new(self, cid)
394
+ _start_action(action, 'record', params, on_completed: on_completed)
395
+ end
396
+
397
+ # ------------------------------------------------------------------
398
+ # Input collection
399
+ # ------------------------------------------------------------------
400
+
401
+ def play_and_collect(media, collect, volume: nil, control_id: nil,
402
+ on_completed: nil, **kwargs)
403
+ cid = control_id || SecureRandom.uuid
404
+ params = { 'control_id' => cid, 'play' => media, 'collect' => collect }
405
+ params['volume'] = volume if volume
406
+ kwargs.each { |k, v| params[k.to_s] = v }
407
+ action = CollectAction.new(self, cid)
408
+ _start_action(action, 'play_and_collect', params, on_completed: on_completed)
409
+ end
410
+
411
+ def collect(collect_opts, control_id: nil, on_completed: nil, **kwargs)
412
+ cid = control_id || SecureRandom.uuid
413
+ params = { 'control_id' => cid }
414
+ params.merge!(collect_opts.transform_keys(&:to_s)) if collect_opts.is_a?(Hash)
415
+ kwargs.each { |k, v| params[k.to_s] = v }
416
+ action = StandaloneCollectAction.new(self, cid)
417
+ _start_action(action, 'collect', params, on_completed: on_completed)
418
+ end
419
+
420
+ # ------------------------------------------------------------------
421
+ # Detect
422
+ # ------------------------------------------------------------------
423
+
424
+ def detect(detect_opts, timeout: nil, control_id: nil,
425
+ on_completed: nil, **kwargs)
426
+ cid = control_id || SecureRandom.uuid
427
+ params = { 'control_id' => cid, 'detect' => detect_opts }
428
+ params['timeout'] = timeout if timeout
429
+ kwargs.each { |k, v| params[k.to_s] = v }
430
+ action = DetectAction.new(self, cid)
431
+ _start_action(action, 'detect', params, on_completed: on_completed)
432
+ end
433
+
434
+ # ------------------------------------------------------------------
435
+ # Fax
436
+ # ------------------------------------------------------------------
437
+
438
+ def send_fax(document:, control_id: nil, on_completed: nil, **kwargs)
439
+ cid = control_id || SecureRandom.uuid
440
+ params = { 'control_id' => cid, 'document' => document }
441
+ kwargs.each { |k, v| params[k.to_s] = v }
442
+ action = FaxAction.new(self, cid, 'send_fax')
443
+ _start_action(action, 'send_fax', params, on_completed: on_completed)
444
+ end
445
+
446
+ def receive_fax(control_id: nil, on_completed: nil, **kwargs)
447
+ cid = control_id || SecureRandom.uuid
448
+ params = { 'control_id' => cid }
449
+ kwargs.each { |k, v| params[k.to_s] = v }
450
+ action = FaxAction.new(self, cid, 'receive_fax')
451
+ _start_action(action, 'receive_fax', params, on_completed: on_completed)
452
+ end
453
+
454
+ # ------------------------------------------------------------------
455
+ # Tap
456
+ # ------------------------------------------------------------------
457
+
458
+ def tap_audio(tap_opts, device:, control_id: nil, on_completed: nil, **kwargs)
459
+ cid = control_id || SecureRandom.uuid
460
+ params = { 'control_id' => cid, 'tap' => tap_opts, 'device' => device }
461
+ kwargs.each { |k, v| params[k.to_s] = v }
462
+ action = TapAction.new(self, cid)
463
+ _start_action(action, 'tap', params, on_completed: on_completed)
464
+ end
465
+
466
+ # ------------------------------------------------------------------
467
+ # Stream
468
+ # ------------------------------------------------------------------
469
+
470
+ def stream(url:, control_id: nil, on_completed: nil, **kwargs)
471
+ cid = control_id || SecureRandom.uuid
472
+ params = { 'control_id' => cid, 'url' => url }
473
+ kwargs.each { |k, v| params[k.to_s] = v }
474
+ action = StreamAction.new(self, cid)
475
+ _start_action(action, 'stream', params, on_completed: on_completed)
476
+ end
477
+
478
+ # ------------------------------------------------------------------
479
+ # Transcribe
480
+ # ------------------------------------------------------------------
481
+
482
+ def transcribe(control_id: nil, on_completed: nil, **kwargs)
483
+ cid = control_id || SecureRandom.uuid
484
+ params = { 'control_id' => cid }
485
+ kwargs.each { |k, v| params[k.to_s] = v }
486
+ action = TranscribeAction.new(self, cid)
487
+ _start_action(action, 'transcribe', params, on_completed: on_completed)
488
+ end
489
+
490
+ # ------------------------------------------------------------------
491
+ # Pay
492
+ # ------------------------------------------------------------------
493
+
494
+ def pay(payment_connector_url:, control_id: nil, on_completed: nil, **kwargs)
495
+ cid = control_id || SecureRandom.uuid
496
+ params = { 'control_id' => cid, 'payment_connector_url' => payment_connector_url }
497
+ kwargs.each { |k, v| params[k.to_s] = v }
498
+ action = PayAction.new(self, cid)
499
+ _start_action(action, 'pay', params, on_completed: on_completed)
500
+ end
501
+
502
+ # ------------------------------------------------------------------
503
+ # AI (returns AIAction)
504
+ # ------------------------------------------------------------------
505
+
506
+ def ai(control_id: nil, on_completed: nil, **kwargs)
507
+ cid = control_id || SecureRandom.uuid
508
+ params = { 'control_id' => cid }
509
+ kwargs.each { |k, v| params[k.to_s] = v }
510
+ action = AIAction.new(self, cid)
511
+ _start_action(action, 'ai', params, on_completed: on_completed)
512
+ end
513
+
514
+ def to_s
515
+ "Call(id=#{@call_id}, state=#{@state}, direction=#{@direction})"
516
+ end
517
+
518
+ def inspect
519
+ to_s
520
+ end
521
+ end
522
+ end
523
+ end