/// <summary>
        /// Modifies an existing webhook.
        /// </summary>
        /// <exception cref="ArgumentException">Thrown if the token is empty or only contains whitespace characters.</exception>
        /// <exception cref="ArgumentNullException">Thrown if token is null.</exception>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task ModifyWebhookWithToken(Snowflake webhookId, string token,
                                                 string name = null, DiscordImageData avatar = null)
        {
            if (token == null)
            {
                throw new ArgumentNullException(nameof(token));
            }
            if (string.IsNullOrWhiteSpace(token))
            {
                throw new ArgumentException("Token cannot be empty or only contain whitespace characters.", nameof(token));
            }

            DiscordApiData postData = DiscordApiData.CreateContainer();

            if (name != null)
            {
                postData.Set("name", name);
            }
            if (avatar != null)
            {
                postData.Set("avatar", avatar);
            }

            await rest.Patch($"webhooks/{webhookId}/{token}", postData,
                             $"webhooks/{webhookId}/token").ConfigureAwait(false);
        }
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.Set("content", Content);
            data.Set("tts", TextToSpeech);
            data.SetSnowflake("nonce", Nonce);

            if (Embed != null)
            {
                data.Set("embed", Embed.Build());
            }

            if (AllowedMentions != null)
            {
                data.Set("allowed_mentions", AllowedMentions.Build());
            }

            if (MessageReference != null)
            {
                data.Set("message_reference", MessageReference.Build());
            }

            return(data);
        }
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            if (Name != null)
            {
                data.Set("name", Name);
            }
            if (Position.HasValue)
            {
                data.Set("position", Position.Value);
            }

            if (PermissionOverwrites != null)
            {
                DiscordApiData permissionOverwritesArray = new DiscordApiData(DiscordApiDataType.Array);
                foreach (OverwriteOptions overwriteParam in PermissionOverwrites)
                {
                    permissionOverwritesArray.Values.Add(overwriteParam.Build());
                }

                data.Set("permission_overwrites", permissionOverwritesArray);
            }

            return(data);
        }
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            if (Nickname != null)
            {
                data.Set("nick", Nickname);
            }
            if (IsServerMute.HasValue)
            {
                data.Set("mute", IsServerMute);
            }
            if (IsServerDeaf.HasValue)
            {
                data.Set("deaf", IsServerDeaf);
            }
            if (ChannelId.HasValue)
            {
                data.SetSnowflake("channel_id", ChannelId);
            }

            if (RoleIds != null)
            {
                DiscordApiData rolesArray = new DiscordApiData(DiscordApiDataType.Array);
                foreach (Snowflake roleId in RoleIds)
                {
                    rolesArray.Values.Add(new DiscordApiData(roleId));
                }

                data.Set("roles", rolesArray);
            }

            return(data);
        }
Example #5
0
        /// <summary>
        /// Edits an existing message in a text channel.
        /// <para>Note: only messages created by the current bot can be edited.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task <DiscordMessage> EditMessage(Snowflake channelId, Snowflake messageId, EditMessageOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            DiscordApiData requestData = new DiscordApiData(DiscordApiDataType.Container);

            requestData.Set("content", options.Content);

            if (options.Embed != null)
            {
                requestData.Set("embed", options.Embed.Build());
            }

            if (options.AllowedMentions != null)
            {
                requestData.Set("allowed_mentions", options.AllowedMentions.Build());
            }

            if (options.Flags != null)
            {
                requestData.Set("flags", (int)options.Flags);
            }

            DiscordApiData returnData = await rest.Patch($"channels/{channelId}/messages/{messageId}", requestData,
                                                         $"channels/{channelId}/messages/message").ConfigureAwait(false);

            return(new DiscordMessage(this, returnData));
        }
