| @@ -1,102 +0,0 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// Provides properties used to create a <see cref="IAutoModRule"/>. | |||||
| /// </summary> | |||||
| public class AutoModRule | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the name for the rule. | |||||
| /// </summary> | |||||
| public string Name { get; } | |||||
| /// <summary> | |||||
| /// Gets the event type for the rule. | |||||
| /// </summary> | |||||
| public AutoModEventType EventType { get; } | |||||
| /// <summary> | |||||
| /// Gets the trigger type for the rule. | |||||
| /// </summary> | |||||
| public AutoModTriggerType TriggerType { get; } | |||||
| /// <summary> | |||||
| /// Gets the keyword filter for the rule. | |||||
| /// </summary> | |||||
| public string[] KeywordFilter { get; } | |||||
| /// <summary> | |||||
| /// Gets regex patterns for the rule. Empty if the rule has no regexes. | |||||
| /// </summary> | |||||
| public string[] RegexPatterns { get; } | |||||
| /// <summary> | |||||
| /// Gets the allow list for the rule. Empty if the rule has no allowed terms. | |||||
| /// </summary> | |||||
| public string[] AllowList { get; } | |||||
| /// <summary> | |||||
| /// Gets total mention limit for the rule. <see langword="null"/> if not provided. | |||||
| /// </summary> | |||||
| public int? MentionLimit { get; } | |||||
| /// <summary> | |||||
| /// Gets the presets for the rule. Empty if the rule has no presets. | |||||
| /// </summary> | |||||
| public KeywordPresetTypes[] Presets { get; } | |||||
| /// <summary> | |||||
| /// Gets the actions for the rule. | |||||
| /// </summary> | |||||
| public AutoModRuleAction[] Actions { get; } | |||||
| /// <summary> | |||||
| /// Gets whether or not the rule is enabled. | |||||
| /// </summary> | |||||
| public bool Enabled { get; } | |||||
| /// <summary> | |||||
| /// Gets the exempt roles for the rule. Empty if the rule has no exempt roles. | |||||
| /// </summary> | |||||
| public ulong[] ExemptRoles { get; } | |||||
| /// <summary> | |||||
| /// Gets the exempt channels for the rule. Empty if the rule has no exempt channels. | |||||
| /// </summary> | |||||
| 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,508 +0,0 @@ | |||||
| using Newtonsoft.Json.Linq; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| using System.Linq; | |||||
| namespace Discord; | |||||
| /// <summary> | |||||
| /// A builder used to create a <see cref="IAutoModRule"/>. | |||||
| /// </summary> | |||||
| public class AutoModRuleBuilder | |||||
| { | |||||
| /// <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; | |||||
| private List<string> _keywordFilter = new(); | |||||
| private List<string> _regexPatterns = new(); | |||||
| private List<string> _allowList = new(); | |||||
| private List<ulong> _exemptRoles = new(); | |||||
| private List<ulong> _exemptChannels = new(); | |||||
| private HashSet<KeywordPresetTypes> _presets = new(); | |||||
| private int? _mentionLimit; | |||||
| /// <summary> | |||||
| /// Gets or sets the list of <see cref="string"/> of an <see cref="AutoModRule"/>. | |||||
| /// </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> | |||||
| /// Gets or sets the list of <see cref="string"/> of an <see cref="AutoModRule"/>. | |||||
| /// </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> | |||||
| /// Gets or sets the list of <see cref="string"/> of an <see cref="AutoModRule"/>. | |||||
| /// </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> | |||||
| /// Gets or sets the list of <see cref="ulong"/> of an <see cref="AutoModRule"/>. | |||||
| /// </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> | |||||
| /// Gets or sets the list of <see cref="ulong"/> of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| public List<ulong> ExemptChannels | |||||
| { | |||||
| get { return _exemptChannels; } | |||||
| set | |||||
| { | |||||
| if (value.Count > MaxExemptChannels) | |||||
| throw new ArgumentException(message: $"Exempt channels count must be less than or equal to {MaxExemptChannels}.", paramName: nameof(RegexPatterns)); | |||||
| _exemptChannels = value; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// Gets or sets the hashset of <see cref="KeywordPresetTypes"/> of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| public HashSet<KeywordPresetTypes> 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; | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// Gets or sets the name of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| public string Name { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the event type of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| public AutoModEventType EventType { get; set; } = AutoModEventType.MessageSend; | |||||
| /// <summary> | |||||
| /// Gets the trigger type of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| public AutoModTriggerType TriggerType { get; } | |||||
| /// <summary> | |||||
| /// Gets or sets the mention limit of an <see cref="AutoModRule"/>. | |||||
| /// </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> | |||||
| /// Gets or sets the list of <see cref="AutoModRuleActionBuilder"/> of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| public List<AutoModRuleActionBuilder> Actions = new(); | |||||
| /// <summary> | |||||
| /// Gets or sets the enabled status of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| public bool Enabled { get; set; } = false; | |||||
| /// <summary> | |||||
| /// Initializes a new instance of <see cref="AutoModRuleBuilder"/> used to create a new <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <param name="type">The trigger type of an <see cref="AutoModRule"/></param> | |||||
| public AutoModRuleBuilder(AutoModTriggerType type) | |||||
| { | |||||
| TriggerType = type; | |||||
| } | |||||
| /// <summary> | |||||
| /// Sets the name of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder WithName(string name) | |||||
| { | |||||
| Name = name; | |||||
| return this; | |||||
| } | |||||
| /// <summary> | |||||
| /// Sets the enabled status of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder WithEnabled(bool enabled) | |||||
| { | |||||
| Enabled = enabled; | |||||
| return this; | |||||
| } | |||||
| /// <summary> | |||||
| /// Sets the event type of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder WithEventType(AutoModEventType eventType) | |||||
| { | |||||
| EventType = eventType; | |||||
| return this; | |||||
| } | |||||
| /// <summary> | |||||
| /// Sets the mention limit of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder WithMentionLimit(int limit) | |||||
| { | |||||
| MentionLimit = limit; | |||||
| return this; | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds a <see cref="string"/> keyword to an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder AddKeyword(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; | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds a <see cref="string"/> allow list keyword to an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder AddAllowListKeyword(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; | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds a <see cref="KeyNotFoundException"/> to an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| 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; | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds a <see cref="string"/> regex pattern to an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| 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; | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds an exempt <see cref="IRole"/> to an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder AddExemptRole(IRole role) | |||||
| { | |||||
| AddExemptRole(role.Id); | |||||
| return this; | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds a exempt role with <see cref="ulong"/> id keyword to an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder AddExemptRole(ulong roleId) | |||||
| { | |||||
| ExemptRoles.Add(roleId); | |||||
| return this; | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds an exempt <see cref="IMessageChannel"/> keyword to an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder AddExemptChannel(IMessageChannel channel) | |||||
| { | |||||
| AddExemptChannel(channel.Id); | |||||
| return this; | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds an exempt channel with <see cref="ulong"/> id keyword to an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder AddExemptChannel(ulong channelId) | |||||
| { | |||||
| ExemptChannels.Add(channelId); | |||||
| return this; | |||||
| } | |||||
| /// <summary> | |||||
| /// Adds an <see cref="AutoModRuleActionBuilder"/> to an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The current builder.</returns> | |||||
| public AutoModRuleBuilder AddAction(AutoModRuleActionBuilder builder) | |||||
| { | |||||
| Actions.Add(builder); | |||||
| return this; | |||||
| } | |||||
| /// <summary> | |||||
| /// Creates a new instance of <see cref="AutoModRuleBuilder"/> with data from a <see cref="IAutoModRule"/>. | |||||
| /// </summary> | |||||
| /// <returns>The new builder.</returns> | |||||
| 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<KeywordPresetTypes>(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 | |||||
| }; | |||||
| /// <summary> | |||||
| /// Builds the <see cref="AutoModRuleBuilder" /> into <see cref="AutoModRule"/> ready to be sent. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| 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()); | |||||
| } | |||||
| } | |||||
| /// <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> | |||||
| /// <remarks> | |||||
| /// This property will be <see langword="null"/> if <see cref="Type"/> is not <see cref="AutoModActionType.SendAlertMessage"/> | |||||
| /// </remarks> | |||||
| public ulong? ChannelId { get; set; } | |||||
| /// <summary> | |||||
| /// Gets or sets the duration of which a user will be timed out for breaking this rule. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// This property will be <see langword="null"/> if <see cref="Type"/> is not <see cref="AutoModActionType.Timeout"/> | |||||
| /// </remarks> | |||||
| 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> | |||||
| /// Creates a new instance of <see cref="AutoModRuleActionBuilder"/> used to define actions of an <see cref="AutoModRule"/>. | |||||
| /// </summary> | |||||
| 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); | |||||
| } | |||||
| @@ -11,6 +11,61 @@ namespace Discord | |||||
| /// </summary> | /// </summary> | ||||
| public class AutoModRuleProperties | 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> | /// <summary> | ||||
| /// Gets or sets the name for the rule. | /// Gets or sets the name for the rule. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -1290,6 +1290,6 @@ namespace Discord | |||||
| /// <returns> | /// <returns> | ||||
| /// A task that represents the asynchronous creation operation. The task result contains the created <see cref="IAutoModRule"/>. | /// A task that represents the asynchronous creation operation. The task result contains the created <see cref="IAutoModRule"/>. | ||||
| /// </returns> | /// </returns> | ||||
| Task<IAutoModRule> CreateAutoModRuleAsync(AutoModRule props, RequestOptions options = null); | |||||
| Task<IAutoModRule> CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options = null); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1039,7 +1039,7 @@ namespace Discord.Rest | |||||
| { | { | ||||
| Enabled = enabled, | Enabled = enabled, | ||||
| Description = description, | Description = description, | ||||
| WelcomeChannels = channels?.Select(ch => new API.WelcomeScreenChannel | |||||
| WelcomeChannels = channels?.Select(ch => new API.WelcomeScreenChannel | |||||
| { | { | ||||
| ChannelId = ch.Id, | ChannelId = ch.Id, | ||||
| Description = ch.Description, | Description = ch.Description, | ||||
| @@ -1050,7 +1050,7 @@ namespace Discord.Rest | |||||
| var model = await client.ApiClient.ModifyGuildWelcomeScreenAsync(args, guild.Id, options); | var model = await client.ApiClient.ModifyGuildWelcomeScreenAsync(args, guild.Id, options); | ||||
| if(model.WelcomeChannels.Length == 0) | |||||
| if (model.WelcomeChannels.Length == 0) | |||||
| return null; | return null; | ||||
| return new WelcomeScreen(model.Description.GetValueOrDefault(null), model.WelcomeChannels.Select( | return new WelcomeScreen(model.Description.GetValueOrDefault(null), model.WelcomeChannels.Select( | ||||
| @@ -1064,8 +1064,123 @@ namespace Discord.Rest | |||||
| #region Auto Mod | #region Auto Mod | ||||
| 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> 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) | 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); | ||||
| @@ -1217,9 +1217,9 @@ namespace Discord.Rest | |||||
| } | } | ||||
| /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | ||||
| public async Task<RestAutoModRule> CreateAutoModRuleAsync(AutoModRule props, RequestOptions options = null) | |||||
| public async Task<RestAutoModRule> CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options = null) | |||||
| { | { | ||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, props.ToProperties(), Discord, options); | |||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, props, Discord, options); | |||||
| return RestAutoModRule.Create(Discord, rule); | return RestAutoModRule.Create(Discord, rule); | ||||
| } | } | ||||
| @@ -1581,7 +1581,7 @@ namespace Discord.Rest | |||||
| => await GetAutoModRulesAsync(options).ConfigureAwait(false); | => await GetAutoModRulesAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(AutoModRule props, RequestOptions options) | |||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options) | |||||
| => await CreateAutoModRuleAsync(props, options).ConfigureAwait(false); | => await CreateAutoModRuleAsync(props, options).ConfigureAwait(false); | ||||
| #endregion | #endregion | ||||
| @@ -214,35 +214,5 @@ namespace Discord.Rest | |||||
| Id = interaction.Id, | Id = interaction.Id, | ||||
| }; | }; | ||||
| } | } | ||||
| public static API.Rest.CreateAutoModRuleParams ToProperties(this AutoModRule builder) | |||||
| { | |||||
| return new API.Rest.CreateAutoModRuleParams | |||||
| { | |||||
| Name = builder.Name, | |||||
| TriggerMetadata = new API.TriggerMetadata | |||||
| { | |||||
| KeywordFilter = builder.KeywordFilter ?? Optional<string[]>.Unspecified, | |||||
| AllowList = builder.AllowList ?? Optional<string[]>.Unspecified, | |||||
| MentionLimit = builder.MentionLimit ?? Optional<int>.Unspecified, | |||||
| Presets = builder.Presets ?? Optional<KeywordPresetTypes[]>.Unspecified, | |||||
| RegexPatterns = builder.RegexPatterns ?? Optional<string[]>.Unspecified, | |||||
| }, | |||||
| 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 ?? Optional<ulong[]>.Unspecified, | |||||
| ExemptRoles = builder.ExemptRoles ?? Optional<ulong[]>.Unspecified, | |||||
| }; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1867,9 +1867,9 @@ namespace Discord.WebSocket | |||||
| } | } | ||||
| /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | /// <inheritdoc cref="IGuild.CreateAutoModRuleAsync"/> | ||||
| public async Task<SocketAutoModRule> CreateAutoModRuleAsync(AutoModRule props, RequestOptions options = null) | |||||
| public async Task<SocketAutoModRule> CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options = null) | |||||
| { | { | ||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, props.ToProperties(), Discord, options); | |||||
| var rule = await GuildHelper.CreateAutoModRuleAsync(this, props, Discord, options); | |||||
| return AddOrUpdateAutoModRule(rule); | return AddOrUpdateAutoModRule(rule); | ||||
| } | } | ||||
| @@ -2138,7 +2138,7 @@ namespace Discord.WebSocket | |||||
| => await GetAutoModRulesAsync(options).ConfigureAwait(false); | => await GetAutoModRulesAsync(options).ConfigureAwait(false); | ||||
| /// <inheritdoc/> | /// <inheritdoc/> | ||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(AutoModRule props, RequestOptions options) | |||||
| async Task<IAutoModRule> IGuild.CreateAutoModRuleAsync(Action<AutoModRuleProperties> props, RequestOptions options) | |||||
| => await CreateAutoModRuleAsync(props, options).ConfigureAwait(false); | => await CreateAutoModRuleAsync(props, options).ConfigureAwait(false); | ||||
| #endregion | #endregion | ||||