using System.ComponentModel; using System.Text; using JetBrains.Annotations; using Octobot.Data; 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.Discord.Extensions.Formatting; using Remora.Rest.Core; using Remora.Results; namespace Octobot.Commands; /// /// Handles the command to clear messages in a channel: /clear. /// [UsedImplicitly] public class ClearCommandGroup : CommandGroup { private readonly IDiscordRestChannelAPI _channelApi; private readonly ICommandContext _context; private readonly FeedbackService _feedback; private readonly GuildDataService _guildData; private readonly IDiscordRestUserAPI _userApi; private readonly UtilityService _utility; public ClearCommandGroup( IDiscordRestChannelAPI channelApi, ICommandContext context, GuildDataService guildData, FeedbackService feedback, IDiscordRestUserAPI userApi, UtilityService utility) { _channelApi = channelApi; _context = context; _guildData = guildData; _feedback = feedback; _userApi = userApi; _utility = utility; } /// /// A slash command that clears messages in the channel it was executed. /// /// The amount of messages to clear. /// /// 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. /// [Command("clear", "очистить")] [DiscordDefaultMemberPermissions(DiscordPermission.ManageMessages)] [DiscordDefaultDMPermission(false)] [RequireContext(ChannelContext.Guild)] [RequireDiscordPermission(DiscordPermission.ManageMessages)] [RequireBotDiscordPermissions(DiscordPermission.ManageMessages)] [Description("Remove multiple messages")] [UsedImplicitly] public async Task ExecuteClear( [Description("Number of messages to remove (2-100)")] [MinValue(2)] [MaxValue(100)] int amount) { 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"); } // The bot's avatar is used when sending messages 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 messagesResult = await _channelApi.GetChannelMessagesAsync( channelId, limit: amount + 1, ct: CancellationToken); if (!messagesResult.IsDefined(out var messages)) { return Result.FromError(messagesResult); } var data = await _guildData.GetData(guildId, CancellationToken); Messages.Culture = GuildSettings.Language.Get(data.Settings); return await ClearMessagesAsync(executor, amount, data, channelId, messages, bot, CancellationToken); } private async Task ClearMessagesAsync( IUser executor, int amount, GuildData data, Snowflake channelId, IReadOnlyList messages, IUser bot, CancellationToken ct = default) { var idList = new List(messages.Count); var builder = new StringBuilder().AppendLine(Mention.Channel(channelId)).AppendLine(); for (var i = messages.Count - 1; i >= 1; i--) // '>= 1' to skip last message ('Octobot is thinking...') { var message = messages[i]; 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()); var description = builder.ToString(); var deleteResult = await _channelApi.BulkDeleteMessagesAsync( channelId, idList, executor.GetTag().EncodeHeader(), ct); if (!deleteResult.IsSuccess) { return Result.FromError(deleteResult.Error); } var logResult = _utility.LogActionAsync( data.Settings, channelId, executor, title, description, bot, ColorsList.Red, false, ct); if (!logResult.IsSuccess) { return Result.FromError(logResult.Error); } var embed = new EmbedBuilder().WithSmallTitle(title, bot) .WithColour(ColorsList.Green).Build(); return await _feedback.SendContextualEmbedResultAsync(embed, ct); } }