예제 #1
0
        internal async Task SendHeartbeatAsync(long seq)
        {
            var more_than_5 = Volatile.Read(ref this._skippedHeartbeats) > 5;
            var guilds_comp = Volatile.Read(ref this._guildDownloadCompleted);

            if (guilds_comp && more_than_5)
            {
                this.Logger.LogCritical(LoggerEvents.HeartbeatFailure, "Server failed to acknowledge more than 5 heartbeats - connection is zombie");
                await this.InternalReconnectAsync(code : 4001, message : "Too many heartbeats missed").ConfigureAwait(false);

                return;
            }
            else if (!guilds_comp && more_than_5)
            {
                this.Logger.LogWarning(LoggerEvents.HeartbeatFailure, "Server failed to acknowledge more than 5 heartbeats, but the guild download is still running - check your connection speed");
            }

            Volatile.Write(ref this._lastSequence, seq);
            this.Logger.LogTrace(LoggerEvents.Heartbeat, "Sending heartbeat");
            var heartbeat = new GatewayPayload
            {
                OpCode = GatewayOpCode.Heartbeat,
                Data   = seq
            };
            var heartbeat_str = JsonConvert.SerializeObject(heartbeat);

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

            this._lastHeartbeat = DateTimeOffset.Now;

            Interlocked.Increment(ref this._skippedHeartbeats);
        }
예제 #2
0
 public void Send(GatewayPayload payload)
 {
     if (!_runCts.IsCancellationRequested)
     {
         _sendQueue?.Add(payload);
     }
 }
    protected override void OnMessage(GatewayPayload payload)
    {
        switch (payload.OpCode)
        {
        case GatewayOpCode.Voice_Hello:
            var helloData = Convert <HelloEventData>(payload.Data);
            Messenger.Broadcast(DiscordEvent.Voice.Hello, helloData);
            break;

        case GatewayOpCode.Voice_Ready:
            var readyData = Convert <VoiceReadyResponse>(payload.Data);
            Messenger.Broadcast(DiscordEvent.Voice.Ready, readyData);
            break;

        case GatewayOpCode.Voice_Heartbeat:
            var heartbeatData = Convert <int>(payload.Data);
            Messenger.Broadcast(DiscordEvent.Voice.HeartbeatACK, heartbeatData);
            break;

        case GatewayOpCode.Voice_SessionDescription:
            var sessionData = Convert <SessionDesciptionResponse>(payload.Data);
            Messenger.Broadcast(DiscordEvent.Voice.SessionDesciption, sessionData);
            break;

        case GatewayOpCode.Voice_Speaking:
            var speakingData = Convert <SpeakingResponse>(payload.Data);
            Messenger.Broadcast(DiscordEvent.Voice.Speaking, speakingData);
            break;
        }
    }
예제 #4
0
        public void SendIdentify(string token)
        {
            Task.Factory.StartNew(async() =>
            {
                var identifyPayload = new IdentifyEventData()
                {
                    Token      = token,
                    Properties = new IdentifyProperties()
                    {
                        OperatingSystem = "windows",
                        Browser         = "test_lib",
                        Device          = "test_lib"
                    }
                };

                var identifyPayloadJson = JsonConvert.SerializeObject(identifyPayload);

                var identifyRequest = new GatewayPayload()
                {
                    Opcode    = DiscordOpcodeEnum.Identify,
                    EventData = JToken.FromObject(identifyPayload)
                };

                await SendAsync(identifyRequest);
            });
        }
예제 #5
0
        private void HandleEvent(GatewayPayload evnt, TaskCompletionSource <bool> readySignal)
        {
            switch (evnt.Operation)
            {
            case GatewayOperation.Dispatch:
                HandleDispatchEvent(evnt, readySignal);
                break;

            case GatewayOperation.InvalidSession:
                if ((bool)evnt.Data != true)     // Is resumable
                {
                    SetSession(null);
                }
                readySignal.TrySetResult(false);
                GatewayInvalidSession?.Invoke((bool)evnt.Data);
                break;

            case GatewayOperation.Heartbeat:
                SendHeartbeatAck();
                GatewayHeartbeat?.Invoke();
                break;

            case GatewayOperation.HeartbeatAck: GatewayHeartbeatAck?.Invoke(); break;

            case GatewayOperation.Hello: GatewayHello?.Invoke(evnt.Data as HelloEvent); break;

            case GatewayOperation.Reconnect: GatewayReconnect?.Invoke(); break;
            }
        }
