예제 #1
0
        internal Task StartAsync()
        {
            // Let's announce our intentions to the server
            var vdp = new VoiceDispatch();

            if (!this.Resume)
            {
                vdp.OpCode  = 0;
                vdp.Payload = new VoiceIdentifyPayload
                {
                    ServerId  = this.ServerData.GuildId,
                    UserId    = this.StateData.UserId.Value,
                    SessionId = this.StateData.SessionId,
                    Token     = this.ServerData.Token
                };
                this.Resume = true;
            }
            else
            {
                vdp.OpCode  = 7;
                vdp.Payload = new VoiceIdentifyPayload
                {
                    ServerId  = this.ServerData.GuildId,
                    SessionId = this.StateData.SessionId,
                    Token     = this.ServerData.Token
                };
            }
            var vdj = JsonConvert.SerializeObject(vdp, Formatting.None);

            this.VoiceWs.SendMessage(vdj);

            return(Task.Delay(0));
        }
예제 #2
0
        private async Task HeartbeatAsync()
        {
            await Task.Yield();

            var token = Token;

            while (true)
            {
                try
                {
                    token.ThrowIfCancellationRequested();

                    var dt = DateTime.Now;
                    Discord.DebugLogger.LogMessage(LogLevel.Debug, "VoiceNext", "Sent heartbeat", dt);

                    var hbd = new VoiceDispatch
                    {
                        OpCode  = 3,
                        Payload = UnixTimestamp(dt)
                    };
                    var hbj = JsonConvert.SerializeObject(hbd);
                    VoiceWs.SendMessage(hbj);

                    LastHeartbeat = dt;
                    await Task.Delay(HeartbeatInterval).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    return;
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Sends a speaking status to the connected voice channel.
        /// </summary>
        /// <param name="speaking">Whether the current user is speaking or not.</param>
        /// <returns>A task representing the sending operation.</returns>
        public async Task SendSpeakingAsync(bool speaking = true)
        {
            if (!this.IsInitialized)
            {
                throw new InvalidOperationException("The connection is not yet initialized");
            }

            if (!speaking)
            {
                this.Synchronizer.Reset();
            }

            var pld = new VoiceDispatch
            {
                OpCode  = 5,
                Payload = new VoiceSpeakingPayload
                {
                    Speaking = speaking,
                    Delay    = 0
                }
            };

            var plj = JsonConvert.SerializeObject(pld, Formatting.None);
            await Task.Run(() => this.VoiceWs.SendMessage(plj));
        }
예제 #4
0
        internal async Task StartAsync()
        {
            // Let's announce our intentions to the server
            var vdp = new VoiceDispatch();

            if (!this.Resume)
            {
                vdp.OpCode  = 0;
                vdp.Payload = new VoiceIdentifyPayload
                {
                    ServerId  = this.ServerData.GuildId,
                    UserId    = this.StateData.UserId.Value,
                    SessionId = this.StateData.SessionId,
                    Token     = this.ServerData.Token
                };
                this.Resume = true;
            }
            else
            {
                vdp.OpCode  = 7;
                vdp.Payload = new VoiceIdentifyPayload
                {
                    ServerId  = this.ServerData.GuildId,
                    SessionId = this.StateData.SessionId,
                    Token     = this.ServerData.Token
                };
            }
            var vdj = JsonConvert.SerializeObject(vdp, Formatting.None);

            await this.WsSendAsync(vdj).ConfigureAwait(false);
        }
예제 #5
0
        private void Heartbeat()
        {
            while (true)
            {
                try
                {
                    var dt = DateTime.Now;
                    this.Discord.DebugLogger.LogMessage(LogLevel.Unnecessary, "VoiceNext", "Sent heartbeat", dt);

                    var hbd = new VoiceDispatch
                    {
                        OpCode  = 3,
                        Payload = UnixTimestamp(dt)
                    };
                    var hbj = JsonConvert.SerializeObject(hbd);
                    this.VoiceWs.SendMessage(hbj);

                    this.LastHeartbeat = dt;
                    Thread.Sleep(this.HeartbeatInterval);
                }
                catch (ThreadAbortException)
                {
                    return;
                }
            }
        }
예제 #6
0
        private async Task HeartbeatAsync()
        {
            await Task.Yield();

            var token = this.Token;

            while (true)
            {
                try
                {
                    token.ThrowIfCancellationRequested();

                    var dt = DateTime.Now;
                    this.Discord.Logger.LogTrace(VoiceNextEvents.VoiceHeartbeat, "Sent heartbeat");

                    var hbd = new VoiceDispatch
                    {
                        OpCode  = 3,
                        Payload = UnixTimestamp(dt)
                    };
                    var hbj = JsonConvert.SerializeObject(hbd);
                    await this.WsSendAsync(hbj).ConfigureAwait(false);

                    this.LastHeartbeat = dt;
                    await Task.Delay(this.HeartbeatInterval).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    return;
                }
            }
        }
예제 #7
0
        private async Task Stage1()
        {
            // Begin heartbeating
            this.HeartbeatTask = Task.Run(this.Heartbeat);

#if !NETSTANDARD1_1
            // IP Discovery
            this.UdpClient.Setup(this.ConnectionEndpoint);
            var pck = new byte[70];
            Array.Copy(BitConverter.GetBytes(this.SSRC), 0, pck, pck.Length - 4, 4);
            await this.UdpClient.SendAsync(pck, pck.Length);

            var ipd = await this.UdpClient.ReceiveAsync();

            var ipe  = Array.IndexOf <byte>(ipd, 0, 4);
            var ip   = new UTF8Encoding(false).GetString(ipd, 4, ipe - 4);
            var port = BitConverter.ToUInt16(ipd, ipd.Length - 2);
            this.DiscoveredEndpoint = new IpEndpoint {
                Address = System.Net.IPAddress.Parse(ip), Port = port
            };
#endif

            // Ready
            var vsp = new VoiceDispatch
            {
                OpCode  = 1,
                Payload = new VoiceSelectProtocolPayload
                {
                    Protocol = "udp",
                    Data     = new VoiceSelectProtocolPayloadData
                    {
#if !NETSTANDARD1_1
                        Address = this.DiscoveredEndpoint.Address.ToString(),
                        Port    = (ushort)this.DiscoveredEndpoint.Port,
#else
                        Address = "0.0.0.0",
                        Port    = 0,
#endif
                        Mode = VOICE_MODE
                    }
                }
            };
            var vsj = JsonConvert.SerializeObject(vsp, Formatting.None);
            this.VoiceWs.SendMessage(vsj);

#if !NETSTANDARD1_1
            if (this.Configuration.EnableIncoming)
            {
                this.ReceiverTask = Task.Run(this.VoiceReceiverTask, this.Token);
            }
#endif
        }
예제 #8
0
        /// <summary>
        /// Connects this Lavalink node to specified Discord channel.
        /// </summary>
        /// <param name="channel">Voice channel to connect to.</param>
        /// <returns>Channel connection, which allows for playback control.</returns>
        public async Task <LavalinkGuildConnection> ConnectAsync(DiscordChannel channel)
        {
            if (this._connectedGuilds.ContainsKey(channel.Guild.Id))
            {
                return(this._connectedGuilds[channel.Guild.Id]);
            }

            if (channel.Guild == null || channel.Type != ChannelType.Voice)
            {
                throw new ArgumentException("Invalid channel specified.", nameof(channel));
            }

            var vstut = new TaskCompletionSource <VoiceStateUpdateEventArgs>();
            var vsrut = new TaskCompletionSource <VoiceServerUpdateEventArgs>();

            this.VoiceStateUpdates[channel.Guild.Id]  = vstut;
            this.VoiceServerUpdates[channel.Guild.Id] = vsrut;

            var vsd = new VoiceDispatch
            {
                OpCode  = 4,
                Payload = new VoiceStateUpdatePayload
                {
                    GuildId   = channel.Guild.Id,
                    ChannelId = channel.Id,
                    Deafened  = false,
                    Muted     = false
                }
            };
            var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);

            await(channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
            var vstu = await vstut.Task.ConfigureAwait(false);

            var vsru = await vsrut.Task.ConfigureAwait(false);

            await this.SendPayloadAsync(new LavalinkVoiceUpdate(vstu, vsru)).ConfigureAwait(false);

            var con = new LavalinkGuildConnection(this, channel, vstu);

            con.ChannelDisconnected += this.Con_ChannelDisconnected;
            con.PlayerUpdated       += (s, e) => this._playerUpdated.InvokeAsync(s, e);
            con.PlaybackStarted     += (s, e) => this._playbackStarted.InvokeAsync(s, e);
            con.PlaybackFinished    += (s, e) => this._playbackFinished.InvokeAsync(s, e);
            con.TrackStuck          += (s, e) => this._trackStuck.InvokeAsync(s, e);
            con.TrackException      += (s, e) => this._trackException.InvokeAsync(s, e);
            this._connectedGuilds[channel.Guild.Id] = con;

            return(con);
        }
        internal async Task SendVoiceUpdateAsync()
        {
            var vsd = new VoiceDispatch
            {
                OpCode  = 4,
                Payload = new VoiceStateUpdatePayload
                {
                    GuildId   = this.GuildId,
                    ChannelId = null,
                    Deafened  = false,
                    Muted     = false
                }
            };
            var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);

            await(this.Channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
        }
예제 #10
0
        internal void SendVoiceUpdate()
        {
            var vsd = new VoiceDispatch
            {
                OpCode  = 4,
                Payload = new VoiceStateUpdatePayload
                {
                    GuildId   = this.GuildId,
                    ChannelId = null,
                    Deafened  = false,
                    Muted     = false
                }
            };
            var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);

            (this.Channel.Discord as DiscordClient)._webSocketClient.SendMessage(vsj);
        }
예제 #11
0
        /// <summary>
        /// Sends a speaking status to the connected voice channel.
        /// </summary>
        /// <param name="speaking">Whether the current user is speaking or not.</param>
        /// <returns>A task representing the sending operation.</returns>
        public async Task SendSpeakingAsync(bool speaking = true)
        {
            if (!IsInitialized)
            {
                throw new InvalidOperationException("The connection is not initialized");
            }

            if (!speaking)
            {
                var nullpcm = new byte[3840];
                for (var i = 0; i < 5; i++)
                {
                    await SendAsync(nullpcm, 20).ConfigureAwait(false);
                }

                SynchronizerTicks = 0;
                if (PlayingWait != null)
                {
                    PlayingWait.SetResult(true);
                }
            }
            else
            {
                if (PlayingWait == null || PlayingWait.Task.IsCompleted)
                {
                    PlayingWait = new TaskCompletionSource <bool>();
                }
            }

            var pld = new VoiceDispatch
            {
                OpCode  = 5,
                Payload = new VoiceSpeakingPayload
                {
                    Speaking = speaking,
                    Delay    = 0
                }
            };

            var plj = JsonConvert.SerializeObject(pld, Formatting.None);

            VoiceWs.SendMessage(plj);
        }
예제 #12
0
        private async Task Stage1()
        {
            // Begin heartbeating
            this.HeartbeatThread = new Thread(this.Heartbeat);
            this.HeartbeatThread.SetApartmentState(ApartmentState.STA);
            this.HeartbeatThread.Name = $"Heartbeat for Voice ({this.SSRC})";
            this.HeartbeatThread.Start();

            // IP Discovery
            this.UdpClient.Connect(this.ConnectionEndpoint.Host, this.ConnectionEndpoint.Port);
            this.UdpClient.AllowNatTraversal(true);
            var pck = new byte[70];

            Array.Copy(BitConverter.GetBytes(this.SSRC), 0, pck, pck.Length - 4, 4);
            await this.UdpClient.SendAsync(pck, pck.Length);

            var ipd = await this.UdpClient.ReceiveAsync();

            var ipe  = Array.IndexOf <byte>(ipd.Buffer, 0, 4);
            var ip   = new UTF8Encoding(false).GetString(ipd.Buffer, 4, ipe - 4);
            var port = BitConverter.ToUInt16(ipd.Buffer, ipd.Buffer.Length - 2);

            this.DiscoveredEndpoint = new IPEndPoint(IPAddress.Parse(ip), port);

            // Ready
            var vsp = new VoiceDispatch
            {
                OpCode  = 1,
                Payload = new VoiceSelectProtocolPayload
                {
                    Protocol = "udp",
                    Data     = new VoiceSelectProtocolPayloadData
                    {
                        Address = this.DiscoveredEndpoint.Address.ToString(),
                        Port    = (ushort)this.DiscoveredEndpoint.Port,
                        Mode    = VOICE_MODE
                    }
                }
            };
            var vsj = JsonConvert.SerializeObject(vsp, Formatting.None);

            this.VoiceWs.SendMessage(vsj);
        }
예제 #13
0
        /// <summary>
        /// Sends a speaking status to the connected voice channel.
        /// </summary>
        /// <param name="speaking">Whether the current user is speaking or not.</param>
        /// <returns>A task representing the sending operation.</returns>
        public async Task SendSpeakingAsync(bool speaking = true)
        {
            if (!this.IsInitialized)
            {
                throw new InvalidOperationException("The connection is not initialized");
            }

            var pld = new VoiceDispatch
            {
                OpCode  = 5,
                Payload = new VoiceSpeakingPayload
                {
                    Speaking = speaking,
                    Delay    = 0
                }
            };

            var plj = JsonConvert.SerializeObject(pld, Formatting.None);

            await this.VoiceWs.SendMessageAsync(plj).ConfigureAwait(false);
        }
예제 #14
0
        /// <summary>
        /// Sends a speaking status to the connected voice channel.
        /// </summary>
        /// <param name="speaking">Whether the current user is speaking or not.</param>
        /// <returns>A task representing the sending operation.</returns>
        public void SendSpeaking(bool speaking = true)
        {
            if (!this.IsInitialized)
            {
                throw new InvalidOperationException("The connection is not initialized");
            }

            var pld = new VoiceDispatch
            {
                OpCode  = 5,
                Payload = new VoiceSpeakingPayload
                {
                    Speaking = speaking,
                    Delay    = 0
                }
            };

            var plj = JsonConvert.SerializeObject(pld, Formatting.None);

            this.VoiceWs.SendMessage(plj);
        }
예제 #15
0
        private void Vnc_VoiceDisconnected(DiscordGuild guild)
        {
            var vnc = (VoiceNextConnection)null;

            if (this.ActiveConnections.ContainsKey(guild.ID))
            {
                this.ActiveConnections.TryRemove(guild.ID, out vnc);
            }

            var vsd = new VoiceDispatch
            {
                OpCode  = 4,
                Payload = new VoiceStateUpdatePayload
                {
                    GuildId   = guild.ID,
                    ChannelId = null
                }
            };
            var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);

            DiscordClient._websocketClient.SendMessage(vsj);
        }
예제 #16
0
        private async Task Vnc_VoiceDisconnected(DiscordGuild guild)
        {
            VoiceNextConnection vnc = null;

            if (this.ActiveConnections.ContainsKey(guild.Id))
            {
                this.ActiveConnections.TryRemove(guild.Id, out vnc);
            }

            var vsd = new VoiceDispatch
            {
                OpCode  = 4,
                Payload = new VoiceStateUpdatePayload
                {
                    GuildId   = guild.Id,
                    ChannelId = null
                }
            };
            var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);

            await(guild.Discord as DiscordClient)._webSocketClient.SendMessageAsync(vsj).ConfigureAwait(false);
        }
