const fs = require('fs');
const {MessageEmbed} = require('discord.js');
/** The Base module class. Every module's MainClass must extend from this. DO NOT INSTANTIATE AS IS. */
class Base {
/**
* Sets up the base values of all modules, such as a reference to the client, a name, description and help message.
* @constructor
* @param {external:Client} client - The Discord.js bot client.
* @property {external:Client} client - The Discord.js bot client.
* @property {string} name - The module's name, used for the help command.
* @property {string} description - The module's description, used for the help command.
* @property {Object} help - Used to create the help message.
* @property {string} commandText - The command the bot will be looking for.
* @property {number} color - The module's description, used for the help command.
* @property {Array<external:Snowflake>} auth - The ID of the users that are authorized to run this command. Authorizes everyone if it's empty.
* @property {boolean} dmEnabled - Whether or not the command can be run from DMs. Defaults to false.
*/
constructor(client) {
this.client = client;
this.name = "[TODO] Add name";
this.description = "[TODO] Add description";
this.help = {
"": "[TODO] Add help"
}
this.commandText = "";
this.color = 0xffffff;
this.auth = [];
this.dmEnabled = false;
}
/**
* Number emojis from 1 to 10.
* @readonly
* @type {string[]}
*/
get NUMBER_EMOJIS() {
return ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣", "🔟"];
}
_testForAuth(message) {
var content = message.content.split(" ").slice(1);
var args = [], kwargs = {};
var insideQuotes = false;
for (var element of content) {
if (element.search(/\S+=\S+/) != -1) {
var key = element.match(/\S+=/)[0];
var value = element.match(/=\S+/)[0];
kwargs[key.substring(0, key.length - 1)] = value.substring(1);
} else if (!insideQuotes) {
if (element.startsWith("\"") && !element.endsWith("\"")) {
insideQuotes = true;
element = element.slice(1);
}
args.push(element);
} else {
if (element.endsWith("\"")) {
insideQuotes = false;
element = element.slice(0, -1);
}
args[args.length - 1] += " " + element;
}
}
if (this.auth.length == 0 || this.auth.includes(message.author.id)) {
this._executeCommand(message, args, kwargs, flags);
} else {
message.reply("You are not authorized to run this command.");
}
}
_executeCommand(message, args, kwargs, flags) {
//console.log(args, kwargs, flags);
if (this["com_" + args[0]]) {
this["com_" + args[0]](message, args, kwargs, flags);
} else {
this.command(message, args, kwargs, flags);
}
}
/**
* Callback function called when a message is sent. Defaults to checking for the command and for auth to execute the command.
* @method
* @param {external:Message} message - The message that was sent.
*/
on_message(message) {
if (message.content.startsWith(process.env.PREFIX) && message.content.split(" ")[0] === process.env.PREFIX + this.commandText && (message.guild || this.dmEnabled)) {
this._testForAuth(message);
}
}
/**
* Sends a choice message, using reactions. The message will be deleted once the collector is closed by default.
* @async
* @method
* @param {(external:TextChannel|external:DMChannel)} channel - The Discord channel the message is sent to.
* @param {(string|external:MessageEmbed)} content - The content of that message.
* @param {string[]} emojis - The emojis to be added as reactions and listened for. Represents the options the user can take.
* @param {string} confirmation_emoji - The emoji that will be displayed when either the collect and remove functions return true, and the confirmation_condition is fulfilled. Clicking it will close the collector.
* @param {collectionCallback} collect_function - The callback function called on the collection of a reaction.
* @param {removalCallback} remove_function - The callback function called on the removal of a reaction. Set to null to use the collect_function.
* @param {confirmationCallback} confirmation_condition - The callback function called to verify if the collector should be closed on the collection of the confrimation emoji.
* @param {endCallback} end_function - The callback function called once the collector is closed.
* @param {Object} options - Options for the message
* @param {boolean} options.dontDelete - Prevents the message from being deleted once the collector is closed.
* @returns {external:Message} The message that was sent.
*/
async sendChoice(channel, content, emojis, confirmation_emoji, collect_function, remove_function, confirmation_condition, end_function, options = {}) {
if (!remove_function) remove_function = collect_function;
return await channel.send(content)
.then(async m => {for (var r of emojis) await m.react(r); return m})
.then(m => {
emojis.push(confirmation_emoji);
var collection = m.createReactionCollector((reaction, user) => emojis.includes(reaction.emoji.name) && !user.bot, { dispose: true });
function updateConfirmationEmoji(base, can_confirm) {
if (can_confirm) {
m.react(confirmation_emoji);
} else {
var r = m.reactions.cache.get(confirmation_emoji);
if (r) r.users.remove(base.client.user);
}
};
collection.on('collect', (reaction, user) => {
if (reaction.emoji.name === confirmation_emoji) {
if (confirmation_condition(reaction, user) && m.reactions.cache.get(confirmation_emoji).me) collection.stop();
} else {
var can_confirm = collect_function(collection, reaction, user);
updateConfirmationEmoji(this, can_confirm);
}
});
collection.on('remove', (reaction, user) => {
if (reaction.emoji.name != confirmation_emoji) {
var can_confirm = remove_function(collection, reaction, user);
updateConfirmationEmoji(this, can_confirm);
}
});
collection.on('end', (collected) => {
if (!options.dontDelete) collection.message.delete();
end_function(collection, collected);
});
return m;
})
.catch(e => this.client.error(channel, this.name, e));
}
_getSavePath() {
return this.client.path + "\\saves\\" + this.name.toLowerCase() + "\\";
}
/**
* Checks the database to see if a save already exists.
* @async
* @param {string} name - The name of the collection to look for. The database has the same name as the module.
*/
async saveExists(name) {
const ret = await this.client.mongo.db(this.name.toLowerCase()).listCollections({name: name}).hasNext();
console.log(ret);
return ret;
//return fs.existsSync(this._getSavePath() + name + ".json");
}
/**
* Saves data into the database.
* @async
* @param {string} name - The name of the collection to look for. The database has the same name as the module.
* @param {Object} data - The data to be stored into the database.
*/
async save(name, data) {
const collection = this.client.mongo.db(this.name.toLowerCase()).collection(name);
await collection.replaceOne({}, data, { upsert: true });
console.log(this.name + " Database Saved");
// var string = JSON.stringify(data);
// if (!fs.existsSync(this._getSavePath())) fs.mkdirSync(this._getSavePath());
// fs.writeFile(this._getSavePath() + name + ".json", string, err => {if (err != null) console.log(err)});
// console.log(this.name + " JSON Data Saved");
}
/**
* Loads data from the database.
* @async
* @param {string} name - The name of the collection to look for. The database has the same name as the module.
* @param {Object} fallback - The data to be saved in case no save already exists.
* @returns {Object} The object fetched from the database, or the fallback object if no save exists.
*/
async load(name, fallback) {
if (!await this.saveExists(name)) {
this.save(name, fallback);
return fallback;
}
var ret = await this.client.mongo.db(this.name.toLowerCase()).collection(name).findOne();
return ret;
// var string = fs.readFileSync(this._getSavePath() + name + ".json");
// return JSON.parse(string);
}
}
module.exports = exports = {Base}
/**
* Called when an emoji is collected.
* @callback collectionCallback
* @param {external:Collection<Emoji,external:MessageReaction>} collection - The current collection of all reactions.
* @param {external:MessageReaction} reaction - The reaction that was added.
* @param {external:User} user - The user that added that reaction.
*/
/**
* Called when an emoji is removed.
* @callback removalCallback
* @param {external:Collection<Emoji,external:MessageReaction>} collection - The current collection of all reactions.
* @param {external:MessageReaction} reaction - The reaction that was added.
* @param {external:User} user - The user that added that reaction.
*/
/**
* Called to verify if the collection can be closed when someone clicks on the confrimation emoji.
* @callback confirmationCallback
* @param {external:MessageReaction} reaction - The reaction that was added.
* @param {external:User} user - The user that added that reaction.
*/
/**
* Called once the reaction collector is closed.
* @callback endCallback
* @param {external:ReactionCollector} collection - The ReactionCollector object.
* @param {external:Collection<Emoji,external:MessageReaction>} collected - The collection of MessageReactions that were collected.
*/