예제 #6
0
        public async Task RunHeartbeat(int heartbeatInterval)
        {
            _logger.LogInformation("Starting heartbeating - interval {0}ms", heartbeatInterval);

            while (!_heartbeatToken.IsCancellationRequested)
            {
                int now = Environment.TickCount;
                if (_heartbeatTimes.Count != 0 && (now - _lastMessageTime) > heartbeatInterval)
                {
                    _logger.LogCritical("Did not receive HeartbeatAck");
                    CloseHeartbeating();
                    await CreateConnectionAsync(WebSocketCloseStatus.EndpointUnavailable);

                    break;
                }

                _heartbeatTimes.Enqueue(now);

                var heartbeatEvent = new GatewayPayload
                {
                    Opcode    = GatewayOpCode.Heartbeat,
                    EventData = _sequenceNumber ?? null,
                };

                var heartbeatEventBytes = Encoding.UTF8.GetBytes(
                    JsonConvert.SerializeObject(heartbeatEvent));
                await _discordSocketClient.SendAsync(heartbeatEventBytes, true);

                await Task.Delay(heartbeatInterval, _heartbeatToken);
            }

            _logger.LogWarning("Client stopped heartbeating");
        }
예제 #7
0
        public async Task SendAsync(GatewayPayload payload)
        {
            var text = JsonConvert.SerializeObject(payload);

            await _socket.SendAsync(
                new ArraySegment <byte>(Encoding.UTF8.GetBytes(text)), WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false);
        }
예제 #8
0
        internal async Task SendIdentifyAsync(StatusUpdate status)
        {
            var identify = new GatewayIdentify
            {
                Token          = Utilities.GetFormattedToken(this),
                Compress       = this.Configuration.GatewayCompressionLevel == GatewayCompressionLevel.Payload,
                LargeThreshold = this.Configuration.LargeThreshold,
                ShardInfo      = new ShardInfo
                {
                    ShardId    = this.Configuration.ShardId,
                    ShardCount = this.Configuration.ShardCount
                },
                Presence = status,
                Intents  = this.Configuration.Intents
            };
            var payload = new GatewayPayload
            {
                OpCode = GatewayOpCode.Identify,
                Data   = identify
            };
            var payloadstr = JsonConvert.SerializeObject(payload);

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

            if (this.Configuration.Intents.HasValue)
            {
                this.Logger.LogDebug(LoggerEvents.Intents, "Registered gateway intents ({0})", this.Configuration.Intents.Value);
            }
        }
예제 #9
0
        /// <summary>
        /// Handles Gateway Payloads received from the SocketClient
        /// </summary>
        /// <param name="payload">Payload received from the SocketClient</param>
        /// <returns></returns>
        private async Task HandleGatewayPayload(GatewayPayload payload)
        {
            var opcode = payload.Opcode;

            switch (opcode)
            {
            case OpCodes.Dispatch:
                break;

            case OpCodes.Heartbeat:
                await this._socketClient.SendAsync(OpCodes.Heartbeat, LastSequenceNumber);

                break;

            case OpCodes.Reconnect:
                break;

            case OpCodes.InvalidSession:
                break;

            case OpCodes.Hello:
                await OnHelloMessageAsync((payload.Data as JObject).ToObject <GatewayHello>());

                break;

            case OpCodes.HeartbeatAcknowledge:
                this._socketClient.LastHeartbeatAcknowledge = DateTime.Now;
                break;

            default:
                break;
            }
        }
예제 #10
0
        private protected override async Task OnMessageAsync(MessageEventArgs messageEventArgs)
        {
            if (messageEventArgs.Data.Length <= 0)
            {
                Logger.LogWarning("Gateway sent empty payload data.");
                return;
            }

            var gatewayPayload = new GatewayPayload(messageEventArgs.Data);

            Logger.LogDebug($"Received {Enum.GetName(typeof(OpCode), gatewayPayload.OpCode)}.");

            switch (gatewayPayload.OpCode)
            {
            case OpCode.Hello when gatewayPayload.OpData is HelloPayload helloPayload:
                _heartBeatTask = SetupHeartbeatAsync(helloPayload.Interval);
                break;

            case OpCode.Ready when gatewayPayload.OpData is ReadyPayload readyPayload:
                await SelectProtocolAsync(readyPayload);

                break;

            case OpCode.SessionDescription
                when gatewayPayload.OpData is SessionDescriptionPayload descriptionPayload:
                break;

            case OpCode.HeartbeatAcknowledge when gatewayPayload.OpData is int nonce:
                Logger.LogDebug($"Heartbeat ACK {nonce}");
                break;
            }
        }
