class Discordrb::Voice::VoiceUDP
Represents a UDP connection to a voice server. This connection is used to send the actual audio data.
Attributes
@return [true, false] whether or not UDP communications are encrypted. @deprecated Discord no longer supports unencrypted voice communication.
@return [true, false] whether or not UDP communications are encrypted. @deprecated Discord no longer supports unencrypted voice communication.
The UDP encryption mode
Sets the secret key used for encryption
Public Class Methods
Creates a new UDP connection. Only creates a socket as the discovery reply may come before the data is initialized.
# File lib/discordrb/voice/network.rb, line 52 def initialize @socket = UDPSocket.new @encrypted = true end
Public Instance Methods
Initializes the UDP socket with data obtained from opcode 2. @param ip [String] The IP address to connect to. @param port [Integer] The port to connect to. @param ssrc [Integer] The Super Secret Relay Code (SSRC). Discord uses this to identify different voice users
on the same endpoint.
# File lib/discordrb/voice/network.rb, line 62 def connect(ip, port, ssrc) @ip = ip @port = port @ssrc = ssrc end
Waits for a UDP discovery reply, and returns the sent data. @return [Array(String
, Integer
)] the IP and port received from the discovery reply.
# File lib/discordrb/voice/network.rb, line 70 def receive_discovery_reply # Wait for a UDP message message = @socket.recv(74) ip = message[8..-3].delete("\0") port = message[-2..].unpack1('n') [ip, port] end
Makes an audio packet from a buffer and sends it to Discord. @param buf [String] The audio data to send, must be exactly one Opus frame @param sequence [Integer] The packet sequence number, incremented by one for subsequent packets @param time [Integer] When this packet should be played back, in no particular unit (essentially just the
sequence number multiplied by 960)
# File lib/discordrb/voice/network.rb, line 83 def send_audio(buf, sequence, time) # Header of the audio packet header = [0x80, 0x78, sequence, time, @ssrc].pack('CCnNN') nonce = generate_nonce(header) buf = encrypt_audio(buf, nonce) data = header + buf # xsalsa20_poly1305 does not require an appended nonce data += nonce unless @mode == 'xsalsa20_poly1305' send_packet(data) end
Sends the UDP discovery packet with the internally stored SSRC. Discord will send a reply afterwards which can be received using {#receive_discovery_reply}
# File lib/discordrb/voice/network.rb, line 100 def send_discovery # Create empty packet discovery_packet = '' # Add Type request (0x1 = request, 0x2 = response) discovery_packet += [0x1].pack('n') # Add Length (excluding Type and itself = 70) discovery_packet += [70].pack('n') # Add SSRC discovery_packet += [@ssrc].pack('N') # Add 66 zeroes so the packet is 74 bytes long discovery_packet += "\0" * 66 send_packet(discovery_packet) end
Private Instance Methods
Encrypts audio data using libsodium @param buf [String] The encoded audio data to be encrypted @param nonce [String] The nonce to be used to encrypt the data @return [String] the audio data, encrypted
# File lib/discordrb/voice/network.rb, line 125 def encrypt_audio(buf, nonce) raise 'No secret key found, despite encryption being enabled!' unless @secret_key secret_box = Discordrb::Voice::SecretBox.new(@secret_key) # Nonces must be 24 bytes in length. We right pad with null bytes for poly1305 and poly1305_lite secret_box.box(nonce.ljust(24, "\0"), buf) end
@param header [String] The header of the packet, to be used as the nonce @return [String] @note
The nonce generated depends on the encryption mode. In xsalsa20_poly1305 the nonce is the header plus twelve null bytes for padding. In xsalsa20_poly1305_suffix, the nonce is 24 random bytes In xsalsa20_poly1305_lite, the nonce is an incremental 4 byte int.
# File lib/discordrb/voice/network.rb, line 145 def generate_nonce(header) case @mode when 'xsalsa20_poly1305' header when 'xsalsa20_poly1305_suffix' Random.urandom(24) when 'xsalsa20_poly1305_lite' case @lite_nonce when nil, 0xff_ff_ff_ff @lite_nonce = 0 else @lite_nonce += 1 end [@lite_nonce].pack('N') else raise "`#{@mode}' is not a supported encryption mode" end end
# File lib/discordrb/voice/network.rb, line 134 def send_packet(packet) @socket.send(packet, 0, @ip, @port) end