public Login() { OnClientUpdated += (sender, e) => { User.SetClient(Client); Guilds.SetClientsInList(Client); Relationships.SetClientsInList(Client); PrivateChannels.SetClientsInList(Client); }; }
public static ChatChannel CreateChannel(Player player, ushort channelId) { if (GetChannel(player, channelId) != null) { return(null); } switch (channelId) { case Constants.ChatChannelGuild: //Guild guild = player.getGuild(); //TODO: GUILD //if (guild) //{ // ChatChannel* newChannel = new ChatChannel(channelId, guild->getName()); // guildChannels[guild->getId()] = newChannel; // return newChannel; //} break; case Constants.ChatChannelParty: //Party* party = player.getParty(); //TODO: PARTY //if (party) //{ // ChatChannel* newChannel = new ChatChannel(channelId, "Party"); // partyChannels[party] = newChannel; // return newChannel; //} break; case Constants.ChatChannelPrivate: //only 1 private channel for each premium player if (!player.IsPremium() || GetPrivateChannel(player) != null) { return(null); } //find a free private channel slot for (ushort i = 100; i < 10000; ++i) { if (!PrivateChannels.ContainsKey(i)) { PrivateChatChannel newChannel = new PrivateChatChannel(i, string.Format("{0}'s Channel", player.CharacterName)) { Owner = player.CharacterId }; PrivateChannels[i] = newChannel; return(newChannel); } } break; } return(null); }
private void Reset() { SessionId = null; if (Config.Cache) { GuildCache.Clear(); PrivateChannels.Clear(); VoiceStates.Clear(); GuildSettings.Clear(); PrivateChannelSettings.Clear(); } }
public Login() { OnClientUpdated += (sender, e) => { User.SetClient(Client); Guilds.SetClientsInList(Client); Relationships.SetClientsInList(Client); PrivateChannels.SetClientsInList(Client); }; JsonUpdated += (sender, json) => { Guilds = json.Value <JArray>("guilds").PopulateListJson <LoginGuild>(); }; }
public static ChatChannel GetChannel(Player player, ushort channelId) { switch (channelId) { case Constants.ChatChannelGuild: //Guild* guild = player.getGuild(); //TODO: GUILD //if (guild) { // auto it = guildChannels.find(guild->getId()); // if (it != guildChannels.end()) { // return it->second; // } //} break; case Constants.ChatChannelParty: //Party* party = player.getParty(); //TODO: PARTY //if (party) { // auto it = partyChannels.find(party); // if (it != partyChannels.end()) { // return it->second; // } //} break; default: ChatChannel channel; if (NormalChannels.TryGetValue(channelId, out channel)) { if (!channel.CanJoin(player)) { return(null); } return(channel); } PrivateChatChannel pChannel; if (PrivateChannels.TryGetValue(channelId, out pChannel)) { if (pChannel.IsInvited(player.CharacterId)) { return(pChannel); } } break; } return(null); }
public static bool DeleteChannel(Player player, ushort channelId) { switch (channelId) { case Constants.ChatChannelGuild: //Guild* guild = player.getGuild(); //TODO: GUILD //if (!guild) { // return false; //} //auto it = guildChannels.find(guild->getId()); //if (it == guildChannels.end()) { // return false; //} //delete it->second; //guildChannels.erase(it); break; case Constants.ChatChannelParty: //Party* party = player.getParty(); //TODO: PARTY //if (!party) { // return false; //} //auto it = partyChannels.find(party); //if (it == partyChannels.end()) { // return false; //} //delete it->second; //partyChannels.erase(it); break; default: PrivateChatChannel channel; if (!PrivateChannels.TryGetValue(channelId, out channel)) { return(false); } channel.CloseChannel(); return(PrivateChannels.Remove(channelId)); } return(true); }
private void Reset() { SessionId = null; foreach (var voiceSession in VoiceSessions) { voiceSession.Session.Disconnect(); } VoiceSessions.Clear(); if (Config.Cache) { GuildCache.Clear(); PrivateChannels.Clear(); PrivateVoiceStates.Clear(); ClientGuildSettings.Clear(); } }
internal Login() { OnClientUpdated += (sender, e) => { User.SetClient(Client); Guilds.SetClientsInList(Client); Relationships.SetClientsInList(Client); Settings.SetClient(Client); PrivateChannels.SetClientsInList(Client); ClientGuildSettings.SetClientsInList(Client); ConnectedAccounts.SetClientsInList(Client); Presences.SetClientsInList(Client); }; JsonUpdated += (sender, json) => { Guilds = json.Value <JArray>("guilds").PopulateListJson <LoginGuild>(); PrivateChannels = json.Value <JArray>("private_channels").PopulateListJson <PrivateChannel>(); Presences = json.Value <JArray>("presences").PopulateListJson <DiscordPresence>(); }; }
internal LoginEventArgs() { OnClientUpdated += (sender, e) => { User.SetClient(Client); Relationships.SetClientsInList(Client); Settings.SetClient(Client); PrivateChannels.SetClientsInList(Client); ClientGuildSettings.SetClientsInList(Client); ConnectedAccounts.SetClientsInList(Client); Presences.SetClientsInList(Client); List <MinimalGuild> guilds = new List <MinimalGuild>(); foreach (var obj in _guilds) { guilds.Add((Client.User.Type == DiscordUserType.User ? obj.ToObject <SocketGuild>() : obj.ToObject <MinimalGuild>()).SetClient(Client)); } Guilds = guilds; }; }
private static async Task OnSocketMessage(string json) { try { Logger.Log("Received Message: " + json); PayloadModel payload = JsonConvert.DeserializeObject <PayloadModel>(json, JsonSettings); if (payload.Sequence.HasValue) { sequence = payload.Sequence.Value; } switch (payload.Op) { case 0: // Event Dispatch break; case 1: // Heartbeat await Send(JsonConvert.SerializeObject(new PayloadModel <int> { Op = 1, Data = sequence }, JsonSettings)); Logger.Log("Heartbeat"); break; case 7: // Reconnect Logger.Log("Reconnect Requested"); await Resume(); break; case 9: // Invalid Session Logger.LogError("Received invalid session from discord."); var resume = payload.As <bool>().Data; if (resume) { await Resume(); } else { Stop(); } break; case 10: // Hello acked = true; var heartbeat = payload.As <HeartbeatModel>().Data; Heartbeat(heartbeat.HeartbeatInterval); break; case 11: // Heartbeat Ack Logger.Log("Heatbeat Ack"); acked = true; break; } if (!string.IsNullOrEmpty(payload.Event)) { switch (payload.Event.ToLower()) { // ------ API ------ case "ready": { Logger.Log("Ready."); var ready = payload.As <ReadyModel>().Data; Sync(() => { Version = ready.Version; session = ready.SessionId; User = new DiscordUser(ready.User); foreach (var channel in ready.PrivateChannels) { PrivateChannels[channel.Id] = new DiscordChannel(channel); } foreach (var guild in ready.Guilds) { Servers[guild.Id] = new DiscordServer(guild); } startTask.SetResult(true); interfaces.OnDiscordAPIOpen(); }); } break; case "resumed": { Logger.Log("Resumed."); Sync(() => interfaces.OnDiscordAPIResumed()); } break; case "reonnect": { Logger.Log("Reconnect."); await Resume(); } break; // ------ Channels ------ case "channel_create": { var channel = payload.As <ChannelModel>().Data; Sync(() => { if (!string.IsNullOrEmpty(channel.GuildId)) { Servers[channel.GuildId].Channels[channel.Id] = new DiscordChannel(channel); interfaces.OnChannelCreated(Servers[channel.GuildId].Channels[channel.Id]); } else { PrivateChannels[channel.Id] = new DiscordChannel(channel); interfaces.OnChannelCreated(Servers[channel.GuildId].Channels[channel.Id]); } }); } break; case "channel_update": { var channel = payload.As <ChannelModel>().Data; Sync(() => { if (!string.IsNullOrEmpty(channel.GuildId)) { Servers[channel.GuildId].Channels[channel.Id] = new DiscordChannel(channel); interfaces.OnChannelUpdated(Servers[channel.GuildId].Channels[channel.Id]); } else { PrivateChannels[channel.Id] = new DiscordChannel(channel); interfaces.OnChannelUpdated(Servers[channel.GuildId].Channels[channel.Id]); } }); } break; case "channel_delete": { var channel = payload.As <ChannelModel>().Data; Sync(() => { if (!string.IsNullOrEmpty(channel.GuildId)) { interfaces.OnChannelDeleted(Servers[channel.GuildId].Channels[channel.Id]); Servers[channel.GuildId].Channels.Remove(channel.Id); } else { interfaces.OnChannelDeleted(Servers[channel.GuildId].Channels[channel.Id]); PrivateChannels.Remove(channel.Id); } }); } break; case "channel_pins_update": { var channelPin = payload.As <ChannelPinsModel>().Data; Sync(() => { if (!string.IsNullOrEmpty(channelPin.GuildId)) { Servers[channelPin.GuildId].Channels[channelPin.ChannelId].LastPinTimestamp = channelPin.LastPinTimestamp; interfaces.OnChannelPinsUpdated(Servers[channelPin.GuildId].Channels[channelPin.ChannelId], channelPin.LastPinTimestamp); } else { PrivateChannels[channelPin.ChannelId].LastPinTimestamp = channelPin.LastPinTimestamp; interfaces.OnChannelPinsUpdated(Servers[channelPin.GuildId].Channels[channelPin.ChannelId], channelPin.LastPinTimestamp); } }); } break; // ------ Servers ------ case "guild_create": { var guild = payload.As <GuildModel>().Data; Sync(() => { Servers[guild.Id] = new DiscordServer(guild); interfaces.OnServerJoined(Servers[guild.Id]); }); } break; case "guild_update": { var guild = payload.As <GuildModel>().Data; Sync(() => { Servers[guild.Id] = new DiscordServer(guild); interfaces.OnServerUpdated(Servers[guild.Id]); }); } break; case "guild_delete": { var guild = payload.As <GuildModel>().Data; Sync(() => { interfaces.OnServerLeft(Servers[guild.Id]); Servers.Remove(guild.Id); }); } break; case "guild_ban_add": { var guildBan = payload.As <GuildBanModel>().Data; Sync(() => { Servers[guildBan.GuildId].Bans[guildBan.User.Id] = new DiscordUser(guildBan.User); interfaces.OnServerBan(Servers[guildBan.GuildId], Servers[guildBan.GuildId].Bans[guildBan.User.Id]); }); } break; case "guild_ban_remove": { var guildBan = payload.As <GuildBanModel>().Data; Sync(() => { interfaces.OnServerUnban(Servers[guildBan.GuildId], Servers[guildBan.GuildId].Bans[guildBan.User.Id]); Servers[guildBan.GuildId].Bans.Remove(guildBan.User.Id); }); } break; case "guild_emojis_update": { var guildEmojis = payload.As <GuildEmojisModel>().Data; Servers[guildEmojis.GuildId].Emojis = guildEmojis.Emojis.ToDictionary(x => x.Id, x => new DiscordEmoji(x)); } break; case "guild_member_add": { var guildMember = payload.As <GuildMemberModel>().Data; Servers[guildMember.GuildId].Members[guildMember.User.Id] = new DiscordServerMember(guildMember); } break; case "guild_member_remove": { var guildMember = payload.As <GuildMemberModel>().Data; Servers[guildMember.GuildId].Members.Remove(guildMember.User.Id); } break; case "guild_member_update": { var guildMember = payload.As <GuildMemberModel>().Data; Servers[guildMember.GuildId].Members[guildMember.User.Id] = new DiscordServerMember(guildMember); } break; case "guild_members_chunk": { var guildMembersChunk = payload.As <GuildMembersChunkModel>().Data; } break; case "guild_role_create": { var guildRole = payload.As <GuildRoleModel>().Data; Servers[guildRole.GuildId].Roles[guildRole.Role.Id] = new DiscordRole(guildRole.Role); } break; case "guild_role_update": { var guildRole = payload.As <GuildRoleModel>().Data; Servers[guildRole.GuildId].Roles[guildRole.Role.Id] = new DiscordRole(guildRole.Role); } break; case "guild_role_delete": { var guildRoleId = payload.As <GuildRoleIdModel>().Data; Servers[guildRoleId.GuildId].Roles.Remove(guildRoleId.RoleId); } break; // ------ Invites ------ case "invite_create": { var invite = payload.As <InviteModel>().Data; Servers[invite.GuildId].Invites[invite.Code] = new DiscordInvite(invite); } break; case "invite_delete": { var invite = payload.As <InviteModel>().Data; Servers[invite.GuildId].Invites.Remove(invite.Code); } break; // ------ Messages ------ case "message_create": { var message = payload.As <MessageModel>().Data; Sync(() => { interfaces.OnMessageCreated(new DiscordMessage(message)); }); } break; case "message_update": { var message = payload.As <MessageModel>().Data; Sync(() => { interfaces.OnMessageUpdated(new DiscordMessage(message)); }); } break; case "message_delete": { var message = payload.As <MessageModel>().Data; Sync(() => { interfaces.OnMessageDeleted(new DiscordMessage(message)); }); } break; case "message_delete_bulk": { var messageBulk = payload.As <MessageBulkModel>().Data; } break; case "message_reaction_add": { var messageReaction = payload.As <MessageReactionModel>().Data; } break; case "message_reaction_remove": { var messageReaction = payload.As <MessageReactionModel>().Data; } break; case "message_reaction_remove_all": { var messageReaction = payload.As <MessageReactionModel>().Data; } break; case "message_reaction_remove_emoji": { var messageReaction = payload.As <MessageReactionModel>().Data; } break; // ------ Status ------ case "presence_update": { var presence = payload.As <PresenceModel>().Data; } break; case "typing_start": { var typing = payload.As <TypingModel>().Data; } break; case "user_update": { var user = payload.As <UserModel>().Data; } break; // ------ Voice ------ case "voice_state_update": { var voiceState = payload.As <VoiceStateModel>().Data; } break; case "voice_server_update": { var voiceServer = payload.As <VoiceServerModel>().Data; } break; // ------ Webhooks ------ case "webhooks_update": { var webhook = payload.As <ServerWebhookModel>().Data; } break; } } } catch (Exception exception) { Logger.LogError("Exception occured while processing message.", exception); } }
private void WebSocket_OnMessageReceived(object sender, DiscordWebSocketMessage <GatewayOpcode> message) { Sequence = message.Sequence; switch (message.Opcode) { case GatewayOpcode.Event: /* * Console.WriteLine(message.EventName); * * File.AppendAllText("Debug.log", $"{message.EventName}: {message.Data}\n"); */ switch (message.EventName) { case "READY": LoginEventArgs login = message.Data.ToObject <LoginEventArgs>().SetClient(this); if (login.Application != null) { _appId = login.Application.Value <ulong>("id"); } this.User = login.User; this.UserSettings = login.Settings; this.SessionId = login.SessionId; if (Config.Cache && this.User.Type == DiscordUserType.User) { PrivateChannels.AddRange(login.PrivateChannels); foreach (var presence in login.Presences) { Presences[presence.UserId] = presence; } foreach (var guild in login.Guilds) { ApplyGuild(GuildCache[guild.Id] = (SocketGuild)guild); VoiceClients[guild.Id] = new DiscordVoiceClient(this, guild.Id); } foreach (var settings in login.ClientGuildSettings) { if (settings.GuildId.HasValue) { GuildSettings.Add(settings.Guild.Id, settings); } else { PrivateChannelSettings = settings.ChannelOverrides.ToList(); } } } LoggedIn = true; State = GatewayConnectionState.Connected; if (OnLoggedIn != null) { Task.Run(() => OnLoggedIn.Invoke(this, login)); } break; case "USER_SETTINGS_UPDATE": if (UserSettings == null) { } UserSettings.Update((JObject)message.Data); if (OnSettingsUpdated != null) { Task.Run(() => OnSettingsUpdated.Invoke(this, new DiscordSettingsEventArgs(UserSettings))); } break; case "USER_GUILD_SETTINGS_UPDATE": if (Config.Cache) { ClientGuildSettings settings = message.Data.ToObject <ClientGuildSettings>(); if (settings.GuildId.HasValue) { GuildSettings[settings.Guild.Id] = settings; } else { PrivateChannelSettings = settings.ChannelOverrides.ToList(); } } break; case "USER_UPDATE": DiscordUser user = message.Data.ToObject <DiscordUser>().SetClient(this); if (user.Id == User.Id) { User.Update(user); } if (Config.Cache) { lock (PrivateChannels.Lock) { foreach (var dm in PrivateChannels) { foreach (var recipient in dm.Recipients) { if (recipient.Id == user.Id) { recipient.Update(user); break; } } } } } if (OnUserUpdated != null) { Task.Run(() => OnUserUpdated.Invoke(this, new UserEventArgs(user))); } break; case "GUILD_MEMBER_LIST_UPDATE": OnMemberListUpdate?.Invoke(this, message.Data.ToObject <GuildMemberListEventArgs>()); break; case "GUILD_CREATE": if (Config.Cache || OnJoinedGuild != null) { var guild = message.Data.ToObject <SocketGuild>().SetClient(this); VoiceClients[guild.Id] = new DiscordVoiceClient(this, guild.Id); if (Config.Cache) { ApplyGuild(GuildCache[guild.Id] = guild); } if (OnJoinedGuild != null) { Task.Run(() => OnJoinedGuild.Invoke(this, new SocketGuildEventArgs(guild, Lurking.HasValue && Lurking.Value == guild.Id))); } } break; case "GUILD_UPDATE": if (Config.Cache || OnGuildUpdated != null) { DiscordGuild guild = message.Data.ToObject <DiscordGuild>().SetClient(this); if (Config.Cache) { GuildCache[guild.Id].Update(guild); } Task.Run(() => OnGuildUpdated?.Invoke(this, new GuildEventArgs(guild))); } break; case "GUILD_DELETE": { UnavailableGuild guild = message.Data.ToObject <UnavailableGuild>(); VoiceClients.Remove(guild.Id); if (Lurking.HasValue && Lurking.Value == guild.Id) { Lurking = null; } if (Config.Cache) { if (guild.Unavailable) { GuildCache[guild.Id].Unavailable = true; } else { GuildCache.Remove(guild.Id); GuildSettings.Remove(guild.Id); } } if (OnLeftGuild != null) { Task.Run(() => OnLeftGuild.Invoke(this, new GuildUnavailableEventArgs(guild))); } } break; case "GUILD_MEMBER_ADD": if (Config.Cache || OnUserJoinedGuild != null) { var member = message.Data.ToObject <GuildMember>().SetClient(this); if (Config.Cache) { GuildCache[member.GuildId].MemberCount++; } Task.Run(() => OnUserJoinedGuild?.Invoke(this, new GuildMemberEventArgs(member))); } break; case "GUILD_MEMBER_REMOVE": if (Config.Cache || OnUserLeftGuild != null) { var member = message.Data.ToObject <PartialGuildMember>().SetClient(this); if (Config.Cache) { GuildCache[member.GuildId].MemberCount--; } Task.Run(() => OnUserLeftGuild?.Invoke(this, new MemberRemovedEventArgs(member))); } break; case "GUILD_MEMBER_UPDATE": if (Config.Cache || OnGuildMemberUpdated != null) { GuildMember member = message.Data.ToObject <GuildMember>().SetClient(this); if (Config.Cache && member.User.Id == User.Id) { SocketGuild guild = this.GetCachedGuild(member.GuildId); // Discord doesn't send us the user's JoinedAt on updates member.JoinedAt = guild.ClientMember.JoinedAt; ClientMembers[guild.Id] = member; break; } Task.Run(() => OnGuildMemberUpdated.Invoke(this, new GuildMemberEventArgs(member))); } break; case "GUILD_MEMBERS_CHUNK": Task.Run(() => OnGuildMembersReceived?.Invoke(this, new GuildMembersEventArgs(message.Data.ToObject <GuildMemberList>().SetClient(this)))); break; case "GIFT_CODE_CREATE": if (OnGiftCodeCreated != null) { Task.Run(() => OnGiftCodeCreated.Invoke(this, message.Data.ToObject <GiftCodeCreatedEventArgs>())); } break; case "GIFT_CODE_UPDATE": if (OnGiftUpdated != null) { var gift = message.Data.ToObject <GiftCodeUpdatedEventArgs>().SetClient(this); gift.Json = (JObject)message.Data; Task.Run(() => OnGiftUpdated.Invoke(this, gift)); } break; case "PRESENCE_UPDATE": if (Config.Cache || OnUserPresenceUpdated != null) { var presence = message.Data.ToObject <DiscordPresence>().SetClient(this); if (Config.Cache) { if (Presences.TryGetValue(presence.UserId, out DiscordPresence existingPresence)) { existingPresence.Update(presence); presence = existingPresence; } else { Presences[presence.UserId] = presence; } } if (OnUserPresenceUpdated != null) { Task.Run(() => OnUserPresenceUpdated.Invoke(this, new PresenceUpdatedEventArgs(presence))); } } break; case "VOICE_STATE_UPDATE": DiscordVoiceState newState = message.Data.ToObject <DiscordVoiceState>().SetClient(this); if (Config.Cache) { if (newState.Guild == null) { VoiceStates[newState.UserId].PrivateChannelVoiceState = newState; } else { VoiceStates[newState.UserId].GuildStates[newState.Guild.Id] = newState; } // we also store voice states within SocketGuilds, so make sure to update those. foreach (var guild in this.GetCachedGuilds()) { if (!guild.Unavailable) { if (newState.Guild == null || guild.Id != newState.Guild.Id) { guild._voiceStates.RemoveFirst(s => s.UserId == newState.UserId); } else { int i = guild._voiceStates.FindIndex(s => s.UserId == newState.UserId); if (i > -1) { guild._voiceStates[i] = newState; } else { guild._voiceStates.Add(newState); } } } } } if (newState.UserId == User.Id) { if (newState.Guild == null) { VoiceClients.Private.SetSessionId(newState.SessionId); } else { VoiceClients[newState.Guild.Id].SetSessionId(newState.SessionId); } } if (OnVoiceStateUpdated != null) { Task.Run(() => OnVoiceStateUpdated.Invoke(this, new VoiceStateEventArgs(newState))); } break; case "VOICE_SERVER_UPDATE": OnMediaServer?.Invoke(this, message.Data.ToObject <DiscordMediaServer>().SetClient(this)); break; case "GUILD_ROLE_CREATE": if (Config.Cache || OnRoleCreated != null) { DiscordRole role = message.Data.ToObject <RoleUpdate>().Role.SetClient(this); if (Config.Cache) { GuildCache[role.GuildId]._roles.Add(role); } if (OnRoleCreated != null) { Task.Run(() => OnRoleCreated.Invoke(this, new RoleEventArgs(role))); } } break; case "GUILD_ROLE_UPDATE": if (Config.Cache || OnRoleUpdated != null) { DiscordRole role = message.Data.ToObject <RoleUpdate>().Role.SetClient(this); if (Config.Cache) { GuildCache[role.GuildId]._roles.ReplaceFirst(r => r.Id == role.Id, role); } if (OnRoleUpdated != null) { Task.Run(() => OnRoleUpdated.Invoke(this, new RoleEventArgs(role))); } } break; case "GUILD_ROLE_DELETE": if (Config.Cache || OnRoleDeleted != null) { DeletedRole role = message.Data.ToObject <DeletedRole>().SetClient(this); if (Config.Cache) { GuildCache[role.Guild]._roles.RemoveFirst(r => r.Id == role.Id); } if (OnRoleDeleted != null) { Task.Run(() => OnRoleDeleted.Invoke(this, new RoleDeletedEventArgs(role))); } } break; case "GUILD_EMOJIS_UPDATE": if (Config.Cache || OnEmojisUpdated != null) { var emojis = message.Data.ToObject <EmojiContainer>().SetClient(this); if (Config.Cache) { GuildCache[emojis.GuildId]._emojis = emojis.Emojis.ToList(); } if (OnEmojisUpdated != null) { Task.Run(() => OnEmojisUpdated.Invoke(this, new EmojisUpdatedEventArgs(emojis))); } } break; case "CHANNEL_CREATE": if (Config.Cache || OnChannelCreated != null) { var channel = ((JObject)message.Data).ParseDeterministic <DiscordChannel>(); if (Config.Cache) { if (channel.Type == ChannelType.DM || channel.Type == ChannelType.Group) { PrivateChannels.Add((PrivateChannel)channel); } else { GuildChannel guildChannel = (GuildChannel)channel; GuildCache[guildChannel.GuildId].ChannelsConcurrent.Add(guildChannel); } } if (OnChannelCreated != null) { Task.Run(() => OnChannelCreated.Invoke(this, new ChannelEventArgs(channel))); } } break; case "CHANNEL_UPDATE": if (Config.Cache || OnChannelUpdated != null) { var channel = ((JObject)message.Data).ParseDeterministic <DiscordChannel>(); if (Config.Cache) { if (channel.Type == ChannelType.DM || channel.Type == ChannelType.Group) { PrivateChannels.ReplaceFirst(c => c.Id == channel.Id, (PrivateChannel)channel); } else { GuildChannel guildChannel = (GuildChannel)channel; GuildCache[guildChannel.GuildId].ChannelsConcurrent.ReplaceFirst(c => c.Id == guildChannel.Id, guildChannel); } } if (OnChannelUpdated != null) { Task.Run(() => OnChannelUpdated.Invoke(this, new ChannelEventArgs(channel))); } } break; case "CHANNEL_DELETE": if (Config.Cache || OnChannelDeleted != null) { var channel = ((JObject)message.Data).ParseDeterministic <DiscordChannel>(); if (Config.Cache) { if (channel.Type == ChannelType.DM || channel.Type == ChannelType.Group) { PrivateChannels.RemoveFirst(c => c.Id == channel.Id); } else { GuildCache[((GuildChannel)channel).GuildId].ChannelsConcurrent.RemoveFirst(c => c.Id == channel.Id); } } if (OnChannelDeleted != null) { Task.Run(() => OnChannelDeleted.Invoke(this, new ChannelEventArgs(channel))); } } break; case "TYPING_START": if (OnUserTyping != null) { Task.Run(() => OnUserTyping.Invoke(this, new UserTypingEventArgs(message.Data.ToObject <UserTyping>().SetClient(this)))); } break; case "MESSAGE_CREATE": if (Config.Cache || OnMessageReceived != null) { var newMessage = message.Data.ToObject <DiscordMessage>().SetClient(this); if (Config.Cache) { try { this.GetChannel(newMessage.Channel.Id).SetLastMessageId(newMessage.Id); } catch (DiscordHttpException) { } } if (OnMessageReceived != null) { Task.Run(() => OnMessageReceived.Invoke(this, new MessageEventArgs(newMessage))); } } break; case "MESSAGE_UPDATE": if (OnMessageEdited != null) { Task.Run(() => OnMessageEdited.Invoke(this, new MessageEventArgs(message.Data.ToObject <DiscordMessage>().SetClient(this)))); } break; case "MESSAGE_DELETE": if (OnMessageDeleted != null) { Task.Run(() => OnMessageDeleted.Invoke(this, new MessageDeletedEventArgs(message.Data.ToObject <DeletedMessage>().SetClient(this)))); } break; case "MESSAGE_REACTION_ADD": if (OnMessageReactionAdded != null) { Task.Run(() => OnMessageReactionAdded.Invoke(this, new ReactionEventArgs(message.Data.ToObject <MessageReactionUpdate>().SetClient(this)))); } break; case "MESSAGE_REACTION_REMOVE": if (OnMessageReactionRemoved != null) { Task.Run(() => OnMessageReactionRemoved.Invoke(this, new ReactionEventArgs(message.Data.ToObject <MessageReactionUpdate>().SetClient(this)))); } break; case "GUILD_BAN_ADD": if (OnUserBanned != null) { Task.Run(() => OnUserBanned.Invoke(this, message.Data.ToObject <BanUpdateEventArgs>().SetClient(this))); } break; case "GUILD_BAN_REMOVE": if (OnUserUnbanned != null) { Task.Run(() => OnUserUnbanned.Invoke(this, message.Data.ToObject <BanUpdateEventArgs>().SetClient(this))); } break; case "INVITE_CREATE": if (OnInviteCreated != null) { Task.Run(() => OnInviteCreated.Invoke(this, message.Data.ToObject <InviteCreatedEventArgs>().SetClient(this))); } break; case "INVITE_DELETE": if (OnInviteDeleted != null) { Task.Run(() => OnInviteDeleted.Invoke(this, message.Data.ToObject <InviteDeletedEventArgs>().SetClient(this))); } break; case "RELATIONSHIP_ADD": if (OnRelationshipAdded != null) { Task.Run(() => OnRelationshipAdded.Invoke(this, new RelationshipEventArgs(message.Data.ToObject <DiscordRelationship>().SetClient(this)))); } break; case "RELATIONSHIP_REMOVE": if (OnRelationshipRemoved != null) { Task.Run(() => OnRelationshipRemoved.Invoke(this, message.Data.ToObject <RemovedRelationshipEventArgs>())); } break; case "CHANNEL_RECIPIENT_ADD": if (Config.Cache || OnChannelRecipientAdded != null) { var recipUpdate = message.Data.ToObject <ChannelRecipientEventArgs>().SetClient(this); if (Config.Cache) { ((PrivateChannel)this.GetChannel(recipUpdate.Channel.Id))._recipients.Add(recipUpdate.User); } if (OnChannelRecipientAdded != null) { Task.Run(() => OnChannelRecipientAdded.Invoke(this, recipUpdate)); } } break; case "CHANNEL_RECIPIENT_REMOVE": if (Config.Cache || OnChannelRecipientAdded != null) { var recipUpdate = message.Data.ToObject <ChannelRecipientEventArgs>().SetClient(this); if (Config.Cache) { ((PrivateChannel)this.GetChannel(recipUpdate.Channel.Id))._recipients.RemoveFirst(u => u.Id == recipUpdate.User.Id); } if (OnChannelRecipientRemoved != null) { Task.Run(() => OnChannelRecipientRemoved.Invoke(this, recipUpdate)); } } break; case "MESSAGE_ACK": // triggered whenever another person logged into the account acknowledges a message break; case "SESSIONS_REPLACE": if (OnSessionsUpdated != null) { Task.Run(() => OnSessionsUpdated.Invoke(this, new DiscordSessionsEventArgs(message.Data.ToObject <List <DiscordSession> >()))); } break; case "CALL_CREATE": if (Config.Cache || OnRinging != null) { var call = message.Data.ToObject <DiscordCall>().SetClient(this); var voiceStates = message.Data.Value <JToken>("voice_states").ToObject <IReadOnlyList <DiscordVoiceState> >().SetClientsInList(this); if (Config.Cache) { foreach (var state in voiceStates) { VoiceStates[state.UserId].PrivateChannelVoiceState = state; } } if (OnRinging != null) { Task.Run(() => OnRinging.Invoke(this, new RingingEventArgs(call, voiceStates))); } } break; case "CALL_UPDATE": if (OnCallUpdated != null) { Task.Run(() => OnCallUpdated.Invoke(this, new CallUpdateEventArgs(message.Data.ToObject <DiscordCall>().SetClient(this)))); } break; case "CALL_DELETE": if (Config.Cache || OnCallEnded != null) { ulong channelId = message.Data.Value <ulong>("channel_id"); if (Config.Cache) { foreach (var state in VoiceStates.CreateCopy().Values) { var privState = state.PrivateChannelVoiceState; if (privState != null && privState.Channel != null && privState.Channel.Id == channelId) { state.PrivateChannelVoiceState = null; } } } if (OnCallEnded != null) { Task.Run(() => OnCallEnded.Invoke(this, new MinimalTextChannel(channelId).SetClient(this))); } } break; case "ENTITLEMENT_CREATE": if (OnEntitlementCreated != null) { Task.Run(() => OnEntitlementCreated.Invoke(this, new EntitlementEventArgs(message.Data.ToObject <DiscordEntitlement>()))); } break; case "ENTITLEMENT_UPDATE": if (OnEntitlementUpdated != null) { Task.Run(() => OnEntitlementUpdated.Invoke(this, new EntitlementEventArgs(message.Data.ToObject <DiscordEntitlement>()))); } break; case "USER_PREMIUM_GUILD_SUBSCRIPTION_SLOT_CREATE": if (OnBoostSlotCreated != null) { Task.Run(() => OnBoostSlotCreated.Invoke(this, new NitroBoostEventArgs(message.Data.ToObject <DiscordBoostSlot>().SetClient(this)))); } break; case "USER_PREMIUM_GUILD_SUBSCRIPTION_SLOT_UPDATE": if (OnBoostSlotUpdated != null) { Task.Run(() => OnBoostSlotUpdated.Invoke(this, new NitroBoostEventArgs(message.Data.ToObject <DiscordBoostSlot>().SetClient(this)))); } break; case "STREAM_SERVER_UPDATE": OnMediaServer?.Invoke(this, message.Data.ToObject <DiscordMediaServer>().SetClient(this)); break; case "STREAM_CREATE": var create = message.Data.ToObject <GoLiveCreate>(); GetVoiceClient(new StreamKey(create.StreamKey).GuildId).Livestream.CreateSession(create); break; case "STREAM_UPDATE": var update = message.Data.ToObject <GoLiveUpdate>(); GetVoiceClient(new StreamKey(update.StreamKey).GuildId).Livestream.UpdateSession(update); break; case "STREAM_DELETE": var delete = message.Data.ToObject <GoLiveDelete>(); GetVoiceClient(new StreamKey(delete.StreamKey).GuildId).Livestream.KillSession(delete); break; case "CHANNEL_UNREAD_UPDATE": if (Config.Cache || OnGuildUnreadMessagesUpdated != null) { var unread = message.Data.ToObject <GuildUnreadMessages>().SetClient(this); if (Config.Cache) { foreach (var unreadChannel in unread.Channels) { this.GetChannel(unreadChannel.Channel.Id).SetLastMessageId(unreadChannel.LastMessageId); } } if (OnGuildUnreadMessagesUpdated != null) { Task.Run(() => OnGuildUnreadMessagesUpdated.Invoke(this, new UnreadMessagesEventArgs(unread))); } } break; case "INTERACTION_CREATE": if (OnInteraction != null) { Task.Run(() => OnInteraction.Invoke(this, new DiscordInteractionEventArgs(message.Data.ToObject <DiscordInteraction>().SetClient(this)))); } break; case "USER_REQUIRED_ACTION_UPDATE": if (OnRequiredUserAction != null) { Task.Run(() => OnRequiredUserAction.Invoke(this, message.Data.ToObject <RequiredActionEventArgs>())); } break; default: break; } break; case GatewayOpcode.InvalidSession: LoggedIn = false; this.LoginToGateway(); break; case GatewayOpcode.Connected: this.LoginToGateway(); Task.Run(() => { int interval = message.Data.ToObject <JObject>().GetValue("heartbeat_interval").ToObject <int>() - 1000; try { while (true) { this.Send(GatewayOpcode.Heartbeat, this.Sequence); Thread.Sleep(interval); } } catch { } }); break; } }
private void SocketDataReceived(object sender, WebSocketSharp.MessageEventArgs result) { GatewayResponse payload = result.Data.Deserialize <GatewayResponse>(); Sequence = payload.Sequence; try { switch (payload.Opcode) { case GatewayOpcode.Event: /* * Console.WriteLine(payload.Title); * * File.AppendAllText("Debug.log", $"{payload.Title}: {payload.Data}\n"); */ switch (payload.Title) { case "READY": Login login = payload.DeserializeEx <Login>().SetClient(this); this.User = login.User; this.UserSettings = login.Settings; this.SessionId = login.SessionId; if (Config.Cache) { if (this.User.Type == DiscordUserType.User) { PrivateChannels = login.PrivateChannels; foreach (var guild in login.Guilds) { GuildCache[guild.Id] = guild.ToSocketGuild(); foreach (var state in GuildCache[guild.Id].VoiceStates) { VoiceStates[state.UserId] = state; } } foreach (var settings in login.ClientGuildSettings) { if (settings.GuildId.HasValue) { GuildSettings.Add(settings.Guild.Id, settings); } else { PrivateChannelSettings = settings.ChannelOverrides.ToList(); } } } } LoggedIn = true; Task.Run(() => OnLoggedIn?.Invoke(this, new LoginEventArgs(login))); break; case "USER_SETTINGS_UPDATE": if (UserSettings != null) // for some reason this is null sometimes :thinking: { var update = payload.Deserialize <JObject>(); foreach (var field in UserSettings.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) { foreach (var attr in field.CustomAttributes) { if (attr.AttributeType == typeof(JsonPropertyAttribute)) { string propertyName = attr.ConstructorArguments[0].Value.ToString(); if (update.ContainsKey(propertyName)) { field.SetValue(UserSettings, update.GetValue(propertyName).ToObject(field.FieldType)); } break; } } } foreach (var property in UserSettings.GetType().GetProperties()) { foreach (var attr in property.CustomAttributes) { if (attr.AttributeType == typeof(JsonPropertyAttribute)) { string propertyName = attr.ConstructorArguments[0].Value.ToString(); if (update.ContainsKey(propertyName)) { property.SetValue(UserSettings, update.GetValue(propertyName).ToObject(property.PropertyType)); } break; } } } Task.Run(() => OnSettingsUpdated?.Invoke(this, new DiscordSettingsEventArgs(UserSettings))); } break; case "USER_GUILD_SETTINGS_UPDATE": if (Config.Cache) { ClientGuildSettings settings = payload.Deserialize <ClientGuildSettings>(); if (settings.GuildId.HasValue) { GuildSettings[settings.Guild.Id] = settings; } else { PrivateChannelSettings = settings.ChannelOverrides.ToList(); } } break; case "USER_UPDATE": DiscordUser user = payload.Deserialize <DiscordUser>().SetClient(this); if (user.Id == User.Id) { User.Update(user); } if (Config.Cache) { foreach (var dm in PrivateChannels) { bool updated = false; foreach (var recipient in dm.Recipients) { if (recipient.Id == user.Id) { recipient.Update(user); updated = true; break; } } if (updated) // this is somewhat resource intensive, so let's reduce the uses as much as possible { dm.UpdateSelfJson(); } } } Task.Run(() => OnUserUpdated?.Invoke(this, new UserEventArgs(user))); break; case "GUILD_MEMBER_LIST_UPDATE": OnMemberListUpdate?.Invoke(this, payload.Deserialize <GuildMemberListEventArgs>()); break; case "GUILD_CREATE": { SocketGuild guild = payload.DeserializeEx <SocketGuild>().SetClient(this); if (Config.Cache) { GuildCache.Remove(guild.Id); GuildCache.Add(guild.Id, guild); } Task.Run(() => OnJoinedGuild?.Invoke(this, new SocketGuildEventArgs(guild, Lurking.HasValue && Lurking.Value == guild.Id))); } break; case "GUILD_UPDATE": { DiscordGuild guild = payload.Deserialize <DiscordGuild>().SetClient(this); if (Config.Cache) { GuildCache[guild].Update(guild); } Task.Run(() => OnGuildUpdated?.Invoke(this, new GuildEventArgs(guild))); } break; case "GUILD_DELETE": { UnavailableGuild guild = payload.Deserialize <UnavailableGuild>(); if (Lurking.HasValue && Lurking.Value == guild.Id) { Lurking = null; } if (Config.Cache) { if (guild.Unavailable) { GuildCache[guild.Id].Unavailable = true; } else { GuildCache.Remove(guild.Id); } GuildSettings.Remove(guild.Id); } Task.Run(() => OnLeftGuild?.Invoke(this, new GuildUnavailableEventArgs(guild))); } break; case "GUILD_MEMBER_ADD": Task.Run(() => OnUserJoinedGuild?.Invoke(this, new GuildMemberEventArgs(payload.Deserialize <GuildMember>().SetClient(this)))); break; case "GUILD_MEMBER_REMOVE": Task.Run(() => OnUserLeftGuild?.Invoke(this, new MemberRemovedEventArgs(payload.Deserialize <PartialGuildMember>().SetClient(this)))); break; case "GUILD_MEMBER_UPDATE": GuildMember member = payload.Deserialize <GuildMember>().SetClient(this); if (Config.Cache && member.User.Id == User.Id) { SocketGuild guild = this.GetCachedGuild(member.GuildId); // Discord doesn't send us the user's JoinedAt on updates member.JoinedAt = guild.Member.JoinedAt; guild.Member = member; break; } Task.Run(() => OnGuildMemberUpdated?.Invoke(this, new GuildMemberEventArgs(member))); break; case "GUILD_MEMBERS_CHUNK": Task.Run(() => OnGuildMembersReceived?.Invoke(this, new GuildMembersEventArgs(payload.Deserialize <GuildMemberList>().SetClient(this)))); break; case "GIFT_CODE_CREATE": Task.Run(() => OnGiftCodeCreated?.Invoke(this, payload.Deserialize <GiftCodeCreatedEventArgs>())); break; case "PRESENCE_UPDATE": Task.Run(() => OnUserPresenceUpdated?.Invoke(this, new PresenceUpdatedEventArgs(payload.DeserializeEx <DiscordPresence>().SetClient(this)))); break; case "VOICE_STATE_UPDATE": DiscordVoiceState newState = payload.Deserialize <DiscordVoiceState>().SetClient(this); if (Config.Cache) { // this doesn't work very well for bot accounts since those can be connected to a channel in multiple guilds at once. VoiceStates[newState.UserId] = newState; // we also store voice states within SocketGuilds, so make sure to update those. foreach (var guild in this.GetCachedGuilds()) { if (newState.Guild == null || guild.Id != newState.Guild.Id) { guild._voiceStates.RemoveAll(s => s.UserId == newState.UserId); } else { int i = guild._voiceStates.FindIndex(s => s.UserId == newState.UserId); if (i > -1) { guild._voiceStates[i] = newState; } else { guild._voiceStates.Add(newState); } } } } Task.Run(() => OnVoiceStateUpdated?.Invoke(this, new VoiceStateEventArgs(newState))); break; case "VOICE_SERVER_UPDATE": Task.Run(() => OnVoiceServer?.Invoke(this, payload.Deserialize <DiscordVoiceServer>().SetClient(this))); break; case "GUILD_ROLE_CREATE": { DiscordRole role = payload.Deserialize <RoleUpdate>().Role.SetClient(this); if (Config.Cache) { GuildCache[role.GuildId]._roles.Add(role); } Task.Run(() => OnRoleCreated?.Invoke(this, new RoleEventArgs(role))); } break; case "GUILD_ROLE_UPDATE": { DiscordRole role = payload.Deserialize <RoleUpdate>().Role.SetClient(this); if (Config.Cache) { var roles = GuildCache[role.GuildId]._roles; roles[roles.FindIndex(r => r.Id == role.Id)] = role; } Task.Run(() => OnRoleUpdated?.Invoke(this, new RoleEventArgs(role))); } break; case "GUILD_ROLE_DELETE": { DeletedRole role = payload.Deserialize <DeletedRole>().SetClient(this); if (Config.Cache) { GuildCache[role.Guild]._roles.RemoveAll(r => r.Id == role.Id); } Task.Run(() => OnRoleDeleted?.Invoke(this, new RoleDeletedEventArgs(role))); } break; case "GUILD_EMOJIS_UPDATE": var emojis = payload.Deserialize <EmojiContainer>().SetClient(this); if (Config.Cache) { GuildCache[emojis.GuildId]._emojis = emojis.Emojis.ToList(); } Task.Run(() => OnEmojisUpdated?.Invoke(this, new EmojisUpdatedEventArgs(emojis))); break; case "CHANNEL_CREATE": { var channel = payload.DeserializeEx <DiscordChannel>().SetClient(this); if (Config.Cache) { if (channel.Type == ChannelType.DM || channel.Type == ChannelType.Group) { PrivateChannels.Add(channel.ToDMChannel()); } else { GuildChannel guildChannel = channel.ToGuildChannel(); GuildCache[guildChannel.GuildId]._channels.Add(guildChannel); } } Task.Run(() => OnChannelCreated?.Invoke(this, new ChannelEventArgs(channel))); } break; case "CHANNEL_UPDATE": { var channel = payload.DeserializeEx <DiscordChannel>().SetClient(this); if (Config.Cache) { if (channel.Type == ChannelType.DM || channel.Type == ChannelType.Group) { PrivateChannels[PrivateChannels.FindIndex(c => c.Id == channel.Id)] = channel.ToDMChannel(); } else { GuildChannel guildChannel = channel.ToGuildChannel(); var channels = GuildCache[guildChannel.GuildId]._channels; channels[channels.FindIndex(c => c.Id == guildChannel.Id)] = guildChannel; } } Task.Run(() => OnChannelUpdated?.Invoke(this, new ChannelEventArgs(channel))); } break; case "CHANNEL_DELETE": { var channel = payload.DeserializeEx <DiscordChannel>().SetClient(this); if (Config.Cache) { if (channel.Type == ChannelType.DM || channel.Type == ChannelType.Group) { PrivateChannels.RemoveAll(c => c.Id == channel.Id); } else { GuildCache[channel.ToGuildChannel().GuildId]._channels.RemoveAll(c => c.Id == channel.Id); } } Task.Run(() => OnChannelDeleted?.Invoke(this, new ChannelEventArgs(channel))); } break; case "TYPING_START": Task.Run(() => OnUserTyping?.Invoke(this, new UserTypingEventArgs(payload.Deserialize <UserTyping>().SetClient(this)))); break; case "MESSAGE_CREATE": var message = payload.Deserialize <DiscordMessage>().SetClient(this); if (Config.Cache) { var channel = this.GetChannel(message.Channel.Id); channel.Json["last_message_id"] = JToken.FromObject(message.Id); } Task.Run(() => OnMessageReceived?.Invoke(this, new MessageEventArgs(message))); break; case "MESSAGE_UPDATE": Task.Run(() => OnMessageEdited?.Invoke(this, new MessageEventArgs(payload.Deserialize <DiscordMessage>().SetClient(this)))); break; case "MESSAGE_DELETE": Task.Run(() => OnMessageDeleted?.Invoke(this, new MessageDeletedEventArgs(payload.Deserialize <DeletedMessage>().SetClient(this)))); break; case "MESSAGE_REACTION_ADD": Task.Run(() => OnMessageReactionAdded?.Invoke(this, new ReactionEventArgs(payload.Deserialize <MessageReactionUpdate>().SetClient(this)))); break; case "MESSAGE_REACTION_REMOVE": Task.Run(() => OnMessageReactionRemoved?.Invoke(this, new ReactionEventArgs(payload.Deserialize <MessageReactionUpdate>().SetClient(this)))); break; case "GUILD_BAN_ADD": Task.Run(() => OnUserBanned?.Invoke(this, payload.Deserialize <BanUpdateEventArgs>().SetClient(this))); break; case "GUILD_BAN_REMOVE": Task.Run(() => OnUserUnbanned?.Invoke(this, payload.Deserialize <BanUpdateEventArgs>().SetClient(this))); break; case "INVITE_CREATE": Task.Run(() => OnInviteCreated?.Invoke(this, payload.Deserialize <InviteCreatedEventArgs>().SetClient(this))); break; case "INVITE_DELETE": Task.Run(() => OnInviteDeleted?.Invoke(this, payload.Deserialize <InviteDeletedEventArgs>().SetClient(this))); break; case "RELATIONSHIP_ADD": Task.Run(() => OnRelationshipAdded?.Invoke(this, new RelationshipEventArgs(payload.Deserialize <Relationship>().SetClient(this)))); break; case "RELATIONSHIP_REMOVE": Task.Run(() => OnRelationshipRemoved?.Invoke(this, new RelationshipEventArgs(payload.Deserialize <Relationship>().SetClient(this)))); break; case "CHANNEL_RECIPIENT_ADD": { var recipUpdate = payload.Deserialize <ChannelRecipientUpdate>().SetClient(this); if (Config.Cache) { foreach (var channel in PrivateChannels) { if (channel.Id == recipUpdate.Channel.Id) { channel._recipients.Add(recipUpdate.User); channel.UpdateSelfJson(); break; } } } } break; case "CHANNEL_RECIPIENT_REMOVE": { var recipUpdate = payload.Deserialize <ChannelRecipientUpdate>().SetClient(this); if (Config.Cache) { foreach (var channel in PrivateChannels) { if (channel.Id == recipUpdate.Channel.Id) { channel._recipients.RemoveAll(u => u.Id == recipUpdate.User.Id); channel.UpdateSelfJson(); break; } } } } break; case "MESSAGE_ACK": // triggered whenever another person logged into the account acknowledges a message break; case "SESSIONS_REPLACE": Task.Run(() => OnSessionsUpdated?.Invoke(this, new DiscordSessionsEventArgs(payload.Deserialize <List <DiscordSession> >()))); break; case "CALL_CREATE": { JObject obj = payload.Deserialize <JObject>(); var call = obj.ToObject <DiscordCall>().SetClient(this); var voiceStates = obj.Value <JToken>("voice_states").ToObject <IReadOnlyList <DiscordVoiceState> >().SetClientsInList(this); foreach (var state in voiceStates) { VoiceStates[state.UserId] = state; } Task.Run(() => OnRinging?.Invoke(this, new RingingEventArgs(call, voiceStates))); } break; case "CALL_UPDATE": Task.Run(() => OnCallUpdated?.Invoke(this, new CallUpdateEventArgs(payload.Deserialize <DiscordCall>().SetClient(this)))); break; case "CALL_DELETE": ulong channelId = payload.Deserialize <JObject>().Value <ulong>("channel_id"); foreach (var state in new Dictionary <ulong, DiscordVoiceState> .ValueCollection(VoiceStates)) { if (state.Channel != null && state.Channel.Id == channelId) { VoiceStates.Remove(state.UserId); } } Task.Run(() => OnCallEnded?.Invoke(this, channelId)); break; case "USER_PREMIUM_GUILD_SUBSCRIPTION_SLOT_UPDATE": Task.Run(() => OnBoostUpdated?.Invoke(this, new NitroBoostUpdatedEventArgs(payload.Deserialize <DiscordNitroBoost>().SetClient(this)))); break; } break; case GatewayOpcode.InvalidSession: LoggedIn = false; this.LoginToGateway(); break; case GatewayOpcode.Connected: this.LoginToGateway(); Task.Run(() => { int interval = payload.Deserialize <JObject>().GetValue("heartbeat_interval").ToObject <int>(); try { while (true) { this.Send(GatewayOpcode.Heartbeat, this.Sequence); Thread.Sleep(interval); } } catch { } }); break; } } catch { } }