Example #1
0
        private async Task VoiceWS_SocketMessage(IWebSocketClient client, SocketMessageEventArgs e)
        {
            if (!(e is SocketTextMessageEventArgs et))
            {
                this.Discord.Logger.LogCritical(VoiceNextEvents.VoiceGatewayError, "Discord Voice Gateway sent binary data - unable to process");
                return;
            }

            if (this.Discord.Configuration.MinimumLogLevel == LogLevel.Trace)
            {
                var cs = new MemoryStream();

                await et.Message.CopyToAsync(cs).ConfigureAwait(false);

                cs.Seek(0, SeekOrigin.Begin);
                et.Message.Seek(0, SeekOrigin.Begin);

                using var sr = new StreamReader(cs, Utilities.UTF8);

                this.Discord.Logger.LogTrace(VoiceNextEvents.VoiceWsRx, await sr.ReadToEndAsync().ConfigureAwait(false));
            }

            var j = await DiscordJson.LoadJObjectAsync(et.Message).ConfigureAwait(false);

            await this.HandleDispatch(j).ConfigureAwait(false);
        }
        private async Task <GatewayInfo> GetGatewayInfoAsync()
        {
            var url  = $"{Utilities.GetApiBaseUri()}{Endpoints.GATEWAY}{Endpoints.BOT}";
            var http = new HttpClient();

            http.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Utilities.GetUserAgent());
            http.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", Utilities.GetFormattedToken(this.Configuration));

            this.Logger.LogDebug(LoggerEvents.ShardRest, $"Obtaining gateway information from GET {Endpoints.GATEWAY}{Endpoints.BOT}...");
            var resp = await http.GetAsync(url).ConfigureAwait(false);

            http.Dispose();

            if (!resp.IsSuccessStatusCode)
            {
                var ratelimited = await HandleHttpError(url, resp).ConfigureAwait(false);

                if (ratelimited)
                {
                    return(await this.GetGatewayInfoAsync().ConfigureAwait(false));
                }
            }

            var timer = new Stopwatch();

            timer.Start();

            var jo = await DiscordJson.LoadJObjectAsync(await resp.Content.ReadAsStreamAsync().ConfigureAwait(false)).ConfigureAwait(false);

            var info = jo.ToObject <GatewayInfo>();

            //There is a delay from parsing here.
            timer.Stop();

            info.SessionBucket.resetAfter -= (int)timer.ElapsedMilliseconds;
            info.SessionBucket.ResetAfter  = DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(info.SessionBucket.resetAfter);

            return(info);

            async Task <bool> HandleHttpError(string reqUrl, HttpResponseMessage msg)
            {
                var code = (int)msg.StatusCode;

                if (code == 401 || code == 403)
                {
                    throw new Exception($"Authentication failed, check your token and try again: {code} {msg.ReasonPhrase}");
                }
                else if (code == 429)
                {
                    this.Logger.LogError(LoggerEvents.ShardClientError, $"Ratelimit hit, requeuing request to {reqUrl}");

                    var hs           = msg.Headers.ToDictionary(xh => xh.Key, xh => string.Join("\n", xh.Value), StringComparer.OrdinalIgnoreCase);
                    var waitInterval = 0;

                    if (hs.TryGetValue("Retry-After", out var retry_after_raw))
                    {
                        waitInterval = int.Parse(retry_after_raw, CultureInfo.InvariantCulture);
                    }

                    await Task.Delay(waitInterval).ConfigureAwait(false);

                    return(true);
                }
                else if (code >= 500)
                {
                    throw new Exception($"Internal Server Error: {code} {msg.ReasonPhrase}");
                }
                else
                {
                    throw new Exception($"An unsuccessful HTTP status code was encountered: {code} {msg.ReasonPhrase}");
                }
            }
        }
