From 35ddd150ba717a72fabc432a0949248ecbd3332c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 28 Apr 2022 06:10:06 +0200 Subject: [PATCH] Pagination is now using buttons instead of reactions --- CHANGELOG.md | 4 + .../Extensions/MedusaExtensions.cs | 6 +- .../Common/SocketMessageEventWrapper.cs | 95 -------------- src/NadekoBot/Modules/Utility/Utility.cs | 2 +- src/NadekoBot/_Extensions/Extensions.cs | 21 --- .../_Extensions/IMessageChannelExtensions.cs | 120 +++++++++--------- 6 files changed, 68 insertions(+), 180 deletions(-) delete mode 100644 src/NadekoBot/Common/SocketMessageEventWrapper.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index f47fefaf3..e458b22af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog. - There shouldn't be any breaking changes - Added `.stondel` command which, when toggled, will make the bot delete online stream messages on the server when the stream goes offline +### Changed + +- Pagination is now using buttons instead of reactions + ### Fixed - Fixed `.deletexp` command diff --git a/src/Nadeko.Medusa/Extensions/MedusaExtensions.cs b/src/Nadeko.Medusa/Extensions/MedusaExtensions.cs index 2c10bad51..7ae420498 100644 --- a/src/Nadeko.Medusa/Extensions/MedusaExtensions.cs +++ b/src/Nadeko.Medusa/Extensions/MedusaExtensions.cs @@ -5,9 +5,13 @@ namespace NadekoBot; public static class MedusaExtensions { - public static Task EmbedAsync(this IMessageChannel ch, IEmbedBuilder embed, string msg = "") + public static Task EmbedAsync(this IMessageChannel ch, + IEmbedBuilder embed, + string msg = "", + MessageComponent? components = null) => ch.SendMessageAsync(msg, embed: embed.Build(), + components: components, options: new() { RetryMode = RetryMode.AlwaysRetry diff --git a/src/NadekoBot/Common/SocketMessageEventWrapper.cs b/src/NadekoBot/Common/SocketMessageEventWrapper.cs deleted file mode 100644 index 8af7ff474..000000000 --- a/src/NadekoBot/Common/SocketMessageEventWrapper.cs +++ /dev/null @@ -1,95 +0,0 @@ -#nullable disable -namespace NadekoBot.Common; - -public sealed class ReactionEventWrapper : IDisposable -{ - public event Action OnReactionAdded = delegate { }; - public event Action OnReactionRemoved = delegate { }; - public event Action OnReactionsCleared = delegate { }; - - public IUserMessage Message { get; } - private readonly DiscordSocketClient _client; - - private bool disposing; - - public ReactionEventWrapper(DiscordSocketClient client, IUserMessage msg) - { - Message = msg ?? throw new ArgumentNullException(nameof(msg)); - _client = client; - - _client.ReactionAdded += Discord_ReactionAdded; - _client.ReactionRemoved += Discord_ReactionRemoved; - _client.ReactionsCleared += Discord_ReactionsCleared; - } - - public void Dispose() - { - if (disposing) - return; - disposing = true; - UnsubAll(); - } - - private Task Discord_ReactionsCleared(Cacheable msg, Cacheable channel) - { - Task.Run(() => - { - try - { - if (msg.Id == Message.Id) - OnReactionsCleared?.Invoke(); - } - catch { } - }); - - return Task.CompletedTask; - } - - private Task Discord_ReactionRemoved( - Cacheable msg, - Cacheable cacheable, - SocketReaction reaction) - { - Task.Run(() => - { - try - { - if (msg.Id == Message.Id) - OnReactionRemoved?.Invoke(reaction); - } - catch { } - }); - - return Task.CompletedTask; - } - - private Task Discord_ReactionAdded( - Cacheable msg, - Cacheable cacheable, - SocketReaction reaction) - { - Task.Run(() => - { - try - { - if (msg.Id == Message.Id) - OnReactionAdded?.Invoke(reaction); - } - catch - { - } - }); - - return Task.CompletedTask; - } - - public void UnsubAll() - { - _client.ReactionAdded -= Discord_ReactionAdded; - _client.ReactionRemoved -= Discord_ReactionRemoved; - _client.ReactionsCleared -= Discord_ReactionsCleared; - OnReactionAdded = null; - OnReactionRemoved = null; - OnReactionsCleared = null; - } -} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 0e7939625..367cb6cd1 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -48,7 +48,7 @@ public partial class Utility : NadekoModule _tracker = tracker; _httpFactory = httpFactory; } - + [Cmd] [RequireContext(ContextType.Guild)] [UserPerm(GuildPerm.ManageMessages)] diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index bcb2d7c04..23e9b415d 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -143,27 +143,6 @@ public static class Extensions public static IEmbedBuilder WithErrorColor(this IEmbedBuilder eb) => eb.WithColor(EmbedColor.Error); - public static ReactionEventWrapper OnReaction( - this IUserMessage msg, - DiscordSocketClient client, - Func reactionAdded, - Func? reactionRemoved = null) - { - if (reactionRemoved is null) - reactionRemoved = _ => Task.CompletedTask; - - var wrap = new ReactionEventWrapper(client, msg); - wrap.OnReactionAdded += r => - { - _ = Task.Run(() => reactionAdded(r)); - }; - wrap.OnReactionRemoved += r => - { - _ = Task.Run(() => reactionRemoved(r)); - }; - return wrap; - } - public static HttpClient AddFakeHeaders(this HttpClient http) { AddFakeHeaders(http.DefaultRequestHeaders); diff --git a/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs b/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs index d6ac14222..849b8d6c1 100644 --- a/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs +++ b/src/NadekoBot/_Extensions/IMessageChannelExtensions.cs @@ -2,12 +2,14 @@ namespace NadekoBot.Extensions; public static class MessageChannelExtensions { - private static readonly IEmote _arrowLeft = new Emoji("⬅"); - private static readonly IEmote _arrowRight = new Emoji("➡"); - - public static Task EmbedAsync(this IMessageChannel ch, IEmbedBuilder embed, string msg = "") + public static Task EmbedAsync( + this IMessageChannel ch, + IEmbedBuilder embed, + string msg = "", + MessageComponent? components = null) => ch.SendMessageAsync(msg, embed: embed.Build(), + components: components, options: new() { RetryMode = RetryMode.AlwaysRetry @@ -117,9 +119,12 @@ public static class MessageChannelExtensions itemsPerPage, addPaginatedFooter); - /// - /// danny kamisama - /// + private const string BUTTON_LEFT = "BUTTON_LEFT"; + private const string BUTTON_RIGHT = "BUTTON_RIGHT"; + + private static readonly IEmote _arrowLeft = new Emoji("⬅️"); + private static readonly IEmote _arrowRight = new Emoji("➡️"); + public static async Task SendPaginatedConfirmAsync( this ICommandContext ctx, int currentPage, @@ -128,87 +133,78 @@ public static class MessageChannelExtensions int itemsPerPage, bool addPaginatedFooter = true) { + var lastPage = (totalElements - 1) / itemsPerPage; + var embed = await pageFunc(currentPage); - var lastPage = (totalElements - 1) / itemsPerPage; - - var canPaginate = true; - if (ctx.Guild is SocketGuild sg && !sg.CurrentUser.GetPermissions((IGuildChannel)ctx.Channel).AddReactions) - canPaginate = false; - - if (!canPaginate) - embed.WithFooter("⚠️ AddReaction permission required for pagination."); - else if (addPaginatedFooter) + if (addPaginatedFooter) embed.AddPaginatedFooter(currentPage, lastPage); - var msg = await ctx.Channel.EmbedAsync(embed); - - if (lastPage == 0 || !canPaginate) - return; - - await msg.AddReactionAsync(_arrowLeft); - await msg.AddReactionAsync(_arrowRight); - - await Task.Delay(2000); - - var lastPageChange = DateTime.MinValue; - - async Task ChangePage(SocketReaction r) + var component = new ComponentBuilder() + .WithButton(new ButtonBuilder() + .WithStyle(ButtonStyle.Secondary) + .WithCustomId(BUTTON_LEFT) + .WithDisabled(lastPage == 0) + .WithEmote(_arrowLeft)) + .WithButton(new ButtonBuilder() + .WithStyle(ButtonStyle.Primary) + .WithCustomId(BUTTON_RIGHT) + .WithDisabled(lastPage == 0) + .WithEmote(_arrowRight)) + .Build(); + + var msg = await ctx.Channel.EmbedAsync(embed, components: component); + + Task OnInteractionAsync(SocketInteraction si) { - try + _ = Task.Run(async () => { - if (r.UserId != ctx.User.Id) + await si.DeferAsync(); + if (si is not SocketMessageComponent smc) return; - if (DateTime.UtcNow - lastPageChange < TimeSpan.FromSeconds(1)) + + if (smc.User.Id != ctx.User.Id || smc.Message.Id != msg.Id) return; - if (r.Emote.Name == _arrowLeft.Name) + + if (smc.Data.CustomId == BUTTON_LEFT) { if (currentPage == 0) return; - lastPageChange = DateTime.UtcNow; + var toSend = await pageFunc(--currentPage); if (addPaginatedFooter) toSend.AddPaginatedFooter(currentPage, lastPage); - await msg.ModifyAsync(x => x.Embed = toSend.Build()); + + await smc.ModifyOriginalResponseAsync(x => x.Embed = toSend.Build()); } - else if (r.Emote.Name == _arrowRight.Name) + else if (smc.Data.CustomId == BUTTON_RIGHT) { if (lastPage > currentPage) { - lastPageChange = DateTime.UtcNow; var toSend = await pageFunc(++currentPage); if (addPaginatedFooter) toSend.AddPaginatedFooter(currentPage, lastPage); - await msg.ModifyAsync(x => x.Embed = toSend.Build()); + + await smc.ModifyOriginalResponseAsync(x => x.Embed = toSend.Build()); } } - } - catch (Exception) - { - //ignored - } + }); + + return Task.CompletedTask; } - using (msg.OnReaction((DiscordSocketClient)ctx.Client, ChangePage, ChangePage)) - { - await Task.Delay(30000); - } + if (lastPage == 0) + return; - try - { - if (msg.Channel is ITextChannel && ((SocketGuild)ctx.Guild).CurrentUser.GuildPermissions.ManageMessages) - await msg.RemoveAllReactionsAsync(); - else - { - await msg.Reactions.Where(x => x.Value.IsMe) - .Select(x => msg.RemoveReactionAsync(x.Key, ctx.Client.CurrentUser)) - .WhenAll(); - } - } - catch - { - // ignored - } + var client = (DiscordSocketClient)ctx.Client; + + client.InteractionCreated += OnInteractionAsync; + + await Task.Delay(30_000); + + client.InteractionCreated -= OnInteractionAsync; + + await msg.ModifyAsync(mp => mp.Components = new ComponentBuilder().Build()); } public static Task OkAsync(this ICommandContext ctx)