예제 #1
0
 internal DiscordEmbedThumbnail(DiscordApiData data)
 {
     Url      = data.GetString("url");
     ProxyUrl = data.GetString("proxy_url");
     Width    = data.GetInteger("width").Value;
     Height   = data.GetInteger("height").Value;
 }
예제 #2
0
 internal DiscordAttachment(DiscordApiData data)
     : base(data)
 {
     FileName = data.GetString("filename");
     Size     = data.GetInteger("size").Value;
     Url      = data.GetString("url");
     ProxyUrl = data.GetString("proxy_url");
     Width    = data.GetInteger("width");
     Height   = data.GetInteger("height");
 }
예제 #3
0
        public void Update(DiscordApiData data)
        {
            Name            = data.GetString("name");
            Icon            = data.GetString("icon");
            Splash          = data.GetString("splash");
            RegionId        = data.GetString("region");
            AfkTimeout      = data.GetInteger("afk_timeout").Value;
            IsEmbedEnabled  = data.GetBoolean("embed_enabled") ?? false;
            OwnerId         = data.GetSnowflake("owner_id").Value;
            AfkChannelId    = data.GetSnowflake("afk_channel_id");
            EmbedChannelId  = data.GetSnowflake("embed_channel_id");
            ApplicationId   = data.GetSnowflake("application_id");
            IsWidgetEnabled = data.GetBoolean("widget_enabled") ?? false;
            WidgetChannelId = data.GetSnowflake("widget_channel_id");
            SystemChannelId = data.GetSnowflake("system_channel_id");

            ExplicitContentFilter       = (GuildExplicitContentFilterLevel)data.GetInteger("explicit_content_filter").Value;
            VerificationLevel           = (GuildVerificationLevel)data.GetInteger("verification_level").Value;
            DefaultMessageNotifications = (GuildNotificationOption)(data.GetInteger("default_message_notifications") ?? 0);
            MfaLevel = (GuildMfaLevel)data.GetInteger("mfa_level").Value;

            // Deserialize features
            IList <DiscordApiData> featuresArray = data.GetArray("features");
            List <string>          features      = new List <string>(featuresArray.Count);

            for (int i = 0; i < features.Count; i++)
            {
                features[i] = featuresArray[i].ToString();
            }

            Features = features;

            // Deserialize roles
            Roles.Clear();
            IList <DiscordApiData> rolesArray = data.GetArray("roles");

            for (int i = 0; i < rolesArray.Count; i++)
            {
                DiscordRole role = new DiscordRole(Http, Id, rolesArray[i]);
                Roles[role.Id] = role;
            }

            // Deserialize emojis
            Emojis.Clear();
            IList <DiscordApiData> emojisArray = data.GetArray("emojis");

            for (int i = 0; i < emojisArray.Count; i++)
            {
                DiscordEmoji emoji = new DiscordEmoji(emojisArray[i]);
                Emojis[emoji.Id] = emoji;
            }

            Dirty();
        }
예제 #4
0
 internal DiscordGuildMetadata(DiscordApiData data)
 {
     GuildId     = data.GetSnowflake("id").Value;
     IsLarge     = data.GetBoolean("large") ?? false;
     JoinedAt    = data.GetDateTime("joined_at").Value;
     MemberCount = data.GetInteger("member_count").Value;
 }
        void HandleDispatchPayload(DiscordApiData payload, DiscordApiData data)
        {
            sequence = payload.GetInteger("s").Value;
            string eventName = payload.GetString("t");

            OnDispatch?.Invoke(this, new DispatchEventArgs(eventName, data));
        }
예제 #6
0
        /// <summary>
        /// Returns the number of members that would be kicked from a guild prune operation.
        /// <para>Requires <see cref="DiscordPermission.KickMembers"/>.</para>
        /// </summary>
        /// <param name="days">The number of days to count prune for (1 or more).</param>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task <int> GetGuildPruneCount(Snowflake guildId, int days)
        {
            DiscordApiData data = await rest.Get($"guilds/{guildId}/prune?days={days}",
                                                 $"guilds/{guildId}/prune").ConfigureAwait(false);

            return(data.GetInteger("pruned").Value);
        }