Example #6
0
        /// <summary>
        /// Creates a new invite for the specified guild channel.
        /// <para>Requires <see cref="DiscordPermission.CreateInstantInvite"/>.</para>
        /// </summary>
        /// <param name="channelId">The ID of the guild channel.</param>
        /// <param name="maxAge">The duration of invite before expiry, or 0 or null for never.</param>
        /// <param name="maxUses">The max number of uses or 0 or null for unlimited.</param>
        /// <param name="temporary">Whether this invite only grants temporary membership.</param>
        /// <param name="unique">
        /// If true, don't try to reuse a similar invite
        /// (useful for creating many unique one time use invites).
        /// </param>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task <DiscordInvite> CreateChannelInvite(Snowflake channelId,
                                                              TimeSpan?maxAge = null, int?maxUses = null, bool?temporary = null, bool?unique = null)
        {
            DiscordApiData requestData = new DiscordApiData(DiscordApiDataType.Container);

            if (maxAge.HasValue)
            {
                requestData.Set("max_age", maxAge.Value.Seconds);
            }
            if (maxUses.HasValue)
            {
                requestData.Set("max_uses", maxUses.Value);
            }
            if (temporary.HasValue)
            {
                requestData.Set("temporary", temporary.Value);
            }
            if (unique.HasValue)
            {
                requestData.Set("unique", unique.Value);
            }

            DiscordApiData returnData = await rest.Post($"channels/{channelId}/invites", requestData,
                                                        $"channels/{channelId}/invites").ConfigureAwait(false);

            return(new DiscordInvite(this, returnData));
        }
Example #7
0
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            if (Name != null)
            {
                data.Set("name", Name);
            }
            if (Permissions.HasValue)
            {
                data.Set("permissions", (long)Permissions.Value);
            }
            if (Color.HasValue)
            {
                data.Set("color", Color.Value.ToHexadecimal());
            }
            if (IsHoisted.HasValue)
            {
                data.Set("hoist", IsHoisted);
            }
            if (IsMentionable.HasValue)
            {
                data.Set("mentionable", IsMentionable);
            }

            return(data);
        }
        /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception>
        public async Task SendIdentifyPayload(string token, int largeThreshold, int shardId, int totalShards)
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.Set("token", token);
            data.Set("compress", true);
            data.Set("large_threshold", largeThreshold);

            if (totalShards > 1)
            {
                DiscordApiData shardData = new DiscordApiData(DiscordApiDataType.Array);
                shardData.Values.Add(new DiscordApiData(shardId));
                shardData.Values.Add(new DiscordApiData(totalShards));
                data.Set("shard", shardData);
            }

            DiscordApiData props = data.Set("properties", new DiscordApiData(DiscordApiDataType.Container));

            props.Set("$os", RuntimeInformation.OSDescription);
            props.Set("$browser", "discore");
            props.Set("$device", "discore");

            log.LogVerbose("[Identify] Sending payload...");

            // Make sure we don't send IDENTIFY's too quickly
            await identifyRateLimiter.Invoke(CancellationToken.None).ConfigureAwait(false);

            // Send IDENTIFY
            await SendPayload(GatewayOPCode.Identify, data).ConfigureAwait(false);
        }
Example #9
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);
        }
Example #10
0
        internal virtual DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData();

            data.Set("name", Name);

            if (Permissions.HasValue)
            {
                data.Set("permissions", (int)Permissions.Value);
            }

            if (Color.HasValue)
            {
                data.Set("color", Color.Value.ToHexadecimal());
            }

            if (IsHoisted.HasValue)
            {
                data.Set("hoist", IsHoisted);
            }

            if (IsMentionable.HasValue)
            {
                data.Set("mentionable", IsMentionable);
            }

            return(data);
        }
Example #11
0
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            if (UsernameOverride != null)
            {
                data.Set("username", UsernameOverride);
            }
            if (AvatarUrl != null)
            {
                data.Set("avatar_url", AvatarUrl);
            }
            if (TextToSpeech.HasValue)
            {
                data.Set("tts", TextToSpeech.Value);
            }
            if (Content != null)
            {
                data.Set("content", Content);
            }

            if (Embeds != null)
            {
                DiscordApiData embedArray = new DiscordApiData(DiscordApiDataType.Array);
                foreach (EmbedOptions builder in Embeds)
                {
                    embedArray.Values.Add(builder.Build());
                }

                data.Set("embeds", embedArray);
            }

            return(data);
        }
Example #12
0
        /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception>
        /// <exception cref="JsonWriterException">Thrown if the given data cannot be serialized as JSON.</exception>
        Task SendPayload(VoiceOPCode op, DiscordApiData data)
        {
            DiscordApiData payload = new DiscordApiData();

            payload.Set("op", (int)op);
            payload.Set("d", data);

            return(SendAsync(payload));
        }
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.Set("name", Name);
            data.Set("image", Image.ToDataUriScheme());

            return(data);
        }
