@joshbrucker/discordjs-utils
Version:
A set of utility classes and functions to aid with discord.js bot development. Paged embeds, emoji utilities, and more!
178 lines (177 loc) • 6.58 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PagedEmbed = exports.PagedEmbedSendError = void 0;
const discord_js_1 = require("discord.js");
const v10_1 = require("discord-api-types/v10");
const PagedEmbedOptions_1 = require("./PagedEmbedOptions");
const errorHandlers_js_1 = require("../utils/errorHandlers.js");
class PagedEmbedSendError extends Error {
constructor(message) {
super(message);
this.name = "PagedEmbedSendError";
}
}
exports.PagedEmbedSendError = PagedEmbedSendError;
/*
Allows for creation and customization of "paged" embeds,
where Discord users can click buttons to page through a list of embeds.
*/
class PagedEmbed {
static BACK_ID = "back";
static FORWARD_ID = "forward";
timeout;
leftEmoji;
rightEmoji;
leftStyle;
rightStyle;
showPageNumbers;
wrapAround;
resetTimerOnPress;
collector;
backButton;
forwardButton;
constructor(options) {
options = { ...PagedEmbedOptions_1.DEFAULT_OPTIONS, ...options };
this.timeout = options.timeout;
this.leftEmoji = options.leftEmoji;
this.rightEmoji = options.rightEmoji;
this.leftStyle = options.leftStyle;
this.rightStyle = options.rightStyle;
this.showPageNumbers = options.showPageNumbers;
this.wrapAround = options.wrapAround;
this.resetTimerOnPress = options.resetTimerOnPress;
this.backButton = new discord_js_1.ButtonBuilder();
this.forwardButton = new discord_js_1.ButtonBuilder();
}
setTimeout(timeout) {
this.timeout = timeout;
return this;
}
setLeftEmoji(leftEmoji) {
this.leftEmoji = leftEmoji;
return this;
}
setRightEmoji(rightEmoji) {
this.rightEmoji = rightEmoji;
return this;
}
setLeftStyle(leftStyle) {
this.leftStyle = leftStyle;
return this;
}
setRightStyle(rightStyle) {
this.rightStyle = rightStyle;
return this;
}
withShowPageNumbers(showPageNumbers) {
this.showPageNumbers = showPageNumbers;
return this;
}
withWrapAround(wrapAround) {
this.wrapAround = wrapAround;
return this;
}
withResetTimerOnPress(resetTimerOnPress) {
this.resetTimerOnPress = resetTimerOnPress;
return this;
}
/*
Expires the paged embed, making the buttons no longer clickable
*/
expire() {
this.collector?.stop();
}
/*
Resets the timer that runs to expire the paged embed.
*/
resetTimer(newTimeout) {
this.collector?.resetTimer({ time: newTimeout || this.timeout });
}
/*
Sends a the paged embed with the given embed list and attachments.
*/
async send(interaction, embeds, attachments = [], startIndex = 0) {
if (embeds.length === 0) {
throw new PagedEmbedSendError("Embed list size must be at least 1.");
}
if (startIndex < 0 || startIndex >= embeds.length) {
throw new PagedEmbedSendError("startIndex must be within bounds of embed list size.");
}
// Hydrate embeds with current page number over total page numbers, if requested
if (this.showPageNumbers) {
for (let i = 0; i < embeds.length; i++) {
const embed = embeds[i];
embed.setFooter({
text: "\u200b\n" +
`Page ${i + 1} / ${embeds.length}` +
(embed.data.footer ? embed.data.footer.text : ""),
iconURL: embed.data.footer?.icon_url,
});
}
}
// Don't set any buttons if there is only one embed
if (embeds.length === 1) {
await interaction.reply({
embeds: [embeds[0]],
files: attachments,
});
return;
}
this.backButton
.setStyle(this.leftStyle)
.setEmoji(this.leftEmoji)
.setCustomId(PagedEmbed.BACK_ID);
this.forwardButton
.setStyle(this.rightStyle)
.setEmoji(this.rightEmoji)
.setCustomId(PagedEmbed.FORWARD_ID);
const getButtonRow = (currentIndex, embedCount) => {
const showBackButton = currentIndex > 0 || this.wrapAround;
const showForwardButton = currentIndex < embedCount - 1 || this.wrapAround;
return new discord_js_1.ActionRowBuilder().addComponents(...(showBackButton ? [this.backButton] : []), ...(showForwardButton ? [this.forwardButton] : []));
};
let currentIndex = startIndex;
const interactionResponse = await interaction.reply({
embeds: [embeds[currentIndex]],
files: attachments,
components: [getButtonRow(currentIndex, embeds.length)],
withResponse: true,
});
const message = interactionResponse.resource?.message;
if (message) {
this.collector = message.createMessageComponentCollector({
time: this.timeout,
});
this.collector.on("collect", async (buttonInteraction) => {
if (this.resetTimerOnPress) {
this.resetTimer();
}
if (buttonInteraction.customId === PagedEmbed.BACK_ID) {
currentIndex -= 1;
}
else if (buttonInteraction.customId === PagedEmbed.FORWARD_ID) {
currentIndex += 1;
}
// Handles wrap around cases from both directions.
currentIndex =
((currentIndex % embeds.length) + embeds.length) % embeds.length;
await buttonInteraction
.update({
embeds: [embeds[currentIndex]],
components: [getButtonRow(currentIndex, embeds.length)],
})
.catch((0, errorHandlers_js_1.ignore)([v10_1.RESTJSONErrorCodes.UnknownInteraction]));
});
this.collector.on("end", async () => {
this.backButton.setDisabled(true);
this.forwardButton.setDisabled(true);
await interaction
.editReply({
components: [getButtonRow(currentIndex, embeds.length)],
})
.catch((0, errorHandlers_js_1.ignore)([v10_1.RESTJSONErrorCodes.UnknownMessage]));
});
}
}
}
exports.PagedEmbed = PagedEmbed;