/// <summary>
 /// <c>Dispose</c>
 /// </summary>
 /// <param name="disposing"></param>
 protected virtual void Dispose(bool disposing)
 {
     if (disposing)
     {
         GatewaySocket?.Dispose();
     }
 }
示例#2
0
        private async Task Cleanup()
        {
            var oldState = State;

            State = ConnectionState.Disconnecting;

            if (oldState == ConnectionState.Connected)
            {
                try { await ClientAPI.Send(new LogoutRequest()).ConfigureAwait(false); }
                catch (OperationCanceledException) { }
            }

            MessageQueue.Clear();

            await GatewaySocket.Disconnect().ConfigureAwait(false);

            ClientAPI.Token = null;

            _servers.Clear();
            _channels.Clear();
            _privateChannels.Clear();

            PrivateUser = null;
            CurrentUser = null;

            State = (int)ConnectionState.Disconnected;
            _connectedEvent.Reset();
            _disconnectedEvent.Set();
        }
示例#3
0
        public AudioClient(DiscordClient client, Server server, int id)
        {
            Id            = id;
            Service       = client.GetService <AudioService>();
            Config        = Service.Config;
            Serializer    = client.Serializer;
            _gatewayState = (int)ConnectionState.Disconnected;

            //Logging
            Logger = client.Log.CreateLogger($"AudioClient #{id}");

            //Async
            _taskManager    = new TaskManager(Cleanup, false);
            _connectionLock = new AsyncLock();
            CancelToken     = new CancellationToken(true);

            //Networking
            if (Config.EnableMultiserver)
            {
                //TODO: We can remove this hack when official API launches
                var baseConfig = client.Config;
                var builder    = new DiscordConfigBuilder
                {
                    AppName               = baseConfig.AppName,
                    AppUrl                = baseConfig.AppUrl,
                    AppVersion            = baseConfig.AppVersion,
                    CacheToken            = baseConfig.CacheDir != null,
                    ConnectionTimeout     = baseConfig.ConnectionTimeout,
                    EnablePreUpdateEvents = false,
                    FailedReconnectDelay  = baseConfig.FailedReconnectDelay,
                    LargeThreshold        = 1,
                    LogLevel              = baseConfig.LogLevel,
                    MessageCacheSize      = 0,
                    ReconnectDelay        = baseConfig.ReconnectDelay,
                    UsePermissionsCache   = false
                };
                _config = builder.Build();

                ClientAPI                = new JsonRestClient(_config, DiscordConfig.ClientAPIUrl, client.Log.CreateLogger($"ClientAPI #{id}"));
                GatewaySocket            = new GatewaySocket(_config, client.Serializer, client.Log.CreateLogger($"Gateway #{id}"));
                GatewaySocket.Connected += (s, e) =>
                {
                    if (_gatewayState == ConnectionState.Connecting)
                    {
                        EndGatewayConnect();
                    }
                };
            }
            else
            {
                _config       = client.Config;
                GatewaySocket = client.GatewaySocket;
            }
            GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e);
            VoiceSocket        = new VoiceSocket(_config, Config, client.Serializer, client.Log.CreateLogger($"Voice #{id}"));
            VoiceSocket.Server = server;
            OutputStream       = new OutStream(this);
        }
