diff --git a/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleBuilder.cs b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleBuilder.cs
new file mode 100644
index 000000000..17b789877
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleBuilder.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Discord;
+
+///
+/// A builder used to create a .
+///
+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 _keywordFilter = new ();
+ private List _regexPatterns = new ();
+ private List _allowList = new ();
+ private List _actions = new ();
+
+ private List _exemptRoles = new();
+ private List _exemptChannels = new();
+
+ private int? _mentionLimit;
+
+ ///
+ ///
+ ///
+ public List 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;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public List 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;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public List 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;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public List 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;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public List 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;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public HashSet Presets = new ();
+
+ ///
+ ///
+ ///
+ public string Name { get; set; }
+
+ ///
+ ///
+ ///
+ public AutoModEventType EventType { get; set; }
+
+ ///
+ ///
+ ///
+ public AutoModTriggerType TriggerType { get; }
+
+ ///
+ ///
+ ///
+ 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;
+ }
+
+ }
+
+ ///
+ ///
+ ///
+ public List Actions;
+
+ ///
+ ///
+ ///
+ public bool Enabled { get; set; } = false;
+
+ ///
+ ///
+ ///
+ ///
+ public AutoModRuleBuilder(AutoModTriggerType type)
+ {
+ TriggerType = type;
+ }
+}
+
+///
+/// Represents an action that will be preformed if a user breaks an .
+///
+public class AutoModRuleActionBuilder
+{
+ private const int MaxTimeoutSeconds = 2419200;
+
+ private TimeSpan? _timeoutDuration;
+
+ ///
+ /// Gets or sets the type for this action.
+ ///
+ public AutoModActionType Type { get; }
+
+ ///
+ /// Get or sets the channel id on which to post alerts.
+ ///
+ public ulong? ChannelId { get; set; }
+
+ ///
+ /// Gets or sets the duration of which a user will be timed out for breaking this rule.
+ ///
+ 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;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public AutoModRuleActionBuilder()
+ {
+
+ }
+
+ ///
+ ///
+ ///
+ public AutoModRuleActionBuilder(AutoModActionType type, ulong? channelId, TimeSpan? duration)
+ {
+ Type = type;
+ ChannelId = channelId;
+ TimeoutDuration = duration;
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
index deb8a010f..80330b042 100644
--- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
+++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
@@ -1290,6 +1290,6 @@ namespace Discord
///
/// A task that represents the asynchronous creation operation. The task result contains the created .
///
- Task CreateAutoModRuleAsync(RequestOptions options = null);
+ Task CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options = null);
}
}
diff --git a/src/Discord.Net.Rest/API/Rest/CreateAutoModRuleParams.cs b/src/Discord.Net.Rest/API/Rest/CreateAutoModRuleParams.cs
index d126d4078..5438700c6 100644
--- a/src/Discord.Net.Rest/API/Rest/CreateAutoModRuleParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/CreateAutoModRuleParams.cs
@@ -1,3 +1,4 @@
+using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,13 +9,28 @@ namespace Discord.API.Rest
{
internal class CreateAutoModRuleParams
{
+ [JsonProperty("name")]
public string Name { get; set; }
+
+ [JsonProperty("event_type")]
public AutoModEventType EventType { get; set; }
+
+ [JsonProperty("trigger_type")]
public AutoModTriggerType TriggerType { get; set; }
+
+ [JsonProperty("trigger_metadata")]
public Optional TriggerMetadata { get; set; }
+
+ [JsonProperty("actions")]
public AutoModAction[] Actions { get; set; }
+
+ [JsonProperty("enabled")]
public Optional Enabled { get; set; }
+
+ [JsonProperty("exempt_roles")]
public Optional ExemptRoles { get; set; }
+
+ [JsonProperty("exempt_channels")]
public Optional ExemptChannels { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
index 6a78cbd53..1706608c6 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
@@ -1064,10 +1064,8 @@ namespace Discord.Rest
#region Auto Mod
- public static Task CreateAutoModRuleAsync(IGuild guild, BaseDiscordClient client, RequestOptions options)
- {
- throw new NotImplementedException();
- }
+ public static async Task CreateAutoModRuleAsync(IGuild guild, CreateAutoModRuleParams args, BaseDiscordClient client, RequestOptions options)
+ => await client.ApiClient.CreateGuildAutoModRuleAsync(guild.Id, args, options);
public static async Task GetAutoModRuleAsync(ulong ruleId, IGuild guild, BaseDiscordClient client, RequestOptions options)
=> await client.ApiClient.GetGuildAutoModRuleAsync(guild.Id, ruleId, options);
diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestAutoModRule.cs b/src/Discord.Net.Rest/Entities/Guilds/RestAutoModRule.cs
index e88f7fe61..f2dc4c9b7 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/RestAutoModRule.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/RestAutoModRule.cs
@@ -89,8 +89,13 @@ public class RestAutoModRule : RestEntity, IAutoModRule
}
///
- public Task ModifyAsync(Action func, RequestOptions options = null) => throw new NotImplementedException();
+ public async Task ModifyAsync(Action func, RequestOptions options = null)
+ {
+ var model = await GuildHelper.ModifyRuleAsync(Discord, this, func, options);
+ Update(model);
+ }
///
- public Task DeleteAsync(RequestOptions options = null) => throw new NotImplementedException();
+ public Task DeleteAsync(RequestOptions options = null)
+ => GuildHelper.DeleteRuleAsync(Discord, this, options);
}
diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
index ef6754b8c..a8d337b2a 100644
--- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
+++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
@@ -1217,10 +1217,11 @@ namespace Discord.Rest
}
///
- public async Task CreateAutoModRuleAsync(RequestOptions options = null)
+ public async Task 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);
///
- async Task IGuild.CreateAutoModRuleAsync(RequestOptions options)
- => await CreateAutoModRuleAsync(options).ConfigureAwait(false);
+ async Task IGuild.CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options)
+ => await CreateAutoModRuleAsync(builder, options).ConfigureAwait(false);
#endregion
}
diff --git a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs
index f5a88486b..cc65a2356 100644
--- a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs
+++ b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs
@@ -214,5 +214,34 @@ namespace Discord.Rest
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.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.Unspecified,
+ DurationSeconds = (int?)x.TimeoutDuration?.TotalSeconds ?? Optional.Unspecified
+ },
+ Type = x.Type,
+ }).ToArray(),
+ Enabled = builder.Enabled,
+ EventType = builder.EventType,
+ ExemptChannels = builder.ExemptChannels?.ToArray() ?? Optional.Unspecified,
+ ExemptRoles = builder.ExemptRoles?.ToArray() ?? Optional .Unspecified,
+ };
+ }
}
}
diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketAutoModRule.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketAutoModRule.cs
index c9f92eb70..d562c5a2d 100644
--- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketAutoModRule.cs
+++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketAutoModRule.cs
@@ -3,7 +3,6 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using Model = Discord.API.AutoModerationRule;
@@ -101,8 +100,11 @@ namespace Discord.WebSocket
}
///
- public Task ModifyAsync(Action func, RequestOptions options = null)
- => GuildHelper.ModifyRuleAsync(Discord, this, func, options);
+ public async Task ModifyAsync(Action func, RequestOptions options = null)
+ {
+ var model = await GuildHelper.ModifyRuleAsync(Discord, this, func, options);
+ Guild.AddOrUpdateAutoModRule(model);
+ }
///
public Task DeleteAsync(RequestOptions options = null)
diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
index c5c99f009..271cbb36b 100644
--- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
+++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
@@ -1827,7 +1827,10 @@ namespace Discord.WebSocket
return socketRule;
}
- internal SocketAutoModRule GetAutoModRule(ulong id)
+ ///
+ /// Gets a single rule configured in a guild from cache. Returns if the rule was not found.
+ ///
+ public SocketAutoModRule GetAutoModRule(ulong id)
{
return _automodRules.TryGetValue(id, out var rule) ? rule : null;
}
@@ -1864,10 +1867,11 @@ namespace Discord.WebSocket
}
///
- public async Task CreateAutoModRuleAsync(RequestOptions options = null)
+ public async Task 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
@@ -2126,8 +2130,8 @@ namespace Discord.WebSocket
=> await GetAutoModRulesAsync(options).ConfigureAwait(false);
///
- async Task IGuild.CreateAutoModRuleAsync(RequestOptions options)
- => await CreateAutoModRuleAsync(options).ConfigureAwait(false);
+ async Task IGuild.CreateAutoModRuleAsync(AutoModRuleBuilder builder, RequestOptions options)
+ => await CreateAutoModRuleAsync(builder, options).ConfigureAwait(false);
#endregion
}