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