示例#4
0
        /// <summary> Initializes a new instance of the DiscordClient class. </summary>
        public DiscordClient(DiscordConfig config)
        {
            Config = config;

            State  = (int)ConnectionState.Disconnected;
            Status = UserStatus.Online;

            //Logging
            Log    = new LogManager(this);
            Logger = Log.CreateLogger("Discord");
            if (config.LogLevel >= LogSeverity.Verbose)
            {
                _connectionStopwatch = new Stopwatch();
            }

            //Async
            _taskManager       = new TaskManager(Cleanup);
            _connectionLock    = new AsyncLock();
            _disconnectedEvent = new ManualResetEvent(true);
            _connectedEvent    = new ManualResetEventSlim(false);
            CancelToken        = new CancellationToken(true);

            //Cache
            //ConcurrentLevel = 2 (only REST and WebSocket can add/remove)
            _servers         = new ConcurrentDictionary <ulong, Server>(2, 0);
            _channels        = new ConcurrentDictionary <ulong, IChannel>(2, 0);
            _privateChannels = new ConcurrentDictionary <ulong, PrivateChannel>(2, 0);

            //Serialization
            Serializer = new JsonSerializer();
            Serializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
#if TEST_RESPONSES
            Serializer.CheckAdditionalContent = true;
            Serializer.MissingMemberHandling  = MissingMemberHandling.Error;
#else
            Serializer.CheckAdditionalContent = false;
            Serializer.MissingMemberHandling  = MissingMemberHandling.Ignore;
#endif
            Serializer.Error += (s, e) =>
            {
                e.ErrorContext.Handled = true;
                Logger.Error("Serialization Failed", e.ErrorContext.Error);
            };

            //Networking
            ClientAPI     = new JsonRestClient(Config, DiscordConfig.ClientAPIUrl, Log.CreateLogger("ClientAPI"));
            StatusAPI     = new JsonRestClient(Config, DiscordConfig.StatusAPIUrl, Log.CreateLogger("StatusAPI"));
            GatewaySocket = new GatewaySocket(Config, Serializer, Log.CreateLogger("Gateway"));

            //GatewaySocket.Disconnected += (s, e) => OnDisconnected(e.WasUnexpected, e.Exception);
            GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e);

            MessageQueue = new MessageQueue(ClientAPI, Log.CreateLogger("MessageQueue"));

            //Extensibility
            _services = new ServiceCollection(this);
        }
示例#5
0
        public AudioClient(AudioService service, int clientId, Server server, GatewaySocket gatewaySocket, Logger logger)
        {
            Service       = service;
            Id            = clientId;
            GatewaySocket = gatewaySocket;
            Logger        = logger;

            _connectionLock = new AsyncLock();

            _serializer = new JsonSerializer();
            _serializer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
            _serializer.Error += (s, e) =>
            {
                e.ErrorContext.Handled = true;
                Logger.Error("Serialization Failed", e.ErrorContext.Error);
            };

            GatewaySocket.ReceivedDispatch += OnReceivedDispatch;

            VoiceSocket        = new VoiceWebSocket(service.Client, this, _serializer, logger);
            VoiceSocket.Server = server;

            /*_voiceSocket.Connected += (s, e) => RaiseVoiceConnected();
             *          _voiceSocket.Disconnected += async (s, e) =>
             *          {
             *                  _voiceSocket.CurrentServerId;
             *                  if (voiceServerId != null)
             *                          _gatewaySocket.SendLeaveVoice(voiceServerId.Value);
             *                  await _voiceSocket.Disconnect().ConfigureAwait(false);
             *                  RaiseVoiceDisconnected(socket.CurrentServerId.Value, e);
             *                  if (e.WasUnexpected)
             *                          await socket.Reconnect().ConfigureAwait(false);
             *          };*/

            /*_voiceSocket.IsSpeaking += (s, e) =>
             *          {
             *                  if (_voiceSocket.State == WebSocketState.Connected)
             *                  {
             *                          var user = _users[e.UserId, socket.CurrentServerId];
             *                          bool value = e.IsSpeaking;
             *                          if (user.IsSpeaking != value)
             *                          {
             *                                  user.IsSpeaking = value;
             *                                  var channel = _channels[_voiceSocket.CurrentChannelId];
             *                                  RaiseUserIsSpeaking(user, channel, value);
             *                                  if (Config.TrackActivity)
             *                                          user.UpdateActivity();
             *                          }
             *                  }
             *          };*/

            /*this.Connected += (s, e) =>
             *          {
             *                  _voiceSocket.ParentCancelToken = _cancelToken;
             *          };*/
        }
示例#6
0
        private void SendVoiceUpdate()
        {
            var serverId = VoiceSocket.Server?.Id;

            if (serverId != null)
            {
                GatewaySocket.SendUpdateVoice(serverId, VoiceSocket.Channel?.Id,
                                              (Service.Config.Mode | AudioMode.Outgoing) == 0,
                                              (Service.Config.Mode | AudioMode.Incoming) == 0);
            }
        }