예제 #11
0
        private async Task SetupHeartbeatAsync(int interval)
        {
            while (Volatile.Read(ref _isOpen))
            {
                var heartbeatPayload = new GatewayPayload(OpCode.Heartbeat, DateTime.Now.Ticks);
                await SocketClient.SendAsync(heartbeatPayload);

                await Task.Delay(interval);
            }
        }
예제 #12
0
        public async Task ProcessMessageAsync(GatewayPayload payload)
        {
            var message = JsonConvert.DeserializeObject <GatewayMessage>(payload.EventData.ToString());

            if (_commandValidator.IsCommand(message))
            {
                var command = new Command(message);

                await _commandDispatcher.DispatchAsync(command);
            }
        }
예제 #13
0
        public async Task DispatchEvent(GatewayPayload @event)
        {
            switch (@event.EventName)
            {
            case "MESSAGE_CREATE":
            {
                await _commandProcessor.ProcessMessageAsync(@event);
            }
            break;

            case "MESSAGE_DELETE":
            {
                // Delete tracked message
            }
            break;

            case "MESSAGE_REACTION_ADD":
            {
                await _commandProcessor.ProcessReactionAsync(@event);
            }
            break;

            case "MESSAGE_REACTION_REMOVE":
            {
                await _commandProcessor.ProcessReactionAsync(@event);
            }
            break;

            case "GUILD_CREATE":
            {
                await _componentContext.Resolve <IGatewayEventHandler <Guild> >()
                .HandleAsync(@event.EventData);
            }
            break;

            case "READY":
            {
                await _componentContext.Resolve <IGatewayEventHandler <ReadyEvent> >()
                .HandleAsync(@event.EventData);
            }
            break;

            case "RESUMED":
            {
                _logger.LogWarning("Session resumed!");
            }
            break;

            default:
                _logger.LogWarning($"Unhandled event received [{@event.Opcode} - {@event.EventName}]");
                break;
            }
        }
예제 #14
0
    protected override void OnMessage(GatewayPayload payload)
    {
        switch (payload.OpCode)
        {
        case GatewayOpCode.Hello:
            var helloData = Convert <HelloEventData>(payload.Data);
            Messenger.Broadcast(DiscordEvent.Hello, helloData);
            break;

        case GatewayOpCode.HeartbeatACK:
            Messenger.Broadcast(DiscordEvent.HeartbeatACK);
            break;

        case GatewayOpCode.Dispatch:
            Messenger.Broadcast(DiscordEvent.SequenceNumber, payload.SequenceNumber);
            switch (payload.EventName)
            {
            case TypingStartEventData.Name:
                var typingData = Convert <TypingStartEventData>(payload.Data);
                Messenger.Broadcast(DiscordEvent.TypingStart, typingData);
                break;

            case MessageCreateEventData.Name:
                var messageData = Convert <MessageCreateEventData>(payload.Data);
                Messenger.Broadcast(DiscordEvent.MessageCreate, messageData);
                break;

            case ReadyEventData.Name:
                var readyData = Convert <ReadyEventData>(payload.Data);
                Messenger.Broadcast(DiscordEvent.Ready, readyData);
                break;

            case GuildCreateEventData.Name:
                var guildData = Convert <GuildCreateEventData>(payload.Data);
                Messenger.Broadcast(DiscordEvent.GuildCreate, guildData);
                break;

            case VoiceServerUpdate.Name:
                var voiceServerUpdate = Convert <VoiceServerUpdate>(payload.Data);
                Messenger.Broadcast(DiscordEvent.Voice.ServerUpdate, voiceServerUpdate);
                break;

            case VoiceStateUpdateResponse.Name:
                var voiceStateUpdate = Convert <VoiceStateUpdateResponse>(payload.Data);
                Messenger.Broadcast(DiscordEvent.Voice.StatusUpdate, voiceStateUpdate);
                break;
            }
            break;
        }
    }
예제 #15
0
        public async Task SendAsync(GatewayPayload payload)
        {
            var name = Enum.GetName(typeof(DiscordOpcodeEnum), payload.Opcode);

            var jsonString = JsonConvert.SerializeObject(payload);

            var buffer = new ArraySegment <byte>(Encoding.UTF8.GetBytes(jsonString));

            await _client.SendAsync(
                buffer,
                WebSocketMessageType.Text,
                true,
                CancellationToken.None);
        }
