From f78188ce198b7b2d45b6c343ad4724ef7b16c899 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 11 Jan 2017 17:15:59 +0100 Subject: [PATCH] Added pagination to .lcr, .lcrg, !!lq. Contact me if you have more ideas where it's needed. It lasts for 30 seconds. --- .../CustomReactions/CustomReactions.cs | 51 +++++++----- src/NadekoBot/Modules/Music/Music.cs | 57 +++++++------ .../Discord/SocketMessageEventWrapper.cs | 52 +++++++----- src/NadekoBot/ShardedDiscordClient.cs | 6 ++ src/NadekoBot/_Extensions/Extensions.cs | 81 ++++++++++++++++--- 5 files changed, 172 insertions(+), 75 deletions(-) diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index d69c31ca..8d95e5d4 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -145,13 +145,17 @@ namespace NadekoBot.Modules.CustomReactions if (customReactions == null || !customReactions.Any()) await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync( - $"Page {page} of custom reactions:", - string.Join("\n", customReactions.OrderBy(cr => cr.Trigger) - .Skip((page - 1) * 20) + { + var lastPage = customReactions.Count / 20; + await Context.Channel.SendPaginatedConfirmAsync(page, curPage => + new EmbedBuilder().WithOkColor() + .WithTitle("Custom reactions") + .WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger) + .Skip((curPage - 1) * 20) .Take(20) - .Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}"))) + .Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}"))), lastPage) .ConfigureAwait(false); + } } public enum All @@ -200,14 +204,22 @@ namespace NadekoBot.Modules.CustomReactions if (customReactions == null || !customReactions.Any()) await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync($"Page {page} of custom reactions (grouped):", - string.Join("\r\n", customReactions - .GroupBy(cr => cr.Trigger) - .OrderBy(cr => cr.Key) - .Skip((page - 1) * 20) - .Take(20) - .Select(cr => $"**{cr.Key.Trim().ToLowerInvariant()}** `x{cr.Count()}`"))) + { + var ordered = customReactions + .GroupBy(cr => cr.Trigger) + .OrderBy(cr => cr.Key) + .ToList(); + + var lastPage = ordered.Count / 20; + await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => + new EmbedBuilder().WithOkColor() + .WithTitle($"Custom Reactions (grouped)") + .WithDescription(string.Join("\r\n", ordered + .Skip((curPage - 1) * 20) + .Take(20) + .Select(cr => $"**{cr.Key.Trim().ToLowerInvariant()}** `x{cr.Count()}`"))), lastPage) .ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -300,13 +312,14 @@ namespace NadekoBot.Modules.CustomReactions { if (page < 1) return; - await Context.Channel.EmbedAsync(ReactionStats.OrderByDescending(x => x.Value) - .Skip((page - 1) * 9) - .Take(9) - .Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction stats page #{page}"), - (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))) - ) - .ConfigureAwait(false); + var ordered = ReactionStats.OrderByDescending(x => x.Value).ToList(); + var lastPage = ordered.Count / 9; + await Context.Channel.SendPaginatedConfirmAsync(page, + (curPage) => ordered.Skip((curPage - 1) * 9) + .Take(9) + .Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction Stats"), + (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))), lastPage) + .ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 3ffb104c..381177a2 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -183,36 +183,41 @@ namespace NadekoBot.Modules.Music try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } const int itemsPerPage = 10; - int startAt = itemsPerPage * (page - 1); - var number = 0 + startAt; var total = musicPlayer.TotalPlaytime; var maxPlaytime = musicPlayer.MaxPlaytimeSeconds; - var embed = new EmbedBuilder() - .WithAuthor(eab => eab.WithName($"Player Queue - Page {page}") - .WithMusicIcon()) - .WithDescription(string.Join("\n", musicPlayer.Playlist - .Skip(startAt) - .Take(10) - .Select(v => $"`{++number}.` {v.PrettyFullName}"))) - .WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " + -$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s | " + -(musicPlayer.FairPlay ? "✔️fairplay" : "✖️fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit"))) - .WithOkColor(); + var lastPage = musicPlayer.Playlist.Count / itemsPerPage; + Func printAction = (curPage) => + { + int startAt = itemsPerPage * (curPage - 1); + var number = 0 + startAt; + var embed = new EmbedBuilder() + .WithAuthor(eab => eab.WithName($"Player Queue") + .WithMusicIcon()) + .WithDescription(string.Join("\n", musicPlayer.Playlist + .Skip(startAt) + .Take(itemsPerPage) + .Select(v => $"`{++number}.` {v.PrettyFullName}"))) + .WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " + + $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s | " + + (musicPlayer.FairPlay ? "✔️fairplay" : "✖️fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit"))) + .WithOkColor(); - if (musicPlayer.RepeatSong) - { - embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyFullTime}"); - } - else if (musicPlayer.RepeatPlaylist) - { - embed.WithTitle("🔁 Repeating Playlist"); - } - if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize) - { - embed.WithTitle("🎵 Song queue is full!"); - } - await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); + if (musicPlayer.RepeatSong) + { + embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyFullTime}"); + } + else if (musicPlayer.RepeatPlaylist) + { + embed.WithTitle("🔁 Repeating Playlist"); + } + if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize) + { + embed.WithTitle("🎵 Song queue is full!"); + } + return embed; + }; + await Context.Channel.SendPaginatedConfirmAsync(page, printAction, lastPage).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs b/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs index 7c24b564..b73961a0 100644 --- a/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs +++ b/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs @@ -10,48 +10,60 @@ namespace NadekoBot.Services.Discord { public class ReactionEventWrapper : IDisposable { - public SocketMessage Message { get; } + public IUserMessage Message { get; } public event Action OnReactionAdded = delegate { }; public event Action OnReactionRemoved = delegate { }; public event Action OnReactionsCleared = delegate { }; - public ReactionEventWrapper(SocketMessage msg) + public ReactionEventWrapper(IUserMessage msg) { if (msg == null) throw new ArgumentNullException(nameof(msg)); Message = msg; - msg.Discord.ReactionAdded += Discord_ReactionAdded; - msg.Discord.ReactionRemoved += Discord_ReactionRemoved; - msg.Discord.ReactionsCleared += Discord_ReactionsCleared; + NadekoBot.Client.ReactionAdded += Discord_ReactionAdded; + NadekoBot.Client.ReactionRemoved += Discord_ReactionRemoved; + NadekoBot.Client.ReactionsCleared += Discord_ReactionsCleared; } - private Task Discord_ReactionsCleared(ulong messageId, Optional reaction) + private void Discord_ReactionsCleared(ulong messageId, Optional reaction) { - if (messageId == Message.Id) - OnReactionsCleared?.Invoke(); - return Task.CompletedTask; + try + { + if (messageId == Message.Id) + OnReactionsCleared?.Invoke(); + } + catch { } } - private Task Discord_ReactionRemoved(ulong messageId, Optional arg2, SocketReaction reaction) + private void Discord_ReactionRemoved(ulong messageId, Optional arg2, SocketReaction reaction) { - if (messageId == Message.Id) - OnReactionRemoved?.Invoke(reaction); - return Task.CompletedTask; + try + { + if (messageId == Message.Id) + OnReactionRemoved?.Invoke(reaction); + } + catch { } } - private Task Discord_ReactionAdded(ulong messageId, Optional message, SocketReaction reaction) + private void Discord_ReactionAdded(ulong messageId, Optional message, SocketReaction reaction) { - if(messageId == Message.Id) - OnReactionAdded?.Invoke(reaction); - return Task.CompletedTask; + try + { + if (messageId == Message.Id) + OnReactionAdded?.Invoke(reaction); + } + catch { } } public void UnsubAll() { - Message.Discord.ReactionAdded -= Discord_ReactionAdded; - Message.Discord.ReactionRemoved -= Discord_ReactionRemoved; - Message.Discord.ReactionsCleared -= Discord_ReactionsCleared; + NadekoBot.Client.ReactionAdded -= Discord_ReactionAdded; + NadekoBot.Client.ReactionRemoved -= Discord_ReactionRemoved; + NadekoBot.Client.ReactionsCleared -= Discord_ReactionsCleared; + OnReactionAdded = null; + OnReactionRemoved = null; + OnReactionsCleared = null; } private bool disposing = false; diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 7a538192..a59dece7 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -28,6 +28,9 @@ namespace NadekoBot public event Action ChannelCreated = delegate { }; public event Action ChannelDestroyed = delegate { }; public event Action ChannelUpdated = delegate { }; + public event Action, SocketReaction> ReactionAdded = delegate { }; + public event Action, SocketReaction> ReactionRemoved = delegate { }; + public event Action> ReactionsCleared = delegate { }; public event Action JoinedGuild = delegate { }; public event Action LeftGuild = delegate { }; @@ -74,6 +77,9 @@ namespace NadekoBot client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; }; client.JoinedGuild += (arg1) => { JoinedGuild(arg1); ++_guildCount; return Task.CompletedTask; }; client.LeftGuild += (arg1) => { LeftGuild(arg1); --_guildCount; return Task.CompletedTask; }; + client.ReactionAdded += (arg1, arg2, arg3) => { ReactionAdded(arg1, arg2, arg3); return Task.CompletedTask; }; + client.ReactionRemoved += (arg1, arg2, arg3) => { ReactionRemoved(arg1, arg2, arg3); return Task.CompletedTask; }; + client.ReactionsCleared += (arg1, arg2) => { ReactionsCleared(arg1, arg2); return Task.CompletedTask; }; _log.Info($"Shard #{i} initialized."); #if GLOBAL_NADEKO diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index 3994e54f..310b5974 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -18,10 +18,70 @@ namespace NadekoBot.Extensions { public static class Extensions { - public static ReactionEventWrapper OnReactionAdded(this SocketMessage msg, Action reactionAdded) + private const string arrow_left = "⬅"; + private const string arrow_right = "➡"; + + /// + /// danny kamisama + /// + public static async Task SendPaginatedConfirmAsync(this IMessageChannel channel, int currentPage, Func pageFunc, int? lastPage = null) { + lastPage += 1; + var embed = pageFunc(currentPage).AddPaginatedFooter(currentPage, lastPage); + + var msg = await channel.EmbedAsync(embed) as IUserMessage; + + if (currentPage >= lastPage && lastPage == 1) + return; + + await msg.AddReactionAsync(arrow_left).ConfigureAwait(false); + await msg.AddReactionAsync(arrow_right).ConfigureAwait(false); + + await Task.Delay(2000).ConfigureAwait(false); + + Action changePage = async r => + { + try + { + if (r.Emoji.Name == arrow_left) + { + if (currentPage == 1) + return; + await msg.ModifyAsync(x => x.Embed = pageFunc(--currentPage).AddPaginatedFooter(currentPage, lastPage).Build()).ConfigureAwait(false); + } + else if (r.Emoji.Name == arrow_right) + { + if (lastPage == null || lastPage > currentPage) + await msg.ModifyAsync(x => x.Embed = pageFunc(++currentPage).AddPaginatedFooter(currentPage, lastPage).Build()).ConfigureAwait(false); + } + } + catch (Exception ex) { Console.WriteLine(ex); } + }; + + using (msg.OnReaction(changePage, changePage)) + { + await Task.Delay(30000).ConfigureAwait(false); + } + + await msg.RemoveAllReactionsAsync().ConfigureAwait(false); + } + + private static EmbedBuilder AddPaginatedFooter(this EmbedBuilder embed, int curPage, int? lastPage) + { + if (lastPage != null) + return embed.WithFooter(efb => efb.WithText($"page {curPage} / {lastPage}")); + else + return embed.WithFooter(efb => efb.WithText($"page {curPage}")); + } + + public static ReactionEventWrapper OnReaction(this IUserMessage msg, Action reactionAdded, Action reactionRemoved = null) + { + if (reactionRemoved == null) + reactionRemoved = delegate { }; + var wrap = new ReactionEventWrapper(msg); wrap.OnReactionAdded += reactionAdded; + wrap.OnReactionRemoved += reactionRemoved; return wrap; } @@ -53,7 +113,8 @@ namespace NadekoBot.Extensions public static string GetPrefix(this ModuleInfo module) => NadekoBot.ModulePrefixes[module.GetTopLevelModule().Name]; - public static ModuleInfo GetTopLevelModule(this ModuleInfo module) { + public static ModuleInfo GetTopLevelModule(this ModuleInfo module) + { while (module.Parent != null) { module = module.Parent; @@ -102,7 +163,7 @@ namespace NadekoBot.Extensions public static bool IsInteger(this decimal number) => number == Math.Truncate(number); - public static string SanitizeMentions(this string str) => + public static string SanitizeMentions(this string str) => str.Replace("@everyone", "@everyοne").Replace("@here", "@һere"); public static double UnixTimestamp(this DateTime dt) => dt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds; @@ -114,7 +175,7 @@ namespace NadekoBot.Extensions => await (await user.CreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text)); public static async Task SendConfirmAsync(this IUser user, string title, string text, string url = null) - => await(await user.CreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text) + => await (await user.CreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text) .WithTitle(title).WithUrl(url)); public static async Task SendErrorAsync(this IUser user, string title, string error, string url = null) @@ -135,7 +196,7 @@ namespace NadekoBot.Extensions public static IEnumerable Members(this IRole role) => role.Guild.GetUsersAsync().GetAwaiter().GetResult().Where(u => u.RoleIds.Contains(role.Id)) ?? Enumerable.Empty(); - + public static Task EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "") => ch.SendMessageAsync(msg, embed: embed); @@ -162,7 +223,7 @@ namespace NadekoBot.Extensions ```"); } - public static Task SendTableAsync(this IMessageChannel ch, IEnumerable items, Func howToPrint, int columns = 3) => + public static Task SendTableAsync(this IMessageChannel ch, IEnumerable items, Func howToPrint, int columns = 3) => ch.SendTableAsync("", items, howToPrint, columns); /// @@ -295,7 +356,7 @@ namespace NadekoBot.Extensions } - public static string ToJson(this T any, Formatting formatting = Formatting.Indented) => + public static string ToJson(this T any, Formatting formatting = Formatting.Indented) => JsonConvert.SerializeObject(any, formatting); public static int KiB(this int value) => value * 1024; @@ -326,7 +387,7 @@ namespace NadekoBot.Extensions var canvasPixels = canvas.Lock(); int offsetX = 0; - foreach (var img in imgList.Select(img=>img.Lock())) + foreach (var img in imgList.Select(img => img.Lock())) { for (int i = 0; i < img.Width; i++) { @@ -335,7 +396,7 @@ namespace NadekoBot.Extensions canvasPixels[i + offsetX, j] = img[i, j]; } } - offsetX += img.Width; + offsetX += img.Width; } return canvas; @@ -354,4 +415,4 @@ namespace NadekoBot.Extensions public static bool IsDiscordInvite(this string str) => filterRegex.IsMatch(str); } -} +} \ No newline at end of file