@novelbrasil/discord.js-embed-menu
Version:
Easily create Discord.js embed menus with reactions and unlimited customisable pages.
321 lines (320 loc) • 15.9 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DiscordEmbedMenu = void 0;
const events_1 = require("events");
const discord_js_1 = require("discord.js");
const discord_embed_menu_page_1 = require("./discord_embed_menu_page");
class DiscordEmbedMenu extends events_1.EventEmitter {
constructor(interaction, pages, timeout = 300000, deleteOnTimeout = true, mention = false, keepUserReactionOnStop = true, loadingMessage) {
super();
this.menu = null;
this.reactionCollector = null;
this.componentCollector = null;
this.data = {};
this.interaction = interaction;
this.channel = interaction.channel;
this.user = interaction.user;
this.timeout = timeout;
this.deleteOnTimeout = deleteOnTimeout;
this.mention = mention;
this.keepUserReactionOnStop = keepUserReactionOnStop;
this.loadingMessage = loadingMessage || DiscordEmbedMenu.LOADING_MESSAGE;
this.isDM = !this.channel || this.channel.isDMBased();
this.userTag = '<@' + this.user.id + '>';
this.pages = [];
for (let i = 0, l = pages.length; i < l; i++) {
let page = pages[i];
this.pages.push(page instanceof discord_embed_menu_page_1.DiscordEmbedMenuPage ? page : new discord_embed_menu_page_1.DiscordEmbedMenuPage(page.name, page.content, page.reactions, i, page.buttons));
}
this.currentPage = this.pages[0];
this.pageIndex = 0;
}
start(options) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.setPage(0, options);
});
}
stop() {
return __awaiter(this, void 0, void 0, function* () {
this.stopReactions(false);
if (this.menu && this.keepUserReactionOnStop) {
if (this.menu instanceof discord_js_1.Message)
this.menu.reactions.cache.forEach((reaction) => __awaiter(this, void 0, void 0, function* () {
if (this.menu && this.menu.client && this.menu.client.user) {
yield reaction.users.remove(this.menu.client.user.id);
}
}));
}
else if (!this.isDM) {
return yield this.clearReactions();
}
});
}
delete() {
return __awaiter(this, void 0, void 0, function* () {
this.stopReactions(false);
if (this.menu) {
return yield this.menu.delete();
}
});
}
clearReactions() {
return __awaiter(this, void 0, void 0, function* () {
if (this.menu && !this.isDM && this.menu instanceof discord_js_1.Message) {
return this.menu.reactions.removeAll();
}
});
}
setPage() {
return __awaiter(this, arguments, void 0, function* (page = 0, options) {
if (typeof (page) === 'string') {
let pageIndex = this.pages.findIndex(p => p.name === page);
if (pageIndex != -1) {
page = pageIndex;
}
else {
throw new Error(`Page "${page}" not found!`);
}
}
this.emit('page-changing', this.pageIndex, this.currentPage, page, this.pages[page]);
this.pageIndex = page;
this.currentPage = this.pages[this.pageIndex];
let content = (!this.isDM && this.mention ? this.userTag : '');
let loadingEmbed = new discord_js_1.EmbedBuilder({
title: this.currentPage.content.data.title,
description: this.loadingMessage
});
if (this.isDM) {
if (this.menu) {
yield this.menu.delete();
this.menu = null;
}
if (this.channel && this.channel instanceof discord_js_1.BaseGuildTextChannel) {
this.menu = yield this.channel.send({ content, embeds: [loadingEmbed] });
}
else {
this.menu = yield this.user.send({ content, embeds: [loadingEmbed] });
this.channel = this.menu.channel;
}
}
else {
if (this.menu) {
yield this.menu.edit({ content, embeds: [loadingEmbed], components: [] });
}
else {
if (options === null || options === void 0 ? void 0 : options.reply)
this.menu = yield this.interaction.reply({ content, embeds: [loadingEmbed], components: [] });
else if (options === null || options === void 0 ? void 0 : options.followUp)
this.menu = yield this.interaction.followUp({ content, embeds: [loadingEmbed], components: [] });
else if (this.channel instanceof discord_js_1.BaseGuildTextChannel)
this.menu = yield this.channel.send({ content, embeds: [loadingEmbed], components: [] });
}
}
if (this.currentPage.reactions) {
this.stopReactions(true);
yield this.addReactions();
this.awaitReactions();
}
const components = [];
if (this.currentPage.buttons) {
const buttons = this.currentPage.buttons;
for (let i = 0; i < Object.keys(buttons).length; i++)
if (i % 5 == 0)
components.push(new discord_js_1.ActionRowBuilder());
for (const row of components) {
for (const button of Object.values(buttons)) {
row.addComponents(button.button);
}
}
}
if (!this.menu)
throw new Error('Menu is not defined, cannot edit it.');
this.menu = yield this.menu.edit({ content, embeds: [this.currentPage.content], components });
if (this.currentPage.buttons) {
yield this.awaitButtons();
}
this.emit('page-changed', this.pageIndex, this.currentPage);
});
}
addReactions() {
return __awaiter(this, void 0, void 0, function* () {
let reactions = [];
if (this.menu && this.menu instanceof discord_js_1.Message) {
for (let reaction in this.currentPage.reactions) {
reactions.push(yield this.menu.react(reaction));
}
}
return reactions;
});
}
stopReactions(triggerEnd = true) {
if (this.reactionCollector) {
if (!triggerEnd) {
this.reactionCollector.removeAllListeners();
}
this.reactionCollector.stop();
this.reactionCollector = null;
}
}
awaitButtons() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (this.menu && this.menu instanceof discord_js_1.Message) {
const filter = (_interaction) => {
return _interaction.user.id === this.user.id;
};
try {
const component = yield this.menu.awaitMessageComponent({ filter, time: this.timeout, componentType: discord_js_1.ComponentType.Button });
if (!component)
return;
yield component.deferUpdate();
const key = Object.prototype.hasOwnProperty.call(this.currentPage.buttons, component.customId)
? component.customId
: Object.prototype.hasOwnProperty.call(this.currentPage.buttons, component.id) ? component.id : null;
if (key && this.menu && this.menu instanceof discord_js_1.Message) {
if (component.user.id !== this.user.id)
return;
if (!this.currentPage.buttons)
return;
if (typeof this.currentPage.buttons[key].action !== 'string') {
this.currentPage.buttons[key].action(this);
}
switch (this.currentPage.buttons[key].action) {
case "first":
this.setPage(0);
break;
case "last":
this.setPage(this.pages.length - 1);
break;
case 'previous': {
if (this.pageIndex > 0) {
this.setPage(this.pageIndex - 1);
}
break;
}
case 'next': {
if (this.pageIndex < this.pages.length - 1) {
this.setPage(this.pageIndex + 1);
}
break;
}
case 'delete': {
this.delete();
break;
}
default: {
this.setPage(this.pages.findIndex(p => this.currentPage.buttons && p.name === this.currentPage.buttons[key].action));
break;
}
}
}
}
catch (error) {
if (!this.isDM) {
if (this.deleteOnTimeout) {
yield this.delete();
}
else {
yield ((_a = this.menu) === null || _a === void 0 ? void 0 : _a.edit({ components: [] }));
}
}
}
}
});
}
awaitReactions() {
if (this.menu && this.menu instanceof discord_js_1.Message) {
const filter = (_reaction, user) => {
return this.menu != null && this.menu.client != null && this.menu.client.user != null && user.id != this.menu.client.user.id;
};
this.reactionCollector = this.menu.createReactionCollector({ filter, time: this.timeout });
let reactionsChanged;
this.reactionCollector.on('end', (reactions) => __awaiter(this, void 0, void 0, function* () {
var _a;
if (!this.isDM) {
if (reactions) {
if (reactionsChanged) {
return yield this.clearReactions();
}
else if (this.menu) {
yield ((_a = reactions.at(0)) === null || _a === void 0 ? void 0 : _a.users.remove(this.menu.client.users.cache.get(this.user.id)));
}
}
else if (this.deleteOnTimeout) {
return yield this.delete();
}
else {
return yield this.clearReactions();
}
}
}));
this.reactionCollector.on('collect', (reaction, user) => {
const reactionName = Object.prototype.hasOwnProperty.call(this.currentPage.reactions, reaction.emoji.name)
? reaction.emoji.name
: Object.prototype.hasOwnProperty.call(this.currentPage.reactions, reaction.emoji.id) ? reaction.emoji.id : null;
if (user.id !== this.user.id || !Object.keys(this.currentPage.reactions).includes(reactionName)) {
return reaction.users.remove(user);
}
if (reactionName && this.menu && this.menu instanceof discord_js_1.Message) {
if (typeof this.currentPage.reactions[reactionName] === 'function') {
// this this flag is not true then the clearReaction() at ligne 188 will be never call when try to change page
// also test when no page change it works too
reactionsChanged = true;
return this.currentPage.reactions[reactionName](this);
}
switch (this.currentPage.reactions[reactionName]) {
case 'first': {
reactionsChanged = JSON.stringify(this.menu.reactions.cache.keys()) != JSON.stringify(Object.keys(this.pages[0].reactions));
this.setPage(0);
break;
}
case 'last': {
reactionsChanged = JSON.stringify(this.menu.reactions.cache.keys()) != JSON.stringify(Object.keys(this.pages[this.pages.length - 1].reactions));
this.setPage(this.pages.length - 1);
break;
}
case 'previous': {
if (this.pageIndex > 0) {
reactionsChanged = JSON.stringify(this.menu.reactions.cache.keys()) != JSON.stringify(Object.keys(this.pages[this.pageIndex - 1].reactions));
this.setPage(this.pageIndex - 1);
}
break;
}
case 'next': {
if (this.pageIndex < this.pages.length - 1) {
reactionsChanged = JSON.stringify(this.menu.reactions.cache.keys()) != JSON.stringify(Object.keys(this.pages[this.pageIndex + 1].reactions));
this.setPage(this.pageIndex + 1);
}
break;
}
case 'stop': {
this.stop();
break;
}
case 'delete': {
this.delete();
break;
}
default: {
reactionsChanged = JSON.stringify(this.menu.reactions.cache.keys()) != JSON.stringify(Object.keys(this.pages.find(p => p.name === this.currentPage.reactions[reactionName]).reactions));
this.setPage(this.pages.findIndex(p => p.name === this.currentPage.reactions[reactionName]));
break;
}
}
}
});
}
}
}
exports.DiscordEmbedMenu = DiscordEmbedMenu;
DiscordEmbedMenu.REQUIRED_PERMS = ['SendMessages', 'EmbedLinks', 'AddReactions', 'ManageMessages'];
DiscordEmbedMenu.LOADING_MESSAGE = 'Carregando, por favor seja paciente...';