예제 #16
0
        internal async Task InternalUpdateStatusAsync(DiscordActivity activity, UserStatus?userStatus, DateTimeOffset?idleSince)
        {
            if (activity != null && activity.Name != null && activity.Name.Length > 128)
            {
                throw new Exception("Game name can't be longer than 128 characters!");
            }

            var since_unix = idleSince != null ? (long?)Utilities.GetUnixTime(idleSince.Value) : null;
            var act        = activity ?? new DiscordActivity();

            var status = new StatusUpdate
            {
                Activity  = new TransportActivity(act),
                IdleSince = since_unix,
                IsAFK     = idleSince != null,
                Status    = userStatus ?? UserStatus.Online
            };

            // Solution to have status persist between sessions
            this._status = status;
            var status_update = new GatewayPayload
            {
                OpCode = GatewayOpCode.StatusUpdate,
                Data   = status
            };

            var statusstr = JsonConvert.SerializeObject(status_update);

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

            if (!this._presences.ContainsKey(this.CurrentUser.Id))
            {
                this._presences[this.CurrentUser.Id] = new DiscordPresence
                {
                    Discord      = this,
                    Activity     = act,
                    Status       = userStatus ?? UserStatus.Online,
                    InternalUser = new TransportUser {
                        Id = this.CurrentUser.Id
                    }
                };
            }
            else
            {
                var pr = this._presences[this.CurrentUser.Id];
                pr.Activity = act;
                pr.Status   = userStatus ?? pr.Status;
            }
        }
예제 #17
0
    private void ToggleSpeaking(bool speaking)
    {
        var payload = new GatewayPayload
        {
            OpCode = GatewayOpCode.Voice_Speaking,
            Data   = new SpeakingRequest
            {
                speaking = speaking,
                delay    = 0,
                ssrc     = udpClient.ssrc
            }
        };

        voiceGateway.Send(payload);
    }
예제 #18
0
        public async Task SendAsync(int opcode, object data, string t = null)
        {
            var payload = new GatewayPayload
            {
                Opcode = opcode,
                Data   = JToken.FromObject(data)
            };

            if (t != null)
            {
                payload.Event = t;
            }

            await SendAsync(payload);
        }
예제 #19
0
        public static IEventData GetEventData(this GatewayPayload payload)
        {
            IEventData result = null;

            if (payload.EventName == EventNameConst.MESSAGE_CREATE)
            {
                result = JsonConvert.DeserializeObject <MessageCreateEventData>(payload.EventData.ToString());
            }
            if (payload.EventName == EventNameConst.READY)
            {
                result = JsonConvert.DeserializeObject <ReadyEventData>(payload.EventData.ToString());
            }

            return(result);
        }
예제 #20
0
        private async Task ResumeSession(string sessionId)
        {
            var resumeEvent = new GatewayPayload
            {
                Opcode    = GatewayOpCode.Resume,
                EventData = new ResumeEvent
                {
                    Token     = Environment.GetEnvironmentVariable("BOT_TOKEN"),
                    SessionId = sessionId,
                    Sequence  = _sequenceNumber.Value
                }
            };

            var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(resumeEvent));
            await _discordSocketClient.SendAsync(bytes, true);
        }
예제 #21
0
    private void SendHeartbeat()
    {
        //Debug.Log($"{gateway.Name}: Heartbeat");
        if (!acknowledged)
        {
            Debug.LogError($"{gateway.Name}: Previous heartbeat wasn't acknowledged");
        }
        var heartbeat = new GatewayPayload
        {
            OpCode = OpCode,
            Data   = Data
        };

        acknowledged = false;
        gateway.Send(heartbeat);
    }
예제 #22
0
    public void JoinVoice(string guildId, string channelId)
    {
        var payload = new GatewayPayload
        {
            OpCode = GatewayOpCode.VoiceStateUpdate,
            Data   = new VoiceStateUpdateRequest
            {
                guild_id   = guildId,
                channel_id = channelId,
                self_mute  = false,
                self_deaf  = false
            }
        };

        gateway.Send(payload);
    }
예제 #23
0
        /// <inheritdoc />
        public override async Task UpdateSpeakingAsync(SpeakingFlags speakingFlags)
        {
            if (!Volatile.Read(ref _isOpen))
            {
                throw new InvalidOperationException();
            }

            var speakingPayload = new GatewayPayload(OpCode.Speaking,
                                                     new SpeakingPayload {
                Speaking = speakingFlags,
                Delay    = 0,
                SSRC     = _ssrc
            });

            await SocketClient.SendAsync(speakingPayload);
        }