예제 #7
0
        void HandleChannelUpdateEvent(DiscordApiData data)
        {
            Snowflake          id      = data.GetSnowflake("id").Value;
            DiscordChannelType type    = (DiscordChannelType)data.GetInteger("type");
            Snowflake          guildId = data.GetSnowflake("guild_id").Value;

            DiscordGuildChannel channel = null;

            if (type == DiscordChannelType.GuildText)
            {
                channel = new DiscordGuildTextChannel(http, data, guildId);
            }
            else if (type == DiscordChannelType.GuildVoice)
            {
                channel = new DiscordGuildVoiceChannel(http, data, guildId);
            }
            else if (type == DiscordChannelType.GuildCategory)
            {
                channel = new DiscordGuildCategoryChannel(http, data, guildId);
            }

            if (channel != null)
            {
                cache.GuildChannels[id] = channel;

                OnGuildChannelUpdated?.Invoke(this, new GuildChannelEventArgs(shard, guildId, channel));
            }
            else
            {
                log.LogWarning($"Failed to update channel {id} because the type ({type}) doesn't have an implementation!");
            }
        }
예제 #8
0
        /// <summary>
        /// Begins a member prune operation,
        /// kicking every member that has been offline for the specified number of days.
        /// <para>Requires <see cref="DiscordPermission.KickMembers"/>.</para>
        /// </summary>
        /// <param name="days">The number of days to prune (1-30).</param>
        /// <param name="includeRoles">
        /// By default, prune will not remove users with roles. You can optionally include specific
        /// roles in your prune by providing the <paramref name="includeRoles"/> parameter. Any inactive
        /// user that has a subset of the provided role(s) will be counted in the prune and users with
        /// additional roles will not.
        /// </param>
        /// <param name="computePruneCount">
        /// For large guilds it's recommended to set this to false. When false, this method will always return 0.
        /// </param>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task <int> BeginGuildPrune(
            Snowflake guildId,
            int?days = null,
            IEnumerable <Snowflake> includeRoles = null,
            bool?computePruneCount = null)
        {
            DiscordApiData requestData = new DiscordApiData(DiscordApiDataType.Container);

            if (days != null)
            {
                requestData.Set("days", days);
            }
            if (includeRoles != null)
            {
                requestData.Set("include_roles", includeRoles.Select(r => r.ToString()).ToArray());
            }
            if (computePruneCount != null)
            {
                requestData.Set("compute_prune_count", computePruneCount.Value);
            }

            DiscordApiData data = await rest.Post($"guilds/{guildId}/prune", requestData,
                                                  $"guilds/{guildId}/prune").ConfigureAwait(false);

            return(data.GetInteger("pruned") ?? 0);
        }
예제 #9
0
 internal DiscordChannelMention(DiscordApiData data)
     : base(data)
 {
     GuildId = data.GetSnowflake("guild_id").GetValueOrDefault();
     Type    = (DiscordChannelType)(data.GetInteger("type") ?? 0);
     Name    = data.GetString("name");
 }
