Class: Pinnacle::Wrapper::Voice::VoiceSocket

Inherits:
Object
  • Object
show all
Defined in:
lib/pinnacle/wrapper/voice/voice_socket.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(socket, create_socket: nil, reconnect: nil) ⇒ VoiceSocket

Returns a new instance of VoiceSocket.



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 16

def initialize(socket, create_socket: nil, reconnect: nil)
  @socket = socket
  @create_socket = create_socket
  @reconnect = normalize_reconnect_options(reconnect || {})
  @listeners = Hash.new { |hash, key| hash[key] = [] }
  @pending_acks = {}
  @closed_by_user = false
  @reconnect_attempts = 0
  @reconnect_thread = nil
  bind_socket
end

Instance Attribute Details

#callObject

Returns the value of attribute call.



13
14
15
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 13

def call
  @call
end

#call_idObject

Returns the value of attribute call_id.



13
14
15
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 13

def call_id
  @call_id
end

#socketObject (readonly)

Returns the value of attribute socket.



14
15
16
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 14

def socket
  @socket
end

Class Method Details

.create_voice_command_idObject



347
348
349
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 347

def create_voice_command_id
  "cmd_#{SecureRandom.uuid}"
end

Instance Method Details

#answer(params = nil, command_id: create_voice_command_id) ⇒ Object



107
108
109
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 107

def answer(params = nil, command_id: create_voice_command_id)
  command(with_optional_params(command_frame(command_id, Types::CALL_ANSWER), params))
end

#cancel_input(command_id: create_voice_command_id) ⇒ Object



143
144
145
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 143

def cancel_input(command_id: create_voice_command_id)
  command(command_frame(command_id, Types::INPUT_CANCEL))
end

#close(code = nil, reason = nil) ⇒ Object



188
189
190
191
192
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 188

def close(code = nil, reason = nil)
  @closed_by_user = true
  @reconnect_thread&.kill
  close_socket(@socket, code, reason)
end

#command(frame) ⇒ Object



96
97
98
99
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 96

def command(frame)
  send(frame)
  frame.fetch(:command_id)
end

#command_and_wait(frame, timeout_ms: 10_000) ⇒ Object



101
102
103
104
105
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 101

def command_and_wait(frame, timeout_ms: 10_000)
  waiter = wait_for_ack(frame.fetch(:command_id), timeout_ms: timeout_ms)
  command(frame)
  waiter.call
end

#connectObject



83
84
85
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 83

def connect
  wait_until_open
end

#end_call(params = nil, command_id: create_voice_command_id) ⇒ Object



111
112
113
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 111

def end_call(params = nil, command_id: create_voice_command_id)
  command(with_optional_params(command_frame(command_id, Types::CALL_END), params))
end

#get_input(params = nil, command_id: create_voice_command_id) ⇒ Object



139
140
141
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 139

def get_input(params = nil, command_id: create_voice_command_id)
  command(with_optional_params(command_frame(command_id, Types::INPUT_GET), params))
end

#handle_message(data) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 205

def handle_message(data)
  frame = parse_voice_frame(data)
  emit("frame", frame)
  case frame["event"]
  when "ack"
    resolve_pending_ack(frame)
    emit("ack", frame)
  when "event"
    emit("event", frame)
  when "media"
    emit("media", frame)
  when "connected"
    nil
  else
    raise "Voice socket received an unknown frame event."
  end
rescue StandardError => e
  emit("error", e)
end

#on(event, &listener) ⇒ Object



87
88
89
90
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 87

def on(event, &listener)
  @listeners[event] << listener
  -> { @listeners[event].delete(listener) }
end

#open?Boolean

Returns:

  • (Boolean)


38
39
40
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 38

def open?
  ready_state == 1
end

#play_audio(params, command_id: create_voice_command_id) ⇒ Object



127
128
129
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 127

def play_audio(params, command_id: create_voice_command_id)
  command(command_frame(command_id, Types::AUDIO_PLAY, params))
end

#ready_stateObject



28
29
30
31
32
33
34
35
36
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 28

def ready_state
  if @socket.respond_to?(:ready_state)
    @socket.ready_state
  elsif @socket.respond_to?(:readyState)
    @socket.readyState
  else
    0
  end