예제 #24
0
        private async Task SelectProtocolAsync(ReadyPayload readyPayload)
        {
            _ssrc = readyPayload.SSRC;
            Logger.LogDebug($"UDP client connecting to {readyPayload.Ip}:{readyPayload.Port}");
            _udpClient.Connect(readyPayload.Ip, readyPayload.Port);

            var selectPayload = new GatewayPayload(OpCode.SelectProtocol, new SelectPayload {
                Protocol = "udp",
                Data     = new {
                    address = readyPayload.Ip,
                    port    = readyPayload.Port,
                    mode    = EncryptionMode.Select(readyPayload.Modes)
                }
            });

            await SocketClient.SendAsync(selectPayload);
        }
예제 #25
0
    //TODO: Handle Resume:
    ///<summary>
    ///See <a href="https://discordapp.com/developers/docs/topics/voice-connections#resuming-voice-connection">Discord API Documentation</a>
    ///</summary>
    ///

    private void OnHello(HelloEventData e)
    {
        var payload = new GatewayPayload
        {
            OpCode = GatewayOpCode.Voice_Identify,
            Data   = new VoiceIdentifyRequest
            {
                server_id  = guildId,
                user_id    = userId,
                session_id = sessionId,
                token      = token
            }
        };

        voiceGateway.Send(payload);
        heartbeatService = new VoiceHeartbeatService(voiceGateway, e.heartbeat_interval);
    }
예제 #26
0
        public void StartHeartbeat()
        {
            Task.Factory.StartNew(async() =>
            {
                while (true)
                {
                    await Task.Delay(_heartbeatInterval);
                    var heartbeatRequest = new GatewayPayload()
                    {
                        Opcode    = DiscordOpcodeEnum.Heartbeat,
                        EventData = new JValue(_lastSequence)
                    };

                    await SendAsync(heartbeatRequest);
                }
            });
        }
예제 #27
0
        internal async Task SendResumeAsync()
        {
            var resume = new GatewayResume
            {
                Token          = Utilities.GetFormattedToken(this),
                SessionId      = this._sessionId,
                SequenceNumber = Volatile.Read(ref this._lastSequence)
            };
            var resume_payload = new GatewayPayload
            {
                OpCode = GatewayOpCode.Resume,
                Data   = resume
            };
            var resumestr = JsonConvert.SerializeObject(resume_payload);

            await this.WsSendAsync(resumestr).ConfigureAwait(false);
        }
예제 #28
0
        /// <summary>
        /// Sending payloads to the websocket
        /// </summary>
        /// <param name="opCode">OpCode of the operation wanted to perform</param>
        /// <param name="data">Data to send with the payload</param>
        /// <param name="sequence">Sequence Number</param>
        /// <param name="eventName">Event Name</param>
        public async Task SendAsync(OpCodes opCode, object data, int?sequence = null, string eventName = null)
        {
            var payload = new GatewayPayload
            {
                Opcode         = opCode,
                Data           = data,
                SequenceNumber = sequence,
                EventName      = eventName
            };
            string jsonString = JsonConvert.SerializeObject(payload);

            var buffer = UTF8Encoding.UTF8.GetBytes(jsonString);
            ArraySegment <byte> arraySegment = new ArraySegment <byte>(buffer);

            await this._webSocket.SendAsync(arraySegment, WebSocketMessageType.Binary, true, this._cancellationTokenSource.Token);

            this._logger?.Debug("Sent payload to the server");
        }
        public async Task HandleAsync(GatewayPayload payload)
        {
            var s = JsonConvert.SerializeObject(payload, Formatting.Indented);

            _logger.LogInformation(s);

            var dispatchEvent = payload.GetEventData();

            if (dispatchEvent is MessageCreateEventData)
            {
                var messageCreateEventData = dispatchEvent as MessageCreateEventData;
                await _messageCreateHandlerService.HandleAsync('!', messageCreateEventData);
            }
            else if (dispatchEvent is ReadyEventData)
            {
                var readyEventData = dispatchEvent as ReadyEventData;
                _userService.DiscordAuthInfo.User = readyEventData.User;
            }
        }
예제 #30
0
        private async Task IdentifyClient()
        {
            var identityEvent = new GatewayPayload
            {
                Opcode    = GatewayOpCode.Identify,
                EventData = new IdentifyEvent
                {
                    Token      = Environment.GetEnvironmentVariable("BOT_TOKEN"),
                    Intents    = 1791,
                    Compress   = false,
                    Properties = new Dictionary <string, string> {
                        { "$os", "windows" },
                        { "$browser", "crabot" },
                        { "$device", "crabot" }
                    }
                }
            };

            var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(identityEvent));
            await _discordSocketClient.SendAsync(bytes, true);
        }