예제 #10
0
        void HandleChannelDeleteEvent(DiscordApiData data)
        {
            Snowflake          id   = data.GetSnowflake("id").Value;
            DiscordChannelType type = (DiscordChannelType)data.GetInteger("type").Value;

            if (type == DiscordChannelType.DirectMessage)
            {
                // DM channel
                DiscordDMChannel dm;
                if (cache.DMChannels.TryRemove(id, out MutableDMChannel mutableDM))
                {
                    mutableDM.ClearReferences();

                    dm = mutableDM.ImmutableEntity;
                }
                else
                {
                    dm = new DiscordDMChannel(http, data);
                }

                OnDMChannelRemoved?.Invoke(this, new DMChannelEventArgs(shard, dm));
            }
            else if (type == DiscordChannelType.GuildText || type == DiscordChannelType.GuildVoice ||
                     type == DiscordChannelType.GuildCategory)
            {
                // Guild channel
                Snowflake guildId = data.GetSnowflake("guild_id").Value;

                DiscordGuildChannel channel;

                if (type == DiscordChannelType.GuildText)
                {
                    if (!cache.GuildChannels.TryRemove(id, out channel))
                    {
                        channel = new DiscordGuildTextChannel(http, data, guildId);
                    }
                }
                else if (type == DiscordChannelType.GuildVoice)
                {
                    if (!cache.GuildChannels.TryRemove(id, out channel))
                    {
                        channel = new DiscordGuildVoiceChannel(http, data, guildId);
                    }
                }
                else if (type == DiscordChannelType.GuildCategory)
                {
                    if (!cache.GuildChannels.TryRemove(id, out channel))
                    {
                        channel = new DiscordGuildCategoryChannel(http, data, guildId);
                    }
                }
                else
                {
                    throw new NotImplementedException($"Guild channel type \"{type}\" has no implementation!");
                }

                OnGuildChannelRemoved?.Invoke(this, new GuildChannelEventArgs(shard, guildId, channel));
            }
        }
예제 #11
0
        void HandleSpeaking(DiscordApiData payload, DiscordApiData data)
        {
            Snowflake userId     = data.GetSnowflake("user_id").Value;
            int       ssrc       = data.GetInteger("ssrc").Value;
            bool      isSpeaking = data.GetBoolean("speaking").Value;

            OnUserSpeaking?.Invoke(this, new VoiceSpeakingEventArgs(userId, ssrc, isSpeaking));
        }
예제 #12
0
        void HandleTypingStartEvent(DiscordApiData data)
        {
            Snowflake userId    = data.GetSnowflake("user_id").Value;
            Snowflake channelId = data.GetSnowflake("channel_id").Value;
            int       timestamp = data.GetInteger("timestamp").Value;

            OnTypingStarted?.Invoke(this, new TypingStartEventArgs(shard, userId, channelId, timestamp));
        }
예제 #13
0
        void HandleChannelCreateEvent(DiscordApiData data)
        {
            Snowflake          id   = data.GetSnowflake("id").Value;
            DiscordChannelType type = (DiscordChannelType)data.GetInteger("type").Value;

            if (type == DiscordChannelType.DirectMessage)
            {
                // DM channel
                DiscordApiData recipientData = data.GetArray("recipients").First();
                Snowflake      recipientId   = recipientData.GetSnowflake("id").Value;

                MutableUser recipient;
                if (!cache.Users.TryGetValue(recipientId, out recipient))
                {
                    recipient = new MutableUser(recipientId, false, http);
                    cache.Users[recipientId] = recipient;
                }

                recipient.Update(recipientData);

                MutableDMChannel mutableDMChannel;
                if (!cache.DMChannels.TryGetValue(id, out mutableDMChannel))
                {
                    mutableDMChannel     = new MutableDMChannel(id, recipient, http);
                    cache.DMChannels[id] = mutableDMChannel;
                }

                OnDMChannelCreated?.Invoke(this, new DMChannelEventArgs(shard, mutableDMChannel.ImmutableEntity));
            }
            else if (type == DiscordChannelType.GuildText || type == DiscordChannelType.GuildVoice)
            {
                // Guild channel
                Snowflake guildId = data.GetSnowflake("guild_id").Value;

                DiscordGuildChannel channel;

                if (type == DiscordChannelType.GuildText)
                {
                    channel = new DiscordGuildTextChannel(http, data, guildId);
                }
                else if (type == DiscordChannelType.GuildVoice)
                {
                    channel = new DiscordGuildVoiceChannel(http, data, guildId);
                }
                else if (type == DiscordChannelType.GuildCategory)
                {
                    channel = new DiscordGuildCategoryChannel(http, data, guildId);
                }
                else
                {
                    throw new NotImplementedException($"Guild channel type \"{type}\" has no implementation!");
                }

                cache.GuildChannels[id] = channel;

                OnGuildChannelCreated?.Invoke(this, new GuildChannelEventArgs(shard, guildId, channel));
            }
        }
