@@ -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 | ||||