示例#7
0
        private async Task BeginConnect(string email, string password, string token = null)
        {
            try
            {
                using (await _connectionLock.LockAsync().ConfigureAwait(false))
                {
                    await Disconnect().ConfigureAwait(false);

                    _taskManager.ClearException();

                    Stopwatch stopwatch = null;
                    if (Config.LogLevel >= LogSeverity.Verbose)
                    {
                        _connectionStopwatch.Restart();
                        stopwatch = Stopwatch.StartNew();
                    }
                    State = ConnectionState.Connecting;
                    _disconnectedEvent.Reset();

                    var cancelSource = new CancellationTokenSource();
                    CancelToken           = cancelSource.Token;
                    ClientAPI.CancelToken = CancelToken;
                    StatusAPI.CancelToken = CancelToken;

                    await Login(email, password, token).ConfigureAwait(false);

                    await GatewaySocket.Connect(ClientAPI, CancelToken).ConfigureAwait(false);

                    var tasks = new[] { CancelToken.Wait() }
                    .Concat(MessageQueue.Run(CancelToken));

                    await _taskManager.Start(tasks, cancelSource).ConfigureAwait(false);

                    GatewaySocket.WaitForConnection(CancelToken);

                    if (Config.LogLevel >= LogSeverity.Verbose)
                    {
                        stopwatch.Stop();
                        double seconds = Math.Round(stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerSecond, 2);
                        Logger.Verbose($"Handshake + Ready took {seconds} sec");
                    }
                }
            }
            catch (Exception ex)
            {
                await _taskManager.SignalError(ex).ConfigureAwait(false);

                throw;
            }
        }
示例#8
0
        private async Task BeginGatewayConnect()
        {
            try
            {
                using (await _connectionLock.LockAsync().ConfigureAwait(false))
                {
                    await Disconnect().ConfigureAwait(false);

                    _taskManager.ClearException();

                    ClientAPI.Token = Service.Client.ClientAPI.Token;

                    Stopwatch stopwatch = null;
                    if (_config.LogLevel >= LogSeverity.Verbose)
                    {
                        stopwatch = Stopwatch.StartNew();
                    }
                    _gatewayState = ConnectionState.Connecting;

                    var cancelSource = new CancellationTokenSource();
                    CancelToken           = cancelSource.Token;
                    ClientAPI.CancelToken = CancelToken;

                    await GatewaySocket.Connect(ClientAPI, CancelToken).ConfigureAwait(false);

                    await _taskManager.Start(new Task[0], cancelSource).ConfigureAwait(false);

                    GatewaySocket.WaitForConnection(CancelToken);

                    if (_config.LogLevel >= LogSeverity.Verbose)
                    {
                        stopwatch.Stop();
                        double seconds = Math.Round(stopwatch.ElapsedTicks / (double)TimeSpan.TicksPerSecond, 2);
                        Logger.Verbose($"Connection took {seconds} sec");
                    }
                }
            }
            catch (Exception ex)
            {
                await _taskManager.SignalError(ex).ConfigureAwait(false);

                throw;
            }
        }
        private void WriteBlockToGateway(string inboundData)
        {
            if (string.IsNullOrEmpty(inboundData))
            {
                throw new ArgumentNullException($"inboundData data Null or Empty");
            }

            if (GatewaySocket == null)
            {
                throw new ArgumentNullException($"Invalid Socket Reference");
            }

            try
            {
                if (DebugMode)
                {
                    LogTaggedRecord(inboundData);
                }

                if (GatewaySocket.Write(inboundData) == false)
                {
                    throw new Exception("Parser recieved a 'failed' response from the gateway");
                }

                if (SendEof)
                {
                    GatewaySocket.Write("<EOF/>");
                }

                if (DebugMode)
                {
                    EventLogger.Debug(inboundData);
                }
            }
            catch (Exception ex)
            {
                EventLogger.Error($"Failed to write to gateway: {ex.Message}");
                throw;
            }
        }