예제 #17
0
        private async Task Stage1(VoiceReadyPayload voiceReady)
        {
            // IP Discovery
            this.UdpClient.Setup(this.UdpEndpoint);

            var pck = new byte[70];

            PreparePacket(pck);
            await this.UdpClient.SendAsync(pck, pck.Length).ConfigureAwait(false);

            var ipd = await this.UdpClient.ReceiveAsync().ConfigureAwait(false);

            ReadPacket(ipd, out var ip, out var port);
            this.DiscoveredEndpoint = new IpEndpoint
            {
                Address = ip,
                Port    = port
            };
            this.Discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Endpoint dicovery finished - discovered endpoint is {0}:{1}", ip, port);

            void PreparePacket(byte[] packet)
            {
                var ssrc       = this.SSRC;
                var packetSpan = packet.AsSpan();

                MemoryMarshal.Write(packetSpan, ref ssrc);
                Helpers.ZeroFill(packetSpan);
            }

            void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort)
            {
                var packetSpan = packet.AsSpan();

                var ipString = Utilities.UTF8.GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0');

                decodedIp = System.Net.IPAddress.Parse(ipString);

                decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan.Slice(68 /* 70 - 2 */));
            }

            // Select voice encryption mode
            var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes);

            this.SelectedEncryptionMode = selectedEncryptionMode.Value;

            // Ready
            this.Discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Selected encryption mode is {0}", selectedEncryptionMode.Key);
            var vsp = new VoiceDispatch
            {
                OpCode  = 1,
                Payload = new VoiceSelectProtocolPayload
                {
                    Protocol = "udp",
                    Data     = new VoiceSelectProtocolPayloadData
                    {
                        Address = this.DiscoveredEndpoint.Address.ToString(),
                        Port    = (ushort)this.DiscoveredEndpoint.Port,
                        Mode    = selectedEncryptionMode.Key
                    }
                }
            };
            var vsj = JsonConvert.SerializeObject(vsp, Formatting.None);

            await this.WsSendAsync(vsj).ConfigureAwait(false);

            this.SenderTokenSource = new CancellationTokenSource();
            this.SenderTask        = Task.Run(this.VoiceSenderTask, this.SenderToken);

            this.ReceiverTokenSource = new CancellationTokenSource();
            this.ReceiverTask        = Task.Run(this.UdpReceiverTask, this.ReceiverToken);
        }