예제 #14
0
        void HandleReadyPayload(DiscordApiData payload, DiscordApiData data)
        {
            int port = data.GetInteger("port").Value;
            int ssrc = data.GetInteger("ssrc").Value;

            IList <DiscordApiData> modesArray = data.GetArray("modes");

            string[] modes = new string[modesArray.Count];
            for (int i = 0; i < modes.Length; i++)
            {
                modes[i] = modesArray[i].ToString();
            }

            log.LogVerbose($"[Ready] ssrc = {ssrc}, port = {port}");

            // Notify
            ReadyQueue.Add(new VoiceReadyEventArgs(port, ssrc, modes));
        }
예제 #15
0
        /// <summary>
        /// Begins a member prune operation,
        /// kicking every member that has been offline for the specified number of days.
        /// <para>Requires <see cref="DiscordPermission.KickMembers"/>.</para>
        /// </summary>
        /// <param name="days">The number of days to prune (1 or more).</param>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task <int> BeginGuildPrune(Snowflake guildId, int days)
        {
            DiscordApiData requestData = new DiscordApiData(DiscordApiDataType.Container);

            requestData.Set("days", days);

            DiscordApiData data = await rest.Post($"guilds/{guildId}/prune", requestData,
                                                  $"guilds/{guildId}/prune").ConfigureAwait(false);

            return(data.GetInteger("pruned").Value);
        }
예제 #16
0
 internal DiscordVoiceRegion(DiscordApiData data)
 {
     Id             = data.GetString("id");
     Name           = data.GetString("name");
     SampleHostname = data.GetString("sample_hostname");
     SamplePort     = data.GetInteger("sample_port").Value;
     IsVIPOnly      = data.GetBoolean("vip").Value;
     IsOptimal      = data.GetBoolean("optimal").Value;
     IsDeprecated   = data.GetBoolean("deprecated").Value;
     IsCustom       = data.GetBoolean("custom").Value;
 }
예제 #17
0
        internal DiscordReaction(DiscordApiData data)
        {
            Count = data.GetInteger("count").Value;
            Me    = data.GetBoolean("me").Value;

            DiscordApiData emojiData = data.Get("emoji");

            if (emojiData != null)
            {
                Emoji = new DiscordReactionEmoji(emojiData);
            }
        }
예제 #18
0
        void HandleHelloPayload(DiscordApiData payload, DiscordApiData data)
        {
            int heartbeatInterval = data.GetInteger("heartbeat_interval").Value;

            // TODO: Remove when Discord's heartbeat_interval bug is fixed
            heartbeatInterval = (int)Math.Floor(heartbeatInterval * 0.75f);

            log.LogVerbose($"[Hello] heartbeat_interval = {heartbeatInterval}ms");

            HelloQueue.Add(heartbeatInterval);

            // Start heartbeat loop
            heartbeatCancellationSource = new CancellationTokenSource();
        }
예제 #19
0
        /// <summary>
        /// Returns the number of members that would be kicked from a guild prune operation.
        /// <para>Requires <see cref="DiscordPermission.KickMembers"/>.</para>
        /// </summary>
        /// <param name="days">The number of days to count prune for (1-30).</param>
        /// <param name="includeRoles">
        /// By default, prune will not remove users with roles. You can optionally include specific
        /// roles in your prune by providing the <paramref name="includeRoles"/> parameter. Any inactive
        /// user that has a subset of the provided role(s) will be counted in the prune and users with
        /// additional roles will not.
        /// </param>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task <int> GetGuildPruneCount(Snowflake guildId, int?days = null, IEnumerable <Snowflake> includeRoles = null)
        {
            UrlParametersBuilder parameters = new UrlParametersBuilder();

            if (days != null)
            {
                parameters["days"] = days.Value.ToString();
            }
            if (includeRoles != null)
            {
                parameters["include_roles"] = string.Join(",", includeRoles.Select(r => r.ToString()));
            }

            DiscordApiData data = await rest.Get($"guilds/{guildId}/prune{parameters.ToQueryString()}",
                                                 $"guilds/{guildId}/prune").ConfigureAwait(false);

            return(data.GetInteger("pruned").Value);
        }