end

#reconnect_nowObject



194
195
196
197
198
199
200
201
202
203
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 194

def reconnect_now
  raise "Voice socket was not configured with a reconnect factory." if @create_socket.nil?

  @closed_by_user = false
  @socket = @create_socket.call
  bind_socket
  @reconnect_attempts = 0
  emit("reconnected", @socket)
  self
end

#reduce_noise(params, command_id: create_voice_command_id) ⇒ Object



135
136
137
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 135

def reduce_noise(params, command_id: create_voice_command_id)
  command(command_frame(command_id, Types::AUDIO_REDUCE_NOISE, params))
end

#send(frame) ⇒ Object



92
93
94
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 92

def send(frame)
  @socket.send(JSON.generate(frame))
end

#send_dtmf(params, command_id: create_voice_command_id) ⇒ Object



147
148
149
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 147

def send_dtmf(params, command_id: create_voice_command_id)
  command(command_frame(command_id, Types::DTMF_SEND, params))
end

#send_media(media) ⇒ Object



155
156
157
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 155

def send_media(media)
  send({ event: "media", media: media })
end

#start_recording(command_id: create_voice_command_id) ⇒ Object



119
120
121
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 119

def start_recording(command_id: create_voice_command_id)
  command(command_frame(command_id, Types::RECORDING_START))
end

#stop_audio(command_id: create_voice_command_id) ⇒ Object



131
132
133
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 131

def stop_audio(command_id: create_voice_command_id)
  command(command_frame(command_id, Types::AUDIO_STOP))
end

#stop_recording(command_id: create_voice_command_id) ⇒ Object



123
124
125
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 123

def stop_recording(command_id: create_voice_command_id)
  command(command_frame(command_id, Types::RECORDING_STOP))
end

#transfer(params, command_id: create_voice_command_id) ⇒ Object



115
116
117
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 115

def transfer(params, command_id: create_voice_command_id)
  command(command_frame(command_id, Types::CALL_TRANSFER, params))
end

#update_state(params, command_id: create_voice_command_id) ⇒ Object



151
152
153
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 151

def update_state(params, command_id: create_voice_command_id)
  command(command_frame(command_id, Types::CALL_UPDATE_STATE, params))
end

#wait_for_ack(command_id, timeout_ms: 10_000) ⇒ Object



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
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 159

def wait_for_ack(command_id, timeout_ms: 10_000)
  raise "Already waiting for ack #{command_id}." if @pending_acks.key?(command_id)

  mutex = Mutex.new
  condition = ConditionVariable.new
  result = nil
  error = nil
  done = false
  @pending_acks[command_id] = ->(ack, next_error) do
    mutex.synchronize do
      result = ack
      error = next_error
      done = true
      condition.signal
    end
  end

  -> do
    mutex.synchronize do
      condition.wait(mutex, timeout_ms / 1000.0) unless done
    end
    @pending_acks.delete(command_id)
    raise Timeout::Error, "Timed out waiting for ack #{command_id}." unless done
    raise error unless error.nil?

    result
  end
end

#wait_until_open(timeout_ms: 10_000) ⇒ Object

Raises:

  • (Timeout::Error)


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/pinnacle/wrapper/voice/voice_socket.rb', line 42

def wait_until_open(timeout_ms: 10_000)
  return if open?

  mutex = Mutex.new
  condition = ConditionVariable.new
  error = nil
  done = false
  unsubscribers = []

  cleanup = -> do
    unsubscribers.each(&:call)
  end
  finish = ->(next_error) do
    mutex.synchronize do
      error = next_error
      done = true
      condition.signal
    end
  end

  unsubscribers << on("open") do
    cleanup.call
    finish.call(nil)
  end
  unsubscribers << on("close") do
    cleanup.call
    finish.call(RuntimeError.new("Voice socket closed before opening."))
  end
  unsubscribers << on("error") do |event|
    cleanup.call
    finish.call(to_error(event))
  end

  mutex.synchronize do
    condition.wait(mutex, timeout_ms / 1000.0) unless done
  end
  cleanup.call unless done
  raise Timeout::Error, "Timed out waiting for voice socket to open." unless done
  raise error unless error.nil?
end