* initial implementation * update models * somewhat working auto mod action executed event * made some properties optional * comments, rest entity, guild methods * add placeholder methods * started working on rule cache * working events * started working on rule builder * working state * fix null issue * commentsssss * public automod rules collection in a socketgulild * forgot nullability * update limits * add Download func to cacheable user * Apply suggestions from code review * Update src/Discord.Net.Rest/DiscordRestApiClient.cs * missing xml doc * reworkkkk * fix the `;` lol --------- Co-authored-by: Quin Lynch <lynchquin@gmail.com> Co-authored-by: Casmir <68127614+csmir@users.noreply.github.com>pull/2241/merge
| @@ -0,0 +1,26 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| public enum AutoModActionType | |||||
| { | |||||
| /// <summary> | |||||
| /// Blocks the content of a message according to the rule. | |||||
| /// </summary> | |||||
| BlockMessage = 1, | |||||
| /// <summary> | |||||
| /// Logs user content to a specified channel. | |||||
| /// </summary> | |||||
| SendAlertMessage = 2, | |||||
| /// <summary> | |||||
| /// Timeout user for a specified duration. | |||||
| /// </summary> | |||||
| Timeout = 3, | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,19 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// An enum indecating in what event context a rule should be checked. | |||||
| /// </summary> | |||||
| public enum AutoModEventType | |||||
| { | |||||
| /// <summary> | |||||
| /// When a member sends or edits a message in the guild. | |||||
| /// </summary> | |||||
| MessageSend = 1 | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,36 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents an action that will be preformed if a user breaks an <see cref="IAutoModRule"/>. | |||||
| /// </summary> | |||||
| public class AutoModRuleAction | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the type for this action. | |||||
| /// </summary> | |||||
| public AutoModActionType Type { get; } | |||||
| /// <summary> | |||||
| /// Get the channel id on which to post alerts. <see langword="null"/> if no channel has been provided. | |||||
| /// </summary> | |||||
| public ulong? ChannelId { get; } | |||||
| /// <summary> | |||||
| /// Gets the duration of which a user will be timed out for breaking this rule. <see langword="null"/> if no timeout duration has been provided. | |||||
| /// </summary> | |||||
| public TimeSpan? TimeoutDuration { get; } | |||||
| internal AutoModRuleAction(AutoModActionType type, ulong? channelId, int? duration) | |||||
| { | |||||
| Type = type; | |||||
| ChannelId = channelId; | |||||
| TimeoutDuration = duration.HasValue ? TimeSpan.FromSeconds(duration.Value) : null; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,151 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// Provides properties used to modify a <see cref="IAutoModRule"/>. | |||||
| /// </summary> | |||||
| public class AutoModRuleProperties | |||||
| { | |||||
| /// <summary> | |||||
| /// Returns the max keyword count for an AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxKeywordCount = 1000; | |||||
| /// <summary> | |||||
| /// Returns the max keyword length for an AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxKeywordLength = 60; | |||||
| /// <summary> | |||||
| /// Returns the max regex pattern count for an AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxRegexPatternCount = 10; | |||||
| /// <summary> | |||||
| /// Returns the max regex pattern length for an AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxRegexPatternLength = 260; | |||||
| /// <summary> | |||||
| /// Returns the max allowlist keyword count for a <see cref="AutoModTriggerType.Keyword"/> AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxAllowListCountKeyword = 100; | |||||
| /// <summary> | |||||
| /// Returns the max allowlist keyword count for a <see cref="AutoModTriggerType.KeywordPreset"/> AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxAllowListCountKeywordPreset = 1000; | |||||
| /// <summary> | |||||
| /// Returns the max allowlist keyword length for an AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxAllowListEntryLength = 60; | |||||
| /// <summary> | |||||
| /// Returns the max mention limit for an AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxMentionLimit = 50; | |||||
| /// <summary> | |||||
| /// Returns the max exempt role count for an AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxExemptRoles = 20; | |||||
| /// <summary> | |||||
| /// Returns the max exempt channel count for an AutoMod rule allowed by Discord. | |||||
| /// </summary> | |||||
| public const int MaxExemptChannels = 50; | |||||
| /// <summary> | |||||
| /// Returns the max timeout duration in seconds for an auto moderation rule action. | |||||
| /// </summary> | |||||
| public const int MaxTimeoutSeconds = 2419200; | |||||
| /// <summary> | |||||
| /// Gets or sets the name for the rule. | |||||
| /// </summary> | |||||
| public Optional<string> Name { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the event type for the rule. | |||||
| /// </summary> | |||||
| public Optional<AutoModEventType> EventType { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the trigger type for the rule. | |||||
| /// </summary> | |||||
| public Optional<AutoModTriggerType> TriggerType { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the keyword filter for the rule. | |||||
| /// </summary> | |||||
| public Optional<string[]> KeywordFilter { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets regex patterns for the rule. | |||||
| /// </summary> | |||||
| public Optional<string[]> RegexPatterns { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the allow list for the rule. | |||||
| /// </summary> | |||||
| public Optional<string[]> AllowList { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets total mention limit for the rule. | |||||
| /// </summary> | |||||
| public Optional<int> MentionLimit { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the presets for the rule. Empty if the rule has no presets. | |||||
| /// </summary> | |||||
| public Optional<KeywordPresetTypes[]> Presets { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the actions for the rule. | |||||
| /// </summary> | |||||
| public Optional<AutoModRuleActionProperties[]> Actions { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets whether or not the rule is enabled. | |||||
| /// </summary> | |||||
| public Optional<bool> Enabled { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the exempt roles for the rule. Empty if the rule has no exempt roles. | |||||
| /// </summary> | |||||
| public Optional<ulong[]> ExemptRoles { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the exempt channels for the rule. Empty if the rule has no exempt channels. | |||||
| /// </summary> | |||||
| public Optional<ulong[]> ExemptChannels { get; set; } | |||||
| } | |||||
| /// <summary> | |||||
| /// Provides properties used to modify a <see cref="AutoModRuleAction"/>. | |||||
| /// </summary> | |||||
| public class AutoModRuleActionProperties | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets or sets the type for this action. | |||||
| /// </summary> | |||||
| public AutoModActionType Type { get; set; } | |||||
| /// <summary> | |||||
| /// Get or sets the channel id on which to post alerts. | |||||
| /// </summary> | |||||
| public ulong? ChannelId { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the duration of which a user will be timed out for breaking this rule. | |||||
| /// </summary> | |||||
| public TimeSpan? TimeoutDuration { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,39 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// An enum representing the type of content which can trigger the rule. | |||||
| /// </summary> | |||||
| public enum AutoModTriggerType | |||||
| { | |||||
| /// <summary> | |||||
| /// Check if content contains words from a user defined list of keywords. | |||||
| /// </summary> | |||||
| Keyword = 1, | |||||
| /// <summary> | |||||
| /// Check if content contains any harmful links. | |||||
| /// </summary> | |||||
| HarmfulLink = 2, | |||||
| /// <summary> | |||||
| /// Check if content represents generic spam. | |||||
| /// </summary> | |||||
| Spam = 3, | |||||
| /// <summary> | |||||
| /// Check if content contains words from internal pre-defined wordsets. | |||||
| /// </summary> | |||||
| KeywordPreset = 4, | |||||
| /// <summary> | |||||
| /// Check if content contains more unique mentions than allowed. | |||||
| /// </summary> | |||||
| MentionSpam = 5, | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,111 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents a auto mod rule within a guild. | |||||
| /// </summary> | |||||
| public interface IAutoModRule : ISnowflakeEntity, IDeletable | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the guild id on which this rule exists. | |||||
| /// </summary> | |||||
| ulong GuildId { get; } | |||||
| /// <summary> | |||||
| /// Get the name of this rule. | |||||
| /// </summary> | |||||
| string Name { get; } | |||||
| /// <summary> | |||||
| /// Gets the id of the user who created this use. | |||||
| /// </summary> | |||||
| ulong CreatorId { get; } | |||||
| /// <summary> | |||||
| /// Gets the event type on which this rule is triggered. | |||||
| /// </summary> | |||||
| AutoModEventType EventType { get; } | |||||
| /// <summary> | |||||
| /// Gets the trigger type on which this rule executes. | |||||
| /// </summary> | |||||
| AutoModTriggerType TriggerType { get; } | |||||
| /// <summary> | |||||
| /// Gets the keyword filter for this rule. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This collection will be empty if <see cref="TriggerType"/> is not | |||||
| /// <see cref="AutoModTriggerType.Keyword"/>. | |||||
| /// </remarks> | |||||
| public IReadOnlyCollection<string> KeywordFilter { get; } | |||||
| /// <summary> | |||||
| /// Gets regex patterns for this rule. Empty if the rule has no regexes. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This collection will be empty if <see cref="TriggerType"/> is not | |||||
| /// <see cref="AutoModTriggerType.Keyword"/>. | |||||
| /// </remarks> | |||||
| public IReadOnlyCollection<string> RegexPatterns { get; } | |||||
| /// <summary> | |||||
| /// Gets the allow list patterns for this rule. Empty if the rule has no allowed terms. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This collection will be empty if <see cref="TriggerType"/> is not | |||||
| /// <see cref="AutoModTriggerType.Keyword"/>. | |||||
| /// </remarks> | |||||
| public IReadOnlyCollection<string> AllowList { get; } | |||||
| /// <summary> | |||||
| /// Gets the preset keyword types for this rule. Empty if the rule has no presets. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This collection will be empty if <see cref="TriggerType"/> is not | |||||
| /// <see cref="AutoModTriggerType.KeywordPreset"/>. | |||||
| /// </remarks> | |||||
| public IReadOnlyCollection<KeywordPresetTypes> Presets { get; } | |||||
| /// <summary> | |||||
| /// Gets the total mention limit for this rule. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This property will be <see langword="null"/> if <see cref="TriggerType"/> is not | |||||
| /// <see cref="AutoModTriggerType.MentionSpam"/>. | |||||
| /// </remarks> | |||||
| public int? MentionTotalLimit { get; } | |||||
| /// <summary> | |||||
| /// Gets a collection of actions that will be preformed if a user breaks this rule. | |||||
| /// </summary> | |||||
| IReadOnlyCollection<AutoModRuleAction> Actions { get; } | |||||
| /// <summary> | |||||
| /// Gets whether or not this rule is enabled. | |||||
| /// </summary> | |||||
| bool Enabled { get; } | |||||
| /// <summary> | |||||
| /// Gets a collection of role ids that are exempt from this rule. Empty if the rule has no exempt roles. | |||||
| /// </summary> | |||||
| IReadOnlyCollection<ulong> ExemptRoles { get; } | |||||
| /// <summary> | |||||
| /// Gets a collection of channel ids that are exempt from this rule. Empty if the rule has no exempt channels. | |||||
| /// </summary> | |||||
| IReadOnlyCollection<ulong> ExemptChannels { get; } | |||||
| /// <summary> | |||||
| /// Modifies this rule. | |||||
| /// </summary> | |||||
| /// <param name="func">The delegate containing the properties to modify the rule with.</param> | |||||
| /// <param name="options">The options to be used when sending the request.</param> | |||||
| Task ModifyAsync(Action<AutoModRuleProperties> func, RequestOptions options = null); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,29 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// An enum representing preset filter types. | |||||
| /// </summary> | |||||
| public enum KeywordPresetTypes | |||||
| { | |||||
| /// <summary> | |||||
| /// Words that may be considered forms of swearing or cursing. | |||||
| /// </summary> | |||||
| Profanity = 1, | |||||
| /// <summary> | |||||
| /// Words that refer to sexually explicit behavior or activity. | |||||
| /// </summary> | |||||
| SexualContent = 2, | |||||
| /// <summary> | |||||
| /// Personal insults or words that may be considered hate speech. | |||||
| /// </summary> | |||||
| Slurs = 3, | |||||
| } | |||||
| } | |||||
| @@ -1267,5 +1267,29 @@ namespace Discord | |||||
| /// A task that represents the asynchronous creation operation. The task result contains a <see cref="WelcomeScreen"/>. | /// A task that represents the asynchronous creation operation. The task result contains a <see cref="WelcomeScreen"/>. | ||||
| /// </returns> | /// </returns> | ||||
| Task<WelcomeScreen> ModifyWelcomeScreenAsync(bool enabled, WelcomeScreenChannelProperties[] channels, string description = null, RequestOptions options = null); | Task<WelcomeScreen> ModifyWelcomeScreenAsync(bool enabled, WelcomeScreenChannelProperties[] channels, string description = null, RequestOptions options = null); | ||||
| /// <summary> | |||||
| /// Get a list of all rules currently configured for the guild. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous creation operation. The task result contains a collection of <see cref="IAutoModRule"/>. | |||||
| /// </returns> | |||||
| Task<IAutoModRule[]> GetAutoModRulesAsync(RequestOptions options = null); | |||||
| /// <summary> | |||||
| /// Gets a single rule configured in a guild. Returns <see langword="null"/> if the rule was not found. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous creation operation. The task result contains a <see cref="IAutoModRule"/>. | |||||
| /// </returns> | |||||
| Task<IAutoModRule> GetAutoModRuleAsync(ulong ruleId, RequestOptions options = null); | |||||
| /// <summary> | |||||
| /// Creates a new auto moderation rule. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A task that represents the asynchronous creation operation. The task result contains the created <see cref="IAutoModRule"/>. | |||||
| /// </returns> | |||||
| Task<IAutoModRule> CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options = null); | |||||
| } | } | ||||
| } | } | ||||
| @@ -48,13 +48,25 @@ namespace Discord | |||||
| /// This intent includes GUILD_SCHEDULED_EVENT_CREATE, GUILD_SCHEDULED_EVENT_UPDATE, GUILD_SCHEDULED_EVENT_DELETE, GUILD_SCHEDULED_EVENT_USER_ADD, GUILD_SCHEDULED_EVENT_USER_REMOVE | /// This intent includes GUILD_SCHEDULED_EVENT_CREATE, GUILD_SCHEDULED_EVENT_UPDATE, GUILD_SCHEDULED_EVENT_DELETE, GUILD_SCHEDULED_EVENT_USER_ADD, GUILD_SCHEDULED_EVENT_USER_REMOVE | ||||
| /// </summary> | /// </summary> | ||||
| GuildScheduledEvents = 1 << 16, | GuildScheduledEvents = 1 << 16, | ||||
| /// <summary> | |||||
| /// This intent includes AUTO_MODERATION_RULE_CREATE, AUTO_MODERATION_RULE_UPDATE, AUTO_MODERATION_RULE_DELETE | |||||
| /// </summary> | |||||
| AutoModerationConfiguration = 1 << 20, | |||||
| /// <summary> | |||||
| /// This intent includes AUTO_MODERATION_ACTION_EXECUTION | |||||
| /// </summary> | |||||
| AutoModerationActionExecution = 1 << 21, | |||||
| /// <summary> | /// <summary> | ||||
| /// This intent includes all but <see cref="GuildMembers"/> and <see cref="GuildPresences"/> | /// This intent includes all but <see cref="GuildMembers"/> and <see cref="GuildPresences"/> | ||||
| /// which are privileged and must be enabled in the Developer Portal. | /// which are privileged and must be enabled in the Developer Portal. | ||||
| /// </summary> | /// </summary> | ||||
| AllUnprivileged = Guilds | GuildBans | GuildEmojis | GuildIntegrations | GuildWebhooks | GuildInvites | | AllUnprivileged = Guilds | GuildBans | GuildEmojis | GuildIntegrations | GuildWebhooks | GuildInvites | | ||||
| GuildVoiceStates | GuildMessages | GuildMessageReactions | GuildMessageTyping | DirectMessages | | GuildVoiceStates | GuildMessages | GuildMessageReactions | GuildMessageTyping | DirectMessages | | ||||
| DirectMessageReactions | DirectMessageTyping | GuildScheduledEvents, | |||||
| DirectMessageReactions | DirectMessageTyping | GuildScheduledEvents | AutoModerationConfiguration | | |||||
| AutoModerationActionExecution, | |||||
| /// <summary> | /// <summary> | ||||
| /// This intent includes all of them, including privileged ones. | /// This intent includes all of them, including privileged ones. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -0,0 +1,18 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class ActionMetadata | |||||
| { | |||||
| [JsonProperty("channel_id")] | |||||
| public Optional<ulong> ChannelId { get; set; } | |||||
| [JsonProperty("duration_seconds")] | |||||
| public Optional<int> DurationSeconds { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class AutoModAction | |||||
| { | |||||
| [JsonProperty("type")] | |||||
| public AutoModActionType Type { get; set; } | |||||
| [JsonProperty("metadata")] | |||||
| public Optional<ActionMetadata> Metadata { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,45 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class AutoModerationRule | |||||
| { | |||||
| [JsonProperty("id")] | |||||
| public ulong Id { get; set; } | |||||
| [JsonProperty("guild_id")] | |||||
| public ulong GuildId { get; set; } | |||||
| [JsonProperty("name")] | |||||
| public string Name { get; set; } | |||||
| [JsonProperty("creator_id")] | |||||
| public ulong CreatorId { get; set; } | |||||
| [JsonProperty("event_type")] | |||||
| public AutoModEventType EventType { get; set; } | |||||
| [JsonProperty("trigger_type")] | |||||
| public AutoModTriggerType TriggerType { get; set; } | |||||
| [JsonProperty("trigger_metadata")] | |||||
| public TriggerMetadata TriggerMetadata { get; set; } | |||||
| [JsonProperty("actions")] | |||||
| public AutoModAction[] Actions { get; set; } | |||||
| [JsonProperty("enabled")] | |||||
| public bool Enabled { get; set; } | |||||
| [JsonProperty("exempt_roles")] | |||||
| public ulong[] ExemptRoles { get; set; } | |||||
| [JsonProperty("exempt_channels")] | |||||
| public ulong[] ExemptChannels { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,27 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class TriggerMetadata | |||||
| { | |||||
| [JsonProperty("keyword_filter")] | |||||
| public Optional<string[]> KeywordFilter { get; set; } | |||||
| [JsonProperty("regex_patterns")] | |||||
| public Optional<string[]> RegexPatterns { get; set; } | |||||
| [JsonProperty("presets")] | |||||
| public Optional<KeywordPresetTypes[]> Presets { get; set; } | |||||
| [JsonProperty("allow_list")] | |||||
| public Optional<string[]> AllowList { get; set; } | |||||
| [JsonProperty("mention_total_limit")] | |||||
| public Optional<int> MentionLimit { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,36 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API.Rest | |||||
| { | |||||
| internal class CreateAutoModRuleParams | |||||
| { | |||||
| [JsonProperty("name")] | |||||
| public string Name { get; set; } | |||||
| [JsonProperty("event_type")] | |||||
| public AutoModEventType EventType { get; set; } | |||||
| [JsonProperty("trigger_type")] | |||||
| public AutoModTriggerType TriggerType { get; set; } | |||||
| [JsonProperty("trigger_metadata")] | |||||
| public Optional<TriggerMetadata> TriggerMetadata { get; set; } | |||||
| [JsonProperty("actions")] | |||||
| public AutoModAction[] Actions { get; set; } | |||||
| [JsonProperty("enabled")] | |||||
| public Optional<bool> Enabled { get; set; } | |||||
| [JsonProperty("exempt_roles")] | |||||
| public Optional<ulong[]> ExemptRoles { get; set; } | |||||
| [JsonProperty("exempt_channels")] | |||||
| public Optional<ulong[]> ExemptChannels { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API.Rest | |||||
| { | |||||
| internal class ModifyAutoModRuleParams | |||||
| { | |||||
| public Optional<string> Name { get; set; } | |||||
| public Optional<AutoModEventType> EventType { get; set; } | |||||
| public Optional<AutoModTriggerType> TriggerType { get; set; } | |||||
| public Optional<TriggerMetadata> TriggerMetadata { get; set; } | |||||
| public Optional<AutoModAction[]> Actions { get; set; } | |||||
| public Optional<bool> Enabled { get; set; } | |||||
| public Optional<ulong[]> ExemptRoles { get; set; } | |||||
| public Optional<ulong[]> ExemptChannels { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -2119,6 +2119,58 @@ namespace Discord.API | |||||
| #endregion | #endregion | ||||
| #region Guild AutoMod | |||||
| public async Task<AutoModerationRule[]> GetGuildAutoModRulesAsync(ulong guildId, RequestOptions options) | |||||
| { | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<AutoModerationRule[]>("GET", () => $"guilds/{guildId}/auto-moderation/rules", new BucketIds(guildId: guildId), options: options); | |||||
| } | |||||
| public async Task<AutoModerationRule> GetGuildAutoModRuleAsync(ulong guildId, ulong ruleId, RequestOptions options) | |||||
| { | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| Preconditions.NotEqual(ruleId, 0, nameof(ruleId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendAsync<AutoModerationRule>("GET", () => $"guilds/{guildId}/auto-moderation/rules/{ruleId}", new BucketIds(guildId), options: options); | |||||
| } | |||||
| public async Task<AutoModerationRule> CreateGuildAutoModRuleAsync(ulong guildId, CreateAutoModRuleParams args, RequestOptions options) | |||||
| { | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendJsonAsync<AutoModerationRule>("POST", () => $"guilds/{guildId}/auto-moderation/rules", args, new BucketIds(guildId: guildId), options: options); | |||||
| } | |||||
| public async Task<AutoModerationRule> ModifyGuildAutoModRuleAsync(ulong guildId, ulong ruleId, ModifyAutoModRuleParams args, RequestOptions options) | |||||
| { | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| Preconditions.NotEqual(ruleId, 0, nameof(ruleId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| return await SendJsonAsync<AutoModerationRule>("PATCH", () => $"guilds/{guildId}/auto-moderation/rules/{ruleId}", args, new BucketIds(guildId: guildId), options: options); | |||||
| } | |||||
| public async Task DeleteGuildAutoModRuleAsync(ulong guildId, ulong ruleId, RequestOptions options) | |||||
| { | |||||
| Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||||
| Preconditions.NotEqual(ruleId, 0, nameof(ruleId)); | |||||
| options = RequestOptions.CreateOrClone(options); | |||||
| await SendAsync("DELETE", () => $"guilds/{guildId}/auto-moderation/rules/{ruleId}", new BucketIds(guildId: guildId), options: options); | |||||
| } | |||||
| #endregion | |||||
| #region Guild Welcome Screen | #region Guild Welcome Screen | ||||
| public async Task<WelcomeScreen> GetGuildWelcomeScreenAsync(ulong guildId, RequestOptions options = null) | public async Task<WelcomeScreen> GetGuildWelcomeScreenAsync(ulong guildId, RequestOptions options = null) | ||||
| @@ -1062,5 +1062,170 @@ namespace Discord.Rest | |||||
| } | } | ||||
| #endregion | #endregion | ||||
| #region Auto Mod | |||||
| public static async Task<AutoModerationRule> CreateAutoModRuleAsync(IGuild guild, Action<AutoModRuleProperties> func, BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| var args = new AutoModRuleProperties(); | |||||
| func(args); | |||||
| if (!args.TriggerType.IsSpecified) | |||||
| throw new ArgumentException(message: $"AutoMod rule must have a specified type.", paramName: nameof(args.TriggerType)); | |||||
| if (!args.Name.IsSpecified || string.IsNullOrWhiteSpace(args.Name.Value)) | |||||
| throw new ArgumentException("Name of the rule must not be empty", paramName: nameof(args.Name)); | |||||
| Preconditions.AtLeast(1, args.Actions.GetValueOrDefault(Array.Empty<AutoModRuleActionProperties>()).Length, nameof(args.Actions), "Auto moderation rule must have at least 1 action"); | |||||
| #region Keyword Validations | |||||
| if (args.RegexPatterns.IsSpecified) | |||||
| { | |||||
| if (args.TriggerType.Value is not AutoModTriggerType.Keyword) | |||||
| throw new ArgumentException(message: $"Regex patterns can only be used with 'Keyword' trigger type.", paramName: nameof(args.RegexPatterns)); | |||||
| Preconditions.AtMost(args.RegexPatterns.Value.Length, AutoModRuleProperties.MaxRegexPatternCount, nameof(args.RegexPatterns), $"Regex pattern count must be less than or equal to {AutoModRuleProperties.MaxRegexPatternCount}."); | |||||
| if (args.RegexPatterns.Value.Any(x => x.Length > AutoModRuleProperties.MaxRegexPatternLength)) | |||||
| throw new ArgumentException(message: $"Regex pattern must be less than or equal to {AutoModRuleProperties.MaxRegexPatternLength}.", paramName: nameof(args.RegexPatterns)); | |||||
| } | |||||
| if (args.KeywordFilter.IsSpecified) | |||||
| { | |||||
| if (args.TriggerType.Value != AutoModTriggerType.Keyword) | |||||
| throw new ArgumentException(message: $"Keyword filter can only be used with 'Keyword' trigger type.", paramName: nameof(args.KeywordFilter)); | |||||
| Preconditions.AtMost(args.KeywordFilter.Value.Length, AutoModRuleProperties.MaxKeywordCount, nameof(args.KeywordFilter), $"Keyword count must be less than or equal to {AutoModRuleProperties.MaxKeywordCount}"); | |||||
| if (args.KeywordFilter.Value.Any(x => x.Length > AutoModRuleProperties.MaxKeywordLength)) | |||||
| throw new ArgumentException(message: $"Keyword length must be less than or equal to {AutoModRuleProperties.MaxKeywordLength}.", paramName: nameof(args.KeywordFilter)); | |||||
| } | |||||
| if (args.TriggerType.Value is AutoModTriggerType.Keyword) | |||||
| Preconditions.AtLeast(args.KeywordFilter.GetValueOrDefault(Array.Empty<string>()).Length + args.RegexPatterns.GetValueOrDefault(Array.Empty<string>()).Length, 1, "KeywordFilter & RegexPatterns","Auto moderation rule must have at least 1 keyword or regex pattern"); | |||||
| if (args.AllowList.IsSpecified) | |||||
| { | |||||
| if (args.TriggerType.Value is not AutoModTriggerType.Keyword or AutoModTriggerType.KeywordPreset) | |||||
| throw new ArgumentException(message: $"Allow list can only be used with 'Keyword' or 'KeywordPreset' trigger type.", paramName: nameof(args.AllowList)); | |||||
| if (args.TriggerType.Value is AutoModTriggerType.Keyword) | |||||
| Preconditions.AtMost(args.AllowList.Value.Length, AutoModRuleProperties.MaxAllowListCountKeyword, nameof(args.AllowList), $"Allow list entry count must be less than or equal to {AutoModRuleProperties.MaxAllowListCountKeyword}."); | |||||
| if (args.TriggerType.Value is AutoModTriggerType.KeywordPreset) | |||||
| Preconditions.AtMost(args.AllowList.Value.Length, AutoModRuleProperties.MaxAllowListCountKeywordPreset, nameof(args.AllowList), $"Allow list entry count must be less than or equal to {AutoModRuleProperties.MaxAllowListCountKeywordPreset}."); | |||||
| if (args.AllowList.Value.Any(x => x.Length > AutoModRuleProperties.MaxAllowListEntryLength)) | |||||
| throw new ArgumentException(message: $"Allow list entry length must be less than or equal to {AutoModRuleProperties.MaxAllowListEntryLength}.", paramName: nameof(args.AllowList)); | |||||
| } | |||||
| if (args.TriggerType.Value is not AutoModTriggerType.KeywordPreset && args.Presets.IsSpecified) | |||||
| throw new ArgumentException(message: $"Keyword presets scan only be used with 'KeywordPreset' trigger type.", paramName: nameof(args.Presets)); | |||||
| #endregion | |||||
| if (args.MentionLimit.IsSpecified) | |||||
| { | |||||
| if (args.TriggerType.Value is AutoModTriggerType.MentionSpam) | |||||
| { | |||||
| Preconditions.AtMost(args.MentionLimit.Value, AutoModRuleProperties.MaxMentionLimit, nameof(args.MentionLimit), $"Mention limit must be less or equal to {AutoModRuleProperties.MaxMentionLimit}"); | |||||
| Preconditions.AtLeast(args.MentionLimit.Value, 1, nameof(args.MentionLimit), $"Mention limit must be greater or equal to 1"); | |||||
| } | |||||
| else | |||||
| { | |||||
| throw new ArgumentException(message: $"MentionLimit can only be used with 'MentionSpam' trigger type.", paramName: nameof(args.MentionLimit)); | |||||
| } | |||||
| } | |||||
| if (args.ExemptRoles.IsSpecified) | |||||
| Preconditions.AtMost(args.ExemptRoles.Value.Length, AutoModRuleProperties.MaxExemptRoles, nameof(args.ExemptRoles), $"Exempt roles count must be less than or equal to {AutoModRuleProperties.MaxExemptRoles}."); | |||||
| if (args.ExemptChannels.IsSpecified) | |||||
| Preconditions.AtMost(args.ExemptChannels.Value.Length, AutoModRuleProperties.MaxExemptChannels, nameof(args.ExemptChannels), $"Exempt channels count must be less than or equal to {AutoModRuleProperties.MaxExemptChannels}."); | |||||
| if (!args.Actions.IsSpecified && args.Actions.Value.Length == 0) | |||||
| { | |||||
| throw new ArgumentException(message: $"At least 1 action must be set for an auto moderation rule.", paramName: nameof(args.Actions)); | |||||
| } | |||||
| if (args.Actions.Value.Any(x => x.TimeoutDuration.GetValueOrDefault().TotalSeconds > AutoModRuleProperties.MaxTimeoutSeconds)) | |||||
| throw new ArgumentException(message: $"Field count must be less than or equal to {AutoModRuleProperties.MaxTimeoutSeconds}.", paramName: nameof(AutoModRuleActionProperties.TimeoutDuration)); | |||||
| var props = new CreateAutoModRuleParams | |||||
| { | |||||
| EventType = args.EventType.GetValueOrDefault(AutoModEventType.MessageSend), | |||||
| Enabled = args.Enabled.GetValueOrDefault(true), | |||||
| ExemptRoles = args.ExemptRoles.GetValueOrDefault(), | |||||
| ExemptChannels = args.ExemptChannels.GetValueOrDefault(), | |||||
| Name = args.Name.Value, | |||||
| TriggerType = args.TriggerType.Value, | |||||
| Actions = args.Actions.Value.Select(x => new AutoModAction | |||||
| { | |||||
| Metadata = new ActionMetadata | |||||
| { | |||||
| ChannelId = x.ChannelId ?? Optional<ulong>.Unspecified, | |||||
| DurationSeconds = (int?)x.TimeoutDuration?.TotalSeconds ?? Optional<int>.Unspecified | |||||
| }, | |||||
| Type = x.Type | |||||
| }).ToArray(), | |||||
| TriggerMetadata = new TriggerMetadata | |||||
| { | |||||
| AllowList = args.AllowList, | |||||
| KeywordFilter = args.KeywordFilter, | |||||
| MentionLimit = args.MentionLimit, | |||||
| Presets = args.Presets, | |||||
| RegexPatterns = args.RegexPatterns, | |||||
| }, | |||||
| }; | |||||
| return await client.ApiClient.CreateGuildAutoModRuleAsync(guild.Id, props, options); | |||||
| } | |||||
| public static async Task<AutoModerationRule> GetAutoModRuleAsync(ulong ruleId, IGuild guild, BaseDiscordClient client, RequestOptions options) | |||||
| => await client.ApiClient.GetGuildAutoModRuleAsync(guild.Id, ruleId, options); | |||||
| public static async Task<AutoModerationRule[]> GetAutoModRulesAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) | |||||
| => await client.ApiClient.GetGuildAutoModRulesAsync(guild.Id, options); | |||||
| public static Task<AutoModerationRule> ModifyRuleAsync(BaseDiscordClient client, IAutoModRule rule, Action<AutoModRuleProperties> func, RequestOptions options) | |||||
| { | |||||
| var args = new AutoModRuleProperties(); | |||||
| func(args); | |||||
| var apiArgs = new API.Rest.ModifyAutoModRuleParams | |||||
| { | |||||
| Actions = args.Actions.IsSpecified ? args.Actions.Value.Select(x => new API.AutoModAction() | |||||
| { | |||||
| Type = x.Type, | |||||
| Metadata = x.ChannelId.HasValue || x.TimeoutDuration.HasValue ? new API.ActionMetadata | |||||
| { | |||||
| ChannelId = x.ChannelId ?? Optional<ulong>.Unspecified, | |||||
| DurationSeconds = x.TimeoutDuration.HasValue ? (int)Math.Floor(x.TimeoutDuration.Value.TotalSeconds) : Optional<int>.Unspecified | |||||
| } : Optional<API.ActionMetadata>.Unspecified | |||||
| }).ToArray() : Optional<API.AutoModAction[]>.Unspecified, | |||||
| Enabled = args.Enabled, | |||||
| EventType = args.EventType, | |||||
| ExemptChannels = args.ExemptChannels, | |||||
| ExemptRoles = args.ExemptRoles, | |||||
| Name = args.Name, | |||||
| TriggerType = args.TriggerType, | |||||
| TriggerMetadata = args.KeywordFilter.IsSpecified || args.Presets.IsSpecified ? new API.TriggerMetadata | |||||
| { | |||||
| KeywordFilter = args.KeywordFilter.GetValueOrDefault(Array.Empty<string>()), | |||||
| RegexPatterns = args.RegexPatterns.GetValueOrDefault(Array.Empty<string>()), | |||||
| AllowList = args.AllowList.GetValueOrDefault(Array.Empty<string>()), | |||||
| MentionLimit = args.MentionLimit, | |||||
| Presets = args.Presets.GetValueOrDefault(Array.Empty<KeywordPresetTypes>()) | |||||
| } : Optional<API.TriggerMetadata>.Unspecified | |||||
| }; | |||||
| return client.ApiClient.ModifyGuildAutoModRuleAsync(rule.GuildId, rule.Id, apiArgs, options); | |||||
| } | |||||
| public static Task DeleteRuleAsync(BaseDiscordClient client, IAutoModRule rule, RequestOptions options) | |||||
| => client.ApiClient.DeleteGuildAutoModRuleAsync(rule.GuildId, rule.Id, options); | |||||
| #endregion | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,101 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | |||||
| using Model = Discord.API.AutoModerationRule; | |||||
| namespace Discord.Rest; | |||||
| public class RestAutoModRule : RestEntity<ulong>, IAutoModRule | |||||
| { | |||||
| /// <inheritdoc /> | |||||
| public DateTimeOffset CreatedAt { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public ulong GuildId { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public string Name { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public ulong CreatorId { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public AutoModEventType EventType { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public AutoModTriggerType TriggerType { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<string> KeywordFilter { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<string> RegexPatterns { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<string> AllowList { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<KeywordPresetTypes> Presets { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public int? MentionTotalLimit { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<AutoModRuleAction> Actions { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public bool Enabled { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<ulong> ExemptRoles { get; private set; } | |||||
| /// <inheritdoc /> | |||||
| public IReadOnlyCollection<ulong> ExemptChannels { get; private set; } | |||||
| internal RestAutoModRule(BaseDiscordClient discord, ulong id) : base(discord, id) | |||||
| { | |||||
| } | |||||
| internal static RestAutoModRule Create(BaseDiscordClient discord, Model model) | |||||
| { | |||||
| var entity = new RestAutoModRule(discord, model.Id); | |||||
| entity.Update(model); | |||||
| return entity; | |||||
| } | |||||
| internal void Update(Model model) | |||||
| { | |||||
| Name = model.Name; | |||||
| CreatorId = model.CreatorId; | |||||
| GuildId = model.GuildId; | |||||
| EventType = model.EventType; | |||||
| TriggerType = model.TriggerType; | |||||
| KeywordFilter = model.TriggerMetadata.KeywordFilter.GetValueOrDefault(Array.Empty<string>()).ToImmutableArray(); | |||||
| Presets = model.TriggerMetadata.Presets.GetValueOrDefault(Array.Empty<KeywordPresetTypes>()).ToImmutableArray(); | |||||
| RegexPatterns = model.TriggerMetadata.RegexPatterns.GetValueOrDefault(Array.Empty<string>()).ToImmutableArray(); | |||||
| AllowList = model.TriggerMetadata.AllowList.GetValueOrDefault(Array.Empty<string>()).ToImmutableArray(); | |||||
| MentionTotalLimit = model.TriggerMetadata.MentionLimit.IsSpecified | |||||
| ? model.TriggerMetadata.MentionLimit.Value | |||||
| : null; | |||||
| Actions = model.Actions.Select(x => new AutoModRuleAction(x.Type, x.Metadata.GetValueOrDefault()?.ChannelId.ToNullable(), x.Metadata.GetValueOrDefault()?.DurationSeconds.ToNullable())).ToImmutableArray(); | |||||
| Enabled = model.Enabled; | |||||
| ExemptRoles = model.ExemptRoles.ToImmutableArray(); | |||||
| ExemptChannels = model.ExemptChannels.ToImmutableArray(); | |||||
| } | |||||
| /// <inheritdoc /> | |||||
| public async Task ModifyAsync(Action<AutoModRuleProperties> func, RequestOptions options = null) | |||||
| { | |||||
| var model = await GuildHelper.ModifyRuleAsync(Discord, this, func, options); | |||||
| Update(model); | |||||
| } | |||||
| /// <inheritdoc /> | |||||
| public Task DeleteAsync(RequestOptions options = null) | |||||
| => GuildHelper.DeleteRuleAsync(Discord, this, options); | |||||
| } | |||||
| @@ -1197,6 +1197,34 @@ namespace Discord.Rest | |||||
| RequestOptions options = null) | RequestOptions options = null) | ||||
| => GuildHelper.CreateGuildEventAsync(Discord, this, name, privacyLevel, startTime, type, description, endTime, channelId, location, coverImage, options); | => GuildHelper.CreateGuildEventAsync(Discord, this, name, privacyLevel, startTime, type, description, endTime, channelId, location, coverImage, options); | ||||
| #endregion | |||||
| #region AutoMod | |||||
| /// <inheritdoc cref="IGuild.GetAutoModRuleAsync"/> | |||||
| public async Task<RestAutoModRule> GetAutoModRuleAsync(ulong ruleId, RequestOptions options = null) | |||||
| { | |||||
| var rule = await GuildHelper.GetAutoModRuleAsync(ruleId, this, Discord, options); | |||||
| return RestAutoModRule.Create(Discord, rule); | |||||
| } | |||||
| /// <inheritdoc cref="IGuild.GetAutoModRulesAsync"/> | |||||
| public async Task<RestAutoModRule[]> GetAutoModRulesAsync(RequestOptions options = null) | |||||
| { | |||||
| var rules = await GuildHelper.GetAutoModRulesAsync(this, Discord, options); | |||||
| return rules.Select(x => RestAutoModRule.Create(Discord, x)).ToArray(); | |||||
| } | |||||
| /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | |||||
| public async Task<RestAutoModRule> CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options = null) | |||||
| { | |||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, props, Discord, options); | |||||
| return RestAutoModRule.Create(Discord, rule); | |||||
| } | |||||
| #endregion | #endregion | ||||
| #region IGuild | #region IGuild | ||||
| @@ -1543,6 +1571,19 @@ namespace Discord.Rest | |||||
| public Task<WelcomeScreen> ModifyWelcomeScreenAsync(bool enabled, WelcomeScreenChannelProperties[] channels, string description = null, RequestOptions options = null) | public Task<WelcomeScreen> ModifyWelcomeScreenAsync(bool enabled, WelcomeScreenChannelProperties[] channels, string description = null, RequestOptions options = null) | ||||
| => GuildHelper.ModifyWelcomeScreenAsync(enabled, description, channels, this, Discord, options); | => GuildHelper.ModifyWelcomeScreenAsync(enabled, description, channels, this, Discord, options); | ||||
| /// <inheritdoc/> | |||||
| async Task<IAutoModRule> IGuild.GetAutoModRuleAsync(ulong ruleId, RequestOptions options) | |||||
| => await GetAutoModRuleAsync(ruleId, options).ConfigureAwait(false); | |||||
| /// <inheritdoc/> | |||||
| async Task<IAutoModRule[]> IGuild.GetAutoModRulesAsync(RequestOptions options) | |||||
| => await GetAutoModRulesAsync(options).ConfigureAwait(false); | |||||
| /// <inheritdoc/> | |||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options) | |||||
| => await CreateAutoModRuleAsync(props, options).ConfigureAwait(false); | |||||
| #endregion | #endregion | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,38 @@ | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Gateway; | |||||
| internal class AutoModActionExecutedEvent | |||||
| { | |||||
| [JsonProperty("guild_id")] | |||||
| public ulong GuildId { get; set; } | |||||
| [JsonProperty("action")] | |||||
| public Discord.API.AutoModAction Action { get; set; } | |||||
| [JsonProperty("rule_id")] | |||||
| public ulong RuleId { get; set; } | |||||
| [JsonProperty("rule_trigger_type")] | |||||
| public AutoModTriggerType TriggerType { get; set; } | |||||
| [JsonProperty("user_id")] | |||||
| public ulong UserId { get; set; } | |||||
| [JsonProperty("channel_id")] | |||||
| public Optional<ulong> ChannelId { get; set; } | |||||
| [JsonProperty("message_id")] | |||||
| public Optional<ulong> MessageId { get; set; } | |||||
| [JsonProperty("alert_system_message_id")] | |||||
| public Optional<ulong> AlertSystemMessageId { get; set; } | |||||
| [JsonProperty("content")] | |||||
| public string Content { get; set; } | |||||
| [JsonProperty("matched_keyword")] | |||||
| public Optional<string> MatchedKeyword { get; set; } | |||||
| [JsonProperty("matched_content")] | |||||
| public Optional<string> MatchedContent { get; set; } | |||||
| } | |||||
| @@ -892,5 +892,49 @@ namespace Discord.WebSocket | |||||
| internal readonly AsyncEvent<Func<SocketGuild, SocketChannel, Task>> _webhooksUpdated = new AsyncEvent<Func<SocketGuild, SocketChannel, Task>>(); | internal readonly AsyncEvent<Func<SocketGuild, SocketChannel, Task>> _webhooksUpdated = new AsyncEvent<Func<SocketGuild, SocketChannel, Task>>(); | ||||
| #endregion | #endregion | ||||
| #region AutoModeration | |||||
| /// <summary> | |||||
| /// Fired when an auto moderation rule is created. | |||||
| /// </summary> | |||||
| public event Func<SocketAutoModRule, Task> AutoModRuleCreated | |||||
| { | |||||
| add => _autoModRuleCreated.Add(value); | |||||
| remove => _autoModRuleCreated.Remove(value); | |||||
| } | |||||
| internal readonly AsyncEvent<Func<SocketAutoModRule, Task>> _autoModRuleCreated = new (); | |||||
| /// <summary> | |||||
| /// Fired when an auto moderation rule is modified. | |||||
| /// </summary> | |||||
| public event Func<Cacheable<SocketAutoModRule, ulong>, SocketAutoModRule, Task> AutoModRuleUpdated | |||||
| { | |||||
| add => _autoModRuleUpdated.Add(value); | |||||
| remove => _autoModRuleUpdated.Remove(value); | |||||
| } | |||||
| internal readonly AsyncEvent<Func<Cacheable<SocketAutoModRule, ulong>, SocketAutoModRule, Task>> _autoModRuleUpdated = new (); | |||||
| /// <summary> | |||||
| /// Fired when an auto moderation rule is deleted. | |||||
| /// </summary> | |||||
| public event Func<SocketAutoModRule, Task> AutoModRuleDeleted | |||||
| { | |||||
| add => _autoModRuleDeleted.Add(value); | |||||
| remove => _autoModRuleDeleted.Remove(value); | |||||
| } | |||||
| internal readonly AsyncEvent<Func<SocketAutoModRule, Task>> _autoModRuleDeleted = new (); | |||||
| /// <summary> | |||||
| /// Fired when an auto moderation rule is triggered by a user. | |||||
| /// </summary> | |||||
| public event Func<SocketGuild, AutoModRuleAction, AutoModActionExecutedData, Task> AutoModActionExecuted | |||||
| { | |||||
| add => _autoModActionExecuted.Add(value); | |||||
| remove => _autoModActionExecuted.Remove(value); | |||||
| } | |||||
| internal readonly AsyncEvent<Func<SocketGuild, AutoModRuleAction, AutoModActionExecutedData, Task>> _autoModActionExecuted = new (); | |||||
| #endregion | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,8 +6,10 @@ using Discord.Net.Udp; | |||||
| using Discord.Net.WebSockets; | using Discord.Net.WebSockets; | ||||
| using Discord.Rest; | using Discord.Rest; | ||||
| using Discord.Utils; | using Discord.Utils; | ||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using Newtonsoft.Json.Linq; | using Newtonsoft.Json.Linq; | ||||
| using System; | using System; | ||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| @@ -16,6 +18,7 @@ using System.IO; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using GameModel = Discord.API.Game; | using GameModel = Discord.API.Game; | ||||
| namespace Discord.WebSocket | namespace Discord.WebSocket | ||||
| @@ -2882,6 +2885,132 @@ namespace Discord.WebSocket | |||||
| #endregion | #endregion | ||||
| #region Auto Moderation | |||||
| case "AUTO_MODERATION_RULE_CREATE": | |||||
| { | |||||
| var data = (payload as JToken).ToObject<AutoModerationRule>(_serializer); | |||||
| var guild = State.GetGuild(data.GuildId); | |||||
| var rule = guild.AddOrUpdateAutoModRule(data); | |||||
| await TimedInvokeAsync(_autoModRuleCreated, nameof(AutoModRuleCreated), rule); | |||||
| } | |||||
| break; | |||||
| case "AUTO_MODERATION_RULE_UPDATE": | |||||
| { | |||||
| var data = (payload as JToken).ToObject<AutoModerationRule>(_serializer); | |||||
| var guild = State.GetGuild(data.GuildId); | |||||
| var cachedRule = guild.GetAutoModRule(data.Id); | |||||
| var cacheableBefore = new Cacheable<SocketAutoModRule, ulong>(cachedRule?.Clone(), | |||||
| data.Id, | |||||
| cachedRule is not null, | |||||
| async () => await guild.GetAutoModRuleAsync(data.Id)); | |||||
| await TimedInvokeAsync(_autoModRuleUpdated, nameof(AutoModRuleUpdated), cacheableBefore, guild.AddOrUpdateAutoModRule(data)); | |||||
| } | |||||
| break; | |||||
| case "AUTO_MODERATION_RULE_DELETE": | |||||
| { | |||||
| var data = (payload as JToken).ToObject<AutoModerationRule>(_serializer); | |||||
| var guild = State.GetGuild(data.GuildId); | |||||
| var rule = guild.RemoveAutoModRule(data); | |||||
| await TimedInvokeAsync(_autoModRuleDeleted, nameof(AutoModRuleDeleted), rule); | |||||
| } | |||||
| break; | |||||
| case "AUTO_MODERATION_ACTION_EXECUTION": | |||||
| { | |||||
| var data = (payload as JToken).ToObject<AutoModActionExecutedEvent>(_serializer); | |||||
| var guild = State.GetGuild(data.GuildId); | |||||
| var action = new AutoModRuleAction(data.Action.Type, | |||||
| data.Action.Metadata.IsSpecified | |||||
| ? data.Action.Metadata.Value.ChannelId.IsSpecified | |||||
| ? data.Action.Metadata.Value.ChannelId.Value | |||||
| : null | |||||
| : null, | |||||
| data.Action.Metadata.IsSpecified | |||||
| ? data.Action.Metadata.Value.DurationSeconds.IsSpecified | |||||
| ? data.Action.Metadata.Value.DurationSeconds.Value | |||||
| : null | |||||
| : null); | |||||
| var member = guild.GetUser(data.UserId); | |||||
| var cacheableUser = new Cacheable<SocketGuildUser, ulong>(member, | |||||
| data.UserId, | |||||
| member is not null, | |||||
| async () => | |||||
| { | |||||
| var model = await ApiClient.GetGuildMemberAsync(data.GuildId, data.UserId); | |||||
| return guild.AddOrUpdateUser(model); | |||||
| } | |||||
| ); | |||||
| ISocketMessageChannel channel = null; | |||||
| if (data.ChannelId.IsSpecified) | |||||
| channel = GetChannel(data.ChannelId.Value) as ISocketMessageChannel; | |||||
| var cacheableChannel = new Cacheable<ISocketMessageChannel, ulong>(channel, | |||||
| data.ChannelId.GetValueOrDefault(0), | |||||
| channel != null, | |||||
| async () => | |||||
| { | |||||
| if(data.ChannelId.IsSpecified) | |||||
| return await GetChannelAsync(data.ChannelId.Value).ConfigureAwait(false) as ISocketMessageChannel; | |||||
| return null; | |||||
| }); | |||||
| var cachedMsg = channel?.GetCachedMessage(data.MessageId.GetValueOrDefault(0)) as IUserMessage; | |||||
| var cacheableMessage = new Cacheable<IUserMessage, ulong>(cachedMsg, | |||||
| data.MessageId.GetValueOrDefault(0), | |||||
| cachedMsg is not null, | |||||
| async () => | |||||
| { | |||||
| if(data.MessageId.IsSpecified) | |||||
| return (await channel.GetMessageAsync(data.MessageId.Value).ConfigureAwait(false)) as IUserMessage; | |||||
| return null; | |||||
| }); | |||||
| var cachedRule = guild.GetAutoModRule(data.RuleId); | |||||
| var cacheableRule = new Cacheable<IAutoModRule, ulong>(cachedRule, | |||||
| data.RuleId, | |||||
| cachedRule is not null, | |||||
| async () => await guild.GetAutoModRuleAsync(data.RuleId)); | |||||
| var eventData = new AutoModActionExecutedData( | |||||
| cacheableRule, | |||||
| data.TriggerType, | |||||
| cacheableUser, | |||||
| cacheableChannel, | |||||
| cachedMsg is not null ? cacheableMessage : null, | |||||
| data.AlertSystemMessageId.GetValueOrDefault(0), | |||||
| data.Content, | |||||
| data.MatchedContent.IsSpecified | |||||
| ? data.MatchedContent.Value | |||||
| : null, | |||||
| data.MatchedKeyword.IsSpecified | |||||
| ? data.MatchedKeyword.Value | |||||
| : null); | |||||
| await TimedInvokeAsync(_autoModActionExecuted, nameof(AutoModActionExecuted), guild, action, eventData); | |||||
| } | |||||
| break; | |||||
| #endregion | |||||
| #region Ignored (User only) | #region Ignored (User only) | ||||
| case "CHANNEL_PINS_ACK": | case "CHANNEL_PINS_ACK": | ||||
| await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false); | await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false); | ||||
| @@ -0,0 +1,86 @@ | |||||
| using Discord.Rest; | |||||
| namespace Discord.WebSocket; | |||||
| public class AutoModActionExecutedData | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the id of the rule which action belongs to. | |||||
| /// </summary> | |||||
| public Cacheable<IAutoModRule, ulong> Rule { get; } | |||||
| /// <summary> | |||||
| /// Gets the trigger type of rule which was triggered. | |||||
| /// </summary> | |||||
| public AutoModTriggerType TriggerType { get; } | |||||
| /// <summary> | |||||
| /// Gets the user which generated the content which triggered the rule. | |||||
| /// </summary> | |||||
| public Cacheable<SocketGuildUser, ulong> User { get; } | |||||
| /// <summary> | |||||
| /// Gets the channel in which user content was posted. | |||||
| /// </summary> | |||||
| public Cacheable<ISocketMessageChannel, ulong> Channel { get; } | |||||
| /// <summary> | |||||
| /// Gets the message that triggered the action. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This property will be <see langword="null"/> if the message was blocked by the automod. | |||||
| /// </remarks> | |||||
| public Cacheable<IUserMessage, ulong>? Message { get; } | |||||
| /// <summary> | |||||
| /// Gets the id of the system auto moderation messages posted as a result of this action. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This property will be <see langword="null"/> if this event does not correspond to an action | |||||
| /// with type <see cref="AutoModActionType.SendAlertMessage"/>. | |||||
| /// </remarks> | |||||
| public ulong AlertMessageId { get; } | |||||
| /// <summary> | |||||
| /// Gets the user-generated text content. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This property will be empty if <see cref="GatewayIntents.MessageContent"/> is disabled. | |||||
| /// </remarks> | |||||
| public string Content { get; } | |||||
| /// <summary> | |||||
| /// Gets the substring in content that triggered the rule. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This property will be empty if <see cref="GatewayIntents.MessageContent"/> is disabled. | |||||
| /// </remarks> | |||||
| public string MatchedContent { get; } | |||||
| /// <summary> | |||||
| /// Gets the word or phrase configured in the rule that triggered the rule. | |||||
| /// </summary> | |||||
| public string MatchedKeyword { get; } | |||||
| internal AutoModActionExecutedData(Cacheable<IAutoModRule, ulong> rule, | |||||
| AutoModTriggerType triggerType, | |||||
| Cacheable<SocketGuildUser, ulong> user, | |||||
| Cacheable<ISocketMessageChannel, ulong> channel, | |||||
| Cacheable<IUserMessage, ulong>? message, | |||||
| ulong alertMessageId, | |||||
| string content, | |||||
| string matchedContent, | |||||
| string matchedKeyword | |||||
| ) | |||||
| { | |||||
| Rule = rule; | |||||
| TriggerType = triggerType; | |||||
| User = user; | |||||
| Channel = channel; | |||||
| Message = message; | |||||
| AlertMessageId = alertMessageId; | |||||
| Content = content; | |||||
| MatchedContent = matchedContent; | |||||
| MatchedKeyword = matchedKeyword; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,122 @@ | |||||
| using Discord.Rest; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | |||||
| using Model = Discord.API.AutoModerationRule; | |||||
| namespace Discord.WebSocket | |||||
| { | |||||
| public class SocketAutoModRule : SocketEntity<ulong>, IAutoModRule | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the guild that this rule is in. | |||||
| /// </summary> | |||||
| public SocketGuild Guild { get; } | |||||
| /// <inheritdoc/> | |||||
| public string Name { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the creator of this rule. | |||||
| /// </summary> | |||||
| public SocketGuildUser Creator { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public AutoModEventType EventType { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public AutoModTriggerType TriggerType { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public IReadOnlyCollection<string> KeywordFilter { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public IReadOnlyCollection<string> RegexPatterns { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public IReadOnlyCollection<string> AllowList { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public IReadOnlyCollection<KeywordPresetTypes> Presets { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public IReadOnlyCollection<AutoModRuleAction> Actions { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public int? MentionTotalLimit { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public bool Enabled { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the roles that are exempt from this rule. | |||||
| /// </summary> | |||||
| public IReadOnlyCollection<SocketRole> ExemptRoles { get; private set; } | |||||
| /// <summary> | |||||
| /// Gets the channels that are exempt from this rule. | |||||
| /// </summary> | |||||
| public IReadOnlyCollection<SocketGuildChannel> ExemptChannels { get; private set; } | |||||
| /// <inheritdoc/> | |||||
| public DateTimeOffset CreatedAt | |||||
| => SnowflakeUtils.FromSnowflake(Id); | |||||
| private ulong _creatorId; | |||||
| internal SocketAutoModRule(DiscordSocketClient discord, ulong id, SocketGuild guild) | |||||
| : base(discord, id) | |||||
| { | |||||
| Guild = guild; | |||||
| } | |||||
| internal static SocketAutoModRule Create(DiscordSocketClient discord, SocketGuild guild, Model model) | |||||
| { | |||||
| var entity = new SocketAutoModRule(discord, model.Id, guild); | |||||
| entity.Update(model); | |||||
| return entity; | |||||
| } | |||||
| internal void Update(Model model) | |||||
| { | |||||
| Name = model.Name; | |||||
| _creatorId = model.CreatorId; | |||||
| Creator ??= Guild.GetUser(_creatorId); | |||||
| EventType = model.EventType; | |||||
| TriggerType = model.TriggerType; | |||||
| KeywordFilter = model.TriggerMetadata.KeywordFilter.GetValueOrDefault(Array.Empty<string>()).ToImmutableArray(); | |||||
| Presets = model.TriggerMetadata.Presets.GetValueOrDefault(Array.Empty<KeywordPresetTypes>()).ToImmutableArray(); | |||||
| RegexPatterns = model.TriggerMetadata.RegexPatterns.GetValueOrDefault(Array.Empty<string>()).ToImmutableArray(); | |||||
| AllowList = model.TriggerMetadata.AllowList.GetValueOrDefault(Array.Empty<string>()).ToImmutableArray(); | |||||
| MentionTotalLimit = model.TriggerMetadata.MentionLimit.IsSpecified | |||||
| ? model.TriggerMetadata.MentionLimit.Value | |||||
| : null; | |||||
| Actions = model.Actions.Select(x => new AutoModRuleAction(x.Type, x.Metadata.GetValueOrDefault()?.ChannelId.ToNullable(), x.Metadata.GetValueOrDefault()?.DurationSeconds.ToNullable())).ToImmutableArray(); | |||||
| Enabled = model.Enabled; | |||||
| ExemptRoles = model.ExemptRoles.Select(x => Guild.GetRole(x)).ToImmutableArray(); | |||||
| ExemptChannels = model.ExemptChannels.Select(x => Guild.GetChannel(x)).ToImmutableArray(); | |||||
| } | |||||
| /// <inheritdoc/> | |||||
| public async Task ModifyAsync(Action<AutoModRuleProperties> func, RequestOptions options = null) | |||||
| { | |||||
| var model = await GuildHelper.ModifyRuleAsync(Discord, this, func, options); | |||||
| Guild.AddOrUpdateAutoModRule(model); | |||||
| } | |||||
| /// <inheritdoc/> | |||||
| public Task DeleteAsync(RequestOptions options = null) | |||||
| => GuildHelper.DeleteRuleAsync(Discord, this, options); | |||||
| internal SocketAutoModRule Clone() => MemberwiseClone() as SocketAutoModRule; | |||||
| #region IAutoModRule | |||||
| IReadOnlyCollection<ulong> IAutoModRule.ExemptRoles => ExemptRoles.Select(x => x.Id).ToImmutableArray(); | |||||
| IReadOnlyCollection<ulong> IAutoModRule.ExemptChannels => ExemptChannels.Select(x => x.Id).ToImmutableArray(); | |||||
| ulong IAutoModRule.GuildId => Guild.Id; | |||||
| ulong IAutoModRule.CreatorId => _creatorId; | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| @@ -11,6 +11,7 @@ using System.IO; | |||||
| using System.Linq; | using System.Linq; | ||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using AutoModRuleModel = Discord.API.AutoModerationRule; | |||||
| using ChannelModel = Discord.API.Channel; | using ChannelModel = Discord.API.Channel; | ||||
| using EmojiUpdateModel = Discord.API.Gateway.GuildEmojiUpdateEvent; | using EmojiUpdateModel = Discord.API.Gateway.GuildEmojiUpdateEvent; | ||||
| using EventModel = Discord.API.GuildScheduledEvent; | using EventModel = Discord.API.GuildScheduledEvent; | ||||
| @@ -43,6 +44,7 @@ namespace Discord.WebSocket | |||||
| private ConcurrentDictionary<ulong, SocketVoiceState> _voiceStates; | private ConcurrentDictionary<ulong, SocketVoiceState> _voiceStates; | ||||
| private ConcurrentDictionary<ulong, SocketCustomSticker> _stickers; | private ConcurrentDictionary<ulong, SocketCustomSticker> _stickers; | ||||
| private ConcurrentDictionary<ulong, SocketGuildEvent> _events; | private ConcurrentDictionary<ulong, SocketGuildEvent> _events; | ||||
| private ConcurrentDictionary<ulong, SocketAutoModRule> _automodRules; | |||||
| private ImmutableArray<GuildEmote> _emotes; | private ImmutableArray<GuildEmote> _emotes; | ||||
| private AudioClient _audioClient; | private AudioClient _audioClient; | ||||
| @@ -391,6 +393,7 @@ namespace Discord.WebSocket | |||||
| { | { | ||||
| _audioLock = new SemaphoreSlim(1, 1); | _audioLock = new SemaphoreSlim(1, 1); | ||||
| _emotes = ImmutableArray.Create<GuildEmote>(); | _emotes = ImmutableArray.Create<GuildEmote>(); | ||||
| _automodRules = new ConcurrentDictionary<ulong, SocketAutoModRule>(); | |||||
| } | } | ||||
| internal static SocketGuild Create(DiscordSocketClient discord, ClientState state, ExtendedModel model) | internal static SocketGuild Create(DiscordSocketClient discord, ClientState state, ExtendedModel model) | ||||
| { | { | ||||
| @@ -1809,6 +1812,78 @@ namespace Discord.WebSocket | |||||
| internal SocketGuild Clone() => MemberwiseClone() as SocketGuild; | internal SocketGuild Clone() => MemberwiseClone() as SocketGuild; | ||||
| #endregion | #endregion | ||||
| #region AutoMod | |||||
| internal SocketAutoModRule AddOrUpdateAutoModRule(AutoModRuleModel model) | |||||
| { | |||||
| if (_automodRules.TryGetValue(model.Id, out var rule)) | |||||
| { | |||||
| rule.Update(model); | |||||
| return rule; | |||||
| } | |||||
| var socketRule = SocketAutoModRule.Create(Discord, this, model); | |||||
| _automodRules.TryAdd(model.Id, socketRule); | |||||
| return socketRule; | |||||
| } | |||||
| /// <summary> | |||||
| /// Gets a single rule configured in a guild from cache. Returns <see langword="null"/> if the rule was not found. | |||||
| /// </summary> | |||||
| public SocketAutoModRule GetAutoModRule(ulong id) | |||||
| { | |||||
| return _automodRules.TryGetValue(id, out var rule) ? rule : null; | |||||
| } | |||||
| internal SocketAutoModRule RemoveAutoModRule(ulong id) | |||||
| { | |||||
| return _automodRules.TryRemove(id, out var rule) ? rule : null; | |||||
| } | |||||
| internal SocketAutoModRule RemoveAutoModRule(AutoModRuleModel model) | |||||
| { | |||||
| if (_automodRules.TryRemove(model.Id, out var rule)) | |||||
| { | |||||
| rule.Update(model); | |||||
| } | |||||
| return rule ?? SocketAutoModRule.Create(Discord, this, model); | |||||
| } | |||||
| /// <inheritdoc cref="IGuild.GetAutoModRuleAsync"/> | |||||
| public async Task<SocketAutoModRule> GetAutoModRuleAsync(ulong ruleId, RequestOptions options = null) | |||||
| { | |||||
| var rule = await GuildHelper.GetAutoModRuleAsync(ruleId, this, Discord, options); | |||||
| return AddOrUpdateAutoModRule(rule); | |||||
| } | |||||
| /// <inheritdoc cref="IGuild.GetAutoModRulesAsync"/> | |||||
| public async Task<SocketAutoModRule[]> GetAutoModRulesAsync(RequestOptions options = null) | |||||
| { | |||||
| var rules = await GuildHelper.GetAutoModRulesAsync(this, Discord, options); | |||||
| return rules.Select(AddOrUpdateAutoModRule).ToArray(); | |||||
| } | |||||
| /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | |||||
| public async Task<SocketAutoModRule> CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options = null) | |||||
| { | |||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, props, Discord, options); | |||||
| return AddOrUpdateAutoModRule(rule); | |||||
| } | |||||
| /// <summary> | |||||
| /// Gets the auto moderation rules defined in this guild. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This property may not always return all auto moderation rules if they haven't been cached. | |||||
| /// </remarks> | |||||
| public IReadOnlyCollection<SocketAutoModRule> AutoModRules => _automodRules.ToReadOnlyCollection(); | |||||
| #endregion | |||||
| #region IGuild | #region IGuild | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| ulong? IGuild.AFKChannelId => AFKChannelId; | ulong? IGuild.AFKChannelId => AFKChannelId; | ||||
| @@ -2053,6 +2128,19 @@ namespace Discord.WebSocket | |||||
| _audioLock?.Dispose(); | _audioLock?.Dispose(); | ||||
| _audioClient?.Dispose(); | _audioClient?.Dispose(); | ||||
| } | } | ||||
| /// <inheritdoc/> | |||||
| async Task<IAutoModRule> IGuild.GetAutoModRuleAsync(ulong ruleId, RequestOptions options) | |||||
| => await GetAutoModRuleAsync(ruleId, options).ConfigureAwait(false); | |||||
| /// <inheritdoc/> | |||||
| async Task<IAutoModRule[]> IGuild.GetAutoModRulesAsync(RequestOptions options) | |||||
| => await GetAutoModRulesAsync(options).ConfigureAwait(false); | |||||
| /// <inheritdoc/> | |||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options) | |||||
| => await CreateAutoModRuleAsync(props, options).ConfigureAwait(false); | |||||
| #endregion | #endregion | ||||
| } | } | ||||
| } | } | ||||