Class: OnyxCord::Voice::VoiceWS

Inherits:
Object
  • Object
show all
Defined in:
lib/onyxcord/voice/network/websocket.rb

Constant Summary collapse

VOICE_GATEWAY_VERSION =

The version of the voice gateway that's supposed to be used.

8

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(channel, bot, token, session, endpoint) ⇒ VoiceWS

Makes a new voice websocket client, but doesn't connect it (see #connect for that)

Parameters:

  • channel (Channel)

    The voice channel to connect to

  • bot (Bot)

    The regular bot to which this vWS is bound

  • token (String)

    The authentication token which is also used for REST requests

  • session (String)

    The voice session ID Discord sends over the regular websocket

  • endpoint (String)

    The endpoint URL to connect to



17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/onyxcord/voice/network/websocket.rb', line 17

def initialize(channel, bot, token, session, endpoint)
  raise 'libsodium is unavailable - unable to create voice bot! Please read https://github.com/kruldevb/OnyxCord/wiki/Installing-libsodium' unless LIBSODIUM_AVAILABLE

  @channel = channel
  @bot = bot
  @token = token
  @session = session

  @endpoint = endpoint

  @udp = VoiceUDP.new
end

Instance Attribute Details

#udpVoiceUDP (readonly)

Returns the UDP voice connection over which the actual audio data is sent.

Returns:

  • (VoiceUDP)

    the UDP voice connection over which the actual audio data is sent.



9
10
11
# File 'lib/onyxcord/voice/network/websocket.rb', line 9

def udp
  @udp
end

Instance Method Details

#connectObject

Communication goes like this: me discord | | websocket connect -> | | | | <- websocket opcode 2 | | UDP discovery -> | | | | <- UDP reply packet | | websocket opcode 1 -> | | | ...



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
# File 'lib/onyxcord/voice/network/websocket.rb', line 161

def connect
  # Connect websocket
  @thread = Thread.new do
    Thread.current[:onyxcord_name] = 'vws'
    init_ws
  end

  @bot.debug('Started websocket initialization, now waiting for UDP discovery reply')

  # Now wait for opcode 2 and the resulting UDP reply packet
  ip, port = @udp.receive_discovery_reply
  @bot.debug("UDP discovery reply received! #{ip} #{port}")

  # Send UDP init packet with received UDP data
  send_udp_connection(ip, port, @udp_mode)

  @bot.debug('Waiting for op 4 now')

  # Wait for op 4, then finish
  @ready_mutex ||= Mutex.new
  @ready_condition ||= ConditionVariable.new
  @ready_mutex.synchronize do
    @ready_condition.wait(@ready_mutex, 30) unless @ready
  end
end

#destroy(join_timeout = 1) ⇒ Object

Disconnects the websocket and kills the thread



188
189
190
191
192
193
194
# File 'lib/onyxcord/voice/network/websocket.rb', line 188

def destroy(join_timeout = 1)
  @heartbeat_running = false
  @client&.close
  @udp.close
  @thread&.join(join_timeout)
  @thread&.kill if @thread&.alive?
end

#send_heartbeatObject

Send a heartbeat (op 3), has to be done every @heartbeat_interval seconds or the connection will terminate



66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/onyxcord/voice/network/websocket.rb', line 66

def send_heartbeat
  millis = (Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)).to_i
  @bot.debug("Sending voice heartbeat at #{millis}")

  send_opcode(
    Opcodes::HEARTBEAT,
    {
      t: millis,
      seq_ack: @seq
    }
  )
end

#send_init(server_id, bot_user_id, session_id, token) ⇒ Object

Send a connection init packet (op 0)

Parameters:

  • server_id (Integer)

    The ID of the server to connect to

  • bot_user_id (Integer)

    The ID of the bot that is connecting

  • session_id (String)

    The voice session ID

  • token (String)

    The Discord authentication token



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/onyxcord/voice/network/websocket.rb', line 35

def send_init(server_id, bot_user_id, session_id, token)
  send_opcode(
    Opcodes::IDENTIFY,
    {
      server_id: server_id,
      user_id: bot_user_id,
      session_id: session_id,
      token: token
    }
  )
end

#send_opcode(opcode, data) ⇒ Object



92
93
94
95
96
97
98
# File 'lib/onyxcord/voice/network/websocket.rb', line 92

def send_opcode(opcode, data)
  @bot.debug("Sending voice opcode #{opcode} with data: #{data}")
  @client.send({
    op: opcode,
    d: data
  }.to_json)
end

#send_speaking(value) ⇒ Object

Send a speaking packet (op 5). This determines the green circle around the avatar in the voice channel

Parameters:

  • value (true, false, Integer)

    Whether or not the bot should be speaking, can also be a bitmask denoting audio type.



81
82
83
84
85
86
87
88
89
90
# File 'lib/onyxcord/voice/network/websocket.rb', line 81

def send_speaking(value)
  @bot.debug("Speaking: #{value}")
  send_opcode(
    Opcodes::SPEAKING,
    {
      speaking: value,
      delay: 0
    }
  )
end

#send_udp_connection(ip, port, mode) ⇒ Object

Sends the UDP connection packet (op 1)

Parameters:

  • ip (String)

    The IP to bind UDP to

  • port (Integer)

    The port to bind UDP to

  • mode (Object)

    Which mode to use for the voice connection



51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/onyxcord/voice/network/websocket.rb', line 51

def send_udp_connection(ip, port, mode)
  send_opcode(
    Opcodes::SELECT_PROTOCOL,
    {
      protocol: 'udp',
      data: {
        address: ip,
        port: port,
        mode: mode
      }
    }
  )
end