예제 #20
0
        protected override Task OnPayloadReceived(DiscordApiData payload)
        {
            VoiceOPCode    op = (VoiceOPCode)payload.GetInteger("op").Value;
            DiscordApiData d  = payload.Get("d");

            PayloadCallback callback;

            if (payloadHandlers.TryGetValue(op, out callback))
            {
                callback(payload, d);
            }
            else
            {
                log.LogWarning($"Missing handler for payload: {op} ({(int)op})");
            }

            return(Task.CompletedTask);
        }
        async Task HandleHelloPayload(DiscordApiData payload, DiscordApiData data)
        {
            if (!receivedHello)
            {
                receivedHello = true;

                // Set heartbeat interval
                heartbeatInterval = data.GetInteger("heartbeat_interval").Value;
                log.LogVerbose($"[Hello] heartbeat_interval = {heartbeatInterval}ms");

                // Begin heartbeat loop
                heartbeatCancellationSource = new CancellationTokenSource();
                heartbeatTask = HeartbeatLoop();

                // Notify so the IDENTIFY or RESUME payloads are sent
                await OnHello?.Invoke();
            }
            else
            {
                log.LogWarning("Received more than one HELLO payload.");
            }
        }
예제 #22
0
        protected override async Task OnPayloadReceived(DiscordApiData payload)
        {
            GatewayOPCode  op   = (GatewayOPCode)payload.GetInteger("op");
            DiscordApiData data = payload.Get("d");

            PayloadCallback callback;

            if (payloadHandlers.TryGetValue(op, out callback))
            {
                if (callback.Synchronous != null)
                {
                    callback.Synchronous(payload, data);
                }
                else
                {
                    await callback.Asynchronous(payload, data).ConfigureAwait(false);
                }
            }
            else
            {
                log.LogWarning($"Missing handler for payload: {op} ({(int)op})");
            }
        }
예제 #23
0
        void HandleReadyEvent(DiscordApiData data)
        {
            // Check gateway protocol
            int protocolVersion = data.GetInteger("v").Value;

            if (protocolVersion != GATEWAY_VERSION)
            {
                log.LogError($"[Ready] Gateway protocol mismatch! Expected v{GATEWAY_VERSION}, got {protocolVersion}.");
            }

            // Check shard
            if (shard.Id != 0 || totalShards > 1)
            {
                IList <DiscordApiData> shardData = data.GetArray("shard");
                if (shardData != null)
                {
                    if (shardData.Count > 0 && shardData[0].ToInteger() != shard.Id)
                    {
                        log.LogError($"[Ready] Shard ID mismatch! Expected {shard.Id}, got {shardData[0].ToInteger()}");
                    }
                    if (shardData.Count > 1 && shardData[1].ToInteger() != totalShards)
                    {
                        log.LogError($"[Ready] Total shards mismatch! Expected {totalShards}, got {shardData[1].ToInteger()}");
                    }
                }
            }

            // Clear the cache
            cache.Clear();

            // Get the current bot's user object
            DiscordApiData userData = data.Get("user");
            Snowflake      userId   = userData.GetSnowflake("id").Value;

            MutableUser user;

            if (!cache.Users.TryGetValue(userId, out user))
            {
                user = new MutableUser(userId, false, http);
                cache.Users[userId] = user;
            }

            user.Update(userData);

            shard.UserId = userId;

            log.LogInfo($"[Ready] user = {user.Username}#{user.Discriminator}");

            // Get session ID
            sessionId = data.GetString("session_id");

            // Get unavailable guilds
            foreach (DiscordApiData unavailableGuildData in data.GetArray("guilds"))
            {
                Snowflake guildId = unavailableGuildData.GetSnowflake("id").Value;

                cache.AddGuildId(guildId);
                cache.SetGuildAvailability(guildId, false);
            }

            LogServerTrace("Ready", data);

            // Signal that the connection is ready
            handshakeCompleteEvent.Set();
        }