示例#10
0
        public AudioClient(DiscordClient client, Server server, int id)
        {
            Id            = id;
            _config       = client.Config;
            Service       = client.Services.Get <AudioService>();
            Config        = Service.Config;
            Serializer    = client.Serializer;
            _gatewayState = (int)ConnectionState.Disconnected;

            //Logging
            Logger = client.Log.CreateLogger($"AudioClient #{id}");

            //Async
            _taskManager    = new TaskManager(Cleanup, false);
            _connectionLock = new AsyncLock();
            CancelToken     = new CancellationToken(true);

            //Networking
            if (Config.EnableMultiserver)
            {
                ClientAPI                = new JsonRestClient(_config, DiscordConfig.ClientAPIUrl, client.Log.CreateLogger($"ClientAPI #{id}"));
                GatewaySocket            = new GatewaySocket(_config, client.Serializer, client.Log.CreateLogger($"Gateway #{id}"));
                GatewaySocket.Connected += (s, e) =>
                {
                    if (_gatewayState == ConnectionState.Connecting)
                    {
                        EndGatewayConnect();
                    }
                };
            }
            else
            {
                GatewaySocket = client.GatewaySocket;
            }
            GatewaySocket.ReceivedDispatch += (s, e) => OnReceivedEvent(e);
            VoiceSocket        = new VoiceSocket(_config, Config, client.Serializer, client.Log.CreateLogger($"Voice #{id}"));
            VoiceSocket.Server = server;
            OutputStream       = new OutStream(this);
        }
示例#11
0
        private async Task Cleanup()
        {
            var oldState = _gatewayState;

            _gatewayState = ConnectionState.Disconnecting;

            if (Config.EnableMultiserver)
            {
                if (oldState == ConnectionState.Connected)
                {
                    try { await ClientAPI.Send(new LogoutRequest()).ConfigureAwait(false); }
                    catch (OperationCanceledException) { }
                }

                await GatewaySocket.Disconnect().ConfigureAwait(false);

                ClientAPI.Token = null;
            }

            var server = VoiceSocket.Server;

            VoiceSocket.Server  = null;
            VoiceSocket.Channel = null;
            if (Config.EnableMultiserver)
            {
                await Service.RemoveClient(server, this).ConfigureAwait(false);
            }
            SendVoiceUpdate(server.Id, null);

            await VoiceSocket.Disconnect().ConfigureAwait(false);

            if (Config.EnableMultiserver)
            {
                await GatewaySocket.Disconnect().ConfigureAwait(false);
            }

            _gatewayState = (int)ConnectionState.Disconnected;
        }
示例#12
0
 static Widget renderWebSocket()
 {
     return(new ListView(
                children: new List <Widget> {
         new GestureDetector(
             onTap: () => {
             GatewaySocket.Connect(
                 $"{Config.apiAddress}/api/socketgw",
                 () => "",
                 () => "",
                 true
                 );
         },
             child: new Container(
                 color: CColors.White,
                 child: new Text(
                     "ConnectWebSocket",
                     style: CTextStyle.H4
                     )
                 )
             )
     }
                ));
 }
示例#13
0
 public void SendVoiceUpdate(ulong?serverId, ulong?channelId)
 {
     GatewaySocket.SendUpdateVoice(serverId, channelId,
                                   (Service.Config.Mode | AudioMode.Outgoing) == 0,
                                   (Service.Config.Mode | AudioMode.Incoming) == 0);
 }