예제 #18
0
        private async Task Stage1(VoiceReadyPayload voiceReady)
        {
            // IP Discovery
            this.UdpClient.Setup(this.UdpEndpoint);

            var pck = new byte[70];

            PreparePacket(pck);
            await this.UdpClient.SendAsync(pck, pck.Length).ConfigureAwait(false);

            // fetch endpoint it it wasn't done already
            IpEndpoint ownEndpoint;

            try
            {
                var ipd = await this.UdpClient.ReceiveAsync().ConfigureAwait(false);

                ReadPacket(ipd, out var ip, out var port);
                ownEndpoint = new IpEndpoint
                {
                    Address = ip,
                    Port    = port
                };
                this.DiscoveredEndpoint = ownEndpoint;
                this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VNext UDP", $"Endpoint discovery resulted in {ip}:{port}", DateTime.Now);
            } catch (Exception e)
            {
                this.Discord.DebugLogger.LogMessage(LogLevel.Error, "VNext UDP", "Endpoint discovery failed", DateTime.Now, e);

                // fallback to last value if present
                if (this.DiscoveredEndpoint.HasValue)
                {
                    ownEndpoint = this.DiscoveredEndpoint.Value;
                    this.Discord.DebugLogger.LogMessage(LogLevel.Info, "VNext UDP", "Endpoint discovery used old endpoint", DateTime.Now, e);
                }
                else
                {
                    throw e;
                }
            }


            void PreparePacket(byte[] packet)
            {
                var ssrc       = this.SSRC;
                var packetSpan = packet.AsSpan();

                MemoryMarshal.Write(packetSpan, ref ssrc);
                Helpers.ZeroFill(packetSpan);
            }

            void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort)
            {
                if (packet.Length != 70)
                {
                    throw new Exception($"Recieved invalid IP discovery data. Expected length 70 but got {packet.Length}");
                }

                var packetSpan = packet.AsSpan();

                var ipString = Utilities.UTF8.GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0');

                decodedIp = System.Net.IPAddress.Parse(ipString);

                decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan.Slice(68 /* 70 - 2 */));
            }

            // Select voice encryption mode
            var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes);

            this.SelectedEncryptionMode = selectedEncryptionMode.Value;

            // Ready
            this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VoiceNext", $"Selected encryption mode: {selectedEncryptionMode.Key}", DateTime.Now);
            var vsp = new VoiceDispatch
            {
                OpCode  = 1,
                Payload = new VoiceSelectProtocolPayload
                {
                    Protocol = "udp",
                    Data     = new VoiceSelectProtocolPayloadData
                    {
                        Address = ownEndpoint.Address.ToString(),
                        Port    = (ushort)ownEndpoint.Port,
                        Mode    = selectedEncryptionMode.Key
                    }
                }
            };
            var vsj = JsonConvert.SerializeObject(vsp, Formatting.None);

            await this.VoiceWs.SendMessageAsync(vsj).ConfigureAwait(false);

            if (this.SenderTokenSource != null)
            {
                this.SenderTokenSource.Cancel();
            }
            this.SenderTokenSource = new CancellationTokenSource();
            this.SenderTask        = Task.Run(this.VoiceSenderTask, this.SenderToken);

            if (this.ReceiverTokenSource != null)
            {
                this.ReceiverTokenSource.Cancel();
            }
            this.ReceiverTokenSource = new CancellationTokenSource();
            this.ReceiverTask        = Task.Run(this.UdpReceiverTask, this.ReceiverToken);
        }
