From afc98e32860e728fbf1f53316eab2c2ad41804f3 Mon Sep 17 00:00:00 2001 From: Misha133 Date: Sun, 29 Jan 2023 23:53:12 +0300 Subject: [PATCH] working state --- .../Guilds/AutoModeration/AutoModRule.cs | 102 +++++++ .../AutoModeration/AutoModRuleBuilder.cs | 274 ++++++++++++++++-- .../Entities/Guilds/IGuild.cs | 2 +- src/Discord.Net.Rest/DiscordRestApiClient.cs | 4 +- .../Entities/Guilds/RestGuild.cs | 8 +- .../Extensions/EntityExtensions.cs | 15 +- .../Entities/Guilds/SocketGuild.cs | 8 +- 7 files changed, 374 insertions(+), 39 deletions(-) create mode 100644 src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRule.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRule.cs b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRule.cs new file mode 100644 index 000000000..d65a70caa --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRule.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + /// + /// Provides properties used to create a . + /// + public class AutoModRule + { + /// + /// Gets the name for the rule. + /// + public string Name { get; } + + /// + /// Gets the event type for the rule. + /// + public AutoModEventType EventType { get; } + + /// + /// Gets the trigger type for the rule. + /// + public AutoModTriggerType TriggerType { get; } + + /// + /// Gets the keyword filter for the rule. + /// + public string[] KeywordFilter { get; } + + /// + /// Gets regex patterns for the rule. + /// + public string[] RegexPatterns { get; } + + /// + /// Gets the allow list for the rule. + /// + public string[] AllowList { get; } + + /// + /// Gets total mention limit for the rule. + /// + public int? MentionLimit { get; } + + /// + /// Gets the presets for the rule. + /// + public KeywordPresetTypes[] Presets { get; } + + /// + /// Gets the actions for the rule. + /// + public AutoModRuleAction[] Actions { get; } + + /// + /// Gets whether or not the rule is enabled. + /// + public bool Enabled { get; } + + /// + /// Gets the exempt roles for the rule. + /// + public ulong[] ExemptRoles { get; } + + /// + /// Gets the exempt channels for the rule. + /// + public ulong[] ExemptChannels { get; } + + internal AutoModRule(string name, + AutoModEventType eventType, + AutoModTriggerType triggerType, + string[] keywordFilter, + string[] regexPatterns, + string[] allowList, + int? mentionLimit, + KeywordPresetTypes[] presets, + AutoModRuleAction[] actions, + bool enabled, + ulong[] exemptRoles, + ulong[] exemptChannels) + { + Name = name; + EventType = eventType; + TriggerType = triggerType; + KeywordFilter = keywordFilter; + RegexPatterns = regexPatterns; + AllowList = allowList; + MentionLimit = mentionLimit; + Presets = presets; + Actions = actions; + Enabled = enabled; + ExemptRoles = exemptRoles; + ExemptChannels = exemptChannels; + } + } + +} diff --git a/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleBuilder.cs b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleBuilder.cs index 17b789877..dd5e5faa0 100644 --- a/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleBuilder.cs +++ b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleBuilder.cs @@ -1,5 +1,8 @@ +using Newtonsoft.Json.Linq; + using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; namespace Discord; @@ -24,10 +27,9 @@ public class AutoModRuleBuilder private const int MaxExemptRoles = 20; private const int MaxExemptChannels = 50; - private List _keywordFilter = new (); - private List _regexPatterns = new (); - private List _allowList = new (); - private List _actions = new (); + private List _keywordFilter = new(); + private List _regexPatterns = new(); + private List _allowList = new(); private List _exemptRoles = new(); private List _exemptChannels = new(); @@ -123,8 +125,8 @@ public class AutoModRuleBuilder get { return _exemptChannels; } set { - if (value.Count > MaxExemptRoles) - throw new ArgumentException(message: $"Exempt channels count must be less than or equal to {MaxExemptRoles}.", paramName: nameof(RegexPatterns)); + if (value.Count > MaxExemptChannels) + throw new ArgumentException(message: $"Exempt channels count must be less than or equal to {MaxExemptChannels}.", paramName: nameof(RegexPatterns)); _exemptChannels = value; } @@ -133,7 +135,17 @@ public class AutoModRuleBuilder /// /// /// - public HashSet Presets = new (); + public HashSet Presets + { + get => _presets; + set + { + if (TriggerType != AutoModTriggerType.KeywordPreset) + throw new ArgumentException(message: $"Keyword presets scan only be used with 'KeywordPreset' trigger type.", paramName: nameof(AllowList)); + + _presets = value; + } + } /// /// @@ -143,12 +155,12 @@ public class AutoModRuleBuilder /// /// /// - public AutoModEventType EventType { get; set; } + public AutoModEventType EventType { get; set; } = AutoModEventType.MessageSend; /// /// /// - public AutoModTriggerType TriggerType { get; } + public AutoModTriggerType TriggerType { get; } /// /// @@ -165,13 +177,14 @@ public class AutoModRuleBuilder throw new ArgumentException(message: $"Mention limit must be less than or equal to {MaxMentionLimit}.", paramName: nameof(MentionLimit)); _mentionLimit = value; } - } /// /// /// - public List Actions; + public List Actions = new(); + + private HashSet _presets = new(); /// /// @@ -186,6 +199,224 @@ public class AutoModRuleBuilder { TriggerType = type; } + + /// + /// + /// + /// + public AutoModRuleBuilder WithName(string name) + { + Name = name; + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder WithEnabled(bool enabled) + { + Enabled = enabled; + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder WithEventType(AutoModEventType eventType) + { + EventType = eventType; + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder WithMentionLimit(int limit) + { + MentionLimit = limit; + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder AddKeywordFilter(string keyword) + { + if (TriggerType != AutoModTriggerType.Keyword) + throw new ArgumentException(message: $"Keyword filter can only be used with 'Keyword' trigger type."); + + if (KeywordFilter.Count >= MaxKeywordCount) + throw new ArgumentException(message: $"Keyword count must be less than or equal to {MaxKeywordCount}."); + + if (keyword.Length > MaxKeywordLength) + throw new ArgumentException(message: $"Keyword length must be less than or equal to {MaxKeywordLength}."); + + KeywordFilter.Add(keyword); + + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder AddAllowList(string keyword) + { + if (TriggerType is not AutoModTriggerType.Keyword or AutoModTriggerType.KeywordPreset) + throw new ArgumentException(message: $"Allow list can only be used with 'Keyword' or 'KeywordPreset' trigger type."); + + if (TriggerType == AutoModTriggerType.Keyword && AllowList.Count >= MaxAllowListCountKeyword) + throw new ArgumentException(message: $"Allow list entry count must be less than or equal to {MaxAllowListCountKeyword}."); + + if (TriggerType == AutoModTriggerType.KeywordPreset && AllowList.Count > MaxAllowListCountKeywordPreset) + throw new ArgumentException(message: $"Allow list entry count must be less than or equal to {MaxAllowListCountKeywordPreset}."); + + if (keyword.Length > MaxAllowListEntryLength) + throw new ArgumentException(message: $"Allow list entry length must be less than or equal to {MaxAllowListEntryLength}."); + + AllowList.Add(keyword); + + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder AddKeywordPreset(KeywordPresetTypes type) + { + if (TriggerType != AutoModTriggerType.KeywordPreset) + throw new ArgumentException(message: $"Keyword presets scan only be used with 'KeywordPreset' trigger type.", paramName: nameof(AllowList)); + + Presets.Add(type); + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder AddRegexPattern(string regex) + { + if (TriggerType != AutoModTriggerType.Keyword) + throw new ArgumentException(message: $"Regex patterns can only be used with 'Keyword' trigger type."); + + if (RegexPatterns.Count >= MaxRegexPatternCount) + throw new ArgumentException(message: $"Regex pattern count must be less than or equal to {MaxRegexPatternCount}."); + + if (regex.Length > MaxRegexPatternLength) + throw new ArgumentException(message: $"Regex pattern must be less than or equal to {MaxRegexPatternLength}."); + + RegexPatterns.Add(regex); + + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder AddExemptRole(IRole role) + { + AddExemptRole(role.Id); + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder AddExemptRole(ulong roleId) + { + ExemptRoles.Add(roleId); + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder AddExemptChannel(IMessageChannel channel) + { + AddExemptChannel(channel.Id); + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder AddExemptChannel(ulong channelId) + { + ExemptChannels.Add(channelId); + return this; + } + + /// + /// + /// + /// + public AutoModRuleBuilder AddAction(AutoModRuleActionBuilder builder) + { + Actions.Add(builder); + return this; + } + + /// + /// + /// + /// + public static AutoModRuleBuilder FromAutoModRule(IAutoModRule rule) + => new(rule.TriggerType) + { + Name = rule.Name, + AllowList = rule.AllowList.ToList(), + RegexPatterns = rule.RegexPatterns.ToList(), + KeywordFilter = rule.KeywordFilter.ToList(), + Presets = new HashSet(rule.Presets), + ExemptChannels = rule.ExemptChannels.ToList(), + ExemptRoles = rule.ExemptRoles.ToList(), + Actions = rule.Actions.Select(AutoModRuleActionBuilder.FromModel).ToList(), + MentionLimit = rule.MentionTotalLimit, + Enabled = rule.Enabled, + EventType = rule.EventType + }; + + /// + /// + /// + /// + public AutoModRule Build() + { + if (string.IsNullOrWhiteSpace(Name)) + throw new ArgumentException("Name of the rule must not be empty", paramName: nameof(Name)); + + Preconditions.AtLeast(1, Actions.Count, nameof(Actions), "Auto moderation rule must have at least 1 action"); + + if(TriggerType is AutoModTriggerType.Keyword) + Preconditions.AtLeast(1, KeywordFilter.Count + RegexPatterns.Count, nameof(KeywordFilter), "Auto moderation rule must have at least 1 keyword or regex pattern"); + + if(TriggerType is AutoModTriggerType.MentionSpam && MentionLimit is null) + throw new ArgumentException("Mention limit must not be empty", paramName: nameof(MentionLimit)); + + return new(Name, + EventType, + TriggerType, + KeywordFilter.ToArray(), + RegexPatterns.ToArray(), + AllowList.ToArray(), + MentionLimit, + Presets.ToArray(), + Actions.Select(x => new AutoModRuleAction(x.Type, x.ChannelId, (int?)x.TimeoutDuration?.TotalSeconds)).ToArray(), + Enabled, + ExemptRoles.ToArray(), + ExemptChannels.ToArray()); + } + } /// @@ -205,17 +436,23 @@ public class AutoModRuleActionBuilder /// /// Get or sets the channel id on which to post alerts. /// + /// + /// This property will be if is not + /// public ulong? ChannelId { get; set; } /// /// Gets or sets the duration of which a user will be timed out for breaking this rule. /// + /// + /// This property will be if is not + /// public TimeSpan? TimeoutDuration { get => _timeoutDuration; set { - if(value is { TotalSeconds: > MaxTimeoutSeconds }) + if (value is { TotalSeconds: > MaxTimeoutSeconds }) throw new ArgumentException(message: $"Field count must be less than or equal to {MaxTimeoutSeconds}.", paramName: nameof(TimeoutDuration)); _timeoutDuration = value; @@ -225,18 +462,13 @@ public class AutoModRuleActionBuilder /// /// /// - public AutoModRuleActionBuilder() - { - - } - - /// - /// - /// - public AutoModRuleActionBuilder(AutoModActionType type, ulong? channelId, TimeSpan? duration) + public AutoModRuleActionBuilder(AutoModActionType type, ulong? channelId = null, TimeSpan? duration = null) { Type = type; ChannelId = channelId; TimeoutDuration = duration; } + + internal static AutoModRuleActionBuilder FromModel(AutoModRuleAction action) + => new(action.Type, action.ChannelId, action.TimeoutDuration); } diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 80330b042..d8feb69dd 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -1290,6 +1290,6 @@ namespace Discord /// /// A task that represents the asynchronous creation operation. The task result contains the created . /// - Task CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options = null); + Task CreateAutoModRuleAsync(AutoModRule props, RequestOptions options = null); } } diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index bb100e24e..13771af71 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -2129,7 +2129,7 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); - return await SendJsonAsync("POST", () => $"guilds/{guildId}/auto-moderation", args, new BucketIds(guildId), options: options); + return await SendJsonAsync("POST", () => $"guilds/{guildId}/auto-moderation/rules", args, new BucketIds(guildId), options: options); } public async Task ModifyGuildAutoModRuleAsync(ulong guildId, ulong ruleId, ModifyAutoModRuleParams args, RequestOptions options) @@ -2139,7 +2139,7 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); - return await SendJsonAsync("PATCH", () => $"guilds/{guildId}/auto-moderation/{ruleId}", args, new BucketIds(guildId), options: options); + return await SendJsonAsync("PATCH", () => $"guilds/{guildId}/auto-moderation/rules/{ruleId}", args, new BucketIds(guildId), options: options); } public async Task DeleteGuildAutoModRuleAsync(ulong guildId, ulong ruleId, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index a8d337b2a..41ce8141b 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -1217,9 +1217,9 @@ namespace Discord.Rest } /// - public async Task CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options = null) + public async Task CreateAutoModRuleAsync(AutoModRule props, RequestOptions options = null) { - var rule = await GuildHelper.CreateAutoModRuleAsync(this, builder.ToProperties(), Discord, options); + var rule = await GuildHelper.CreateAutoModRuleAsync(this, props.ToProperties(), Discord, options); return RestAutoModRule.Create(Discord, rule); } @@ -1581,8 +1581,8 @@ namespace Discord.Rest => await GetAutoModRulesAsync(options).ConfigureAwait(false); /// - async Task IGuild.CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options) - => await CreateAutoModRuleAsync(builder, options).ConfigureAwait(false); + async Task IGuild.CreateAutoModRuleAsync(AutoModRule props, RequestOptions options) + => await CreateAutoModRuleAsync(props, options).ConfigureAwait(false); #endregion } diff --git a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs index cc65a2356..914e66c0c 100644 --- a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs +++ b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs @@ -215,17 +215,18 @@ namespace Discord.Rest }; } - public static API.Rest.CreateAutoModRuleParams ToProperties(this AutoModRuleBuilder builder) + public static API.Rest.CreateAutoModRuleParams ToProperties(this AutoModRule builder) { return new API.Rest.CreateAutoModRuleParams { + Name = builder.Name, TriggerMetadata = new API.TriggerMetadata { - KeywordFilter = builder.KeywordFilter?.ToArray(), - AllowList = builder.AllowList?.ToArray(), + KeywordFilter = builder.KeywordFilter ?? Optional.Unspecified, + AllowList = builder.AllowList ?? Optional.Unspecified, MentionLimit = builder.MentionLimit ?? Optional.Unspecified, - Presets = builder.Presets?.ToArray(), - RegexPatterns = builder.RegexPatterns?.ToArray(), + Presets = builder.Presets ?? Optional.Unspecified, + RegexPatterns = builder.RegexPatterns ?? Optional.Unspecified, }, TriggerType = builder.TriggerType, Actions = builder.Actions?.Select(x => new API.AutoModAction @@ -239,8 +240,8 @@ namespace Discord.Rest }).ToArray(), Enabled = builder.Enabled, EventType = builder.EventType, - ExemptChannels = builder.ExemptChannels?.ToArray() ?? Optional.Unspecified, - ExemptRoles = builder.ExemptRoles?.ToArray() ?? Optional .Unspecified, + ExemptChannels = builder.ExemptChannels ?? Optional.Unspecified, + ExemptRoles = builder.ExemptRoles ?? Optional.Unspecified, }; } } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 271cbb36b..1496c101f 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -1867,9 +1867,9 @@ namespace Discord.WebSocket } /// - public async Task CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options = null) + public async Task CreateAutoModRuleAsync(AutoModRule props, RequestOptions options = null) { - var rule = await GuildHelper.CreateAutoModRuleAsync(this, builder.ToProperties(), Discord, options); + var rule = await GuildHelper.CreateAutoModRuleAsync(this, props.ToProperties(), Discord, options); return AddOrUpdateAutoModRule(rule); } @@ -2130,8 +2130,8 @@ namespace Discord.WebSocket => await GetAutoModRulesAsync(options).ConfigureAwait(false); /// - async Task IGuild.CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options) - => await CreateAutoModRuleAsync(builder, options).ConfigureAwait(false); + async Task IGuild.CreateAutoModRuleAsync(AutoModRule props, RequestOptions options) + => await CreateAutoModRuleAsync(props, options).ConfigureAwait(false); #endregion }