internal override async Task OnReceivedEvent(WebSocketEventEventArgs e) { try { switch (e.Type) { //Global case "READY": //Resync { base.OnReceivedEvent(e).Wait(); //This cannot be an await, or we'll get later messages before we're ready var data = e.Payload.ToObject <ReadyEvent>(_serializer); _currentUser = _users.GetOrAdd(data.User.Id, null); _currentUser.Update(data.User); foreach (var model in data.Guilds) { if (!model.Unavailable) { var server = _servers.GetOrAdd(model.Id); server.Update(model); } } foreach (var model in data.PrivateChannels) { var user = _users.GetOrAdd(model.Recipient.Id, null); user.Update(model.Recipient); var channel = _channels.GetOrAdd(model.Id, null, user.Id); channel.Update(model); } } break; //Servers case "GUILD_CREATE": { var data = e.Payload.ToObject <GuildCreateEvent>(_serializer); if (!data.Unavailable) { var server = _servers.GetOrAdd(data.Id); server.Update(data); if (data.Unavailable == false) { RaiseServerAvailable(server); } else { RaiseServerCreated(server); } } } break; case "GUILD_UPDATE": { var data = e.Payload.ToObject <GuildUpdateEvent>(_serializer); var server = _servers[data.Id]; if (server != null) { server.Update(data); RaiseServerUpdated(server); } } break; case "GUILD_DELETE": { var data = e.Payload.ToObject <GuildDeleteEvent>(_serializer); var server = _servers.TryRemove(data.Id); if (server != null) { if (data.Unavailable == true) { RaiseServerAvailable(server); } else { RaiseServerDestroyed(server); } } } break; //Channels case "CHANNEL_CREATE": { var data = e.Payload.ToObject <ChannelCreateEvent>(_serializer); Channel channel; if (data.IsPrivate) { var user = _users.GetOrAdd(data.Recipient.Id, null); user.Update(data.Recipient); channel = _channels.GetOrAdd(data.Id, null, user.Id); } else { channel = _channels.GetOrAdd(data.Id, data.GuildId, null); } channel.Update(data); RaiseChannelCreated(channel); } break; case "CHANNEL_UPDATE": { var data = e.Payload.ToObject <ChannelUpdateEvent>(_serializer); var channel = _channels[data.Id]; if (channel != null) { channel.Update(data); RaiseChannelUpdated(channel); } } break; case "CHANNEL_DELETE": { var data = e.Payload.ToObject <ChannelDeleteEvent>(_serializer); var channel = _channels.TryRemove(data.Id); if (channel != null) { RaiseChannelDestroyed(channel); } } break; //Members case "GUILD_MEMBER_ADD": { var data = e.Payload.ToObject <MemberAddEvent>(_serializer); var user = _users.GetOrAdd(data.User.Id, data.GuildId); user.Update(data); if (Config.TrackActivity) { user.UpdateActivity(); } RaiseUserAdded(user); } break; case "GUILD_MEMBER_UPDATE": { var data = e.Payload.ToObject <MemberUpdateEvent>(_serializer); var user = _users[data.User.Id, data.GuildId]; if (user != null) { user.Update(data); RaiseUserUpdated(user); } } break; case "GUILD_MEMBER_REMOVE": { var data = e.Payload.ToObject <MemberRemoveEvent>(_serializer); var user = _users.TryRemove(data.UserId, data.GuildId); if (user != null) { RaiseUserRemoved(user); } } break; case "GUILD_MEMBERS_CHUNK": { var data = e.Payload.ToObject <MembersChunkEvent>(_serializer); foreach (var memberData in data.Members) { var user = _users.GetOrAdd(memberData.User.Id, memberData.GuildId); user.Update(memberData); //RaiseUserAdded(user); } } break; //Roles case "GUILD_ROLE_CREATE": { var data = e.Payload.ToObject <RoleCreateEvent>(_serializer); var role = _roles.GetOrAdd(data.Data.Id, data.GuildId); role.Update(data.Data); var server = _servers[data.GuildId]; if (server != null) { server.AddRole(role); } RaiseRoleUpdated(role); } break; case "GUILD_ROLE_UPDATE": { var data = e.Payload.ToObject <RoleUpdateEvent>(_serializer); var role = _roles[data.Data.Id]; if (role != null) { role.Update(data.Data); RaiseRoleUpdated(role); } } break; case "GUILD_ROLE_DELETE": { var data = e.Payload.ToObject <RoleDeleteEvent>(_serializer); var role = _roles.TryRemove(data.RoleId); if (role != null) { RaiseRoleDeleted(role); var server = _servers[data.GuildId]; if (server != null) { server.RemoveRole(role); } } } break; //Bans case "GUILD_BAN_ADD": { var data = e.Payload.ToObject <BanAddEvent>(_serializer); var server = _servers[data.GuildId]; if (server != null) { server.AddBan(data.User?.Id); RaiseUserBanned(data.User?.Id, server); } } break; case "GUILD_BAN_REMOVE": { var data = e.Payload.ToObject <BanRemoveEvent>(_serializer); var server = _servers[data.GuildId]; if (server != null && server.RemoveBan(data.User?.Id)) { RaiseUserUnbanned(data.User?.Id, server); } } break; //Messages case "MESSAGE_CREATE": { var data = e.Payload.ToObject <MessageCreateEvent>(_serializer); Message msg = null; bool isAuthor = data.Author.Id == _userId; if (msg == null) { msg = _messages.GetOrAdd(data.Id, data.ChannelId, data.Author.Id); } msg.Update(data); if (Config.TrackActivity) { var channel = msg.Channel; if (channel?.IsPrivate == false) { var user = msg.User; if (user != null) { user.UpdateActivity(data.Timestamp); } } } RaiseMessageReceived(msg); if (Config.AckMessages && !isAuthor) { await _api.AckMessage(data.Id, data.ChannelId).ConfigureAwait(false); } } break; case "MESSAGE_UPDATE": { var data = e.Payload.ToObject <MessageUpdateEvent>(_serializer); var msg = _messages[data.Id]; if (msg != null) { msg.Update(data); RaiseMessageUpdated(msg); } } break; case "MESSAGE_DELETE": { var data = e.Payload.ToObject <MessageDeleteEvent>(_serializer); var msg = _messages.TryRemove(data.Id); if (msg != null) { RaiseMessageDeleted(msg); } } break; case "MESSAGE_ACK": { var data = e.Payload.ToObject <MessageAckEvent>(_serializer); var msg = GetMessage(data.MessageId); if (msg != null) { RaiseMessageReadRemotely(msg); } } break; //Statuses case "PRESENCE_UPDATE": { var data = e.Payload.ToObject <PresenceUpdateEvent>(_serializer); var user = _users.GetOrAdd(data.User.Id, data.GuildId); if (user != null) { user.Update(data); RaiseUserPresenceUpdated(user); } } break; case "TYPING_START": { var data = e.Payload.ToObject <TypingStartEvent>(_serializer); var channel = _channels[data.ChannelId]; if (channel != null) { var user = _users[data.UserId, channel.Server?.Id]; if (user != null) { if (channel != null) { RaiseUserIsTyping(user, channel); } } if (Config.TrackActivity) { if (!channel.IsPrivate) { if (user != null) { user.UpdateActivity(); } } } } } break; //Voice case "VOICE_STATE_UPDATE": { var data = e.Payload.ToObject <MemberVoiceStateUpdateEvent>(_serializer); var user = _users[data.UserId, data.GuildId]; if (user != null) { var voiceChannel = user.VoiceChannel; if (voiceChannel != null && data.ChannelId != voiceChannel.Id && user.IsSpeaking) { user.IsSpeaking = false; RaiseUserIsSpeaking(user, _channels[voiceChannel.Id], false); } user.Update(data); RaiseUserVoiceStateUpdated(user); } } break; //Settings case "USER_UPDATE": { var data = e.Payload.ToObject <UserUpdateEvent>(_serializer); var user = _globalUsers[data.Id]; if (user != null) { user.Update(data); RaiseProfileUpdated(); } } break; //Ignored case "USER_SETTINGS_UPDATE": case "GUILD_INTEGRATIONS_UPDATE": break; //Internal (handled in DataWebSocket) case "RESUMED": break; //Pass to DiscordWSClient case "VOICE_SERVER_UPDATE": await base.OnReceivedEvent(e).ConfigureAwait(false); break; //Others default: RaiseOnLog(LogMessageSeverity.Warning, LogMessageSource.DataWebSocket, $"Unknown message type: {e.Type}"); break; } } catch (Exception ex) { RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.Client, $"Error handling {e.Type} event: {ex.GetBaseException().Message}"); } }