diff --git a/src/commands/fun/subcommands/eco-shop.ts b/src/commands/fun/subcommands/eco-shop.ts index 24ebcd7..f5c167b 100644 --- a/src/commands/fun/subcommands/eco-shop.ts +++ b/src/commands/fun/subcommands/eco-shop.ts @@ -10,7 +10,7 @@ export const ShopCommand = new Command({ description: "Displays the list of items you can buy in the shop.", async run({guild, channel, author}) { if (isAuthorized(guild, channel)) { - function getShopEmbed(selection: ShopItem[], title = "Shop") { + function getShopEmbed(selection: ShopItem[], title: string) { const fields: EmbedField[] = []; for (const item of selection) @@ -32,17 +32,15 @@ export const ShopCommand = new Command({ }; } - // In case there's just one page, omit unnecessary details. - if (ShopItems.length <= 5) channel.send(getShopEmbed(ShopItems)); - else { - const shopPages = split(ShopItems, 5); - const pageAmount = shopPages.length; - const msg = await channel.send(getShopEmbed(shopPages[0], `Shop (Page 1 of ${pageAmount})`)); + const shopPages = split(ShopItems, 5); + const pageAmount = shopPages.length; - paginate(msg, author.id, pageAmount, (page) => { - msg.edit(getShopEmbed(shopPages[page], `Shop (Page ${page + 1} of ${pageAmount})`)); - }); - } + paginate(channel, author.id, pageAmount, (page, hasMultiplePages) => { + return getShopEmbed( + shopPages[page], + hasMultiplePages ? `Shop (Page ${page + 1} of ${pageAmount})` : "Shop" + ); + }); } } }); diff --git a/src/commands/utilities/lsemotes.ts b/src/commands/utilities/lsemotes.ts index 6188439..c369ab0 100644 --- a/src/commands/utilities/lsemotes.ts +++ b/src/commands/utilities/lsemotes.ts @@ -93,33 +93,21 @@ async function displayEmoteList(emotes: GuildEmoji[], channel: TextChannel | DMC }); const sections = split(emotes, 20); const pages = sections.length; - const embed = new MessageEmbed().setTitle("**Emotes**").setColor("AQUA"); - let desc = ""; + const embed = new MessageEmbed().setColor("AQUA"); // Gather the first page (if it even exists, which it might not if there no valid emotes appear) if (pages > 0) { - for (const emote of sections[0]) { - desc += `${emote} ${emote.name} (**${emote.guild.name}**)\n`; - } + paginate(channel, author.id, pages, (page, hasMultiplePages) => { + embed.setTitle(hasMultiplePages ? `**Emotes** (Page ${page + 1} of ${pages})` : "**Emotes**"); - embed.setDescription(desc); + let desc = ""; + for (const emote of sections[page]) { + desc += `${emote} ${emote.name} (**${emote.guild.name}**)\n`; + } + embed.setDescription(desc); - if (pages > 1) { - embed.setTitle(`**Emotes** (Page 1 of ${pages})`); - const msg = await channel.send({embed}); - - paginate(msg, author.id, pages, (page) => { - let desc = ""; - for (const emote of sections[page]) { - desc += `${emote} ${emote.name} (**${emote.guild.name}**)\n`; - } - embed.setTitle(`**Emotes** (Page ${page + 1} of ${pages})`); - embed.setDescription(desc); - msg.edit(embed); - }); - } else { - channel.send({embed}); - } + return embed; + }); } else { channel.send("No valid emotes found by that query."); } diff --git a/src/core/libd.ts b/src/core/libd.ts index 9c44fa4..3cc6b04 100644 --- a/src/core/libd.ts +++ b/src/core/libd.ts @@ -1,5 +1,14 @@ // Library for Discord-specific functions -import {Message, Guild, GuildMember, Permissions} from "discord.js"; +import { + Message, + Guild, + GuildMember, + Permissions, + TextChannel, + DMChannel, + NewsChannel, + MessageOptions +} from "discord.js"; import {get} from "https"; import FileManager from "./storage"; import {eventListeners} from "../events/messageReactionRemove"; @@ -36,57 +45,69 @@ export function updateGlobalEmoteRegistry(): void { // Pagination function that allows for customization via a callback. // Define your own pages outside the function because this only manages the actual turning of pages. export async function paginate( - message: Message, + channel: TextChannel | DMChannel | NewsChannel, senderID: string, total: number, - callback: (page: number) => void, + callback: (page: number, hasMultiplePages: boolean) => MessageOptions & {split?: false}, duration = 60000 ) { - let page = 0; - const turn = (amount: number) => { - page += amount; + const hasMultiplePages = total > 1; + const message = await channel.send(callback(0, hasMultiplePages)); - if (page < 0) page += total; - else if (page >= total) page -= total; + if (hasMultiplePages) { + let page = 0; + const turn = (amount: number) => { + page += amount; - callback(page); - }; - const BACKWARDS_EMOJI = "⬅️"; - const FORWARDS_EMOJI = "➡️"; - const handle = (emote: string, reacterID: string) => { - switch (emote) { - case BACKWARDS_EMOJI: - turn(-1); - break; - case FORWARDS_EMOJI: - turn(1); - break; - } - }; + if (page < 0) page += total; + else if (page >= total) page -= total; - // Listen for reactions and call the handler. - let backwardsReaction = await message.react(BACKWARDS_EMOJI); - let forwardsReaction = await message.react(FORWARDS_EMOJI); - eventListeners.set(message.id, handle); - await message.awaitReactions( - (reaction, user) => { - if (user.id === senderID) { - // The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error. - // This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission. - const canDeleteEmotes = botHasPermission(message.guild, Permissions.FLAGS.MANAGE_MESSAGES); - handle(reaction.emoji.name, user.id); - - if (canDeleteEmotes) reaction.users.remove(user); + message.edit(callback(page, true)); + }; + const BACKWARDS_EMOJI = "⬅️"; + const FORWARDS_EMOJI = "➡️"; + const handle = (emote: string, reacterID: string) => { + if (senderID === reacterID) { + switch (emote) { + case BACKWARDS_EMOJI: + turn(-1); + break; + case FORWARDS_EMOJI: + turn(1); + break; + } } + }; - return false; - }, - {time: duration} - ); - // When time's up, remove the bot's own reactions. - eventListeners.delete(message.id); - backwardsReaction.users.remove(message.author); - forwardsReaction.users.remove(message.author); + // Listen for reactions and call the handler. + let backwardsReaction = await message.react(BACKWARDS_EMOJI); + let forwardsReaction = await message.react(FORWARDS_EMOJI); + eventListeners.set(message.id, handle); + const collector = message.createReactionCollector( + (reaction, user) => { + if (user.id === senderID) { + // The reason this is inside the call is because it's possible to switch a user's permissions halfway and suddenly throw an error. + // This will dynamically adjust for that, switching modes depending on whether it currently has the "Manage Messages" permission. + const canDeleteEmotes = botHasPermission(message.guild, Permissions.FLAGS.MANAGE_MESSAGES); + handle(reaction.emoji.name, user.id); + if (canDeleteEmotes) reaction.users.remove(user); + collector.resetTimer(); + } + + return false; + }, + // Apparently, regardless of whether you put "time" or "idle", it won't matter to the collector. + // In order to actually reset the timer, you have to do it manually via collector.resetTimer(). + {time: duration} + ); + + // When time's up, remove the bot's own reactions. + collector.on("end", () => { + eventListeners.delete(message.id); + backwardsReaction.users.remove(message.author); + forwardsReaction.users.remove(message.author); + }); + } } // Waits for the sender to either confirm an action or let it pass (and delete the message).