discord-menu-buttons
Version:
Create a configurable Menu with the new Discord Buttons
150 lines (137 loc) • 5.44 kB
JavaScript
const { DMChannel, TextChannel, MessageEmbed } = require('discord.js');
const Page = require('./Page');
const Button = require('./Button');
const channelPermissions = ['SEND_MESSAGES', 'EMBED_LINKS'];
/**
* The Menu that will be displayed
*/
class Menu {
/**
*
* @param {DMChannel|TextChannel} channel The channel where the Menu will be initialized.
* @param {String|null} target The target of the Menu, if any.
* @param {Object[]} pages The Pages of the Menu.
* @param {String} pages.name The name of the page.
* @param {String|MessageEmbed} pages.content The content of the page.
* @param {Button[]} pages.buttons The buttons of the page.
* @param {Number} time The time, in milliseconds, after which the Menu will be closed if not any clicking are valid.
* @param {Boolean} deleted If the Menu will be deleted after the stored time.
*/
constructor(channel, target, pages, time = 2 * 60 * 1000, deleted = true) {
this.channel = channel;
this.target = target;
this.setPages(pages);
this.time = time;
this.deleted = deleted;
}
/**
* Add a page to the Menu using an object
*/
addPage(page) {
this.pages.push(new Page(page.name, page.content, page.buttons));
}
/**
* Add multiple pages to the Menu using an array of objects
*/
addPages(pages) {
for (const page of pages) {
this.addPage(page);
}
}
/**
* Overwrite the existing pages of the Menu using an array of objects
*/
setPages(pages) {
this.pages = [];
this.addPages(pages);
}
/**
* Start the Menu
*/
start() {
if (!this.pages[0]) return new Error("[discord-menu-buttons] You are trying to start an empty Menu. Please retry with at least one page in it.");
this.current = this.pages[0];
this.page = 1;
if (!this.channel) {
this.channel.client.users.cache.get(target).createDM()
.then(channel => this.channel = channel);
}
if (this.channel.type === 'dm') {
this.channel.send(this.current.toObject())
.then(message => {
this.message = message;
this.awaitButtons();
})
.catch(() => console.log("[discord-menu-buttons] The User you are trying to send the Menu to has his DM blocked."));
} else {
if (channelPermissions.some(p => !this.channel.permissionsFor(this.channel.client.user.id).has(p))) return new Error("[discord-menu-buttons] Your bot does not have one of the following permissions: " + channelPermissions.join(" | "));
this.channel.send(this.current.toObject())
.then(message => {
this.message = message;
this.awaitButtons();
})
.catch(err => console.log("[discord-menu-buttons] An error has occured while trying to send the Menu into the desired channel.\n", err));
}
return this;
}
/**
* Await for a valid button to be pressed by the authorized user(s)
*/
async awaitButtons() {
const filter = button => this.target ? this.target === button.clicker.user.id : true;
const button = (await this.message.awaitButtons(filter, { time: this.time, max: 1})).first();
if (!button) this.stop();
else {
const buttonData = this.current.buttons.find(b => b.custom_id === button.id);
if (!button.reply.has) button.reply.defer().catch(() => {});
switch (button.id) {
case "previous":
if (this.page > 1) this.updateMenu(this.page-1);
break;
case "next":
if (this.page < this.pages.length) this.updateMenu(this.page+1);
break;
case "start":
if (this.page !== 1) this.updateMenu(1);
break;
case "end":
if (this.page !== this.pages.length) this.updateMenu(this.pages.length);
break;
case "function":
buttonData.function(button, this);
break;
case "delete":
this.stop(true);
break;
case "stop":
this.stop();
break;
}
}
}
/**
* Update the Menu with the given page number
*/
updateMenu(page) {
this.page = page;
this.current = this.pages[page-1];
this.message.edit(this.current.toObject());
this.awaitButtons();
}
/**
* Stop the Menu and delete it, if forced or initialized
*/
stop(force) {
if (force === false || force === true) this.deleted = force;
if (!this.deleted) {
const object = this.current.toObject();
object.components[0].components = object.components[0].components.map(b => {
b.disabled = true;
return b;
})
this.message.edit(object);
}
else if (!this.message.deleted) this.message.delete();
}
}
module.exports = Menu;