From ab2158a6487005811f328f898645e2e74903a7cd Mon Sep 17 00:00:00 2001 From: mctaylors Date: Tue, 19 Mar 2024 18:46:56 +0300 Subject: [PATCH 1/9] Initial implementation of /warn Signed-off-by: mctaylors --- locale/Messages.resx | 30 ++++ locale/Messages.ru.resx | 30 ++++ locale/Messages.tt-ru.resx | 30 ++++ src/Commands/BanCommandGroup.cs | 2 +- src/Commands/KickCommandGroup.cs | 2 +- src/Commands/MuteCommandGroup.cs | 2 +- src/Commands/SettingsCommandGroup.cs | 5 +- src/Commands/WarnCommandGroup.cs | 253 +++++++++++++++++++++++++++ src/Data/GuildSettings.cs | 7 + src/Data/MemberData.cs | 1 + src/Data/Options/AllOptionsEnum.cs | 5 +- src/Data/Options/IntOption.cs | 31 ++++ src/Messages.Designer.cs | 49 ++++++ 13 files changed, 442 insertions(+), 5 deletions(-) create mode 100644 src/Commands/WarnCommandGroup.cs create mode 100644 src/Data/Options/IntOption.cs diff --git a/locale/Messages.resx b/locale/Messages.resx index c8ef510..49103c8 100644 --- a/locale/Messages.resx +++ b/locale/Messages.resx @@ -660,4 +660,34 @@ Welcome messages channel + + {0} received a warning + + + {0} no longer has warnings + + + You have been warned + + + Your warnings have been revoked + + + Warns: {0} + + + This user has no warnings! + + + Received too many warnings + + + Punishment type for warnings + + + Warnings threshold + + + Punishment duration for warnings + diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx index eb8e57b..3933a1a 100644 --- a/locale/Messages.ru.resx +++ b/locale/Messages.ru.resx @@ -660,4 +660,34 @@ Канал для приветствий + + {0} получил предупреждение + + + {0} больше не имеет предупреждений + + + Вы получили предупреждение + + + Ваши предупреждения были отозваны + + + Предупреждений: {0} + + + Этот пользователь не имеет предупреждений! + + + Получил слишком много предупреждений + + + Тип наказания для предупреждений + + + Порог предупреждений + + + Длительность наказания для предупреждений + diff --git a/locale/Messages.tt-ru.resx b/locale/Messages.tt-ru.resx index df50d8d..b108287 100644 --- a/locale/Messages.tt-ru.resx +++ b/locale/Messages.tt-ru.resx @@ -660,4 +660,34 @@ канал куда говорить здравствуйте + + {0} схлопотал варн + + + {0} получил амнистию + + + вы схлопотали варн + + + вы получили амнистию + + + варнов: {0} + + + его еще никто ни в чем не обвинял + + + схлопотал много варнов + + + сколько времени держать варновый бан + + + тип варнового бана + + + сколько варнов чтобы потом бан + diff --git a/src/Commands/BanCommandGroup.cs b/src/Commands/BanCommandGroup.cs index c350729..6c8b004 100644 --- a/src/Commands/BanCommandGroup.cs +++ b/src/Commands/BanCommandGroup.cs @@ -127,7 +127,7 @@ public class BanCommandGroup : CommandGroup return await BanUserAsync(executor, target, reason, timeSpan, guild, data, channelId, bot, CancellationToken); } - private async Task BanUserAsync( + public async Task BanUserAsync( IUser executor, IUser target, string reason, TimeSpan? duration, IGuild guild, GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) { diff --git a/src/Commands/KickCommandGroup.cs b/src/Commands/KickCommandGroup.cs index 0faa1d3..4262487 100644 --- a/src/Commands/KickCommandGroup.cs +++ b/src/Commands/KickCommandGroup.cs @@ -110,7 +110,7 @@ public class KickCommandGroup : CommandGroup return await KickUserAsync(executor, target, reason, guild, channelId, data, bot, CancellationToken); } - private async Task KickUserAsync( + public async Task KickUserAsync( IUser executor, IUser target, string reason, IGuild guild, Snowflake channelId, GuildData data, IUser bot, CancellationToken ct = default) { diff --git a/src/Commands/MuteCommandGroup.cs b/src/Commands/MuteCommandGroup.cs index c2542e8..43eddf1 100644 --- a/src/Commands/MuteCommandGroup.cs +++ b/src/Commands/MuteCommandGroup.cs @@ -121,7 +121,7 @@ public class MuteCommandGroup : CommandGroup return await MuteUserAsync(executor, target, reason, duration, guildId, data, channelId, bot, CancellationToken); } - private async Task MuteUserAsync( + public async Task MuteUserAsync( IUser executor, IUser target, string reason, TimeSpan duration, Snowflake guildId, GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) { diff --git a/src/Commands/SettingsCommandGroup.cs b/src/Commands/SettingsCommandGroup.cs index 86f031f..9dd9e04 100644 --- a/src/Commands/SettingsCommandGroup.cs +++ b/src/Commands/SettingsCommandGroup.cs @@ -38,12 +38,14 @@ public class SettingsCommandGroup : CommandGroup private static readonly IOption[] AllOptions = [ GuildSettings.Language, + GuildSettings.WarnPunishment, GuildSettings.WelcomeMessage, GuildSettings.ReceiveStartupMessages, GuildSettings.RemoveRolesOnMute, GuildSettings.ReturnRolesOnRejoin, GuildSettings.AutoStartEvents, GuildSettings.RenameHoistedUsers, + GuildSettings.WarnsThreshold, GuildSettings.PublicFeedbackChannel, GuildSettings.PrivateFeedbackChannel, GuildSettings.WelcomeMessagesChannel, @@ -51,7 +53,8 @@ public class SettingsCommandGroup : CommandGroup GuildSettings.DefaultRole, GuildSettings.MuteRole, GuildSettings.EventNotificationRole, - GuildSettings.EventEarlyNotificationOffset + GuildSettings.EventEarlyNotificationOffset, + GuildSettings.WarnPunishmentDuration ]; private readonly ICommandContext _context; diff --git a/src/Commands/WarnCommandGroup.cs b/src/Commands/WarnCommandGroup.cs new file mode 100644 index 0000000..a712857 --- /dev/null +++ b/src/Commands/WarnCommandGroup.cs @@ -0,0 +1,253 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Text; +using JetBrains.Annotations; +using Octobot.Data; +using Octobot.Extensions; +using Octobot.Services; +using Remora.Commands.Attributes; +using Remora.Commands.Groups; +using Remora.Discord.API.Abstractions.Objects; +using Remora.Discord.API.Abstractions.Rest; +using Remora.Discord.Commands.Attributes; +using Remora.Discord.Commands.Conditions; +using Remora.Discord.Commands.Contexts; +using Remora.Discord.Commands.Feedback.Services; +using Remora.Discord.Extensions.Embeds; +using Remora.Rest.Core; +using Remora.Results; + +namespace Octobot.Commands; + +[UsedImplicitly] +public class WarnCommandGroup : CommandGroup +{ + private readonly IDiscordRestChannelAPI _channelApi; + private readonly ICommandContext _context; + private readonly IFeedbackService _feedback; + private readonly IDiscordRestGuildAPI _guildApi; + private readonly GuildDataService _guildData; + private readonly IDiscordRestUserAPI _userApi; + private readonly Utility _utility; + + public WarnCommandGroup( + ICommandContext context, IDiscordRestChannelAPI channelApi, GuildDataService guildData, + IFeedbackService feedback, IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi, + Utility utility) + { + _context = context; + _channelApi = channelApi; + _guildData = guildData; + _feedback = feedback; + _guildApi = guildApi; + _userApi = userApi; + _utility = utility; + } + + [Command("warn")] + [DiscordDefaultMemberPermissions(DiscordPermission.KickMembers)] + [DiscordDefaultDMPermission(false)] + [RequireContext(ChannelContext.Guild)] + [RequireDiscordPermission(DiscordPermission.KickMembers)] + [RequireBotDiscordPermissions(DiscordPermission.KickMembers)] + [Description("Warn user")] + [UsedImplicitly] + public async Task ExecuteWarnAsync( + [Description("User to warn")] IUser target, + [Description("Warn reason")] [MaxLength(256)] + string reason) + { + if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var executorId)) + { + return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context"); + } + + var botResult = await _userApi.GetCurrentUserAsync(CancellationToken); + if (!botResult.IsDefined(out var bot)) + { + return Result.FromError(botResult); + } + + var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken); + if (!executorResult.IsDefined(out var executor)) + { + return Result.FromError(executorResult); + } + + var guildResult = await _guildApi.GetGuildAsync(guildId, ct: CancellationToken); + if (!guildResult.IsDefined(out var guild)) + { + return Result.FromError(guildResult); + } + + var data = await _guildData.GetData(guild.ID, CancellationToken); + Messages.Culture = GuildSettings.Language.Get(data.Settings); + + return await WarnUserAsync(executor, target, reason, guild, data, channelId, bot, CancellationToken); + } + + private async Task WarnUserAsync(IUser executor, IUser target, string reason, IGuild guild, + GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) + { + var memberData = data.GetOrCreateMemberData(target.ID); + memberData.Warns++; + + var warnsThreshold = GuildSettings.WarnsThreshold.Get(data.Settings); + + var builder = new StringBuilder() + .AppendBulletPointLine(string.Format(Messages.DescriptionActionReason, reason)) + .AppendBulletPointLine(string.Format(Messages.DescriptionActionWarns, + warnsThreshold is 0 ? memberData.Warns : $"{memberData.Warns}/{warnsThreshold}")); + + var title = string.Format(Messages.UserWarned, target.GetTag()); + var description = builder.ToString(); + + var dmChannelResult = await _userApi.CreateDMAsync(target.ID, ct); + if (dmChannelResult.IsDefined(out var dmChannel)) + { + var dmEmbed = new EmbedBuilder().WithGuildTitle(guild) + .WithTitle(Messages.YouHaveBeenWarned) + .WithDescription(description) + .WithActionFooter(executor) + .WithCurrentTimestamp() + .WithColour(ColorsList.Yellow) + .Build(); + + await _channelApi.CreateMessageWithEmbedResultAsync(dmChannel.ID, embedResult: dmEmbed, ct: ct); + } + + _utility.LogAction( + data.Settings, channelId, executor, title, description, target, ColorsList.Yellow, false, ct); + + if (memberData.Warns >= warnsThreshold && + GuildSettings.WarnPunishment.Get(data.Settings) is not "off" and not "disable" and not "disabled") + { + memberData.Warns = 0; + return await PunishUserAsync(target, guild, data, channelId, bot, CancellationToken); + } + + var embed = new EmbedBuilder().WithSmallTitle( + title, target) + .WithColour(ColorsList.Green).Build(); + + return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct); + } + + private async Task PunishUserAsync(IUser target, IGuild guild, + GuildData data, Snowflake channelId, IUser bot, CancellationToken ct) + { + var settings = data.Settings; + var duration = GuildSettings.WarnPunishmentDuration.Get(settings); + + if (GuildSettings.WarnPunishment.Get(settings) is "ban" + && duration != TimeSpan.Zero) + { + var banCommandGroup = new BanCommandGroup(_context, _channelApi, _guildData, _feedback, _guildApi, _userApi, _utility); + await banCommandGroup.BanUserAsync(bot, target, Messages.ReceivedTooManyWarnings, + duration, guild, data, channelId, bot, ct); + } + + if (GuildSettings.WarnPunishment.Get(settings) is "kick") + { + var kickCommandGroup = new KickCommandGroup(_context, _channelApi, _guildData, _feedback, _guildApi, _userApi, _utility); + await kickCommandGroup.KickUserAsync(bot, target, Messages.ReceivedTooManyWarnings, + guild, channelId, data, bot, ct); + } + + if (GuildSettings.WarnPunishment.Get(settings) is "mute" + && duration != TimeSpan.Zero) + { + var muteCommandGroup = new MuteCommandGroup(_context, _guildData, _feedback, _guildApi, _userApi, _utility); + await muteCommandGroup.MuteUserAsync(bot, target, Messages.ReceivedTooManyWarnings, + duration, guild.ID, data, channelId, bot, ct); + } + + return Result.FromSuccess(); + } + + [Command("unwarn")] + [DiscordDefaultMemberPermissions(DiscordPermission.KickMembers)] + [DiscordDefaultDMPermission(false)] + [RequireContext(ChannelContext.Guild)] + [RequireDiscordPermission(DiscordPermission.KickMembers)] + [RequireBotDiscordPermissions(DiscordPermission.KickMembers)] + [Description("Remove warns from user")] + [UsedImplicitly] + public async Task ExecuteUnwarnAsync( + [Description("User to remove warns from")] + IUser target, + [Description("Warns remove reason")] [MaxLength(256)] + string reason) + { + if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var executorId)) + { + return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context"); + } + + var botResult = await _userApi.GetCurrentUserAsync(CancellationToken); + if (!botResult.IsDefined(out var bot)) + { + return Result.FromError(botResult); + } + + var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken); + if (!executorResult.IsDefined(out var executor)) + { + return Result.FromError(executorResult); + } + + var guildResult = await _guildApi.GetGuildAsync(guildId, ct: CancellationToken); + if (!guildResult.IsDefined(out var guild)) + { + return Result.FromError(guildResult); + } + + var data = await _guildData.GetData(guild.ID, CancellationToken); + Messages.Culture = GuildSettings.Language.Get(data.Settings); + + return await RemoveUserWarnsAsync(executor, target, reason, guild, data, channelId, bot, CancellationToken); + } + + private async Task RemoveUserWarnsAsync(IUser executor, IUser target, string reason, IGuild guild, + GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) + { + var memberData = data.GetOrCreateMemberData(target.ID); + if (memberData.Warns is 0) + { + var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.UserHasNoWarnings, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct); + } + + memberData.Warns = 0; + + var builder = new StringBuilder().AppendBulletPointLine(string.Format(Messages.DescriptionActionReason, reason)); + + var title = string.Format(Messages.UserWarnsRemoved, target.GetTag()); + var description = builder.ToString(); + + var dmChannelResult = await _userApi.CreateDMAsync(target.ID, ct); + if (dmChannelResult.IsDefined(out var dmChannel)) + { + var dmEmbed = new EmbedBuilder().WithGuildTitle(guild) + .WithTitle(Messages.YourWarningsHaveBeenRevoked) + .WithDescription(description) + .WithActionFooter(executor) + .WithCurrentTimestamp() + .WithColour(ColorsList.Green) + .Build(); + + await _channelApi.CreateMessageWithEmbedResultAsync(dmChannel.ID, embedResult: dmEmbed, ct: ct); + } + + var embed = new EmbedBuilder().WithSmallTitle( + title, target) + .WithColour(ColorsList.Green).Build(); + + _utility.LogAction( + data.Settings, channelId, executor, title, description, target, ColorsList.Yellow, false, ct); + + return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct); + } +} diff --git a/src/Data/GuildSettings.cs b/src/Data/GuildSettings.cs index 5a99505..4e46be4 100644 --- a/src/Data/GuildSettings.cs +++ b/src/Data/GuildSettings.cs @@ -12,6 +12,8 @@ public static class GuildSettings { public static readonly LanguageOption Language = new("Language", "en"); + public static readonly Option WarnPunishment = new("WarnPunishment", "disabled"); + /// /// Controls what message should be sent in when a new member joins the server. /// @@ -46,6 +48,8 @@ public static class GuildSettings /// public static readonly BoolOption RenameHoistedUsers = new("RenameHoistedUsers", false); + public static readonly IntOption WarnsThreshold = new("WarnsThreshold", 0); + /// /// Controls what channel should all public messages be sent to. /// @@ -71,4 +75,7 @@ public static class GuildSettings /// public static readonly TimeSpanOption EventEarlyNotificationOffset = new( "EventEarlyNotificationOffset", TimeSpan.Zero); + + public static readonly TimeSpanOption WarnPunishmentDuration = new( + "WarnPunishmentDuration", TimeSpan.Zero); } diff --git a/src/Data/MemberData.cs b/src/Data/MemberData.cs index 8e23e54..2701be7 100644 --- a/src/Data/MemberData.cs +++ b/src/Data/MemberData.cs @@ -18,6 +18,7 @@ public sealed class MemberData public ulong Id { get; } public DateTimeOffset? BannedUntil { get; set; } public DateTimeOffset? MutedUntil { get; set; } + public int Warns { get; set; } public bool Kicked { get; set; } public List Roles { get; set; } = []; public List Reminders { get; } = []; diff --git a/src/Data/Options/AllOptionsEnum.cs b/src/Data/Options/AllOptionsEnum.cs index e9637d6..f3ef3eb 100644 --- a/src/Data/Options/AllOptionsEnum.cs +++ b/src/Data/Options/AllOptionsEnum.cs @@ -13,12 +13,14 @@ namespace Octobot.Data.Options; public enum AllOptionsEnum { [UsedImplicitly] Language, + [UsedImplicitly] WarnPunishment, [UsedImplicitly] WelcomeMessage, [UsedImplicitly] ReceiveStartupMessages, [UsedImplicitly] RemoveRolesOnMute, [UsedImplicitly] ReturnRolesOnRejoin, [UsedImplicitly] AutoStartEvents, [UsedImplicitly] RenameHoistedUsers, + [UsedImplicitly] WarnsThreshold, [UsedImplicitly] PublicFeedbackChannel, [UsedImplicitly] PrivateFeedbackChannel, [UsedImplicitly] WelcomeMessagesChannel, @@ -26,5 +28,6 @@ public enum AllOptionsEnum [UsedImplicitly] DefaultRole, [UsedImplicitly] MuteRole, [UsedImplicitly] EventNotificationRole, - [UsedImplicitly] EventEarlyNotificationOffset + [UsedImplicitly] EventEarlyNotificationOffset, + [UsedImplicitly] WarnPunishmentDuration } diff --git a/src/Data/Options/IntOption.cs b/src/Data/Options/IntOption.cs new file mode 100644 index 0000000..62b2883 --- /dev/null +++ b/src/Data/Options/IntOption.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Nodes; +using Remora.Results; + +namespace Octobot.Data.Options; + +public sealed class IntOption : Option +{ + public IntOption(string name, int defaultValue) : base(name, defaultValue) { } + + public override string Display(JsonNode settings) + { + return settings[Name]?.GetValue() ?? "0"; + } + + public override Result Set(JsonNode settings, string from) + { + if (!int.TryParse(from, out _)) + { + return new ArgumentInvalidError(nameof(from), Messages.InvalidSettingValue); + } + + settings[Name] = from; + return Result.FromSuccess(); + } + + public override int Get(JsonNode settings) + { + var property = settings[Name]; + return property != null ? Convert.ToInt32(property.GetValue()) : DefaultValue; + } +} diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs index 5ad741e..3d3c0d8 100644 --- a/src/Messages.Designer.cs +++ b/src/Messages.Designer.cs @@ -1191,5 +1191,54 @@ namespace Octobot { return ResourceManager.GetString("SettingsWelcomeMessagesChannel", resourceCulture); } } + + internal static string UserWarned + { + get { + return ResourceManager.GetString("UserWarned", resourceCulture); + } + } + + internal static string UserWarnsRemoved + { + get { + return ResourceManager.GetString("UserWarnsRemoved", resourceCulture); + } + } + + internal static string YouHaveBeenWarned + { + get { + return ResourceManager.GetString("YouHaveBeenWarned", resourceCulture); + } + } + + internal static string YourWarningsHaveBeenRevoked + { + get { + return ResourceManager.GetString("YourWarningsHaveBeenRevoked", resourceCulture); + } + } + + internal static string DescriptionActionWarns + { + get { + return ResourceManager.GetString("DescriptionActionWarns", resourceCulture); + } + } + + internal static string UserHasNoWarnings + { + get { + return ResourceManager.GetString("UserHasNoWarnings", resourceCulture); + } + } + + internal static string ReceivedTooManyWarnings + { + get { + return ResourceManager.GetString("ReceivedTooManyWarnings", resourceCulture); + } + } } } From 88448cdb49c315d2b8909eee6830c6446f3b193d Mon Sep 17 00:00:00 2001 From: mctaylors Date: Sun, 24 Mar 2024 15:55:26 +0300 Subject: [PATCH 2/9] Better implementation of /warn Signed-off-by: mctaylors --- locale/Messages.resx | 51 ++++++++ locale/Messages.ru.resx | 51 ++++++++ locale/Messages.tt-ru.resx | 53 +++++++- src/Commands/WarnCommandGroup.cs | 217 +++++++++++++++++++++++++++++-- src/Data/MemberData.cs | 9 +- src/Data/Warn.cs | 10 ++ src/Messages.Designer.cs | 119 +++++++++++++++++ 7 files changed, 495 insertions(+), 15 deletions(-) create mode 100644 src/Data/Warn.cs diff --git a/locale/Messages.resx b/locale/Messages.resx index 09bb4db..27c0d3f 100644 --- a/locale/Messages.resx +++ b/locale/Messages.resx @@ -705,4 +705,55 @@ Punishment duration for warnings + + Here's your warnings, {0}: + + + You have no warnings! + + + Received on {0} + + + Warning #{0} has been removed from {1} + + + Your warning has been revoked + + + Wrong warning number selected! + + + You cannot warn me! + + + You cannot warn the owner of this guild! + + + You cannot warn this member! + + + You cannot warn yourself! + + + You cannot unwarn me! + + + You cannot unwarn the owner of this guild! + + + You cannot unwarn this member! + + + You cannot unwarn yourself! + + + I cannot warn members from this guild! + + + I cannot warn this member! + + + I cannot unwarn this member! + diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx index b37ea64..eec45aa 100644 --- a/locale/Messages.ru.resx +++ b/locale/Messages.ru.resx @@ -705,4 +705,55 @@ Длительность наказания для предупреждений + + Вот ваши предупреждения, {0}: + + + У вас нет предупреждений! + + + Получено {0} + + + Предупреждение №{0} было снято с {1} + + + Ваше предупреждение было отозвано + + + Выбрано неверное число предупреждения! + + + Ты не можешь меня предупредить! + + + Ты не можешь предупредить владельца этого сервера! + + + Ты не можешь предупредить этого участника! + + + Ты не можешь себя предупредить! + + + Ты не можешь снять с меня предупреждения! + + + Ты не можешь снять предупреждения с владельца этого сервера! + + + Ты не можешь снять предупреждения с этого участника! + + + Ты не можешь снять с себя предупреждения! + + + Я не могу снимать предупреждения этого участника! + + + Я не могу предупредить этого участника! + + + Я не могу предупреждать участников этого сервера! + diff --git a/locale/Messages.tt-ru.resx b/locale/Messages.tt-ru.resx index 050dc6d..178e83e 100644 --- a/locale/Messages.tt-ru.resx +++ b/locale/Messages.tt-ru.resx @@ -685,7 +685,7 @@ вы схлопотали варн - вы получили амнистию + вы получили карт-бланш варнов: {0} @@ -705,4 +705,55 @@ сколько варнов чтобы потом бан + + хаха, смотри {0}, это все ты заслужил: + + + ого, да на тебя еще нет претензий! + + + получил {0} + + + варн {0} снят с {1} + + + вы получили амнистию + + + да нету такого варна вроде + + + ээбля френдли фаер огонь по своим + + + самовар нельзя + + + варн этому шизику нельзя + + + варн админу нельзя + + + ну, к сожалению тебе этого не дано + + + разварн админу нельзя + + + разварн этому шизику нельзя + + + саморазвар нельзя + + + я не могу его заварить... + + + я не могу его разварить... + + + я не могу ваще никого варить... + diff --git a/src/Commands/WarnCommandGroup.cs b/src/Commands/WarnCommandGroup.cs index a712857..9c08aa2 100644 --- a/src/Commands/WarnCommandGroup.cs +++ b/src/Commands/WarnCommandGroup.cs @@ -14,8 +14,10 @@ using Remora.Discord.Commands.Conditions; using Remora.Discord.Commands.Contexts; using Remora.Discord.Commands.Feedback.Services; using Remora.Discord.Extensions.Embeds; +using Remora.Discord.Extensions.Formatting; using Remora.Rest.Core; using Remora.Results; +using static System.DateTimeOffset; namespace Octobot.Commands; @@ -49,7 +51,8 @@ public class WarnCommandGroup : CommandGroup [DiscordDefaultDMPermission(false)] [RequireContext(ChannelContext.Guild)] [RequireDiscordPermission(DiscordPermission.KickMembers)] - [RequireBotDiscordPermissions(DiscordPermission.KickMembers)] + [RequireBotDiscordPermissions(DiscordPermission.KickMembers, + DiscordPermission.ModerateMembers, DiscordPermission.BanMembers)] [Description("Warn user")] [UsedImplicitly] public async Task ExecuteWarnAsync( @@ -89,15 +92,37 @@ public class WarnCommandGroup : CommandGroup private async Task WarnUserAsync(IUser executor, IUser target, string reason, IGuild guild, GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) { + var interactionResult + = await _utility.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Warn", ct); + if (!interactionResult.IsSuccess) + { + return ResultExtensions.FromError(interactionResult); + } + + if (interactionResult.Entity is not null) + { + var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); + } + var memberData = data.GetOrCreateMemberData(target.ID); - memberData.Warns++; + var warns = memberData.Warns; + + warns.Add(new Warn + { + WarnedBy = executor.ID.Value, + At = UtcNow, + Reason = reason + }); var warnsThreshold = GuildSettings.WarnsThreshold.Get(data.Settings); var builder = new StringBuilder() .AppendBulletPointLine(string.Format(Messages.DescriptionActionReason, reason)) .AppendBulletPointLine(string.Format(Messages.DescriptionActionWarns, - warnsThreshold is 0 ? memberData.Warns : $"{memberData.Warns}/{warnsThreshold}")); + warnsThreshold is 0 ? warns.Count : $"{warns.Count}/{warnsThreshold}")); var title = string.Format(Messages.UserWarned, target.GetTag()); var description = builder.ToString(); @@ -119,10 +144,10 @@ public class WarnCommandGroup : CommandGroup _utility.LogAction( data.Settings, channelId, executor, title, description, target, ColorsList.Yellow, false, ct); - if (memberData.Warns >= warnsThreshold && + if (warns.Count >= warnsThreshold && GuildSettings.WarnPunishment.Get(data.Settings) is not "off" and not "disable" and not "disabled") { - memberData.Warns = 0; + warns.Clear(); return await PunishUserAsync(target, guild, data, channelId, bot, CancellationToken); } @@ -170,14 +195,15 @@ public class WarnCommandGroup : CommandGroup [DiscordDefaultDMPermission(false)] [RequireContext(ChannelContext.Guild)] [RequireDiscordPermission(DiscordPermission.KickMembers)] - [RequireBotDiscordPermissions(DiscordPermission.KickMembers)] [Description("Remove warns from user")] [UsedImplicitly] public async Task ExecuteUnwarnAsync( [Description("User to remove warns from")] IUser target, - [Description("Warns remove reason")] [MaxLength(256)] - string reason) + [Description("Warn remove reason")] [MaxLength(256)] + string reason, + [Description("Number of the warning to be deleted")] + int? number = null) { if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var executorId)) { @@ -205,14 +231,37 @@ public class WarnCommandGroup : CommandGroup var data = await _guildData.GetData(guild.ID, CancellationToken); Messages.Culture = GuildSettings.Language.Get(data.Settings); + if (number is not null) + { + return await RemoveUserWarnAsync(executor, target, reason, number.Value, guild, data, channelId, bot, + CancellationToken); + } + return await RemoveUserWarnsAsync(executor, target, reason, guild, data, channelId, bot, CancellationToken); } - private async Task RemoveUserWarnsAsync(IUser executor, IUser target, string reason, IGuild guild, - GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) + private async Task RemoveUserWarnAsync(IUser executor, IUser target, string reason, int warnNumber, + IGuild guild, GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) { + var interactionResult + = await _utility.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Unwarn", ct); + if (!interactionResult.IsSuccess) + { + return ResultExtensions.FromError(interactionResult); + } + + if (interactionResult.Entity is not null) + { + var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); + } + var memberData = data.GetOrCreateMemberData(target.ID); - if (memberData.Warns is 0) + var warns = memberData.Warns; + + if (warns.Count is 0) { var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.UserHasNoWarnings, bot) .WithColour(ColorsList.Red).Build(); @@ -220,10 +269,82 @@ public class WarnCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct); } - memberData.Warns = 0; + var index = warnNumber - 1; + + if (index >= warns.Count || index < 0) + { + var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.WrongWarningNumberSelected, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct); + } + + var builder = new StringBuilder() + .Append("> ").AppendLine(warns[index].Reason) + .AppendBulletPointLine(string.Format(Messages.DescriptionActionReason, reason)); + + warns.RemoveAt(index); + + var title = string.Format(Messages.UserWarnRemoved, warnNumber, target.GetTag()); + var description = builder.ToString(); + + var dmChannelResult = await _userApi.CreateDMAsync(target.ID, ct); + if (dmChannelResult.IsDefined(out var dmChannel)) + { + var dmEmbed = new EmbedBuilder().WithGuildTitle(guild) + .WithTitle(Messages.YourWarningHasBeenRevoked) + .WithDescription(description) + .WithActionFooter(executor) + .WithCurrentTimestamp() + .WithColour(ColorsList.Green) + .Build(); + + await _channelApi.CreateMessageWithEmbedResultAsync(dmChannel.ID, embedResult: dmEmbed, ct: ct); + } + + var embed = new EmbedBuilder().WithSmallTitle( + title, target) + .WithColour(ColorsList.Green).Build(); + + _utility.LogAction( + data.Settings, channelId, executor, title, description, target, ColorsList.Yellow, false, ct); + + return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct); + } + + private async Task RemoveUserWarnsAsync(IUser executor, IUser target, string reason, + IGuild guild, GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) + { + var interactionResult + = await _utility.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Unwarn", ct); + if (!interactionResult.IsSuccess) + { + return ResultExtensions.FromError(interactionResult); + } + + if (interactionResult.Entity is not null) + { + var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); + } + + var memberData = data.GetOrCreateMemberData(target.ID); + var warns = memberData.Warns; + + if (warns.Count is 0) + { + var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.UserHasNoWarnings, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct); + } var builder = new StringBuilder().AppendBulletPointLine(string.Format(Messages.DescriptionActionReason, reason)); + warns.Clear(); + var title = string.Format(Messages.UserWarnsRemoved, target.GetTag()); var description = builder.ToString(); @@ -250,4 +371,76 @@ public class WarnCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct); } + + [Command("listwarn")] + [DiscordDefaultDMPermission(false)] + [Ephemeral] + [Description("(Ephemeral) Get your current warns")] + [UsedImplicitly] + public async Task ExecuteListWarnsAsync() + { + if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId)) + { + return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context"); + } + + var botResult = await _userApi.GetCurrentUserAsync(CancellationToken); + if (!botResult.IsDefined(out var bot)) + { + return Result.FromError(botResult); + } + + var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken); + if (!executorResult.IsDefined(out var executor)) + { + return Result.FromError(executorResult); + } + + var guildResult = await _guildApi.GetGuildAsync(guildId, ct: CancellationToken); + if (!guildResult.IsDefined(out var guild)) + { + return Result.FromError(guildResult); + } + + var data = await _guildData.GetData(guild.ID, CancellationToken); + Messages.Culture = GuildSettings.Language.Get(data.Settings); + + return await ListWarnsAsync(executor, data, bot, CancellationToken); + } + + private async Task ListWarnsAsync(IUser executor, GuildData data, IUser bot, CancellationToken ct = default) + { + var memberData = data.GetOrCreateMemberData(executor.ID); + var warns = memberData.Warns; + + if (warns.Count is 0) + { + var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.YouHaveNoWarnings, bot) + .WithColour(ColorsList.Green).Build(); + + return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct); + } + + var warnsThreshold = GuildSettings.WarnsThreshold.Get(data.Settings); + + var description = new StringBuilder() + .AppendLine(string.Format(Messages.DescriptionActionWarns, + warnsThreshold is 0 ? warns.Count : $"{warns.Count}/{warnsThreshold}")); + + var warnCount = 0; + foreach (var warn in warns) + { + warnCount++; + description.Append(warnCount).Append(". ").AppendLine(warn.Reason) + .AppendSubBulletPoint(Messages.IssuedBy).Append(' ').AppendLine(Mention.User(warn.WarnedBy.ToSnowflake())) + .AppendSubBulletPointLine(string.Format(Messages.ReceivedOn, Markdown.Timestamp(warn.At))); + } + + var embed = new EmbedBuilder() + .WithSmallTitle(string.Format(Messages.ListWarnTitle, executor.GetTag()), executor) + .WithDescription(description.ToString()) + .WithColour(ColorsList.Default).Build(); + + return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct); + } } diff --git a/src/Data/MemberData.cs b/src/Data/MemberData.cs index 8975a25..fe29e7f 100644 --- a/src/Data/MemberData.cs +++ b/src/Data/MemberData.cs @@ -5,20 +5,25 @@ namespace Octobot.Data; /// public sealed class MemberData { - public MemberData(ulong id, List? reminders = null) + public MemberData(ulong id, List? reminders = null, List? warns = null) { Id = id; if (reminders is not null) { Reminders = reminders; } + + if (warns is not null) + { + Warns = warns; + } } public ulong Id { get; } public DateTimeOffset? BannedUntil { get; set; } public DateTimeOffset? MutedUntil { get; set; } - public int Warns { get; set; } public bool Kicked { get; set; } public List Roles { get; set; } = []; public List Reminders { get; } = []; + public List Warns { get; } = []; } diff --git a/src/Data/Warn.cs b/src/Data/Warn.cs new file mode 100644 index 0000000..97e93f0 --- /dev/null +++ b/src/Data/Warn.cs @@ -0,0 +1,10 @@ +using Remora.Rest.Core; + +namespace Octobot.Data; + +public struct Warn +{ + public ulong WarnedBy { get; init; } + public DateTimeOffset At { get; init; } + public string Reason { get; init; } +} diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs index 16cd3eb..9c50043 100644 --- a/src/Messages.Designer.cs +++ b/src/Messages.Designer.cs @@ -1247,6 +1247,13 @@ namespace Octobot { } } + internal static string YouHaveNoWarnings + { + get { + return ResourceManager.GetString("YouHaveNoWarnings", resourceCulture); + } + } + internal static string ReceivedTooManyWarnings { get { @@ -1265,5 +1272,117 @@ namespace Octobot { return ResourceManager.GetString("ButtonOpenWiki", resourceCulture); } } + + internal static string ListWarnTitle + { + get { + return ResourceManager.GetString("ListWarnTitle", resourceCulture); + } + } + + internal static string ReceivedOn + { + get { + return ResourceManager.GetString("ReceivedOn", resourceCulture); + } + } + + internal static string UserWarnRemoved + { + get { + return ResourceManager.GetString("UserWarnRemoved", resourceCulture); + } + } + + internal static string YourWarningHasBeenRevoked + { + get { + return ResourceManager.GetString("YourWarningHasBeenRevoked", resourceCulture); + } + } + + internal static string WrongWarningNumberSelected + { + get { + return ResourceManager.GetString("WrongWarningNumberSelected", resourceCulture); + } + } + + internal static string UserCannotWarnBot + { + get { + return ResourceManager.GetString("UserCannotWarnBot", resourceCulture); + } + } + + internal static string UserCannotWarnOwner + { + get { + return ResourceManager.GetString("UserCannotWarnOwner", resourceCulture); + } + } + + internal static string UserCannotWarnTarget + { + get { + return ResourceManager.GetString("UserCannotWarnTarget", resourceCulture); + } + } + + internal static string UserCannotWarnThemselves + { + get { + return ResourceManager.GetString("UserCannotWarnThemselves", resourceCulture); + } + } + + internal static string UserCannotUnwarnBot + { + get { + return ResourceManager.GetString("UserCannotUnwarnBot", resourceCulture); + } + } + + internal static string UserCannotUnwarnOwner + { + get { + return ResourceManager.GetString("UserCannotUnwarnOwner", resourceCulture); + } + } + + internal static string UserCannotUnwarnTarget + { + get { + return ResourceManager.GetString("UserCannotUnwarnTarget", resourceCulture); + } + } + + internal static string UserCannotUnwarnThemselves + { + get { + return ResourceManager.GetString("UserCannotUnwarnThemselves", resourceCulture); + } + } + + internal static string BotCannotWarnTarget + { + get { + return ResourceManager.GetString("BotCannotWarnTarget", resourceCulture); + } + } + + internal static string BotCannotWarnMembers + { + get { + return ResourceManager.GetString("BotCannotWarnMembers", resourceCulture); + } + } + + internal static string BotCannotUnwarnTarget + { + get { + return ResourceManager.GetString("BotCannotUnwarnTarget", resourceCulture); + } + } } } From dbd14a458a41a36e1c8716cf1c54a184f7579d85 Mon Sep 17 00:00:00 2001 From: mctaylors Date: Sun, 24 Mar 2024 16:09:25 +0300 Subject: [PATCH 3/9] what Signed-off-by: mctaylors --- src/Commands/WarnCommandGroup.cs | 2 +- src/Data/Warn.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Commands/WarnCommandGroup.cs b/src/Commands/WarnCommandGroup.cs index 9c08aa2..a57b079 100644 --- a/src/Commands/WarnCommandGroup.cs +++ b/src/Commands/WarnCommandGroup.cs @@ -375,7 +375,7 @@ public class WarnCommandGroup : CommandGroup [Command("listwarn")] [DiscordDefaultDMPermission(false)] [Ephemeral] - [Description("(Ephemeral) Get your current warns")] + [Description("(Ephemeral) Get current warns")] [UsedImplicitly] public async Task ExecuteListWarnsAsync() { diff --git a/src/Data/Warn.cs b/src/Data/Warn.cs index 97e93f0..f70d46e 100644 --- a/src/Data/Warn.cs +++ b/src/Data/Warn.cs @@ -1,6 +1,4 @@ -using Remora.Rest.Core; - -namespace Octobot.Data; +namespace Octobot.Data; public struct Warn { From 152682e4568cea5169c4cae15647b027a5fba4eb Mon Sep 17 00:00:00 2001 From: mctaylors Date: Tue, 26 Mar 2024 20:59:56 +0300 Subject: [PATCH 4/9] (Almost) Final implementation of /warn Signed-off-by: mctaylors --- locale/Messages.resx | 37 +++++- locale/Messages.ru.resx | 37 +++++- locale/Messages.tt-ru.resx | 37 +++++- src/Commands/WarnCommandGroup.cs | 165 +++++++++++++++++++++------ src/Messages.Designer.cs | 76 +++--------- src/Services/AccessControlService.cs | 3 +- 6 files changed, 253 insertions(+), 102 deletions(-) diff --git a/locale/Messages.resx b/locale/Messages.resx index 5379ab6..326e1b8 100644 --- a/locale/Messages.resx +++ b/locale/Messages.resx @@ -693,7 +693,7 @@ Your warnings have been revoked - + Warns: {0} @@ -711,7 +711,7 @@ Punishment duration for warnings - + Here's your warnings, {0}: @@ -762,4 +762,37 @@ I cannot unwarn this member! + + You cannot get my warns! + + + You cannot get owner's warns! + + + You cannot get warns of this member! + + + Use this command without options instead. + + + You cannot warn members in this guild! + + + You cannot unwarn members in this guild! + + + You cannot get warns of other members in this guild! + + + Warnings given to {0}: + + + Punishment type: {0} + + + Warn threshold has been exceeded. ({0}) + + + The WarnPunishmentDuration setting is not set for the current punishment type. + diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx index 072a2e8..d28b2ef 100644 --- a/locale/Messages.ru.resx +++ b/locale/Messages.ru.resx @@ -693,7 +693,7 @@ Ваши предупреждения были отозваны - + Предупреждений: {0} @@ -711,7 +711,7 @@ Длительность наказания для предупреждений - + Вот ваши предупреждения, {0}: @@ -762,4 +762,37 @@ Я не могу предупреждать участников этого сервера! + + Ты не можешь просмотреть мои предупреждения! + + + Ты не можешь просмотреть предупреждения владельца этого сервера! + + + Ты не можешь просмотреть предупреждения этого участника! + + + Вместо этого, используйте эту команду без параметров. + + + Ты не можешь снимать предупреждения с участников этого сервера! + + + Ты не можешь предупреждать участников этого сервера! + + + Ты не можешь просматривать предупреждения участников этого сервера! + + + Предупреждения пользователя {0}: + + + Тип наказания: {0} + + + Превышен порог предупреждений. ({0}) + + + Настройка WarnPunishmentDuration не установлена для текущего типа наказания. + diff --git a/locale/Messages.tt-ru.resx b/locale/Messages.tt-ru.resx index 8f56514..9dbf513 100644 --- a/locale/Messages.tt-ru.resx +++ b/locale/Messages.tt-ru.resx @@ -693,7 +693,7 @@ вы получили карт-бланш - + варнов: {0} @@ -711,7 +711,7 @@ сколько варнов чтобы потом бан - + хаха, смотри {0}, это все ты заслужил: @@ -762,4 +762,37 @@ я не могу ваще никого варить... + + тебе нельзя варить шизоидов + + + тебе нельзя разваривать шизоидов + + + а ты специально указал себя когда мог этого не делать? + + + тебе нельзя чекать варны шизоидов + + + чекать варны админа нельзя + + + чекать варны бота нельзя + + + чекать варты этого шизика нельзя + + + самовары {0}: + + + тип бана: {0} + + + мы дошли до лимита варнов. ({0}) + + + наказание что было установлено хочет установленного WarnPunishmentDuration + diff --git a/src/Commands/WarnCommandGroup.cs b/src/Commands/WarnCommandGroup.cs index a57b079..74b9040 100644 --- a/src/Commands/WarnCommandGroup.cs +++ b/src/Commands/WarnCommandGroup.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System.Collections; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Text; using JetBrains.Annotations; @@ -24,6 +25,7 @@ namespace Octobot.Commands; [UsedImplicitly] public class WarnCommandGroup : CommandGroup { + private readonly AccessControlService _access; private readonly IDiscordRestChannelAPI _channelApi; private readonly ICommandContext _context; private readonly IFeedbackService _feedback; @@ -35,7 +37,7 @@ public class WarnCommandGroup : CommandGroup public WarnCommandGroup( ICommandContext context, IDiscordRestChannelAPI channelApi, GuildDataService guildData, IFeedbackService feedback, IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi, - Utility utility) + Utility utility, AccessControlService access) { _context = context; _channelApi = channelApi; @@ -44,13 +46,14 @@ public class WarnCommandGroup : CommandGroup _guildApi = guildApi; _userApi = userApi; _utility = utility; + _access = access; } [Command("warn")] - [DiscordDefaultMemberPermissions(DiscordPermission.KickMembers)] + [DiscordDefaultMemberPermissions(DiscordPermission.ManageMessages)] [DiscordDefaultDMPermission(false)] [RequireContext(ChannelContext.Guild)] - [RequireDiscordPermission(DiscordPermission.KickMembers)] + [RequireDiscordPermission(DiscordPermission.ManageMessages)] [RequireBotDiscordPermissions(DiscordPermission.KickMembers, DiscordPermission.ModerateMembers, DiscordPermission.BanMembers)] [Description("Warn user")] @@ -93,7 +96,7 @@ public class WarnCommandGroup : CommandGroup GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) { var interactionResult - = await _utility.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Warn", ct); + = await _access.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Warn", ct); if (!interactionResult.IsSuccess) { return ResultExtensions.FromError(interactionResult); @@ -110,6 +113,17 @@ public class WarnCommandGroup : CommandGroup var memberData = data.GetOrCreateMemberData(target.ID); var warns = memberData.Warns; + var warnThreshold = GuildSettings.WarnsThreshold.Get(data.Settings); + + if (warns.Count >= warnThreshold && warnThreshold is not 0) + { + var errorEmbed = new EmbedBuilder() + .WithSmallTitle(string.Format(Messages.WarnThresholdExceeded, warnThreshold), bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); + } + warns.Add(new Warn { WarnedBy = executor.ID.Value, @@ -117,12 +131,10 @@ public class WarnCommandGroup : CommandGroup Reason = reason }); - var warnsThreshold = GuildSettings.WarnsThreshold.Get(data.Settings); - var builder = new StringBuilder() .AppendBulletPointLine(string.Format(Messages.DescriptionActionReason, reason)) - .AppendBulletPointLine(string.Format(Messages.DescriptionActionWarns, - warnsThreshold is 0 ? warns.Count : $"{warns.Count}/{warnsThreshold}")); + .AppendBulletPointLine(string.Format(Messages.DescriptionWarns, + warnThreshold is 0 ? warns.Count : $"{warns.Count}/{warnThreshold}")); var title = string.Format(Messages.UserWarned, target.GetTag()); var description = builder.ToString(); @@ -144,11 +156,10 @@ public class WarnCommandGroup : CommandGroup _utility.LogAction( data.Settings, channelId, executor, title, description, target, ColorsList.Yellow, false, ct); - if (warns.Count >= warnsThreshold && + if (warns.Count >= warnThreshold && GuildSettings.WarnPunishment.Get(data.Settings) is not "off" and not "disable" and not "disabled") { - warns.Clear(); - return await PunishUserAsync(target, guild, data, channelId, bot, CancellationToken); + return await PunishUserAsync(target, guild, data, channelId, bot, warns, CancellationToken); } var embed = new EmbedBuilder().WithSmallTitle( @@ -159,42 +170,56 @@ public class WarnCommandGroup : CommandGroup } private async Task PunishUserAsync(IUser target, IGuild guild, - GuildData data, Snowflake channelId, IUser bot, CancellationToken ct) + GuildData data, Snowflake channelId, IUser bot, IList warns, CancellationToken ct) { var settings = data.Settings; + var warnPunishment = GuildSettings.WarnPunishment.Get(settings); var duration = GuildSettings.WarnPunishmentDuration.Get(settings); - if (GuildSettings.WarnPunishment.Get(settings) is "ban" - && duration != TimeSpan.Zero) + if (warnPunishment is "ban" && duration != TimeSpan.Zero) { - var banCommandGroup = new BanCommandGroup(_context, _channelApi, _guildData, _feedback, _guildApi, _userApi, _utility); + warns.Clear(); + var banCommandGroup = new BanCommandGroup( + _access, _channelApi, _context, _feedback, _guildApi, _guildData, _userApi, _utility); await banCommandGroup.BanUserAsync(bot, target, Messages.ReceivedTooManyWarnings, duration, guild, data, channelId, bot, ct); } - if (GuildSettings.WarnPunishment.Get(settings) is "kick") + if (warnPunishment is "kick") { - var kickCommandGroup = new KickCommandGroup(_context, _channelApi, _guildData, _feedback, _guildApi, _userApi, _utility); + warns.Clear(); + var kickCommandGroup = new KickCommandGroup( + _access, _channelApi, _context, _feedback, _guildApi, _guildData, _userApi, _utility); await kickCommandGroup.KickUserAsync(bot, target, Messages.ReceivedTooManyWarnings, guild, channelId, data, bot, ct); } - if (GuildSettings.WarnPunishment.Get(settings) is "mute" - && duration != TimeSpan.Zero) + if (warnPunishment is "mute" && duration != TimeSpan.Zero) { - var muteCommandGroup = new MuteCommandGroup(_context, _guildData, _feedback, _guildApi, _userApi, _utility); + warns.Clear(); + var muteCommandGroup = new MuteCommandGroup( + _access, _context, _feedback, _guildApi, _guildData, _userApi, _utility); await muteCommandGroup.MuteUserAsync(bot, target, Messages.ReceivedTooManyWarnings, duration, guild.ID, data, channelId, bot, ct); } - return Result.FromSuccess(); + if (warnPunishment is not ("ban" or "mute") || duration != TimeSpan.Zero) + { + return Result.FromSuccess(); + } + + var errorEmbed = new EmbedBuilder() + .WithSmallTitle(Messages.WarnPunishmentDurationNotSet, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); } [Command("unwarn")] - [DiscordDefaultMemberPermissions(DiscordPermission.KickMembers)] + [DiscordDefaultMemberPermissions(DiscordPermission.ManageMessages)] [DiscordDefaultDMPermission(false)] [RequireContext(ChannelContext.Guild)] - [RequireDiscordPermission(DiscordPermission.KickMembers)] + [RequireDiscordPermission(DiscordPermission.ManageMessages)] [Description("Remove warns from user")] [UsedImplicitly] public async Task ExecuteUnwarnAsync( @@ -244,7 +269,7 @@ public class WarnCommandGroup : CommandGroup IGuild guild, GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) { var interactionResult - = await _utility.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Unwarn", ct); + = await _access.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Unwarn", ct); if (!interactionResult.IsSuccess) { return ResultExtensions.FromError(interactionResult); @@ -316,7 +341,7 @@ public class WarnCommandGroup : CommandGroup IGuild guild, GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) { var interactionResult - = await _utility.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Unwarn", ct); + = await _access.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Unwarn", ct); if (!interactionResult.IsSuccess) { return ResultExtensions.FromError(interactionResult); @@ -377,7 +402,9 @@ public class WarnCommandGroup : CommandGroup [Ephemeral] [Description("(Ephemeral) Get current warns")] [UsedImplicitly] - public async Task ExecuteListWarnsAsync() + public async Task ExecuteListWarnsAsync( + [Description("(Moderator-only) Get target's current warns")] + IUser? target = null) { if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId)) { @@ -405,10 +432,75 @@ public class WarnCommandGroup : CommandGroup var data = await _guildData.GetData(guild.ID, CancellationToken); Messages.Culture = GuildSettings.Language.Get(data.Settings); - return await ListWarnsAsync(executor, data, bot, CancellationToken); + if (target is not null) + { + return await ListTargetWarnsAsync(executor, target, guild, data, bot, CancellationToken); + } + + return await ListExecutorWarnsAsync(executor, data, bot, CancellationToken); } - private async Task ListWarnsAsync(IUser executor, GuildData data, IUser bot, CancellationToken ct = default) + private async Task ListTargetWarnsAsync(IUser executor, IUser target, IGuild guild, + GuildData data, IUser bot, CancellationToken ct = default) + { + var interactionResult + = await _access.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "GetWarns", ct); + if (!interactionResult.IsSuccess) + { + return ResultExtensions.FromError(interactionResult); + } + + if (interactionResult.Entity is not null) + { + var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); + } + + var memberData = data.GetOrCreateMemberData(target.ID); + var warns = memberData.Warns; + + if (warns.Count is 0) + { + var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.UserHasNoWarnings, bot) + .WithColour(ColorsList.Green).Build(); + + return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct); + } + + var warnThreshold = GuildSettings.WarnsThreshold.Get(data.Settings); + + var punishmentType = GuildSettings.WarnPunishment.Get(data.Settings); + + var description = new StringBuilder() + .AppendLine(string.Format(Messages.DescriptionWarns, + warnThreshold is 0 ? warns.Count : $"{warns.Count}/{warnThreshold}")); + if (punishmentType is not "off" and not "disable" and not "disabled") + { + description.AppendLine(string.Format( + Messages.DescriptionPunishmentType, Markdown.InlineCode(punishmentType))); + } + + var warnCount = 0; + foreach (var warn in warns) + { + warnCount++; + description.Append(warnCount).Append(". ").AppendLine(warn.Reason) + .AppendSubBulletPoint(Messages.IssuedBy).Append(' ').AppendLine(Mention.User(warn.WarnedBy.ToSnowflake())) + .AppendSubBulletPointLine(string.Format(Messages.ReceivedOn, Markdown.Timestamp(warn.At))); + } + + var embed = new EmbedBuilder() + .WithSmallTitle(string.Format(Messages.ListTargetWarnsTitle, target.GetTag()), target) + .WithDescription(description.ToString()) + .WithColour(ColorsList.Default).Build(); + + return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct); + } + + private async Task ListExecutorWarnsAsync(IUser executor, GuildData data, IUser bot, + CancellationToken ct = default) { var memberData = data.GetOrCreateMemberData(executor.ID); var warns = memberData.Warns; @@ -421,11 +513,18 @@ public class WarnCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct); } - var warnsThreshold = GuildSettings.WarnsThreshold.Get(data.Settings); + var warnThreshold = GuildSettings.WarnsThreshold.Get(data.Settings); + + var punishmentType = GuildSettings.WarnPunishment.Get(data.Settings); var description = new StringBuilder() - .AppendLine(string.Format(Messages.DescriptionActionWarns, - warnsThreshold is 0 ? warns.Count : $"{warns.Count}/{warnsThreshold}")); + .AppendLine(string.Format(Messages.DescriptionWarns, + warnThreshold is 0 ? warns.Count : $"{warns.Count}/{warnThreshold}")); + if (punishmentType is not "off" and not "disable" and not "disabled") + { + description.AppendLine(string.Format( + Messages.DescriptionPunishmentType, Markdown.InlineCode(punishmentType))); + } var warnCount = 0; foreach (var warn in warns) @@ -437,7 +536,7 @@ public class WarnCommandGroup : CommandGroup } var embed = new EmbedBuilder() - .WithSmallTitle(string.Format(Messages.ListWarnTitle, executor.GetTag()), executor) + .WithSmallTitle(string.Format(Messages.ListExecutorWarnsTitle, executor.GetTag()), executor) .WithDescription(description.ToString()) .WithColour(ColorsList.Default).Build(); diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs index 9c50043..cadb676 100644 --- a/src/Messages.Designer.cs +++ b/src/Messages.Designer.cs @@ -1233,10 +1233,10 @@ namespace Octobot { } } - internal static string DescriptionActionWarns + internal static string DescriptionWarns { get { - return ResourceManager.GetString("DescriptionActionWarns", resourceCulture); + return ResourceManager.GetString("DescriptionWarns", resourceCulture); } } @@ -1273,10 +1273,10 @@ namespace Octobot { } } - internal static string ListWarnTitle + internal static string ListTargetWarnsTitle { get { - return ResourceManager.GetString("ListWarnTitle", resourceCulture); + return ResourceManager.GetString("ListTargetWarnsTitle", resourceCulture); } } @@ -1308,80 +1308,32 @@ namespace Octobot { } } - internal static string UserCannotWarnBot + internal static string ListExecutorWarnsTitle { get { - return ResourceManager.GetString("UserCannotWarnBot", resourceCulture); + return ResourceManager.GetString("ListExecutorWarnsTitle", resourceCulture); } } - internal static string UserCannotWarnOwner + internal static string DescriptionPunishmentType { get { - return ResourceManager.GetString("UserCannotWarnOwner", resourceCulture); + return ResourceManager.GetString("DescriptionPunishmentType", resourceCulture); } } - internal static string UserCannotWarnTarget + internal static string WarnThresholdExceeded { get { - return ResourceManager.GetString("UserCannotWarnTarget", resourceCulture); + return ResourceManager.GetString("WarnThresholdExceeded", resourceCulture); } } - internal static string UserCannotWarnThemselves + internal static string WarnPunishmentDurationNotSet { - get { - return ResourceManager.GetString("UserCannotWarnThemselves", resourceCulture); - } - } - - internal static string UserCannotUnwarnBot - { - get { - return ResourceManager.GetString("UserCannotUnwarnBot", resourceCulture); - } - } - - internal static string UserCannotUnwarnOwner - { - get { - return ResourceManager.GetString("UserCannotUnwarnOwner", resourceCulture); - } - } - - internal static string UserCannotUnwarnTarget - { - get { - return ResourceManager.GetString("UserCannotUnwarnTarget", resourceCulture); - } - } - - internal static string UserCannotUnwarnThemselves - { - get { - return ResourceManager.GetString("UserCannotUnwarnThemselves", resourceCulture); - } - } - - internal static string BotCannotWarnTarget - { - get { - return ResourceManager.GetString("BotCannotWarnTarget", resourceCulture); - } - } - - internal static string BotCannotWarnMembers - { - get { - return ResourceManager.GetString("BotCannotWarnMembers", resourceCulture); - } - } - - internal static string BotCannotUnwarnTarget - { - get { - return ResourceManager.GetString("BotCannotUnwarnTarget", resourceCulture); + get + { + return ResourceManager.GetString("WarnPunishmentDurationNotSet", resourceCulture); } } } diff --git a/src/Services/AccessControlService.cs b/src/Services/AccessControlService.cs index cb235f9..d164ed1 100644 --- a/src/Services/AccessControlService.cs +++ b/src/Services/AccessControlService.cs @@ -116,7 +116,8 @@ public sealed class AccessControlService { "Ban" => DiscordPermission.BanMembers, "Kick" => DiscordPermission.KickMembers, - "Mute" or "Unmute" => DiscordPermission.ModerateMembers, + "Mute" or "Unmute" or "Warn" or "Unwarn" or "GetWarns" + => DiscordPermission.ModerateMembers, _ => throw new Exception() }); From 5239b8280682b7ea4e22e9fdbb3a67fd05e36b6e Mon Sep 17 00:00:00 2001 From: mctaylors Date: Tue, 26 Mar 2024 21:54:31 +0300 Subject: [PATCH 5/9] Some workarounds... Signed-off-by: mctaylors --- locale/Messages.resx | 5 +- locale/Messages.ru.resx | 5 +- locale/Messages.tt-ru.resx | 5 +- src/Commands/WarnCommandGroup.cs | 137 ++++++++++++++++--------------- src/Messages.Designer.cs | 8 ++ 5 files changed, 89 insertions(+), 71 deletions(-) diff --git a/locale/Messages.resx b/locale/Messages.resx index 326e1b8..9f771e3 100644 --- a/locale/Messages.resx +++ b/locale/Messages.resx @@ -793,6 +793,9 @@ Warn threshold has been exceeded. ({0}) - The WarnPunishmentDuration setting is not set for the current punishment type. + Warn Punishment Duration is not set for the current punishment type. + + + Warn Threshold is set, but the Warn Punishment is not. diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx index d28b2ef..1dd0ffc 100644 --- a/locale/Messages.ru.resx +++ b/locale/Messages.ru.resx @@ -793,6 +793,9 @@ Превышен порог предупреждений. ({0}) - Настройка WarnPunishmentDuration не установлена для текущего типа наказания. + Длительность наказания предупреждения не установлена для текущего типа наказания. + + + Порог предупреждения установлен, но наказание нет. diff --git a/locale/Messages.tt-ru.resx b/locale/Messages.tt-ru.resx index 9dbf513..13f12bf 100644 --- a/locale/Messages.tt-ru.resx +++ b/locale/Messages.tt-ru.resx @@ -793,6 +793,9 @@ мы дошли до лимита варнов. ({0}) - наказание что было установлено хочет установленного WarnPunishmentDuration + наказание что было установлено хочет установленную ддлительность в настройках + + + лимит стоит, а наказания нет. diff --git a/src/Commands/WarnCommandGroup.cs b/src/Commands/WarnCommandGroup.cs index 74b9040..d7e6fc5 100644 --- a/src/Commands/WarnCommandGroup.cs +++ b/src/Commands/WarnCommandGroup.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Text; +using System.Text.Json.Nodes; using JetBrains.Annotations; using Octobot.Data; using Octobot.Extensions; @@ -89,14 +90,8 @@ public class WarnCommandGroup : CommandGroup var data = await _guildData.GetData(guild.ID, CancellationToken); Messages.Culture = GuildSettings.Language.Get(data.Settings); - return await WarnUserAsync(executor, target, reason, guild, data, channelId, bot, CancellationToken); - } - - private async Task WarnUserAsync(IUser executor, IUser target, string reason, IGuild guild, - GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) - { var interactionResult - = await _access.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Warn", ct); + = await _access.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Warn", CancellationToken); if (!interactionResult.IsSuccess) { return ResultExtensions.FromError(interactionResult); @@ -107,13 +102,41 @@ public class WarnCommandGroup : CommandGroup var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, bot) .WithColour(ColorsList.Red).Build(); - return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: CancellationToken); } + return await WarnPreparationAsync(executor, target, reason, guild, data, channelId, bot, CancellationToken); + } + + private async Task WarnPreparationAsync(IUser executor, IUser target, string reason, IGuild guild, + GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) + { var memberData = data.GetOrCreateMemberData(target.ID); var warns = memberData.Warns; - var warnThreshold = GuildSettings.WarnsThreshold.Get(data.Settings); + var settings = data.Settings; + + var warnThreshold = GuildSettings.WarnsThreshold.Get(settings); + var warnPunishment = GuildSettings.WarnPunishment.Get(settings); + var warnDuration = GuildSettings.WarnPunishmentDuration.Get(settings); + + if (warnPunishment is "off" or "disable" or "disabled" && warns.Count - 1 >= warnThreshold) + { + var errorEmbed = new EmbedBuilder() + .WithSmallTitle(Messages.WarnPunishmentDisabled, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: CancellationToken); + } + + if (warnPunishment is "ban" or "mute" && warnDuration == TimeSpan.Zero) + { + var errorEmbed = new EmbedBuilder() + .WithSmallTitle(Messages.WarnPunishmentDurationNotSet, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: CancellationToken); + } if (warns.Count >= warnThreshold && warnThreshold is not 0) { @@ -124,6 +147,14 @@ public class WarnCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); } + return await WarnUserAsync(executor, target, reason, guild, data, channelId, bot, settings, + warns, warnThreshold, warnPunishment, warnDuration, ct); + } + + private async Task WarnUserAsync(IUser executor, IUser target, string reason, IGuild guild, + GuildData data, Snowflake channelId, IUser bot, JsonNode settings, IList warns, int warnThreshold, + string warnPunishment, TimeSpan warnDuration, CancellationToken ct = default) + { warns.Add(new Warn { WarnedBy = executor.ID.Value, @@ -153,66 +184,51 @@ public class WarnCommandGroup : CommandGroup await _channelApi.CreateMessageWithEmbedResultAsync(dmChannel.ID, embedResult: dmEmbed, ct: ct); } - _utility.LogAction( - data.Settings, channelId, executor, title, description, target, ColorsList.Yellow, false, ct); - - if (warns.Count >= warnThreshold && - GuildSettings.WarnPunishment.Get(data.Settings) is not "off" and not "disable" and not "disabled") - { - return await PunishUserAsync(target, guild, data, channelId, bot, warns, CancellationToken); - } + _utility.LogAction(settings, channelId, executor, title, description, + target, ColorsList.Yellow, false, ct); var embed = new EmbedBuilder().WithSmallTitle( title, target) .WithColour(ColorsList.Green).Build(); + if (warns.Count >= warnThreshold) + { + return await PunishUserAsync(target, guild, data, channelId, bot, warns, warnPunishment, warnDuration, CancellationToken); + } + return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct); } - private async Task PunishUserAsync(IUser target, IGuild guild, - GuildData data, Snowflake channelId, IUser bot, IList warns, CancellationToken ct) + private async Task PunishUserAsync(IUser target, IGuild guild, GuildData data, + Snowflake channelId, IUser bot, IList warns, string punishment, TimeSpan duration, CancellationToken ct) { - var settings = data.Settings; - var warnPunishment = GuildSettings.WarnPunishment.Get(settings); - var duration = GuildSettings.WarnPunishmentDuration.Get(settings); + warns.Clear(); - if (warnPunishment is "ban" && duration != TimeSpan.Zero) + if (punishment is "ban" && duration != TimeSpan.Zero) { - warns.Clear(); var banCommandGroup = new BanCommandGroup( _access, _channelApi, _context, _feedback, _guildApi, _guildData, _userApi, _utility); await banCommandGroup.BanUserAsync(bot, target, Messages.ReceivedTooManyWarnings, duration, guild, data, channelId, bot, ct); } - if (warnPunishment is "kick") + if (punishment is "kick") { - warns.Clear(); var kickCommandGroup = new KickCommandGroup( _access, _channelApi, _context, _feedback, _guildApi, _guildData, _userApi, _utility); await kickCommandGroup.KickUserAsync(bot, target, Messages.ReceivedTooManyWarnings, guild, channelId, data, bot, ct); } - if (warnPunishment is "mute" && duration != TimeSpan.Zero) + if (punishment is "mute" && duration != TimeSpan.Zero) { - warns.Clear(); var muteCommandGroup = new MuteCommandGroup( _access, _context, _feedback, _guildApi, _guildData, _userApi, _utility); await muteCommandGroup.MuteUserAsync(bot, target, Messages.ReceivedTooManyWarnings, duration, guild.ID, data, channelId, bot, ct); } - if (warnPunishment is not ("ban" or "mute") || duration != TimeSpan.Zero) - { - return Result.FromSuccess(); - } - - var errorEmbed = new EmbedBuilder() - .WithSmallTitle(Messages.WarnPunishmentDurationNotSet, bot) - .WithColour(ColorsList.Red).Build(); - - return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); + return Result.FromSuccess(); } [Command("unwarn")] @@ -256,6 +272,21 @@ public class WarnCommandGroup : CommandGroup var data = await _guildData.GetData(guild.ID, CancellationToken); Messages.Culture = GuildSettings.Language.Get(data.Settings); + var interactionResult + = await _access.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Unwarn", CancellationToken); + if (!interactionResult.IsSuccess) + { + return ResultExtensions.FromError(interactionResult); + } + + if (interactionResult.Entity is not null) + { + var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: CancellationToken); + } + if (number is not null) { return await RemoveUserWarnAsync(executor, target, reason, number.Value, guild, data, channelId, bot, @@ -268,21 +299,6 @@ public class WarnCommandGroup : CommandGroup private async Task RemoveUserWarnAsync(IUser executor, IUser target, string reason, int warnNumber, IGuild guild, GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) { - var interactionResult - = await _access.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Unwarn", ct); - if (!interactionResult.IsSuccess) - { - return ResultExtensions.FromError(interactionResult); - } - - if (interactionResult.Entity is not null) - { - var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, bot) - .WithColour(ColorsList.Red).Build(); - - return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); - } - var memberData = data.GetOrCreateMemberData(target.ID); var warns = memberData.Warns; @@ -340,21 +356,6 @@ public class WarnCommandGroup : CommandGroup private async Task RemoveUserWarnsAsync(IUser executor, IUser target, string reason, IGuild guild, GuildData data, Snowflake channelId, IUser bot, CancellationToken ct = default) { - var interactionResult - = await _access.CheckInteractionsAsync(guild.ID, executor.ID, target.ID, "Unwarn", ct); - if (!interactionResult.IsSuccess) - { - return ResultExtensions.FromError(interactionResult); - } - - if (interactionResult.Entity is not null) - { - var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, bot) - .WithColour(ColorsList.Red).Build(); - - return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); - } - var memberData = data.GetOrCreateMemberData(target.ID); var warns = memberData.Warns; diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs index cadb676..8141cb0 100644 --- a/src/Messages.Designer.cs +++ b/src/Messages.Designer.cs @@ -1336,5 +1336,13 @@ namespace Octobot { return ResourceManager.GetString("WarnPunishmentDurationNotSet", resourceCulture); } } + + internal static string WarnPunishmentDisabled + { + get + { + return ResourceManager.GetString("WarnPunishmentDisabled", resourceCulture); + } + } } } From 5ff23722ce5b51a9c0323824184eef5584d7f89b Mon Sep 17 00:00:00 2001 From: mctaylors Date: Tue, 26 Mar 2024 22:08:57 +0300 Subject: [PATCH 6/9] ...and a few small touches Signed-off-by: mctaylors --- src/Commands/WarnCommandGroup.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Commands/WarnCommandGroup.cs b/src/Commands/WarnCommandGroup.cs index d7e6fc5..7ad1f80 100644 --- a/src/Commands/WarnCommandGroup.cs +++ b/src/Commands/WarnCommandGroup.cs @@ -191,7 +191,7 @@ public class WarnCommandGroup : CommandGroup title, target) .WithColour(ColorsList.Green).Build(); - if (warns.Count >= warnThreshold) + if (warns.Count >= warnThreshold && warnThreshold is not 0) { return await PunishUserAsync(target, guild, data, channelId, bot, warns, warnPunishment, warnDuration, CancellationToken); } @@ -202,8 +202,6 @@ public class WarnCommandGroup : CommandGroup private async Task PunishUserAsync(IUser target, IGuild guild, GuildData data, Snowflake channelId, IUser bot, IList warns, string punishment, TimeSpan duration, CancellationToken ct) { - warns.Clear(); - if (punishment is "ban" && duration != TimeSpan.Zero) { var banCommandGroup = new BanCommandGroup( @@ -228,6 +226,7 @@ public class WarnCommandGroup : CommandGroup duration, guild.ID, data, channelId, bot, ct); } + warns.Clear(); return Result.FromSuccess(); } From 422becf6c6338db4c5dec49592019e353df69299 Mon Sep 17 00:00:00 2001 From: mctaylors Date: Mon, 8 Apr 2024 15:55:23 +0300 Subject: [PATCH 7/9] and we're back to our PR Signed-off-by: mctaylors --- locale/Messages.resx | 4 ++-- locale/Messages.ru.resx | 4 ++-- src/Commands/WarnCommandGroup.cs | 16 +++++----------- src/Messages.Designer.cs | 4 ++-- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/locale/Messages.resx b/locale/Messages.resx index 9f771e3..191e4a1 100644 --- a/locale/Messages.resx +++ b/locale/Messages.resx @@ -795,7 +795,7 @@ Warn Punishment Duration is not set for the current punishment type. - - Warn Threshold is set, but the Warn Punishment is not. + + Increase the Warn Threshold or set a Warn Punishment. diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx index 1dd0ffc..f3fb587 100644 --- a/locale/Messages.ru.resx +++ b/locale/Messages.ru.resx @@ -795,7 +795,7 @@ Длительность наказания предупреждения не установлена для текущего типа наказания. - - Порог предупреждения установлен, но наказание нет. + + Увеличьте порог предупреждения или установите наказание за предупреждение. diff --git a/src/Commands/WarnCommandGroup.cs b/src/Commands/WarnCommandGroup.cs index 7ad1f80..dea3102 100644 --- a/src/Commands/WarnCommandGroup.cs +++ b/src/Commands/WarnCommandGroup.cs @@ -120,10 +120,13 @@ public class WarnCommandGroup : CommandGroup var warnPunishment = GuildSettings.WarnPunishment.Get(settings); var warnDuration = GuildSettings.WarnPunishmentDuration.Get(settings); - if (warnPunishment is "off" or "disable" or "disabled" && warns.Count - 1 >= warnThreshold) + if (warnPunishment is "off" or "disable" or "disabled" + && warns.Count + 1 >= warnThreshold + && warnThreshold is not 0) { var errorEmbed = new EmbedBuilder() - .WithSmallTitle(Messages.WarnPunishmentDisabled, bot) + .WithSmallTitle(string.Format(Messages.WarnThresholdExceeded, warnThreshold), bot) + .WithDescription(Messages.WarnThresholdExceededDescription) .WithColour(ColorsList.Red).Build(); return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: CancellationToken); @@ -138,15 +141,6 @@ public class WarnCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: CancellationToken); } - if (warns.Count >= warnThreshold && warnThreshold is not 0) - { - var errorEmbed = new EmbedBuilder() - .WithSmallTitle(string.Format(Messages.WarnThresholdExceeded, warnThreshold), bot) - .WithColour(ColorsList.Red).Build(); - - return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); - } - return await WarnUserAsync(executor, target, reason, guild, data, channelId, bot, settings, warns, warnThreshold, warnPunishment, warnDuration, ct); } diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs index 8141cb0..19737fd 100644 --- a/src/Messages.Designer.cs +++ b/src/Messages.Designer.cs @@ -1337,11 +1337,11 @@ namespace Octobot { } } - internal static string WarnPunishmentDisabled + internal static string WarnThresholdExceededDescription { get { - return ResourceManager.GetString("WarnPunishmentDisabled", resourceCulture); + return ResourceManager.GetString("WarnThresholdExceededDescription", resourceCulture); } } } From 1e9e1b4bb2d011f2bde6dec7c9b8fac7e3f2dbed Mon Sep 17 00:00:00 2001 From: mctaylors Date: Mon, 8 Apr 2024 16:38:03 +0300 Subject: [PATCH 8/9] Permission validation, PunishmentOption and that's it...? Signed-off-by: mctaylors --- locale/Messages.resx | 3 +++ locale/Messages.ru.resx | 3 +++ src/Commands/WarnCommandGroup.cs | 25 +++++++++++++++++++++++-- src/Data/GuildSettings.cs | 2 +- src/Data/Options/PunishmentOption.cs | 23 +++++++++++++++++++++++ src/Messages.Designer.cs | 8 ++++++++ 6 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/Data/Options/PunishmentOption.cs diff --git a/locale/Messages.resx b/locale/Messages.resx index 191e4a1..037ad3f 100644 --- a/locale/Messages.resx +++ b/locale/Messages.resx @@ -798,4 +798,7 @@ Increase the Warn Threshold or set a Warn Punishment. + + Invalid Warn Punishment is set. + diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx index f3fb587..68062d4 100644 --- a/locale/Messages.ru.resx +++ b/locale/Messages.ru.resx @@ -798,4 +798,7 @@ Увеличьте порог предупреждения или установите наказание за предупреждение. + + Установлено неверное наказание за предупреждение. + diff --git a/src/Commands/WarnCommandGroup.cs b/src/Commands/WarnCommandGroup.cs index dea3102..f4c250a 100644 --- a/src/Commands/WarnCommandGroup.cs +++ b/src/Commands/WarnCommandGroup.cs @@ -141,6 +141,28 @@ public class WarnCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: CancellationToken); } + if (warns.Count + 1 < warnThreshold) + { + return await WarnUserAsync(executor, target, reason, guild, data, channelId, bot, settings, + warns, warnThreshold, warnPunishment, warnDuration, ct); + } + + var interactionResult + = await _access.CheckInteractionsAsync(guild.ID, bot.ID, target.ID, + $"{char.ToUpperInvariant(warnPunishment[0])}{warnPunishment[1..]}", ct); + if (!interactionResult.IsSuccess) + { + return ResultExtensions.FromError(interactionResult); + } + + if (interactionResult.Entity is not null) + { + var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: ct); + } + return await WarnUserAsync(executor, target, reason, guild, data, channelId, bot, settings, warns, warnThreshold, warnPunishment, warnDuration, ct); } @@ -181,8 +203,7 @@ public class WarnCommandGroup : CommandGroup _utility.LogAction(settings, channelId, executor, title, description, target, ColorsList.Yellow, false, ct); - var embed = new EmbedBuilder().WithSmallTitle( - title, target) + var embed = new EmbedBuilder().WithSmallTitle(title, target) .WithColour(ColorsList.Green).Build(); if (warns.Count >= warnThreshold && warnThreshold is not 0) diff --git a/src/Data/GuildSettings.cs b/src/Data/GuildSettings.cs index 83d8d2d..fc616bd 100644 --- a/src/Data/GuildSettings.cs +++ b/src/Data/GuildSettings.cs @@ -12,7 +12,7 @@ public static class GuildSettings { public static readonly LanguageOption Language = new("Language", "en"); - public static readonly Option WarnPunishment = new("WarnPunishment", "disabled"); + public static readonly PunishmentOption WarnPunishment = new("WarnPunishment", "disabled"); /// /// Controls what message should be sent in when a new member joins the guild. diff --git a/src/Data/Options/PunishmentOption.cs b/src/Data/Options/PunishmentOption.cs new file mode 100644 index 0000000..f8d5414 --- /dev/null +++ b/src/Data/Options/PunishmentOption.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Nodes; +using Remora.Results; + +namespace Octobot.Data.Options; + +/// +public sealed class PunishmentOption : Option +{ + private static readonly List AllowedValues = + [ + "ban", "kick", "mute", "off", "disable", "disabled" + ]; + + public PunishmentOption(string name, string defaultValue) : base(name, defaultValue) { } + + /// + public override Result Set(JsonNode settings, string from) + { + return AllowedValues.Contains(from.ToLowerInvariant()) + ? base.Set(settings, from.ToLowerInvariant()) + : new ArgumentInvalidError(nameof(from), Messages.InvalidWarnPunishment); + } +} diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs index 19737fd..8266e7f 100644 --- a/src/Messages.Designer.cs +++ b/src/Messages.Designer.cs @@ -1344,5 +1344,13 @@ namespace Octobot { return ResourceManager.GetString("WarnThresholdExceededDescription", resourceCulture); } } + + internal static string InvalidWarnPunishment + { + get + { + return ResourceManager.GetString("InvalidWarnPunishment", resourceCulture); + } + } } } From 646c80af5986b06eb247965c42e011e1d3cf4b2e Mon Sep 17 00:00:00 2001 From: mctaylors Date: Mon, 8 Apr 2024 16:46:17 +0300 Subject: [PATCH 9/9] oops Signed-off-by: mctaylors --- src/Commands/WarnCommandGroup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/WarnCommandGroup.cs b/src/Commands/WarnCommandGroup.cs index f4c250a..8f4eb29 100644 --- a/src/Commands/WarnCommandGroup.cs +++ b/src/Commands/WarnCommandGroup.cs @@ -141,7 +141,7 @@ public class WarnCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(errorEmbed, ct: CancellationToken); } - if (warns.Count + 1 < warnThreshold) + if (warns.Count + 1 < warnThreshold || warnThreshold is 0) { return await WarnUserAsync(executor, target, reason, guild, data, channelId, bot, settings, warns, warnThreshold, warnPunishment, warnDuration, ct);