예제 #19
0
        /// <summary>
        /// Create a VoiceNext connection for the specified channel.
        /// </summary>
        /// <param name="channel">Channel to connect to.</param>
        /// <returns>VoiceNext connection for this channel.</returns>
        public async Task <VoiceNextConnection> ConnectAsync(DiscordChannel channel)
        {
            if (channel.Type != ChannelType.Voice)
            {
                throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be voice channel");
            }

            if (channel.Parent == null)
            {
                throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be guild channel");
            }

            var gld = channel.Parent;

            if (ActiveConnections.ContainsKey(gld.ID))
            {
                throw new InvalidOperationException("This guild already has a voice connection");
            }

            var vstut = new TaskCompletionSource <VoiceStateUpdateEventArgs>();
            var vsrut = new TaskCompletionSource <VoiceServerUpdateEventArgs>();

            this.VoiceStateUpdates[gld.ID]  = vstut;
            this.VoiceServerUpdates[gld.ID] = vsrut;

            var vsd = new VoiceDispatch
            {
                OpCode  = 4,
                Payload = new VoiceStateUpdatePayload
                {
                    GuildId   = gld.ID,
                    ChannelId = channel.ID,
                    Deafened  = false,
                    Muted     = false
                }
            };
            var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);

            DiscordClient._websocketClient.SendMessage(vsj);

            var vstu  = await vstut.Task;
            var vstup = new VoiceStateUpdatePayload
            {
                SessionId = vstu.SessionID,
                UserId    = vstu.UserID
            };
            var vsru  = await vsrut.Task;
            var vsrup = new VoiceServerUpdatePayload
            {
                Endpoint = vsru.Endpoint,
                GuildId  = vsru.GuildID,
                Token    = vsru.VoiceToken
            };

            var d1 = (TaskCompletionSource <VoiceStateUpdateEventArgs>)null;
            var d2 = (TaskCompletionSource <VoiceServerUpdateEventArgs>)null;

            this.VoiceStateUpdates.TryRemove(gld.ID, out d1);
            this.VoiceServerUpdates.TryRemove(gld.ID, out d2);

            var vnc = new VoiceNextConnection(this.Client, gld, channel, this.Configuration, vsrup, vstup);

            vnc.VoiceDisconnected += this.Vnc_VoiceDisconnected;
            await vnc.ConnectAsync();

            await vnc.WaitForReady();

            this.ActiveConnections[gld.ID] = vnc;
            return(vnc);
        }