예제 #24
0
 internal DiscordInviteChannel(DiscordApiData data)
 {
     ChannelId = data.GetSnowflake("id").Value;
     Name      = data.GetString("name");
     Type      = (DiscordChannelType)data.GetInteger("type");
 }
예제 #25
0
 internal DiscordGame(DiscordApiData data)
 {
     Name = data.GetString("name");
     Type = (DiscordGameType)(data.GetInteger("type") ?? 0);
     Url  = data.GetString("url");
 }
예제 #26
0
        void HandleReadyEvent(DiscordApiData data)
        {
            // Check gateway protocol
            int protocolVersion = data.GetInteger("v").Value;

            if (protocolVersion != GATEWAY_VERSION)
            {
                log.LogError($"[Ready] Gateway protocol mismatch! Expected v{GATEWAY_VERSION}, got {protocolVersion}.");
            }

            // Clear the cache
            cache.Clear();

            // Get the current bot's user object
            DiscordApiData userData = data.Get("user");
            Snowflake      userId   = userData.GetSnowflake("id").Value;

            MutableUser user;

            if (!cache.Users.TryGetValue(userId, out user))
            {
                user = new MutableUser(userId, false, http);
                cache.Users[userId] = user;
            }

            user.Update(userData);

            shard.UserId = userId;

            log.LogInfo($"[Ready] user = {user.Username}#{user.Discriminator}");

            // Get session ID
            sessionId = data.GetString("session_id");

            // Get unavailable guilds
            foreach (DiscordApiData unavailableGuildData in data.GetArray("guilds"))
            {
                Snowflake guildId = unavailableGuildData.GetSnowflake("id").Value;

                cache.AddGuildId(guildId);
                cache.SetGuildAvailability(guildId, false);
            }

            // Get DM channels
            foreach (DiscordApiData dmChannelData in data.GetArray("private_channels"))
            {
                DiscordChannelType type = (DiscordChannelType)dmChannelData.GetInteger("type").Value;
                if (type != DiscordChannelType.DirectMessage)
                {
                    // TODO: Support group DMs
                    continue;
                }

                Snowflake      channelId     = dmChannelData.GetSnowflake("id").Value;
                DiscordApiData recipientData = dmChannelData.GetArray("recipients").First();
                Snowflake      recipientId   = recipientData.GetSnowflake("id").Value;

                MutableUser recipient;
                if (!cache.Users.TryGetValue(recipientId, out recipient))
                {
                    recipient = new MutableUser(recipientId, false, http);
                    cache.Users[recipientId] = recipient;
                }

                recipient.Update(recipientData);

                MutableDMChannel mutableDMChannel;
                if (!cache.DMChannels.TryGetValue(channelId, out mutableDMChannel))
                {
                    mutableDMChannel            = new MutableDMChannel(channelId, recipient, http);
                    cache.DMChannels[channelId] = mutableDMChannel;
                }

                mutableDMChannel.Update(dmChannelData);
            }

            LogServerTrace("Ready", data);

            // Signal that the connection is ready
            handshakeCompleteEvent.Set();
        }
예제 #27
0
 public GatewayBotResponse(DiscordApiData data)
 {
     Url    = data.GetString("url");
     Shards = data.GetInteger("shards").Value;
 }
예제 #28
0
 internal DiscordMessageActivity(DiscordApiData data)
 {
     Type    = (DiscordMessageActivityType)(data.GetInteger("type") ?? 0);
     PartyId = data.GetString("party_id");
 }
예제 #29
0
 internal DiscordEmbedVideo(DiscordApiData data)
 {
     Url    = data.GetString("url");
     Width  = data.GetInteger("width").Value;
     Height = data.GetInteger("height").Value;
 }
