diff --git a/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleAction.cs b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleAction.cs index 531d435a..47f8f075 100644 --- a/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleAction.cs +++ b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleAction.cs @@ -21,16 +21,23 @@ namespace Discord /// public ulong? ChannelId { get; } + /// + /// Gets the custom message that will be shown to members whenever their message is blocked. + /// if no message has been set. + /// + public Optional CustomMessage { get; set; } + /// /// Gets the duration of which a user will be timed out for breaking this rule. if no timeout duration has been provided. /// public TimeSpan? TimeoutDuration { get; } - internal AutoModRuleAction(AutoModActionType type, ulong? channelId, int? duration) + internal AutoModRuleAction(AutoModActionType type, ulong? channelId, int? duration, string customMessage) { Type = type; ChannelId = channelId; TimeoutDuration = duration.HasValue ? TimeSpan.FromSeconds(duration.Value) : null; + CustomMessage = customMessage; } } } diff --git a/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleBuilder.cs b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleBuilder.cs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleProperties.cs b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleProperties.cs index 9af9832e..d4e86f06 100644 --- a/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleProperties.cs +++ b/src/Discord.Net.Core/Entities/Guilds/AutoModeration/AutoModRuleProperties.cs @@ -66,6 +66,12 @@ namespace Discord /// public const int MaxTimeoutSeconds = 2419200; + /// + /// Returns the max custom message length AutoMod rule action allowed by Discord. + /// + public const int MaxCustomBlockMessageLength = 50; + + /// /// Gets or sets the name for the rule. /// @@ -146,6 +152,11 @@ namespace Discord /// Gets or sets the duration of which a user will be timed out for breaking this rule. /// public TimeSpan? TimeoutDuration { get; set; } + + /// + /// Gets or sets the custom message that will be shown to members whenever their message is blocked. + /// + public Optional CustomMessage { get; set; } } } diff --git a/src/Discord.Net.Rest/API/Common/ActionMetadata.cs b/src/Discord.Net.Rest/API/Common/ActionMetadata.cs index 148c54cd..93b85d0e 100644 --- a/src/Discord.Net.Rest/API/Common/ActionMetadata.cs +++ b/src/Discord.Net.Rest/API/Common/ActionMetadata.cs @@ -14,5 +14,8 @@ namespace Discord.API [JsonProperty("duration_seconds")] public Optional DurationSeconds { get; set; } + + [JsonProperty("custom_message")] + public Optional CustomMessage { get; set; } } } diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 15005f7e..bbcb2bb5 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -1071,6 +1071,8 @@ namespace Discord.Rest var args = new AutoModRuleProperties(); func(args); + #region Validations + if (!args.TriggerType.IsSpecified) throw new ArgumentException(message: $"AutoMod rule must have a specified type.", paramName: nameof(args.TriggerType)); @@ -1078,9 +1080,7 @@ namespace Discord.Rest throw new ArgumentException("Name of the rule must not be empty", paramName: nameof(args.Name)); Preconditions.AtLeast(1, args.Actions.GetValueOrDefault(Array.Empty()).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) @@ -1124,9 +1124,7 @@ namespace Discord.Rest 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) @@ -1154,6 +1152,11 @@ namespace Discord.Rest 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)); + if (args.Actions.Value.Any(x => x.CustomMessage.IsSpecified && x.CustomMessage.Value.Length > AutoModRuleProperties.MaxCustomBlockMessageLength)) + throw new ArgumentException(message: $"Custom message length must be less than or equal to {AutoModRuleProperties.MaxCustomBlockMessageLength}.", paramName: nameof(AutoModRuleActionProperties.CustomMessage)); + + #endregion + var props = new CreateAutoModRuleParams { EventType = args.EventType.GetValueOrDefault(AutoModEventType.MessageSend), @@ -1167,7 +1170,8 @@ namespace Discord.Rest Metadata = new ActionMetadata { ChannelId = x.ChannelId ?? Optional.Unspecified, - DurationSeconds = (int?)x.TimeoutDuration?.TotalSeconds ?? Optional.Unspecified + DurationSeconds = (int?)x.TimeoutDuration?.TotalSeconds ?? Optional.Unspecified, + CustomMessage = x.CustomMessage, }, Type = x.Type }).ToArray(), @@ -1203,7 +1207,8 @@ namespace Discord.Rest Metadata = x.ChannelId.HasValue || x.TimeoutDuration.HasValue ? new API.ActionMetadata { ChannelId = x.ChannelId ?? Optional.Unspecified, - DurationSeconds = x.TimeoutDuration.HasValue ? (int)Math.Floor(x.TimeoutDuration.Value.TotalSeconds) : Optional.Unspecified + DurationSeconds = x.TimeoutDuration.HasValue ? (int)Math.Floor(x.TimeoutDuration.Value.TotalSeconds) : Optional.Unspecified, + CustomMessage = x.CustomMessage, } : Optional.Unspecified }).ToArray() : Optional.Unspecified, Enabled = args.Enabled, diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestAutoModRule.cs b/src/Discord.Net.Rest/Entities/Guilds/RestAutoModRule.cs index f2dc4c9b..2b7b07b7 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestAutoModRule.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestAutoModRule.cs @@ -82,7 +82,16 @@ public class RestAutoModRule : RestEntity, IAutoModRule MentionTotalLimit = model.TriggerMetadata.MentionLimit.IsSpecified ? model.TriggerMetadata.MentionLimit.Value : null; - Actions = model.Actions.Select(x => new AutoModRuleAction(x.Type, x.Metadata.GetValueOrDefault()?.ChannelId.ToNullable(), x.Metadata.GetValueOrDefault()?.DurationSeconds.ToNullable())).ToImmutableArray(); + Actions = model.Actions.Select(x => new AutoModRuleAction( + x.Type, + x.Metadata.GetValueOrDefault()?.ChannelId.ToNullable(), + x.Metadata.GetValueOrDefault()?.DurationSeconds.ToNullable(), + x.Metadata.IsSpecified + ? x.Metadata.Value.CustomMessage.IsSpecified + ? x.Metadata.Value.CustomMessage.Value + : null + : null + )).ToImmutableArray(); Enabled = model.Enabled; ExemptRoles = model.ExemptRoles.ToImmutableArray(); ExemptChannels = model.ExemptChannels.ToImmutableArray(); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index f2091df9..99c9af97 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -2895,7 +2895,7 @@ namespace Discord.WebSocket var rule = guild.AddOrUpdateAutoModRule(data); - await TimedInvokeAsync(_autoModRuleCreated, nameof(AutoModRuleCreated), rule); + await TimedInvokeAsync(_autoModRuleCreated, nameof(AutoModRuleCreated), rule); } break; @@ -2942,8 +2942,14 @@ namespace Discord.WebSocket ? data.Action.Metadata.Value.DurationSeconds.IsSpecified ? data.Action.Metadata.Value.DurationSeconds.Value : null + : null, + data.Action.Metadata.IsSpecified + ? data.Action.Metadata.Value.CustomMessage.IsSpecified + ? data.Action.Metadata.Value.CustomMessage.Value + : null : null); + var member = guild.GetUser(data.UserId); var cacheableUser = new Cacheable(member, @@ -2965,7 +2971,7 @@ namespace Discord.WebSocket channel != null, async () => { - if(data.ChannelId.IsSpecified) + if (data.ChannelId.IsSpecified) return await GetChannelAsync(data.ChannelId.Value).ConfigureAwait(false) as ISocketMessageChannel; return null; }); @@ -2980,7 +2986,7 @@ namespace Discord.WebSocket cachedMsg is not null, async () => { - if(data.MessageId.IsSpecified) + if (data.MessageId.IsSpecified) return (await channel!.GetMessageAsync(data.MessageId.Value).ConfigureAwait(false)) as IUserMessage; return null; }); diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketAutoModRule.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketAutoModRule.cs index d562c5a2..844a6828 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketAutoModRule.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketAutoModRule.cs @@ -93,7 +93,16 @@ namespace Discord.WebSocket MentionTotalLimit = model.TriggerMetadata.MentionLimit.IsSpecified ? model.TriggerMetadata.MentionLimit.Value : null; - Actions = model.Actions.Select(x => new AutoModRuleAction(x.Type, x.Metadata.GetValueOrDefault()?.ChannelId.ToNullable(), x.Metadata.GetValueOrDefault()?.DurationSeconds.ToNullable())).ToImmutableArray(); + Actions = model.Actions.Select(x => new AutoModRuleAction( + x.Type, + x.Metadata.GetValueOrDefault()?.ChannelId.ToNullable(), + x.Metadata.GetValueOrDefault()?.DurationSeconds.ToNullable(), + x.Metadata.IsSpecified + ? x.Metadata.Value.CustomMessage.IsSpecified + ? x.Metadata.Value.CustomMessage.Value + : null + : null + )).ToImmutableArray(); Enabled = model.Enabled; ExemptRoles = model.ExemptRoles.Select(x => Guild.GetRole(x)).ToImmutableArray(); ExemptChannels = model.ExemptChannels.Select(x => Guild.GetChannel(x)).ToImmutableArray();