예제 #20
0
        /// <summary>
        /// Create a VoiceNext connection for the specified channel.
        /// </summary>
        /// <param name="channel">Channel to connect to.</param>
        /// <returns>VoiceNext connection for this channel.</returns>
        public async Task <VoiceNextConnection> ConnectAsync(DiscordChannel channel)
        {
            if (channel.Type != ChannelType.Voice)
            {
                throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be voice channel");
            }

            if (channel.Guild == null)
            {
                throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be guild channel");
            }

            if (!channel.PermissionsFor(channel.Guild.CurrentMember).HasPermission(Permissions.UseVoice))
            {
                throw new InvalidOperationException("You need UseVoice permission to connect to this voice channel");
            }

            var gld = channel.Guild;

            if (ActiveConnections.ContainsKey(gld.Id))
            {
                throw new InvalidOperationException("This guild already has a voice connection");
            }

            var vstut = new TaskCompletionSource <VoiceStateUpdateEventArgs>();
            var vsrut = new TaskCompletionSource <VoiceServerUpdateEventArgs>();

            this.VoiceStateUpdates[gld.Id]  = vstut;
            this.VoiceServerUpdates[gld.Id] = vsrut;

            var vsd = new VoiceDispatch
            {
                OpCode  = 4,
                Payload = new VoiceStateUpdatePayload
                {
                    GuildId   = gld.Id,
                    ChannelId = channel.Id,
                    Deafened  = false,
                    Muted     = false
                }
            };
            var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);

            await(channel.Discord as DiscordClient)._webSocketClient.SendMessageAsync(vsj).ConfigureAwait(false);

            var vstu = await vstut.Task.ConfigureAwait(false);

            var vstup = new VoiceStateUpdatePayload
            {
                SessionId = vstu.SessionId,
                UserId    = vstu.User.Id
            };
            var vsru = await vsrut.Task.ConfigureAwait(false);

            var vsrup = new VoiceServerUpdatePayload
            {
                Endpoint = vsru.Endpoint,
                GuildId  = vsru.Guild.Id,
                Token    = vsru.VoiceToken
            };

            var vnc = new VoiceNextConnection(this.Client, gld, channel, this.Configuration, vsrup, vstup);

            vnc.VoiceDisconnected += this.Vnc_VoiceDisconnected;
            await vnc.ConnectAsync().ConfigureAwait(false);

            await vnc.WaitForReadyAsync().ConfigureAwait(false);

            this.ActiveConnections[gld.Id] = vnc;
            return(vnc);
        }