Example #14
0
        /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception>
        public Task SendSpeakingPayload(bool speaking, int ssrc)
        {
            DiscordApiData data = new DiscordApiData();

            data.Set("speaking", speaking);
            data.Set("delay", value: 0);
            data.Set("ssrc", ssrc);

            return(SendPayload(VoiceOPCode.Speaking, data));
        }
        /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception>
        public Task SendRequestGuildMembersPayload(Snowflake guildId, string query, int limit)
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.SetSnowflake("guild_id", guildId);
            data.Set("query", query);
            data.Set("limit", limit);

            return(SendPayload(GatewayOPCode.RequestGuildMembers, data));
        }
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.Set("expire_behavior", ExpireBehavior);
            data.Set("expire_grace_period", ExpireGracePeriod);
            data.Set("enable_emoticons", EnableEmoticons);

            return(data);
        }
Example #17
0
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.SetSnowflake("id", Id);
            data.Set("type", Type.ToString().ToLower());
            data.Set("allow", (int)Allow);
            data.Set("deny", (int)Deny);

            return(data);
        }
Example #18
0
        /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception>
        public Task SendResumePayload(Snowflake serverId, string sessionId, string token)
        {
            DiscordApiData data = new DiscordApiData();

            data.SetSnowflake("server_id", serverId);
            data.Set("session_id", sessionId);
            data.Set("token", token);

            log.LogVerbose("[Resume] Sending payload...");
            return(SendPayload(VoiceOPCode.Resume, data));
        }
        /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception>
        public Task SendVoiceStateUpdatePayload(Snowflake guildId, Snowflake?channelId, bool isMute, bool isDeaf)
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.SetSnowflake("guild_id", guildId);
            data.SetSnowflake("channel_id", channelId);
            data.Set("self_mute", isMute);
            data.Set("self_deaf", isDeaf);

            return(SendPayload(GatewayOPCode.VoiceStateUpdate, data));
        }
        /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception>
        public Task SendResumePayload(string token, string sessionId, int sequence)
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.Set("token", token);
            data.Set("session_id", sessionId);
            data.Set("seq", sequence);

            log.LogVerbose("[Resume] Sending payload...");

            return(SendPayload(GatewayOPCode.Resume, data));
        }
        /// <summary>
        /// Edits a guild channel permission overwrite for a user or role.
        /// <para>Requires <see cref="DiscordPermission.ManageRoles"/>.</para>
        /// </summary>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task EditChannelPermissions(Snowflake channelId, Snowflake overwriteId,
                                                 DiscordPermission allow, DiscordPermission deny, DiscordOverwriteType type)
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.Set("allow", (int)allow);
            data.Set("deny", (int)deny);
            data.Set("type", type.ToString().ToLower());

            await rest.Put($"channels/{channelId}/permissions/{overwriteId}", data,
                           $"channels/{channelId}/permissions/permission").ConfigureAwait(false);
        }
        /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception>
        /// <exception cref="JsonWriterException">Thrown if the given data cannot be serialized as JSON.</exception>
        async Task SendPayload(GatewayOPCode op, DiscordApiData data)
        {
            DiscordApiData payload = new DiscordApiData(DiscordApiDataType.Container);

            payload.Set("op", (int)op);
            payload.Set("d", data);

            // Check with the payload rate limiter
            await outboundPayloadRateLimiter.Invoke().ConfigureAwait(false);

            // Send payload
            await SendAsync(payload).ConfigureAwait(false);
        }
Example #23
0
        /// <exception cref="DiscordWebSocketException">Thrown if the payload fails to send because of a WebSocket error.</exception>
        /// <exception cref="InvalidOperationException">Thrown if the socket is not connected.</exception>
        public Task SendSelectProtocolPayload(string ip, int port, string encryptionMode)
        {
            DiscordApiData selectProtocol = new DiscordApiData();

            selectProtocol.Set("protocol", "udp");
            DiscordApiData data = selectProtocol.Set("data", DiscordApiData.CreateContainer());

            data.Set("address", ip);
            data.Set("port", port);
            data.Set("mode", encryptionMode);

            log.LogVerbose($"[SelectProtocol] Sending to {ip}:{port}...");
            return(SendPayload(VoiceOPCode.SelectProtocol, selectProtocol));
        }