示例#14
0
        private void OnReceivedEvent(WebSocketEventEventArgs e)
        {
            try
            {
                switch (e.Type)
                {
                //Global
                case "READY":
                {
                    //TODO: None of this is really threadsafe - should only replace the cache collections when they have been fully populated

                    var data = e.Payload.ToObject <ReadyEvent>(Serializer);

                    int channelCount = 0;
                    for (int i = 0; i < data.Guilds.Length; i++)
                    {
                        channelCount += data.Guilds[i].Channels.Length;
                    }

                    //ConcurrencyLevel = 2 (only REST and WebSocket can add/remove)
                    _servers         = new ConcurrentDictionary <ulong, Server>(2, (int)(data.Guilds.Length * 1.05));
                    _channels        = new ConcurrentDictionary <ulong, IChannel>(2, (int)(channelCount * 1.05));
                    _privateChannels = new ConcurrentDictionary <ulong, PrivateChannel>(2, (int)(data.PrivateChannels.Length * 1.05));
                    List <ulong> largeServers = new List <ulong>();

                    SessionId   = data.SessionId;
                    PrivateUser = new User(data.User, this, null);
                    PrivateUser.Update(data.User);
                    CurrentUser = new Profile(data.User, this);
                    CurrentUser.Update(data.User);

                    for (int i = 0; i < data.Guilds.Length; i++)
                    {
                        var model = data.Guilds[i];
                        if (model.Unavailable != true)
                        {
                            var server = AddServer(model.Id);
                            server.Update(model);
                            if (model.IsLarge)
                            {
                                largeServers.Add(server.Id);
                            }
                        }
                    }
                    for (int i = 0; i < data.PrivateChannels.Length; i++)
                    {
                        AddPrivateChannel(data.PrivateChannels[i]);
                    }
                    if (largeServers.Count > 0)
                    {
                        GatewaySocket.SendRequestMembers(largeServers, "", 0);
                    }
                    else
                    {
                        EndConnect();
                    }
                }
                break;

                //Servers
                case "GUILD_CREATE":
                {
                    var data = e.Payload.ToObject <GuildCreateEvent>(Serializer);
                    if (data.Unavailable != true)
                    {
                        var server = AddServer(data.Id);
                        server.Update(data);

                        if (data.Unavailable != false)
                        {
                            Logger.Info($"GUILD_CREATE: {server}");
                        }
                        else
                        {
                            Logger.Info($"GUILD_AVAILABLE: {server}");
                        }

                        if (data.Unavailable != false)
                        {
                            OnJoinedServer(server);
                        }
                        OnServerAvailable(server);
                    }
                }
                break;

                case "GUILD_UPDATE":
                {
                    var data   = e.Payload.ToObject <GuildUpdateEvent>(Serializer);
                    var server = GetServer(data.Id);
                    if (server != null)
                    {
                        var before = Config.EnablePreUpdateEvents ? server.Clone() : null;
                        server.Update(data);
                        Logger.Info($"GUILD_UPDATE: {server}");
                        OnServerUpdated(before, server);
                    }
                    else
                    {
                        Logger.Warning("GUILD_UPDATE referenced an unknown guild.");
                    }
                }
                break;

                case "GUILD_DELETE":
                {
                    var    data   = e.Payload.ToObject <GuildDeleteEvent>(Serializer);
                    Server server = RemoveServer(data.Id);
                    if (server != null)
                    {
                        if (data.Unavailable != true)
                        {
                            Logger.Info($"GUILD_DELETE: {server}");
                        }
                        else
                        {
                            Logger.Info($"GUILD_UNAVAILABLE: {server}");
                        }

                        OnServerUnavailable(server);
                        if (data.Unavailable != true)
                        {
                            OnLeftServer(server);
                        }
                    }
                    else
                    {
                        Logger.Warning("GUILD_DELETE referenced an unknown guild.");
                    }
                }
                break;

                //Channels
                case "CHANNEL_CREATE":
                {
                    var data = e.Payload.ToObject <ChannelCreateEvent>(Serializer);

                    IChannel channel = null;
                    if (data.GuildId != null)
                    {
                        var server = GetServer(data.GuildId.Value);
                        if (server != null)
                        {
                            channel = server.AddChannel(data, true);
                        }
                        else
                        {
                            Logger.Warning("CHANNEL_CREATE referenced an unknown guild.");
                            break;
                        }
                    }
                    else
                    {
                        channel = AddPrivateChannel(data);
                    }
                    Logger.Info($"CHANNEL_CREATE: {channel}");
                    OnChannelCreated(channel);
                }
                break;

                case "CHANNEL_UPDATE":
                {
                    var data    = e.Payload.ToObject <ChannelUpdateEvent>(Serializer);
                    var channel = GetChannel(data.Id);
                    if (channel != null)
                    {
                        var before = Config.EnablePreUpdateEvents ? (channel as Channel).Clone() : null;
                        (channel as Channel).Update(data);
                        Logger.Info($"CHANNEL_UPDATE: {channel}");
                        OnChannelUpdated(before, channel);
                    }
                    else
                    {
                        Logger.Warning("CHANNEL_UPDATE referenced an unknown channel.");
                    }
                }
                break;

                case "CHANNEL_DELETE":
                {
                    var data    = e.Payload.ToObject <ChannelDeleteEvent>(Serializer);
                    var channel = RemoveChannel(data.Id);
                    if (channel != null)
                    {
                        Logger.Info($"CHANNEL_DELETE: {channel}");
                        OnChannelDestroyed(channel);
                    }
                    else
                    {
                        Logger.Warning("CHANNEL_DELETE referenced an unknown channel.");
                    }
                }
                break;

                //Members
                case "GUILD_MEMBER_ADD":
                {
                    var data   = e.Payload.ToObject <GuildMemberAddEvent>(Serializer);
                    var server = GetServer(data.GuildId.Value);
                    if (server != null)
                    {
                        var user = server.AddUser(data, true, true);
                        user.Update(data);
                        user.UpdateActivity();
                        Logger.Info($"GUILD_MEMBER_ADD: {user}");
                        OnUserJoined(user);
                    }
                    else
                    {
                        Logger.Warning("GUILD_MEMBER_ADD referenced an unknown guild.");
                    }
                }
                break;

                case "GUILD_MEMBER_UPDATE":
                {
                    var data   = e.Payload.ToObject <GuildMemberUpdateEvent>(Serializer);
                    var server = GetServer(data.GuildId.Value);
                    if (server != null)
                    {
                        var user = server.GetUser(data.User.Id);
                        if (user != null)
                        {
                            var before = Config.EnablePreUpdateEvents ? user.Clone() : null;
                            user.Update(data);
                            Logger.Info($"GUILD_MEMBER_UPDATE: {user}");
                            OnUserUpdated(before, user);
                        }
                        else
                        {
                            Logger.Warning("GUILD_MEMBER_UPDATE referenced an unknown user.");
                        }
                    }
                    else
                    {
                        Logger.Warning("GUILD_MEMBER_UPDATE referenced an unknown guild.");
                    }
                }
                break;

                case "GUILD_MEMBER_REMOVE":
                {
                    var data   = e.Payload.ToObject <GuildMemberRemoveEvent>(Serializer);
                    var server = GetServer(data.GuildId.Value);
                    if (server != null)
                    {
                        var user = server.RemoveUser(data.User.Id);
                        if (user != null)
                        {
                            Logger.Info($"GUILD_MEMBER_REMOVE: {user}");
                            OnUserLeft(user);
                        }
                        else
                        {
                            Logger.Warning("GUILD_MEMBER_REMOVE referenced an unknown user.");
                        }
                    }
                    else
                    {
                        Logger.Warning("GUILD_MEMBER_REMOVE referenced an unknown guild.");
                    }
                }
                break;

                case "GUILD_MEMBERS_CHUNK":
                {
                    var data   = e.Payload.ToObject <GuildMembersChunkEvent>(Serializer);
                    var server = GetServer(data.GuildId);
                    if (server != null)
                    {
                        foreach (var memberData in data.Members)
                        {
                            var user = server.AddUser(memberData, true, false);
                            user.Update(memberData);
                        }
                        Logger.Verbose($"GUILD_MEMBERS_CHUNK: {data.Members.Length} users");

                        if (server.CurrentUserCount >= server.UserCount)         //Finished downloading for there
                        {
                            bool isConnectComplete = true;
                            foreach (var server2 in _servers.Select(x => x.Value))
                            {
                                if (server2.CurrentUserCount < server2.UserCount)
                                {
                                    isConnectComplete = false;
                                }
                            }
                            if (isConnectComplete)
                            {
                                EndConnect();
                            }
                        }
                    }
                    else
                    {
                        Logger.Warning("GUILD_MEMBERS_CHUNK referenced an unknown guild.");
                    }
                }
                break;

                //Roles
                case "GUILD_ROLE_CREATE":
                {
                    var data   = e.Payload.ToObject <GuildRoleCreateEvent>(Serializer);
                    var server = GetServer(data.GuildId);
                    if (server != null)
                    {
                        var role = server.AddRole(data.Data.Id);
                        role.Update(data.Data, false);
                        Logger.Info($"GUILD_ROLE_CREATE: {role}");
                        OnRoleCreated(role);
                    }
                    else
                    {
                        Logger.Warning("GUILD_ROLE_CREATE referenced an unknown guild.");
                    }
                }
                break;

                case "GUILD_ROLE_UPDATE":
                {
                    var data   = e.Payload.ToObject <GuildRoleUpdateEvent>(Serializer);
                    var server = GetServer(data.GuildId);
                    if (server != null)
                    {
                        var role = server.GetRole(data.Data.Id);
                        if (role != null)
                        {
                            var before = Config.EnablePreUpdateEvents ? role.Clone() : null;
                            role.Update(data.Data, true);
                            Logger.Info($"GUILD_ROLE_UPDATE: {role}");
                            OnRoleUpdated(before, role);
                        }
                        else
                        {
                            Logger.Warning("GUILD_ROLE_UPDATE referenced an unknown role.");
                        }
                    }
                    else
                    {
                        Logger.Warning("GUILD_ROLE_UPDATE referenced an unknown guild.");
                    }
                }
                break;

                case "GUILD_ROLE_DELETE":
                {
                    var data   = e.Payload.ToObject <GuildRoleDeleteEvent>(Serializer);
                    var server = GetServer(data.GuildId);
                    if (server != null)
                    {
                        var role = server.RemoveRole(data.RoleId);
                        if (role != null)
                        {
                            Logger.Info($"GUILD_ROLE_DELETE: {role}");
                            OnRoleDeleted(role);
                        }
                        else
                        {
                            Logger.Warning("GUILD_ROLE_DELETE referenced an unknown role.");
                        }
                    }
                    else
                    {
                        Logger.Warning("GUILD_ROLE_DELETE referenced an unknown guild.");
                    }
                }
                break;

                //Bans
                case "GUILD_BAN_ADD":
                {
                    var data   = e.Payload.ToObject <GuildBanAddEvent>(Serializer);
                    var server = GetServer(data.GuildId.Value);
                    if (server != null)
                    {
                        var user = server.GetUser(data.User.Id);
                        if (user != null)
                        {
                            Logger.Info($"GUILD_BAN_ADD: {user}");
                            OnUserBanned(user);
                        }
                        else
                        {
                            Logger.Warning("GUILD_BAN_ADD referenced an unknown user.");
                        }
                    }
                    else
                    {
                        Logger.Warning("GUILD_BAN_ADD referenced an unknown guild.");
                    }
                }
                break;

                case "GUILD_BAN_REMOVE":
                {
                    var data   = e.Payload.ToObject <GuildBanRemoveEvent>(Serializer);
                    var server = GetServer(data.GuildId.Value);
                    if (server != null)
                    {
                        var user = new User(data.User, this, server);
                        user.Update(data.User);
                        Logger.Info($"GUILD_BAN_REMOVE: {user}");
                        OnUserUnbanned(user);
                    }
                    else
                    {
                        Logger.Warning("GUILD_BAN_REMOVE referenced an unknown guild.");
                    }
                }
                break;

                //Messages
                case "MESSAGE_CREATE":
                {
                    var data = e.Payload.ToObject <MessageCreateEvent>(Serializer);

                    var channel = GetChannel(data.ChannelId) as ITextChannel;
                    if (channel != null)
                    {
                        var user = (channel as Channel).GetUser(data.Author.Id);

                        if (user != null)
                        {
                            Message msg = null;
                            msg = (channel as Channel).MessageManager.Add(data, user);
                            user.UpdateActivity();

                            Logger.Verbose($"MESSAGE_CREATE: {channel} ({user})");
                            OnMessageReceived(msg);
                        }
                        else
                        {
                            Logger.Warning("MESSAGE_CREATE referenced an unknown user.");
                        }
                    }
                    else
                    {
                        Logger.Warning("MESSAGE_CREATE referenced an unknown channel.");
                    }
                }
                break;

                case "MESSAGE_UPDATE":
                {
                    var data    = e.Payload.ToObject <MessageUpdateEvent>(Serializer);
                    var channel = GetChannel(data.ChannelId) as ITextChannel;
                    if (channel != null)
                    {
                        var msg    = (channel as Channel).MessageManager.Get(data.Id, data.Author?.Id);
                        var before = Config.EnablePreUpdateEvents ? msg.Clone() : null;
                        msg.Update(data);
                        Logger.Verbose($"MESSAGE_UPDATE: {channel} ({data.Author?.Username ?? "Unknown"})");
                        OnMessageUpdated(before, msg);
                    }
                    else
                    {
                        Logger.Warning("MESSAGE_UPDATE referenced an unknown channel.");
                    }
                }
                break;

                case "MESSAGE_DELETE":
                {
                    var data    = e.Payload.ToObject <MessageDeleteEvent>(Serializer);
                    var channel = GetChannel(data.ChannelId) as ITextChannel;
                    if (channel != null)
                    {
                        var msg = (channel as Channel).MessageManager.Remove(data.Id);
                        Logger.Verbose($"MESSAGE_DELETE: {channel} ({msg.User?.Name ?? "Unknown"})");
                        OnMessageDeleted(msg);
                    }
                    else
                    {
                        Logger.Warning("MESSAGE_DELETE referenced an unknown channel.");
                    }
                }
                break;

                //Statuses
                case "PRESENCE_UPDATE":
                {
                    var    data = e.Payload.ToObject <PresenceUpdateEvent>(Serializer);
                    User   user;
                    Server server;
                    if (data.GuildId == null)
                    {
                        server = null;
                        user   = GetPrivateChannel(data.User.Id)?.Recipient;
                    }
                    else
                    {
                        server = GetServer(data.GuildId.Value);
                        if (server == null)
                        {
                            Logger.Warning("PRESENCE_UPDATE referenced an unknown server.");
                            break;
                        }
                        else
                        {
                            user = server.GetUser(data.User.Id);
                        }
                    }

                    if (user != null)
                    {
                        if (Config.LogLevel == LogSeverity.Debug)
                        {
                            Logger.Debug($"PRESENCE_UPDATE: {user}");
                        }
                        var before = Config.EnablePreUpdateEvents ? user.Clone() : null;
                        user.Update(data);
                        OnUserUpdated(before, user);
                    }

                    /*else //Occurs when a user leaves a server
                     *  Logger.Warning("PRESENCE_UPDATE referenced an unknown user.");*/
                }
                break;

                case "TYPING_START":
                {
                    var data    = e.Payload.ToObject <TypingStartEvent>(Serializer);
                    var channel = GetChannel(data.ChannelId) as ITextChannel;
                    if (channel != null)
                    {
                        User user = (channel as Channel).GetUser(data.UserId);
                        if (user != null)
                        {
                            if (Config.LogLevel == LogSeverity.Debug)
                            {
                                Logger.Debug($"TYPING_START: {user.ToString(channel)}");
                            }
                            OnUserIsTypingUpdated(channel, user);
                            user.UpdateActivity();
                        }
                    }
                }
                break;

                //Voice
                case "VOICE_STATE_UPDATE":
                {
                    var data   = e.Payload.ToObject <VoiceStateUpdateEvent>(Serializer);
                    var server = GetServer(data.GuildId);
                    if (server != null)
                    {
                        var user = server.GetUser(data.UserId);
                        if (user != null)
                        {
                            if (Config.LogLevel == LogSeverity.Debug)
                            {
                                Logger.Debug($"VOICE_STATE_UPDATE: {user}");
                            }
                            var before = Config.EnablePreUpdateEvents ? user.Clone() : null;
                            user.Update(data);
                            //Logger.Verbose($"Voice Updated: {server.Name}/{user.Name}");
                            OnUserUpdated(before, user);
                        }

                        /*else //Occurs when a user leaves a server
                         *  Logger.Warning("VOICE_STATE_UPDATE referenced an unknown user.");*/
                    }
                    else
                    {
                        Logger.Warning("VOICE_STATE_UPDATE referenced an unknown server.");
                    }
                }
                break;

                //Settings
                case "USER_UPDATE":
                {
                    var data = e.Payload.ToObject <UserUpdateEvent>(Serializer);
                    if (data.Id == CurrentUser.Id)
                    {
                        var before = Config.EnablePreUpdateEvents ? CurrentUser.Clone() : null;
                        CurrentUser.Update(data);
                        PrivateUser.Update(data);
                        foreach (var server in _servers)
                        {
                            server.Value.CurrentUser.Update(data);
                        }
                        Logger.Info($"USER_UPDATE");
                        OnProfileUpdated(before, CurrentUser);
                    }
                }
                break;

                //Handled in GatewaySocket
                case "RESUMED":
                    break;

                //Ignored
                case "USER_SETTINGS_UPDATE":
                case "GUILD_INTEGRATIONS_UPDATE":
                case "VOICE_SERVER_UPDATE":
                case "GUILD_EMOJIS_UPDATE":
                case "MESSAGE_ACK":
                    Logger.Debug($"{e.Type} [Ignored]");
                    break;

                //Others
                default:
                    Logger.Warning($"Unknown message type: {e.Type}");
                    break;
                }
            }
            catch (Exception ex)
            {
                Logger.Error($"Error handling {e.Type} event", ex);
            }
        }