예제 #21
0
        private async Task Stage1(VoiceReadyPayload voiceReady)
        {
#if !NETSTANDARD1_1
            // IP Discovery
            this.UdpClient.Setup(this.ConnectionEndpoint);

            var pck = new byte[70];
            PreparePacket(pck);
            await this.UdpClient.SendAsync(pck, pck.Length).ConfigureAwait(false);

            var ipd = await this.UdpClient.ReceiveAsync().ConfigureAwait(false);

            ReadPacket(ipd, out var ip, out var port);
            this.DiscoveredEndpoint = new IpEndpoint
            {
                Address = ip,
                Port    = port
            };
            this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VNext UDP", $"Endpoint discovery resulted in {ip}:{port}", DateTime.Now);

            void PreparePacket(byte[] packet)
            {
                var ssrc       = this.SSRC;
                var packetSpan = packet.AsSpan();

                MemoryMarshal.Write(packetSpan, ref ssrc);
                Helpers.ZeroFill(packetSpan);
            }

            void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort)
            {
                var packetSpan = packet.AsSpan();

                var ipString = new UTF8Encoding(false).GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0');

                decodedIp = System.Net.IPAddress.Parse(ipString);

                decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan.Slice(68 /* 70 - 2 */));
            }
#else
            this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VNext UDP", $"Voice receive not supported - not performing endpoint discovery", DateTime.Now);
            await Task.Yield(); // just stop bothering me VS