Example #24
0
            internal DiscordApiData Build()
            {
                DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

                if (Text != null)
                {
                    data.Set("text", Text);
                }
                if (IconUrl != null)
                {
                    data.Set("icon_url", IconUrl);
                }

                return(data);
            }
Example #25
0
        /// <summary>
        /// Deletes a group of messages all at once from a text channel.
        /// This is much faster than calling DeleteMessage for each message.
        /// <para>Requires <see cref="DiscordPermission.ManageMessages"/>.</para>
        /// </summary>
        /// <param name="filterTooOldMessages">Whether to ignore deleting messages that are older than 2 weeks (this causes an API error).</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task BulkDeleteMessages(Snowflake channelId, IEnumerable <Snowflake> messageIds,
                                             bool filterTooOldMessages = true)
        {
            if (messageIds == null)
            {
                throw new ArgumentNullException(nameof(messageIds));
            }

            DiscordApiData requestData = new DiscordApiData(DiscordApiDataType.Container);
            DiscordApiData messages    = requestData.Set("messages", new DiscordApiData(DiscordApiDataType.Array));

            ulong minimumAllowedSnowflake = 0;

            if (filterTooOldMessages)
            {
                // See https://github.com/hammerandchisel/discord-api-docs/issues/208

                ulong secondsSinceUnixEpoch = (ulong)DateTimeOffset.Now.ToUnixTimeSeconds();
                minimumAllowedSnowflake = (secondsSinceUnixEpoch - 14 * 24 * 60 * 60) * 1000 - 1420070400000L << 22;
            }

            foreach (Snowflake messageId in messageIds)
            {
                if (!filterTooOldMessages && messageId.Id < minimumAllowedSnowflake)
                {
                    continue;
                }

                messages.Values.Add(new DiscordApiData(messageId));
            }

            await rest.Post($"channels/{channelId}/messages/bulk-delete", requestData,
                            $"channels/{channelId}/messages/message/delete/bulk").ConfigureAwait(false);
        }
Example #26
0
            internal DiscordApiData Build()
            {
                DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

                if (Name != null)
                {
                    data.Set("name", Name);
                }
                if (Value != null)
                {
                    data.Set("value", Value);
                }

                data.Set("inline", IsInline);

                return(data);
            }
Example #27
0
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);
            data.Set("enabled", Enabled);
            data.SetSnowflake("channel_id", ChannelId);

            return data;
        }
        /// <summary>
        /// Modifies the current bot's user object.
        /// Parameters left null will leave the properties unchanged.
        /// </summary>
        /// <exception cref="DiscordHttpApiException"></exception>
        public async Task <DiscordUser> ModifyCurrentUser(string username = null, DiscordImageData avatar = null)
        {
            DiscordApiData requestData = new DiscordApiData(DiscordApiDataType.Container);

            if (username != null)
            {
                requestData.Set("username", username);
            }
            if (avatar != null)
            {
                requestData.Set("avatar", avatar.ToDataUriScheme());
            }

            DiscordApiData returnData = await rest.Patch("users/@me", requestData, "users/@me").ConfigureAwait(false);

            return(returnData.IsNull ? null : new DiscordUser(false, returnData));
        }
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            if (Name != null)
            {
                data.Set("name", Name);
            }
            if (Position.HasValue)
            {
                data.Set("position", Position.Value);
            }
            if (Topic != null)
            {
                data.Set("topic", Topic);
            }
            if (Nsfw.HasValue)
            {
                data.Set("nsfw", Nsfw.Value);
            }

            if (ParentId.HasValue)
            {
                if (ParentId.Value == Snowflake.None)
                {
                    data.SetSnowflake("parent_id", null);
                }
                else
                {
                    data.SetSnowflake("parent_id", ParentId.Value);
                }
            }

            if (PermissionOverwrites != null)
            {
                DiscordApiData permissionOverwritesArray = new DiscordApiData(DiscordApiDataType.Array);
                foreach (OverwriteOptions overwriteParam in PermissionOverwrites)
                {
                    permissionOverwritesArray.Values.Add(overwriteParam.Build());
                }

                data.Set("permission_overwrites", permissionOverwritesArray);
            }

            return(data);
        }
Example #30
0
        internal DiscordApiData Build()
        {
            DiscordApiData data = new DiscordApiData(DiscordApiDataType.Container);

            data.SetSnowflake("id", Id);
            data.Set("position", Position);

            return(data);
        }