From 90e9f1c6bdba3abd136526d509b14a86f2498fa1 Mon Sep 17 00:00:00 2001 From: Patrick Klaeren Date: Thu, 21 Sep 2023 20:03:48 +0100 Subject: [PATCH] Start removing channel hiding/showing --- .../CommandGroups/NitroBoosterCommandGroup.cs | 56 ++++ .../UserChannelHidingCommandGroup.cs | 312 +++--------------- .../BotServiceCollectionExtensions.cs | 3 +- .../GetAllUsersHiddenChannels.cs | 27 ++ 4 files changed, 129 insertions(+), 269 deletions(-) create mode 100644 src/Accord.Bot/CommandGroups/NitroBoosterCommandGroup.cs create mode 100644 src/Accord.Services/UserHiddenChannels/GetAllUsersHiddenChannels.cs diff --git a/src/Accord.Bot/CommandGroups/NitroBoosterCommandGroup.cs b/src/Accord.Bot/CommandGroups/NitroBoosterCommandGroup.cs new file mode 100644 index 0000000..2e1195c --- /dev/null +++ b/src/Accord.Bot/CommandGroups/NitroBoosterCommandGroup.cs @@ -0,0 +1,56 @@ +using System.ComponentModel; +using System.Linq; +using System.Threading.Tasks; +using Accord.Bot.Helpers; +using Remora.Commands.Attributes; +using Remora.Discord.API.Abstractions.Rest; +using Remora.Discord.Commands.Attributes; +using Remora.Discord.Commands.Contexts; +using Remora.Discord.Commands.Feedback.Services; +using Remora.Results; + +namespace Accord.Bot.CommandGroups; + +// TODO: This needs to be enabled after more extensive testing + +[Group("nitro"), AutoConstructor] +public partial class NitroBoosterCommandGroup : AccordCommandGroup +{ + private readonly FeedbackService _feedbackService; + private readonly IDiscordRestGuildAPI _guildApi; + private readonly ICommandContext _commandContext; + + private const string NITRO_ROLE = "Nitro Booster"; + private const string RANKED_NITRO_ROLE = "Ranked Nitro Booster"; + + [Command("apply"), Description("Applies Ranked Nitro Booster role to your profile so you become all pink and special"), Ephemeral] + public async Task Apply() + { + var proxy = _commandContext.GetCommandProxy(); + + var roles = await _guildApi.GetGuildRolesAsync(proxy.GuildId); + + if (roles.IsSuccess && roles.Entity.Any(x => x.Name == RANKED_NITRO_ROLE)) + { + var role = roles.Entity.Single(x => x.Name == RANKED_NITRO_ROLE); + await _guildApi.AddGuildMemberRoleAsync(proxy.GuildId, proxy.UserId, role.ID); + } + + return await _feedbackService.SendContextualAsync("Applied!"); + } + + [Command("remove"), Description("Removes Ranked Nitro Booster role from your profile so you go back to your default role colour"), Ephemeral] + public async Task Remove() + { + var proxy = _commandContext.GetCommandProxy(); + var roles = await _guildApi.GetGuildRolesAsync(proxy.GuildId); + + if (roles.IsSuccess && roles.Entity.Any(x => x.Name == RANKED_NITRO_ROLE)) + { + var role = roles.Entity.Single(x => x.Name == RANKED_NITRO_ROLE); + await _guildApi.RemoveGuildMemberRoleAsync(proxy.GuildId, proxy.UserId, role.ID); + } + + return await _feedbackService.SendContextualAsync("Removed!"); + } +} \ No newline at end of file diff --git a/src/Accord.Bot/CommandGroups/UserChannelHidingCommandGroup.cs b/src/Accord.Bot/CommandGroups/UserChannelHidingCommandGroup.cs index e67694d..7fe57d1 100644 --- a/src/Accord.Bot/CommandGroups/UserChannelHidingCommandGroup.cs +++ b/src/Accord.Bot/CommandGroups/UserChannelHidingCommandGroup.cs @@ -1,312 +1,88 @@ -using System.Collections.Generic; -using System.ComponentModel; +using System.ComponentModel; using System.Linq; -using System.Text; using System.Threading.Tasks; using Accord.Bot.Helpers; using Accord.Bot.Helpers.Permissions; -using Accord.Bot.Parsers; -using Accord.Services.Helpers; using Accord.Services.UserHiddenChannels; using MediatR; -using Microsoft.Extensions.Logging; using Remora.Commands.Attributes; using Remora.Discord.API.Abstractions.Objects; using Remora.Discord.API.Abstractions.Rest; -using Remora.Discord.API.Objects; -using Remora.Discord.Commands.Attributes; -using Remora.Discord.Commands.Contexts; -using Remora.Discord.Commands.Feedback.Messages; +using Remora.Discord.Commands.Conditions; using Remora.Discord.Commands.Feedback.Services; using Remora.Results; namespace Accord.Bot.CommandGroups; [Group("channel"), AutoConstructor] -public partial class UserChannelHidingCommandGroup: AccordCommandGroup +public partial class UserChannelHidingCommandGroup : AccordCommandGroup { - private readonly ICommandContext _commandContext; private readonly IMediator _mediator; private readonly IDiscordRestChannelAPI _channelApi; private readonly FeedbackService _feedbackService; private readonly DiscordCache _discordCache; - private readonly DiscordAvatarHelper _discordAvatarHelper; - private readonly DiscordPermissionHelper _discordPermissionHelper; - private readonly ILogger _logger; - [Command("hidden"), Description("Display your hidden channels"), Ephemeral] - public async Task GetHiddenChannels() + [RequireDiscordPermission(DiscordPermission.Administrator), Command("self-destruct"), Description("Time to self destruct, baby!")] + public async Task RemoveFunctionality() { - var proxy = _commandContext.GetCommandProxy(); - - var hiddenChannelsForUser = await _mediator.Send(new GetUserHiddenChannelsRequest(proxy.UserId.Value)); - - if (!hiddenChannelsForUser.Any()) - { - return await _feedbackService.SendContextualAsync("You don't have any hidden channel yet"); - } - - StringBuilder stringBuilder = new(); + await _feedbackService.SendContextualAsync("Hold onto ye' butts, get ready for spam *sorry, not sorry*"); - foreach (var blockedChannel in hiddenChannelsForUser.Where(x => x.ParentDiscordChannelId == null)) - { - stringBuilder.AppendLine($"{DiscordFormatter.ChannelIdToMention(blockedChannel.DiscordChannelId)}"); - } - - var userRequest = await _discordCache.GetGuildMember(proxy.UserId.Value); + var hiddenChannelsForUser = await _mediator.Send(new GetAllUsersHiddenChannelsRequest()); - if (!userRequest.IsSuccess) + if (!hiddenChannelsForUser.Any()) { - return await _feedbackService.SendContextualAsync("Failed listing hidden channels"); + return await _feedbackService.SendContextualAsync("No hidden channels"); } - var user = userRequest.Entity.User.Value; + var grouped = hiddenChannelsForUser.GroupBy(x => x.UserId); - return await _feedbackService.SendContextualEmbedAsync(new Embed - { - Author = new EmbedAuthor( - DiscordHandleHelper.BuildHandle(user.Username, user.Discriminator), - IconUrl: _discordAvatarHelper.GetAvatarUrl(user.ID.Value, - user.Discriminator, - user.Avatar?.Value, - user.Avatar?.HasGif == true)), - Title = "Hidden Channels", - Description = stringBuilder.ToString() - }); - } - - [Command("hide"), Description("Hide a channel for you"), Ephemeral] - public async Task HideChannel(IChannel channel) - { - var proxy = _commandContext.GetCommandProxy(); - - var guildMember = await _discordCache.GetGuildMember(proxy.UserId.Value); - - if (!guildMember.IsSuccess) - return await _feedbackService.SendContextualAsync("There was an error executing this command. Try again later."); - - var activeHiddenChannels = await _mediator.Send(new GetUserHiddenChannelsRequest(proxy.UserId.Value)); - if (activeHiddenChannels.Any(x => x.DiscordChannelId == channel.ID.Value)) - return await _feedbackService.SendContextualAsync("This channel is already hidden for you."); - - var isCascadeEnabled = await _mediator.Send(new GetIsUserHiddenChannelsCascadeHideEnabledRequest()); - var hasUserPermissionToViewTheChannel = await _discordPermissionHelper.HasUserEffectivePermissionsInChannel( - guildMember.Entity, - channel, - DiscordPermission.ViewChannel); - var hasBotPermissionToManageTheChannel = await _discordPermissionHelper.HasBotEffectivePermissionsInChannel( - channel, - DiscordPermission.ViewChannel, DiscordPermission.ManageRoles); - - if (!hasBotPermissionToManageTheChannel || !hasUserPermissionToViewTheChannel) - return await _feedbackService.SendContextualAsync($"This channel cannot be hidden!"); - - var resultList = new List<(ulong Channel, Result Result)> - { - ( - channel.ID.Value, - await _channelApi.EditChannelPermissionsAsync( - channel.ID, - proxy.UserId, - type: PermissionOverwriteType.Member, - deny: new DiscordPermissionSet(DiscordPermission.ViewChannel)) - ) - }; + var channels = await _discordCache.GetGuildChannels(); - if (channel.Type == ChannelType.GuildCategory && isCascadeEnabled) + foreach (var group in grouped) { - var channels = await _discordCache.GetGuildChannels(); + await _feedbackService.SendContextualAsync($"Processing user {group.Key} with {group.Count()} channels hidden"); + var guildMember = await _discordCache.GetGuildMember(group.Key); - foreach (var inheritedChannel in channels) + foreach (var channelToShow in group) { - if (inheritedChannel.ParentID == channel.ID) - { - var hasUserPermissionToViewTheInheritedChannel = await _discordPermissionHelper.HasUserEffectivePermissionsInChannel( - guildMember.Entity, - inheritedChannel, - DiscordPermission.ViewChannel); - - var hasBotPermissionToManageTheInheritedChannel = - await _discordPermissionHelper.HasBotEffectivePermissionsInChannel( - inheritedChannel, - DiscordPermission.ViewChannel, DiscordPermission.ManageRoles); + await _feedbackService.SendContextualAsync($"Processing {channelToShow.DiscordChannelId} for user {group.Key}"); + var channel = channels.FirstOrDefault(x => x.ID.Value == channelToShow.DiscordChannelId); - if (!hasBotPermissionToManageTheInheritedChannel || !hasUserPermissionToViewTheInheritedChannel) - continue; - - if (activeHiddenChannels.Any(x => x.DiscordChannelId == inheritedChannel.ID.Value)) - resultList.Add((inheritedChannel.ID.Value, Result.FromSuccess())); - else - resultList.Add(( - inheritedChannel.ID.Value, - await _channelApi.EditChannelPermissionsAsync( - inheritedChannel.ID, - proxy.UserId, - type: PermissionOverwriteType.Member, - deny: new DiscordPermissionSet(DiscordPermission.ViewChannel)) - )); + if (channel is null) + { + await _feedbackService.SendContextualAsync($"Channel {channelToShow.DiscordChannelId} not found, skipping"); + continue; } - } - - var successfulHiddenChannels = resultList.Where(x => x.Result.IsSuccess).ToList(); - var failedHiddenChannels = resultList.Except(successfulHiddenChannels).ToList(); - - if (successfulHiddenChannels.Count != resultList.Count) - { - await _feedbackService.SendContextualAsync( - $"{successfulHiddenChannels.Count}/{resultList.Count} channels in the category {DiscordFormatter.ChannelIdToMention(channel.ID.Value)} have been hidden."); - - var errors = string.Join("\n\t", failedHiddenChannels.Select(x => x.Result.Error!.Message)); - - _logger.LogWarning("Hiding Channels under the category {Category} for user: {User}#{Discriminator}({Id}) produced the following errors:\n{Errors}", - channel.Name.Value, - guildMember.Entity.User.Value.Username, - guildMember.Entity.User.Value.Discriminator, - guildMember.Entity.User.Value.ID.Value, - errors); - } - else - { - await _feedbackService.SendContextualAsync($"All the channels in the category: {DiscordFormatter.ChannelIdToMention(channel.ID.Value)} have been hidden."); - } - - await _mediator.Send(new AddUserHiddenChannelsRequest( - proxy.UserId.Value, - channel.ID.Value, - successfulHiddenChannels.Select(x => x.Channel).Where(x => x != channel.ID.Value).ToList())); - } - else - { - if (!resultList[0].Result.IsSuccess) - { - await _feedbackService.SendContextualAsync($"An error occured while hiding channel: {DiscordFormatter.ChannelIdToMention(channel.ID.Value)}"); - _logger.LogWarning("Hiding channel {Channel} for user: {User}#{Discriminator}({Id}) produced the following error:\n\t{Error}", - channel.Name.Value, - guildMember.Entity.User.Value.Username, - guildMember.Entity.User.Value.Discriminator, - guildMember.Entity.User.Value.ID.Value, - resultList[0].Result.Error!.Message); - } - else - { - await _feedbackService.SendContextualAsync($"Channel {DiscordFormatter.ChannelIdToMention(channel.ID.Value)} has been hidden"); - await _mediator.Send(new AddUserHiddenChannelRequest( - proxy.UserId.Value, - channel.ID.Value - )); - } - } - - return Result.FromSuccess(); - } - - [Command("show"), Description("Show a channel you've hidden"), Ephemeral] - public async Task ShowChannel(string channelText) - { - var channels = await _discordCache.GetGuildChannels(); - IChannel? channel; - - if (!channels.Any()) - { - await _feedbackService.SendContextualAsync("I found no channels in this Discord!"); - return Result.FromSuccess(); - } - - if(ulong.TryParse(channelText, out var channelId)) - { - channel = channels - .Where(x => x.ID.Value == channelId) - .FirstOrDefault(); - } - else - { - channel = channels - .Where(x => string.Equals(x.Name.Value, channelText, System.StringComparison.InvariantCultureIgnoreCase)) - .FirstOrDefault(); - } - - if (channel is null) - { - return await _feedbackService.SendContextualAsync($"Channel: {channelText} not found"); - } - - var proxy = _commandContext.GetCommandProxy(); - - var isCascadeEnabled = await _mediator.Send(new GetIsUserHiddenChannelsCascadeHideEnabledRequest()); - - var resultList = new List<(ulong Channel, Result Result)>(); - var activeUserHiddenChannels = await _mediator.Send(new GetUserHiddenChannelsRequest(proxy.UserId.Value)); - - var guildMember = await _discordCache.GetGuildMember(proxy.UserId.Value); - - var userHasDenyPermission = channel.HasUserPermissionOverwrite(guildMember.Entity.User.Value, DiscordPermission.ViewChannel, DiscordPermissionType.Deny); - - if (activeUserHiddenChannels.All(x => x.DiscordChannelId != channel.ID.Value) && !userHasDenyPermission) - return await _feedbackService.SendContextualAsync("This channel is not hidden for you."); + var userHasDenyPermission = channel.HasUserPermissionOverwrite(guildMember.Entity.User.Value, DiscordPermission.ViewChannel, DiscordPermissionType.Deny); + - if (!userHasDenyPermission) - resultList.Add((channel.ID.Value, Result.FromSuccess())); - else - resultList.Add((channel.ID.Value, await _channelApi.DeleteChannelPermissionAsync(channel.ID, proxy.UserId))); - - if (channel.Type == ChannelType.GuildCategory && isCascadeEnabled) - { - foreach (var inheritedChannel in channels) - { - if (inheritedChannel.ParentID == channel.ID) + if (userHasDenyPermission) { - if (!inheritedChannel.HasUserPermissionOverwrite(guildMember.Entity.User.Value, DiscordPermission.ViewChannel, DiscordPermissionType.Deny)) - resultList.Add((inheritedChannel.ID.Value, Result.FromSuccess())); + await _feedbackService.SendContextualAsync("User has channel hidden via Discord perms"); + + if (channel.Type == ChannelType.GuildCategory) + { + await _feedbackService.SendContextualAsync("Channel is a category.... *sigh*"); + + foreach (var inheritedChannel in channels) + { + if (inheritedChannel.ParentID == channel.ID) + { + if (inheritedChannel.HasUserPermissionOverwrite(guildMember.Entity.User.Value, DiscordPermission.ViewChannel, DiscordPermissionType.Deny)) + await _channelApi.DeleteChannelPermissionAsync(inheritedChannel.ID, guildMember.Entity.User.Value.ID); + } + } + } else - resultList.Add((inheritedChannel.ID.Value, await _channelApi.DeleteChannelPermissionAsync(inheritedChannel.ID, proxy.UserId))); + { + await _channelApi.DeleteChannelPermissionAsync(channel.ID, guildMember.Entity.User.Value.ID); + await _feedbackService.SendContextualAsync("Channel override removed"); + } } } - - var successfulShownChannels = resultList.Where(x => x.Result.IsSuccess).ToList(); - var failedShownChannels = resultList.Except(successfulShownChannels).ToList(); - - if (successfulShownChannels.Count != resultList.Count) - { - await _feedbackService.SendContextualAsync( - $"{successfulShownChannels.Count}/{resultList.Count} channels in the category {DiscordFormatter.ChannelIdToMention(channel.ID.Value)} are now visible."); - - var errors = string.Join("\n\t", failedShownChannels.Select(x => x.Result.Error!.Message)); - - _logger.LogWarning("Showing Channels under the category {Category} for user: {User}#{Discriminator}({Id}) produced the following errors:\n{Errors}", - channel.Name.Value, - guildMember.Entity.User.Value.Username, - guildMember.Entity.User.Value.Discriminator, - guildMember.Entity.User.Value.ID.Value, - errors); - } - else - { - await _feedbackService.SendContextualAsync($"All the channels in the category: {DiscordFormatter.ChannelIdToMention(channel.ID.Value)} are now visible."); - } - - var channelsToRemoveFromDb = successfulShownChannels.Where(x => activeUserHiddenChannels.Any(y => y.DiscordChannelId == x.Channel)).Select(x => x.Channel).ToList(); - await _mediator.Send(new DeleteUserHiddenChannelRequest(proxy.UserId.Value, channel.ID.Value, channelsToRemoveFromDb)); - } - else - { - if (!resultList[0].Result.IsSuccess) - { - await _feedbackService.SendContextualAsync($"An error occured while showing channel: {DiscordFormatter.ChannelIdToMention(channel.ID.Value)}"); - _logger.LogWarning("Showing channel {Channel} for user: {User}#{Discriminator}({Id}) produced the following error:\n\t{Error}", - channel.Name.Value, - guildMember.Entity.User.Value.Username, - guildMember.Entity.User.Value.Discriminator, - guildMember.Entity.User.Value.ID.Value, - resultList[0].Result.Error!.Message); - } - else - { - await _feedbackService.SendContextualAsync($"Channel {DiscordFormatter.ChannelIdToMention(channel.ID.Value)} is now visible"); - await _mediator.Send(new DeleteUserHiddenChannelRequest(proxy.UserId.Value, channel.ID.Value)); - } } - return Result.FromSuccess(); + return await _feedbackService.SendContextualAsync("Natural order has been returned"); } } \ No newline at end of file diff --git a/src/Accord.Bot/Infrastructure/BotServiceCollectionExtensions.cs b/src/Accord.Bot/Infrastructure/BotServiceCollectionExtensions.cs index 3b8a17e..66ff0c6 100644 --- a/src/Accord.Bot/Infrastructure/BotServiceCollectionExtensions.cs +++ b/src/Accord.Bot/Infrastructure/BotServiceCollectionExtensions.cs @@ -39,7 +39,8 @@ public static IServiceCollection AddDiscordBot(this IServiceCollection services, .AddPostExecutionEvent() .AddParser(); - services.AddCommandTree() + services + .AddCommandTree() .WithCommandGroup() .WithCommandGroup() .WithCommandGroup() diff --git a/src/Accord.Services/UserHiddenChannels/GetAllUsersHiddenChannels.cs b/src/Accord.Services/UserHiddenChannels/GetAllUsersHiddenChannels.cs new file mode 100644 index 0000000..06a558b --- /dev/null +++ b/src/Accord.Services/UserHiddenChannels/GetAllUsersHiddenChannels.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Accord.Domain; +using Accord.Domain.Model; +using LazyCache; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Accord.Services.UserHiddenChannels; + +public sealed record GetAllUsersHiddenChannelsRequest() : IRequest>; + +[AutoConstructor] +public partial class GetAllUsersHiddenChannels : + IRequestHandler> +{ + private readonly AccordContext _db; + + public async Task> Handle(GetAllUsersHiddenChannelsRequest request, CancellationToken cancellationToken) + { + return await _db.UserHiddenChannels + .AsNoTracking() + .ToListAsync(cancellationToken: cancellationToken); } +} \ No newline at end of file