#endif

            // Select voice encryption mode
            var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes);
            this.SelectedEncryptionMode = selectedEncryptionMode.Value;

            // Ready
            this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VoiceNext", $"Selected encryption mode: {selectedEncryptionMode.Key}", DateTime.Now);
            var vsp = new VoiceDispatch
            {
                OpCode  = 1,
                Payload = new VoiceSelectProtocolPayload
                {
                    Protocol = "udp",
                    Data     = new VoiceSelectProtocolPayloadData
                    {
#if !NETSTANDARD1_1
                        Address = this.DiscoveredEndpoint.Address.ToString(),
                        Port    = (ushort)this.DiscoveredEndpoint.Port,
#else
                        Address = "0.0.0.0",
                        Port    = 0,
#endif
                        Mode = selectedEncryptionMode.Key
                    }
                }
            };
            var vsj = JsonConvert.SerializeObject(vsp, Formatting.None);
            this.VoiceWs.SendMessage(vsj);

            this.SenderTokenSource = new CancellationTokenSource();
            this.SenderTask        = Task.Run(this.VoiceSenderTask, this.SenderToken);

            this.ReceiverTokenSource = new CancellationTokenSource();
            this.ReceiverTask        = Task.Run(this.UdpReceiverTask, this.ReceiverToken);
        }