| @@ -0,0 +1,242 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| namespace Discord; | |||||
| /// <summary> | |||||
| /// A builder used to create a <see cref="IAutoModRule"/>. | |||||
| /// </summary> | |||||
| public class AutoModRuleBuilder | |||||
| { | |||||
| private const int MaxKeywordCount = 1000; | |||||
| private const int MaxKeywordLength = 30; | |||||
| private const int MaxRegexPatternCount = 10; | |||||
| private const int MaxRegexPatternLength = 260; | |||||
| private const int MaxAllowListCountKeyword = 100; | |||||
| private const int MaxAllowListCountKeywordPreset = 1000; | |||||
| private const int MaxAllowListEntryLength = 30; | |||||
| private const int MaxMentionLimit = 50; | |||||
| private const int MaxExemptRoles = 20; | |||||
| private const int MaxExemptChannels = 50; | |||||
| private List<string> _keywordFilter = new (); | |||||
| private List<string> _regexPatterns = new (); | |||||
| private List<string> _allowList = new (); | |||||
| private List<AutoModRuleActionBuilder> _actions = new (); | |||||
| private List<ulong> _exemptRoles = new(); | |||||
| private List<ulong> _exemptChannels = new(); | |||||
| private int? _mentionLimit; | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public List<string> KeywordFilter | |||||
| { | |||||
| get { return _keywordFilter; } | |||||
| set | |||||
| { | |||||
| if (TriggerType != AutoModTriggerType.Keyword) | |||||
| throw new ArgumentException(message: $"Keyword filter can only be used with 'Keyword' trigger type.", paramName: nameof(KeywordFilter)); | |||||
| if (value.Count > MaxKeywordCount) | |||||
| throw new ArgumentException(message: $"Keyword count must be less than or equal to {MaxKeywordCount}.", paramName: nameof(KeywordFilter)); | |||||
| if (value.Any(x => x.Length > MaxKeywordLength)) | |||||
| throw new ArgumentException(message: $"Keyword length must be less than or equal to {MaxKeywordLength}.", paramName: nameof(KeywordFilter)); | |||||
| _keywordFilter = value; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public List<string> RegexPatterns | |||||
| { | |||||
| get { return _regexPatterns; } | |||||
| set | |||||
| { | |||||
| if (TriggerType != AutoModTriggerType.Keyword) | |||||
| throw new ArgumentException(message: $"Regex patterns can only be used with 'Keyword' trigger type.", paramName: nameof(RegexPatterns)); | |||||
| if (value.Count > MaxRegexPatternCount) | |||||
| throw new ArgumentException(message: $"Regex pattern count must be less than or equal to {MaxRegexPatternCount}.", paramName: nameof(RegexPatterns)); | |||||
| if (value.Any(x => x.Length > MaxRegexPatternLength)) | |||||
| throw new ArgumentException(message: $"Regex pattern must be less than or equal to {MaxRegexPatternLength}.", paramName: nameof(RegexPatterns)); | |||||
| _regexPatterns = value; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public List<string> AllowList | |||||
| { | |||||
| get { return _allowList; } | |||||
| set | |||||
| { | |||||
| 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.", paramName: nameof(AllowList)); | |||||
| if (TriggerType == AutoModTriggerType.Keyword && value.Count > MaxAllowListCountKeyword) | |||||
| throw new ArgumentException(message: $"Allow list entry count must be less than or equal to {MaxAllowListCountKeyword}.", paramName: nameof(AllowList)); | |||||
| if (TriggerType == AutoModTriggerType.KeywordPreset && value.Count > MaxAllowListCountKeywordPreset) | |||||
| throw new ArgumentException(message: $"Allow list entry count must be less than or equal to {MaxAllowListCountKeywordPreset}.", paramName: nameof(AllowList)); | |||||
| if (value.Any(x => x.Length > MaxAllowListEntryLength)) | |||||
| throw new ArgumentException(message: $"Allow list entry length must be less than or equal to {MaxAllowListEntryLength}.", paramName: nameof(AllowList)); | |||||
| _allowList = value; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public List<ulong> ExemptRoles | |||||
| { | |||||
| get { return _exemptRoles; } | |||||
| set | |||||
| { | |||||
| if (value.Count > MaxExemptRoles) | |||||
| throw new ArgumentException(message: $"Exempt roles count must be less than or equal to {MaxExemptRoles}.", paramName: nameof(RegexPatterns)); | |||||
| _exemptRoles = value; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public List<ulong> ExemptChannels | |||||
| { | |||||
| 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)); | |||||
| _exemptChannels = value; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public HashSet<KeywordPresetTypes> Presets = new (); | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public string Name { get; set; } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public AutoModEventType EventType { get; set; } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public AutoModTriggerType TriggerType { get; } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public int? MentionLimit | |||||
| { | |||||
| get => _mentionLimit; | |||||
| set | |||||
| { | |||||
| if (TriggerType != AutoModTriggerType.Keyword) | |||||
| throw new ArgumentException(message: $"MentionLimit can only be used with 'MentionSpam' trigger type.", paramName: nameof(MentionLimit)); | |||||
| if (value is not null && value > MaxMentionLimit) | |||||
| throw new ArgumentException(message: $"Mention limit must be less than or equal to {MaxMentionLimit}.", paramName: nameof(MentionLimit)); | |||||
| _mentionLimit = value; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public List<AutoModRuleActionBuilder> Actions; | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public bool Enabled { get; set; } = false; | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| /// <param name="type"></param> | |||||
| public AutoModRuleBuilder(AutoModTriggerType type) | |||||
| { | |||||
| TriggerType = type; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// Represents an action that will be preformed if a user breaks an <see cref="IAutoModRule"/>. | |||||
| /// </summary> | |||||
| public class AutoModRuleActionBuilder | |||||
| { | |||||
| private const int MaxTimeoutSeconds = 2419200; | |||||
| private TimeSpan? _timeoutDuration; | |||||
| /// <summary> | |||||
| /// Gets or sets the type for this action. | |||||
| /// </summary> | |||||
| public AutoModActionType Type { get; } | |||||
| /// <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 => _timeoutDuration; | |||||
| set | |||||
| { | |||||
| if(value is { TotalSeconds: > MaxTimeoutSeconds }) | |||||
| throw new ArgumentException(message: $"Field count must be less than or equal to {MaxTimeoutSeconds}.", paramName: nameof(TimeoutDuration)); | |||||
| _timeoutDuration = value; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public AutoModRuleActionBuilder() | |||||
| { | |||||
| } | |||||
| /// <summary> | |||||
| /// | |||||
| /// </summary> | |||||
| public AutoModRuleActionBuilder(AutoModActionType type, ulong? channelId, TimeSpan? duration) | |||||
| { | |||||
| Type = type; | |||||
| ChannelId = channelId; | |||||
| TimeoutDuration = duration; | |||||
| } | |||||
| } | |||||
| @@ -1290,6 +1290,6 @@ namespace Discord | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents the asynchronous creation operation. The task result contains the created <see cref="WelcomeScreen"/>. | /// A task that represents the asynchronous creation operation. The task result contains the created <see cref="WelcomeScreen"/>. | ||||
| /// </returns> | /// </returns> | ||||
| Task<IAutoModRule> CreateAutoModRuleAsync(RequestOptions options = null); | |||||
| Task<IAutoModRule> CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options = null); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,3 +1,4 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | using System.Linq; | ||||
| @@ -8,13 +9,28 @@ namespace Discord.API.Rest | |||||
| { | { | ||||
| internal class CreateAutoModRuleParams | internal class CreateAutoModRuleParams | ||||
| { | { | ||||
| [JsonProperty("name")] | |||||
| public string Name { get; set; } | public string Name { get; set; } | ||||
| [JsonProperty("event_type")] | |||||
| public AutoModEventType EventType { get; set; } | public AutoModEventType EventType { get; set; } | ||||
| [JsonProperty("trigger_type")] | |||||
| public AutoModTriggerType TriggerType { get; set; } | public AutoModTriggerType TriggerType { get; set; } | ||||
| [JsonProperty("trigger_metadata")] | |||||
| public Optional<TriggerMetadata> TriggerMetadata { get; set; } | public Optional<TriggerMetadata> TriggerMetadata { get; set; } | ||||
| [JsonProperty("actions")] | |||||
| public AutoModAction[] Actions { get; set; } | public AutoModAction[] Actions { get; set; } | ||||
| [JsonProperty("enabled")] | |||||
| public Optional<bool> Enabled { get; set; } | public Optional<bool> Enabled { get; set; } | ||||
| [JsonProperty("exempt_roles")] | |||||
| public Optional<ulong[]> ExemptRoles { get; set; } | public Optional<ulong[]> ExemptRoles { get; set; } | ||||
| [JsonProperty("exempt_channels")] | |||||
| public Optional<ulong[]> ExemptChannels { get; set; } | public Optional<ulong[]> ExemptChannels { get; set; } | ||||
| } | } | ||||
| } | } | ||||
| @@ -1064,10 +1064,8 @@ namespace Discord.Rest | |||||
| #region Auto Mod | #region Auto Mod | ||||
| public static Task<AutoModerationRule> CreateAutoModRuleAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) | |||||
| { | |||||
| throw new NotImplementedException(); | |||||
| } | |||||
| public static async Task<AutoModerationRule> CreateAutoModRuleAsync(IGuild guild, CreateAutoModRuleParams args, BaseDiscordClient client, RequestOptions options) | |||||
| => await client.ApiClient.CreateGuildAutoModRuleAsync(guild.Id, args, options); | |||||
| public static async Task<AutoModerationRule> GetAutoModRuleAsync(ulong ruleId, IGuild guild, BaseDiscordClient client, RequestOptions options) | public static async Task<AutoModerationRule> GetAutoModRuleAsync(ulong ruleId, IGuild guild, BaseDiscordClient client, RequestOptions options) | ||||
| => await client.ApiClient.GetGuildAutoModRuleAsync(guild.Id, ruleId, options); | => await client.ApiClient.GetGuildAutoModRuleAsync(guild.Id, ruleId, options); | ||||
| @@ -89,8 +89,13 @@ public class RestAutoModRule : RestEntity<ulong>, IAutoModRule | |||||
| } | } | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task ModifyAsync(Action<AutoModRuleProperties> func, RequestOptions options = null) => throw new NotImplementedException(); | |||||
| public async Task ModifyAsync(Action<AutoModRuleProperties> func, RequestOptions options = null) | |||||
| { | |||||
| var model = await GuildHelper.ModifyRuleAsync(Discord, this, func, options); | |||||
| Update(model); | |||||
| } | |||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public Task DeleteAsync(RequestOptions options = null) => throw new NotImplementedException(); | |||||
| public Task DeleteAsync(RequestOptions options = null) | |||||
| => GuildHelper.DeleteRuleAsync(Discord, this, options); | |||||
| } | } | ||||
| @@ -1217,10 +1217,11 @@ namespace Discord.Rest | |||||
| } | } | ||||
| /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | ||||
| public async Task<RestAutoModRule> CreateAutoModRuleAsync(RequestOptions options = null) | |||||
| public async Task<RestAutoModRule> CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options = null) | |||||
| { | { | ||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, Discord, options); | |||||
| throw new NotImplementedException(); | |||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, builder.ToProperties(), Discord, options); | |||||
| return RestAutoModRule.Create(Discord, rule); | |||||
| } | } | ||||
| @@ -1580,8 +1581,8 @@ namespace Discord.Rest | |||||
| => await GetAutoModRulesAsync(options).ConfigureAwait(false); | => await GetAutoModRulesAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(RequestOptions options) | |||||
| => await CreateAutoModRuleAsync(options).ConfigureAwait(false); | |||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options) | |||||
| => await CreateAutoModRuleAsync(builder, options).ConfigureAwait(false); | |||||
| #endregion | #endregion | ||||
| } | } | ||||
| @@ -214,5 +214,34 @@ namespace Discord.Rest | |||||
| Id = interaction.Id, | Id = interaction.Id, | ||||
| }; | }; | ||||
| } | } | ||||
| public static API.Rest.CreateAutoModRuleParams ToProperties(this AutoModRuleBuilder builder) | |||||
| { | |||||
| return new API.Rest.CreateAutoModRuleParams | |||||
| { | |||||
| TriggerMetadata = new API.TriggerMetadata | |||||
| { | |||||
| KeywordFilter = builder.KeywordFilter?.ToArray(), | |||||
| AllowList = builder.AllowList?.ToArray(), | |||||
| MentionLimit = builder.MentionLimit ?? Optional<int>.Unspecified, | |||||
| Presets = builder.Presets?.ToArray(), | |||||
| RegexPatterns = builder.RegexPatterns?.ToArray(), | |||||
| }, | |||||
| TriggerType = builder.TriggerType, | |||||
| Actions = builder.Actions?.Select(x => new API.AutoModAction | |||||
| { | |||||
| Metadata = new API.ActionMetadata | |||||
| { | |||||
| ChannelId = x.ChannelId ?? Optional<ulong>.Unspecified, | |||||
| DurationSeconds = (int?)x.TimeoutDuration?.TotalSeconds ?? Optional<int>.Unspecified | |||||
| }, | |||||
| Type = x.Type, | |||||
| }).ToArray(), | |||||
| Enabled = builder.Enabled, | |||||
| EventType = builder.EventType, | |||||
| ExemptChannels = builder.ExemptChannels?.ToArray() ?? Optional<ulong[]>.Unspecified, | |||||
| ExemptRoles = builder.ExemptRoles?.ToArray() ?? Optional <ulong[]>.Unspecified, | |||||
| }; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -3,7 +3,6 @@ using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Linq; | using System.Linq; | ||||
| using System.Text; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.AutoModerationRule; | using Model = Discord.API.AutoModerationRule; | ||||
| @@ -101,8 +100,11 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| public Task ModifyAsync(Action<AutoModRuleProperties> func, RequestOptions options = null) | |||||
| => GuildHelper.ModifyRuleAsync(Discord, this, func, options); | |||||
| public async Task ModifyAsync(Action<AutoModRuleProperties> func, RequestOptions options = null) | |||||
| { | |||||
| var model = await GuildHelper.ModifyRuleAsync(Discord, this, func, options); | |||||
| Guild.AddOrUpdateAutoModRule(model); | |||||
| } | |||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| public Task DeleteAsync(RequestOptions options = null) | public Task DeleteAsync(RequestOptions options = null) | ||||
| @@ -1827,7 +1827,10 @@ namespace Discord.WebSocket | |||||
| return socketRule; | return socketRule; | ||||
| } | } | ||||
| internal SocketAutoModRule GetAutoModRule(ulong id) | |||||
| /// <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; | return _automodRules.TryGetValue(id, out var rule) ? rule : null; | ||||
| } | } | ||||
| @@ -1864,10 +1867,11 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | ||||
| public async Task<SocketAutoModRule> CreateAutoModRuleAsync(RequestOptions options = null) | |||||
| public async Task<SocketAutoModRule> CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options = null) | |||||
| { | { | ||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, Discord, options); | |||||
| throw new NotImplementedException(); | |||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, builder.ToProperties(), Discord, options); | |||||
| return AddOrUpdateAutoModRule(rule); | |||||
| } | } | ||||
| #endregion | #endregion | ||||
| @@ -2126,8 +2130,8 @@ namespace Discord.WebSocket | |||||
| => await GetAutoModRulesAsync(options).ConfigureAwait(false); | => await GetAutoModRulesAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(RequestOptions options) | |||||
| => await CreateAutoModRuleAsync(options).ConfigureAwait(false); | |||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options) | |||||
| => await CreateAutoModRuleAsync(builder, options).ConfigureAwait(false); | |||||
| #endregion | #endregion | ||||
| } | } | ||||