easy-embed-pages
Version:
An awesome module for easy and 100% customizable dynamic embeds with interactive pages!
175 lines (146 loc) • 8.9 kB
JavaScript
const Discord = require('discord.js');
// the array chunking function
const chunk = (array, chunkSize = 0) => {
if (!Array.isArray(array)) throw new Error('First parameter must be an array');
return array.reduce((previous, current) => {
let chunk;
if (!previous.length || previous[previous.length - 1].length === chunkSize) {
chunk = [];
previous.push(chunk);
} else chunk = previous[previous.length - 1];
chunk.push(current);
return previous;
}, []);
}
var allowedReactions = ['⏪', '⬅️', '➡️', '⏩', '⏹️'];
module.exports = class EasyEmbedPages {
/**
*
* @param {Discord.TextChannel} channel
* @param {Object} data
* @param {Function} pageGen - to change embed using a function
*/
constructor(channel, data = {}, pageGen = () => {}) {
this.channel = channel;
this.allowStop = data.allowStop || true; // if we want the stop emoji '⏹️' to stop the interactive process
this.time = data.time; // idle time to stop the interactive process
this.pages = []; // embed pages... automagically generated xD
this.page = 0; // currect page number
this.dataPages = data.pages || []; // page data for extra configuration
this.color = data.color; // embed color
this.url = data.url; // embed url
this.title = data.title; // embed title
this.author = data.author; // embed author object
this.footer = data.footer; // embed footer object
this.thumbnail = data.thumbnail; // embed thumbnail
this.image = data.image; // embed large image
this.description = data.content || data.description || null; // the content to be presented dynamically
this.pageGen = pageGen; // the function to customize embeds
this.ratelimit = Number(data.ratelimit) || null; // the reaction ratelimit, to prevent reaction spamming
this.lastReaction = 0; // used to store last reaction timestamp
}
/**
* The magic function which generates the embeds
*/
generatePages() {
let text,great,array;
if(this.description){
text = this.description.split("");
great = text.length > 2000 ? Math.floor(text.length/2000) : false;
array = great ? chunk(text, 2000) : [text];
}
let x = Math.max(array ? array.length : 0, this.dataPages.length);
this.pages = [];
for (let index = 0; index < x; index++) {
const data = { fields: [] };
if (this.description && array[index]) {
let i = array[index].join("");
if (index < great) i = `${i}...`;
else if (index) i = `...${i}`;
data.description = i;
if (this.dataPages[index] && (this.dataPages[index].description || this.dataPages[index].content)) data.fields.push({ name: "\u200b", value: this.dataPages[index].description || this.dataPages[index].content, inline: false});
}
else {
if (this.dataPages[index] && (this.dataPages[index].description || this.dataPages[index].content)) data.description = this.dataPages[index].description || this.dataPages[index].content;
}
if ((this.dataPages[index] && this.dataPages[index].color) || this.color) data.color = this.dataPages[index] && this.dataPages[index].color || this.color;
if ((this.dataPages[index] && this.dataPages[index].url) || this.url) data.url = this.dataPages[index] && this.dataPages[index].url || this.url;
if ((this.dataPages[index] && this.dataPages[index].title) || this.title) data.title = this.dataPages[index] && this.dataPages[index].title || this.title;
if ((this.dataPages[index] && this.dataPages[index].author) || this.author) data.author = this.dataPages[index] && this.dataPages[index].author || this.author;
if ((this.dataPages[index] && this.dataPages[index].footer) || this.footer) data.footer = this.dataPages[index] && this.dataPages[index].footer || this.footer;
else data.footer = { text: `Page ${index + 1} of ${x} page${x > 1 ? 's' : ''}` };
if ((this.dataPages[index] && this.dataPages[index].thumbnail) || this.thumbnail) data.thumbnail = this.dataPages[index] && this.dataPages[index].thumbnail || this.thumbnail;
if ((this.dataPages[index] && this.dataPages[index].image) || this.image) data.image = this.dataPages[index] && this.dataPages[index].image || this.image;
if (this.dataPages[index] && this.dataPages[index].fields) this.dataPages[index].fields.map(x => data.fields.push({ name: x.name || "\u200b" , value: x.value || "\u200b" , inline: x.inline || false }));
const embed = new Discord.MessageEmbed(data);
this.pageGen(embed);
this.pages.push(embed);
};
}
/**
* Function used to start the dynamic embed pagination.
*
* @param {Object} options
* @param {Discord.Channel} options.channel
* @param {Discord.User} options.user
* @param {Number} page
*/
async start(options = {}, page = 0) {
this.page = page;
if (options instanceof Discord.Channel) options = { channel: options };
else if (!options || typeof options !== 'object') options = {};
let condition;
if (options.allowStop) this.stop = options.allowStop;
if (options.time) this.time = options.time;
if (!this.allowStop) allowedReactions.pop();
if (options.user) condition = (reaction, user) => user.id == options.user.id && allowedReactions.includes(reaction.emoji.name);
else condition = (reaction) => allowedReactions.includes(reaction.emoji.name);
if (options.channel instanceof Discord.TextChannel) this.channel = options.channel;
if (!this.channel instanceof Discord.TextChannel) throw new Error("No text channel specified!");
this.generatePages();
if (this.page > this.pages.length) throw new Error("Page number greater than total pages!");
this.message = await this.channel.send(this.pages[this.page]);
if (this.pages.length > 1) {
await Promise.all(allowedReactions.map(async (reaction) => await this.message.react(reaction)));
this.collector = this.message.createReactionCollector(condition, { dispose: true, idle: this.time });
this.collector.on('collect', this._handleReaction.bind(this));
this.collector.on('remove', this._handleReaction.bind(this));
this.collector.once('end', () => this.message.reactions.removeAll().catch(() => {}));
}
}
/**
* Reaction handing function - the function which does the magic of changing the embed!
* @param {Discord.MessageReaction} reaction
* @param {Discord.User} user
*/
async _handleReaction(reaction, user) {
if ((!allowedReactions.includes(reaction.emoji.name)) || (this.ratelimit && ((Date.now() - this.lastReaction) < this.ratelimit))) return;
this.lastReaction = Date.now();
switch (reaction.emoji.name) {
case '⏪':
if (this.page === 0) break;
this.page = 0;
this.message.edit(this.pages[0]);
break;
case '⬅️':
if (this.page > 0) --this.page;
this.message.edit(this.pages[this.page]);
break;
case '➡️':
if (this.page < this.pages.length-1) ++this.page;
this.message.edit(this.pages[this.page]);
break;
case '⏩':
if (this.page === (this.pages.length - 1)) break;
this.page = this.pages.length - 1;
this.message.edit(this.pages[this.pages.length-1]);
break;
case '⏹️':
if (!this.allowStop) break;
this.collector.stop('User requested');
break;
default:
break;
}
}
}