Example #3
0
        private async Task WebSocket_OnMessage(IWebSocketClient client, SocketMessageEventArgs e)
        {
            if (!(e is SocketTextMessageEventArgs et))
            {
                this.Discord.Logger.LogCritical(LavalinkEvents.LavalinkConnectionError, "Lavalink sent binary data - unable to process");
                return;
            }

            if (this.Discord.Configuration.MinimumLogLevel == LogLevel.Trace)
            {
                var cs = new MemoryStream();

                await et.Message.CopyToAsync(cs).ConfigureAwait(false);

                cs.Seek(0, SeekOrigin.Begin);
                et.Message.Seek(0, SeekOrigin.Begin);

                using var sr = new StreamReader(cs, Utilities.UTF8);

                this.Discord.Logger.LogTrace(LavalinkEvents.LavalinkWsRx, await sr.ReadToEndAsync().ConfigureAwait(false));
            }

            var json     = et.Message;
            var jsonData = await DiscordJson.LoadJObjectAsync(json).ConfigureAwait(false);

            switch (jsonData["op"].ToString())
            {
            case "playerUpdate":
                var gid   = (ulong)jsonData["guildId"];
                var state = jsonData["state"].ToObject <LavalinkState>();
                if (this._connectedGuilds.TryGetValue(gid, out var lvl))
                {
                    await lvl.InternalUpdatePlayerStateAsync(state).ConfigureAwait(false);
                }
                break;

            case "stats":
                var statsRaw = jsonData.ToObject <LavalinkStats>();
                this.Statistics.Update(statsRaw);
                await this._statsReceived.InvokeAsync(this, new StatisticsReceivedEventArgs(this.Statistics)).ConfigureAwait(false);

                break;

            case "event":
                var evtype  = jsonData["type"].ToObject <EventType>();
                var guildId = (ulong)jsonData["guildId"];
                switch (evtype)
                {
                case EventType.TrackStartEvent:
                    if (this._connectedGuilds.TryGetValue(guildId, out var lvl_evtst))
                    {
                        await lvl_evtst.InternalPlaybackStartedAsync(jsonData["track"].ToString()).ConfigureAwait(false);
                    }
                    break;

                case EventType.TrackEndEvent:
                    TrackEndReason reason = TrackEndReason.Cleanup;
                    switch (jsonData["reason"].ToString())
                    {
                    case "FINISHED":
                        reason = TrackEndReason.Finished;
                        break;

                    case "LOAD_FAILED":
                        reason = TrackEndReason.LoadFailed;
                        break;

                    case "STOPPED":
                        reason = TrackEndReason.Stopped;
                        break;

                    case "REPLACED":
                        reason = TrackEndReason.Replaced;
                        break;

                    case "CLEANUP":
                        reason = TrackEndReason.Cleanup;
                        break;
                    }
                    if (this._connectedGuilds.TryGetValue(guildId, out var lvl_evtf))
                    {
                        await lvl_evtf.InternalPlaybackFinishedAsync(new TrackFinishData { Track = jsonData["track"].ToString(), Reason = reason }).ConfigureAwait(false);
                    }
                    break;

                case EventType.TrackStuckEvent:
                    if (this._connectedGuilds.TryGetValue(guildId, out var lvl_evts))
                    {
                        await lvl_evts.InternalTrackStuckAsync(new TrackStuckData { Track = jsonData["track"].ToString(), Threshold = (long)jsonData["thresholdMs"] }).ConfigureAwait(false);
                    }
                    break;

                case EventType.TrackExceptionEvent:
                    if (this._connectedGuilds.TryGetValue(guildId, out var lvl_evte))
                    {
                        await lvl_evte.InternalTrackExceptionAsync(new TrackExceptionData { Track = jsonData["track"].ToString(), Error = jsonData["error"].ToString() }).ConfigureAwait(false);
                    }
                    break;

                case EventType.WebSocketClosedEvent:
                    if (this._connectedGuilds.TryGetValue(guildId, out var lvl_ewsce))
                    {
                        lvl_ewsce.VoiceWsDisconnectTcs.SetResult(true);
                        await lvl_ewsce.InternalWebSocketClosedAsync(new WebSocketCloseEventArgs(jsonData["code"].ToObject <int>(), jsonData["reason"].ToString(), jsonData["byRemote"].ToObject <bool>())).ConfigureAwait(false);
                    }
                    break;
                }
                break;
            }
        }