diff --git a/locale/Messages.resx b/locale/Messages.resx index ab821ac..31ed7b3 100644 --- a/locale/Messages.resx +++ b/locale/Messages.resx @@ -564,4 +564,10 @@ Boost count + + There are no messages matching your filter! + + + Cleared {0} messages from {1} + diff --git a/locale/Messages.ru.resx b/locale/Messages.ru.resx index f38032b..cb65749 100644 --- a/locale/Messages.ru.resx +++ b/locale/Messages.ru.resx @@ -564,4 +564,10 @@ Количество бустов + + Нет сообщений, которые подходят под твой фильтр! + + + Очищено {0} сообщений от {1} + diff --git a/locale/Messages.tt-ru.resx b/locale/Messages.tt-ru.resx index 52d6c7e..b5f6ad1 100644 --- a/locale/Messages.tt-ru.resx +++ b/locale/Messages.tt-ru.resx @@ -564,4 +564,10 @@ кол-во бустов + + алло а чё мне удалять-то + + + вырезано {0} забавных сообщений от {1} + diff --git a/src/Commands/ClearCommandGroup.cs b/src/Commands/ClearCommandGroup.cs index a6ac188..714c9de 100644 --- a/src/Commands/ClearCommandGroup.cs +++ b/src/Commands/ClearCommandGroup.cs @@ -45,9 +45,10 @@ public class ClearCommandGroup : CommandGroup } /// - /// A slash command that clears messages in the channel it was executed. + /// A slash command that clears messages in the channel it was executed, optionally filtering by message author. /// /// The amount of messages to clear. + /// The user whose messages will be cleared. /// /// A feedback sending result which may or may not have succeeded. A successful result does not mean that any messages /// were cleared and vice-versa. @@ -62,7 +63,8 @@ public class ClearCommandGroup : CommandGroup [UsedImplicitly] public async Task ExecuteClear( [Description("Number of messages to remove (2-100)")] [MinValue(2)] [MaxValue(100)] - int amount) + int amount, + IUser? author = null) { if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var executorId)) { @@ -92,11 +94,11 @@ public class ClearCommandGroup : CommandGroup var data = await _guildData.GetData(guildId, CancellationToken); Messages.Culture = GuildSettings.Language.Get(data.Settings); - return await ClearMessagesAsync(executor, amount, data, channelId, messages, bot, CancellationToken); + return await ClearMessagesAsync(executor, author, data, channelId, messages, bot, CancellationToken); } private async Task ClearMessagesAsync( - IUser executor, int amount, GuildData data, Snowflake channelId, IReadOnlyList messages, IUser bot, + IUser executor, IUser? author, GuildData data, Snowflake channelId, IReadOnlyList messages, IUser bot, CancellationToken ct = default) { var idList = new List(messages.Count); @@ -104,12 +106,27 @@ public class ClearCommandGroup : CommandGroup for (var i = messages.Count - 1; i >= 1; i--) // '>= 1' to skip last message ('Octobot is thinking...') { var message = messages[i]; + if (author is not null && message.Author.ID != author.ID) + { + continue; + } + idList.Add(message.ID); builder.AppendLine(string.Format(Messages.MessageFrom, Mention.User(message.Author))); builder.Append(message.Content.InBlockCode()); } - var title = string.Format(Messages.MessagesCleared, amount.ToString()); + if (idList.Count == 0) + { + var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.NoMessagesToClear, bot) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct); + } + + var title = author is not null + ? string.Format(Messages.MessagesClearedFiltered, idList.Count.ToString(), author.GetTag()) + : string.Format(Messages.MessagesCleared, idList.Count.ToString()); var description = builder.ToString(); var deleteResult = await _channelApi.BulkDeleteMessagesAsync( diff --git a/src/Data/MemberData.cs b/src/Data/MemberData.cs index c7ddc27..b63f8ad 100644 --- a/src/Data/MemberData.cs +++ b/src/Data/MemberData.cs @@ -5,10 +5,14 @@ namespace Octobot.Data; /// public sealed class MemberData { - public MemberData(ulong id, DateTimeOffset? bannedUntil = null) + public MemberData(ulong id, DateTimeOffset? bannedUntil = null, List? reminders = null) { Id = id; BannedUntil = bannedUntil; + if (reminders is not null) + { + Reminders = reminders; + } } public ulong Id { get; } diff --git a/src/Data/Reminder.cs b/src/Data/Reminder.cs index 828fadb..42144f9 100644 --- a/src/Data/Reminder.cs +++ b/src/Data/Reminder.cs @@ -2,7 +2,7 @@ namespace Octobot.Data; public struct Reminder { - public DateTimeOffset At; - public string Text; - public ulong Channel; + public DateTimeOffset At { get; init; } + public string Text { get; init; } + public ulong Channel { get; init; } } diff --git a/src/Messages.Designer.cs b/src/Messages.Designer.cs index ebf7fec..4a771d0 100644 --- a/src/Messages.Designer.cs +++ b/src/Messages.Designer.cs @@ -980,5 +980,21 @@ namespace Octobot { return ResourceManager.GetString("GuildInfoBoostCount", resourceCulture); } } + + internal static string NoMessagesToClear + { + get + { + return ResourceManager.GetString("NoMessagesToClear", resourceCulture); + } + } + + internal static string MessagesClearedFiltered + { + get + { + return ResourceManager.GetString("MessagesClearedFiltered", resourceCulture); + } + } } } diff --git a/src/Services/Update/ScheduledEventUpdateService.cs b/src/Services/Update/ScheduledEventUpdateService.cs index f97f3bb..f1d3524 100644 --- a/src/Services/Update/ScheduledEventUpdateService.cs +++ b/src/Services/Update/ScheduledEventUpdateService.cs @@ -181,6 +181,11 @@ public sealed class ScheduledEventUpdateService : BackgroundService private async Task SendScheduledEventCreatedMessage( IGuildScheduledEvent scheduledEvent, JsonNode settings, CancellationToken ct = default) { + if (GuildSettings.EventNotificationChannel.Get(settings).Empty()) + { + return Result.FromSuccess(); + } + if (!scheduledEvent.Creator.IsDefined(out var creator)) { return new ArgumentNullError(nameof(scheduledEvent.Creator)); @@ -281,6 +286,11 @@ public sealed class ScheduledEventUpdateService : BackgroundService { data.ScheduledEvents[scheduledEvent.ID.Value].ActualStartTime = DateTimeOffset.UtcNow; + if (GuildSettings.EventNotificationChannel.Get(data.Settings).Empty()) + { + return Result.FromSuccess(); + } + var embedDescriptionResult = scheduledEvent.EntityType switch { GuildScheduledEventEntityType.StageInstance or GuildScheduledEventEntityType.Voice => @@ -290,7 +300,7 @@ public sealed class ScheduledEventUpdateService : BackgroundService }; var contentResult = await _utility.GetEventNotificationMentions( - scheduledEvent, data.Settings, ct); + scheduledEvent, data, ct); if (!contentResult.IsDefined(out var content)) { return Result.FromError(contentResult); @@ -417,8 +427,13 @@ public sealed class ScheduledEventUpdateService : BackgroundService private async Task SendEarlyEventNotificationAsync( IGuildScheduledEvent scheduledEvent, GuildData data, CancellationToken ct) { + if (GuildSettings.EventNotificationChannel.Get(data.Settings).Empty()) + { + return Result.FromSuccess(); + } + var contentResult = await _utility.GetEventNotificationMentions( - scheduledEvent, data.Settings, ct); + scheduledEvent, data, ct); if (!contentResult.IsDefined(out var content)) { return Result.FromError(contentResult); diff --git a/src/Services/UtilityService.cs b/src/Services/UtilityService.cs index b144ca7..5bc06ea 100644 --- a/src/Services/UtilityService.cs +++ b/src/Services/UtilityService.cs @@ -160,16 +160,16 @@ public sealed class UtilityService : IHostedService /// /// The scheduled event whose subscribers will be mentioned. /// - /// The settings of the guild containing the scheduled event + /// The data of the guild containing the scheduled event. /// The cancellation token for this operation. /// A result containing the string which may or may not have succeeded. public async Task> GetEventNotificationMentions( - IGuildScheduledEvent scheduledEvent, JsonNode settings, CancellationToken ct = default) + IGuildScheduledEvent scheduledEvent, GuildData data, CancellationToken ct = default) { var builder = new StringBuilder(); - var role = GuildSettings.EventNotificationRole.Get(settings); + var role = GuildSettings.EventNotificationRole.Get(data.Settings); var subscribersResult = await _eventApi.GetGuildScheduledEventUsersAsync( - scheduledEvent.GuildID, scheduledEvent.ID, withMember: true, ct: ct); + scheduledEvent.GuildID, scheduledEvent.ID, ct: ct); if (!subscribersResult.IsDefined(out var subscribers)) { return Result.FromError(subscribersResult); @@ -181,7 +181,7 @@ public sealed class UtilityService : IHostedService } builder = subscribers.Where( - subscriber => subscriber.GuildMember.IsDefined(out var member) && !member.Roles.Contains(role)) + subscriber => !data.GetOrCreateMemberData(subscriber.User.ID).Roles.Contains(role.Value)) .Aggregate(builder, (current, subscriber) => current.Append($"{Mention.User(subscriber.User)} ")); return builder.ToString(); }