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()
});