/// <summary> /// <c>Dispose</c> /// </summary> /// <param name="disposing"></param> protected virtual void Dispose(bool disposing) { if (disposing) { GatewaySocket?.Dispose(); } }
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(); }
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); }
/// <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); }
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; * };*/ }
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); } }
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; } }
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; } }
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); }
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; }
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 ) ) ) } )); }
public void SendVoiceUpdate(ulong?serverId, ulong?channelId) { GatewaySocket.SendUpdateVoice(serverId, channelId, (Service.Config.Mode | AudioMode.Outgoing) == 0, (Service.Config.Mode | AudioMode.Incoming) == 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); } }