예제 #30
0
        void HandleGuildCreateEvent(DiscordApiData data)
        {
            Snowflake guildId = data.GetSnowflake("id").Value;

            bool wasUnavailable = !cache.IsGuildAvailable(guildId);

            // Update guild
            MutableGuild mutableGuild;

            if (!cache.Guilds.TryGetValue(guildId, out mutableGuild))
            {
                mutableGuild          = new MutableGuild(guildId, http);
                cache.Guilds[guildId] = mutableGuild;
            }

            mutableGuild.Update(data);

            // Ensure the cache guildId list contains this guild (it uses a hashset so don't worry about duplicates).
            cache.AddGuildId(guildId);

            // GUILD_CREATE specifics
            // Update metadata
            cache.GuildMetadata[guildId] = new DiscordGuildMetadata(data);

            // Deserialize members
            cache.GuildMembers.Clear(guildId);
            IList <DiscordApiData> membersArray = data.GetArray("members");

            for (int i = 0; i < membersArray.Count; i++)
            {
                DiscordApiData memberData = membersArray[i];

                DiscordApiData userData = memberData.Get("user");
                Snowflake      userId   = userData.GetSnowflake("id").Value;

                MutableUser user;
                if (!cache.Users.TryGetValue(userId, out user))
                {
                    user = new MutableUser(userId, false, http);
                    cache.Users[userId] = user;
                }

                user.Update(userData);

                MutableGuildMember member;
                if (!cache.GuildMembers.TryGetValue(guildId, userId, out member))
                {
                    member = new MutableGuildMember(user, guildId, http);
                    cache.GuildMembers[guildId, userId] = member;
                }

                member.Update(memberData);
            }

            // Deserialize channels
            cache.ClearGuildChannels(guildId);
            IList <DiscordApiData> channelsArray = data.GetArray("channels");

            for (int i = 0; i < channelsArray.Count; i++)
            {
                DiscordApiData     channelData = channelsArray[i];
                DiscordChannelType channelType = (DiscordChannelType)channelData.GetInteger("type");

                DiscordGuildChannel channel = null;
                if (channelType == DiscordChannelType.GuildText)
                {
                    channel = new DiscordGuildTextChannel(http, channelData, guildId);
                }
                else if (channelType == DiscordChannelType.GuildVoice)
                {
                    channel = new DiscordGuildVoiceChannel(http, channelData, guildId);
                }
                else if (channelType == DiscordChannelType.GuildCategory)
                {
                    channel = new DiscordGuildCategoryChannel(http, channelData, guildId);
                }

                if (channel != null)
                {
                    cache.AddGuildChannel(channel);
                }
            }

            // Deserialize voice states
            cache.GuildVoiceStates.Clear(guildId);
            IList <DiscordApiData> voiceStatesArray = data.GetArray("voice_states");

            for (int i = 0; i < voiceStatesArray.Count; i++)
            {
                DiscordVoiceState state = new DiscordVoiceState(guildId, voiceStatesArray[i]);
                UpdateMemberVoiceState(state);
            }

            // Deserialize presences
            cache.GuildPresences.Clear(guildId);
            IList <DiscordApiData> presencesArray = data.GetArray("presences");

            for (int i = 0; i < presencesArray.Count; i++)
            {
                // Presence's in GUILD_CREATE do not contain full user objects,
                // so don't attempt to update them here.

                DiscordApiData presenceData = presencesArray[i];
                Snowflake      userId       = presenceData.LocateSnowflake("user.id").Value;

                cache.GuildPresences[guildId, userId] = new DiscordUserPresence(userId, presenceData);
            }

            // Mark the guild as available
            cache.SetGuildAvailability(guildId, true);

            // Fire event
            if (wasUnavailable)
            {
                OnGuildAvailable?.Invoke(this, new GuildEventArgs(shard, mutableGuild.ImmutableEntity));
            }
            else
            {
                OnGuildCreated?.Invoke(this, new GuildEventArgs(shard, mutableGuild.ImmutableEntity));
            }
        }