From 0760ef9d0e070da6d11e763694586dde85fac97e Mon Sep 17 00:00:00 2001 From: Evelyne Lachance Date: Sat, 14 Jan 2017 01:13:05 -0500 Subject: [PATCH 01/56] Bugfix: prefixMention undefined When the ready event takes too long and a message event triggers before it executes, an error crashed the bot. This is now resolved (but prefix mentions are ignored until it's available) --- app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 1c381055..74bb12dc 100644 --- a/app.js +++ b/app.js @@ -50,9 +50,9 @@ exports.start = (config) => { msg.guildConf = conf; client.i18n.use(conf.lang); client.funcs.runMessageMonitors(client, msg).catch(reason => msg.channel.sendMessage(reason).catch(console.error)); - if (!msg.content.startsWith(conf.prefix) && !client.config.prefixMention.test(msg.content)) return; + if (!msg.content.startsWith(conf.prefix) && (client.config.prefixMention && !client.config.prefixMention.test(msg.content))) return; let prefixLength = conf.prefix.length; - if (client.config.prefixMention.test(msg.content)) prefixLength = client.config.prefixMention.exec(msg.content)[0].length + 1; + if (client.config.prefixMention && client.config.prefixMention.test(msg.content)) prefixLength = client.config.prefixMention.exec(msg.content)[0].length + 1; const command = msg.content.slice(prefixLength).split(" ")[0].toLowerCase(); const suffix = msg.content.slice(prefixLength).split(" ").slice(1).join(" "); let commandLog; From 3aee73e3f7eb6b566b6182c665ad6bd37174ea8a Mon Sep 17 00:00:00 2001 From: Evelyne Lachance Date: Sat, 14 Jan 2017 01:13:53 -0500 Subject: [PATCH 02/56] Event Logging (warn, error, disconnect) Added logging for warn, error, and disconnect events emitted by discord.js --- app.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app.js b/app.js index 74bb12dc..19471ffc 100644 --- a/app.js +++ b/app.js @@ -44,6 +44,10 @@ exports.start = (config) => { client.config.prefixMention = new RegExp(`^<@!?${client.user.id}>`); }); + client.on("error", e => client.funcs.log(e, "error")); + client.on("warn", w => client.funcs.log(w, "warning")); + client.on("disconnect", e => client.funcs.log(e, "error")); + client.on("message", (msg) => { if (msg.author.bot) return; const conf = client.funcs.confs.get(msg.guild); From 7bd1aa8f5ca3ac038e1ff1ce4aaa7b04b3f53b2e Mon Sep 17 00:00:00 2001 From: Evelyne Lachance Date: Sat, 14 Jan 2017 01:14:35 -0500 Subject: [PATCH 03/56] msg.member.permLevel Added permission level to msg.member so commands can do sub-checks (for example, a command with various actions with different permission levels) --- inhibitors/permissions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/inhibitors/permissions.js b/inhibitors/permissions.js index 4b37788d..3d670e1d 100644 --- a/inhibitors/permissions.js +++ b/inhibitors/permissions.js @@ -6,6 +6,7 @@ exports.conf = { exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { client.funcs.permissionLevel(client, msg.author, msg.guild) .then((permlvl) => { + msg.member.permLevel = permlvl; if (permlvl >= cmd.conf.permLevel) { resolve(); } else { From f897c2bb05a915d7cabc5851be045c10885b2d32 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sat, 14 Jan 2017 19:00:04 +1100 Subject: [PATCH 04/56] chore(package): update dependencies (#105) https://greenkeeper.io/ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 88d98fe3..a5ccc342 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "eslint": "^3.10.2", - "eslint-config-airbnb-base": "^10.0.1", + "eslint-config-airbnb-base": "^11.0.1", "eslint-plugin-import": "^2.2.0" } } From 2e475bb6c25083a6aaf1a6274baabddc9584168b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89velyne=20Lachance?= Date: Sun, 15 Jan 2017 06:53:38 -0500 Subject: [PATCH 05/56] [BUGFIX] Fix function inits (#106) * [BUG-FIX] Move Function init() to ready SEMVER: PATCH Why: * Some function inits require cache to work properly This change addresses the need by: * If a function's init() required something from a client collection, it failed to execute properly. * Moving init() functions the ready event seems like an appropriate workaround for now. Fixes #106 * [PR] Bump Package.json SEMVER: Why: * Package.json is being bumped by a semver patch increment * Remove stupid comment This damn "guild removed but not found" message has been plaguing me since I added confs. --- app.js | 3 +++ functions/confs.js | 2 +- functions/loadFunctions.js | 7 ++++--- package.json | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index 19471ffc..d72bcc0c 100644 --- a/app.js +++ b/app.js @@ -42,6 +42,9 @@ exports.start = (config) => { client.once("ready", () => { client.config.prefixMention = new RegExp(`^<@!?${client.user.id}>`); + for (const func in client.funcs) { + if (client.funcs[func].init) client.funcs[func].init(client); + } }); client.on("error", e => client.funcs.log(e, "error")); diff --git a/functions/confs.js b/functions/confs.js index 91725667..8a15a040 100644 --- a/functions/confs.js +++ b/functions/confs.js @@ -50,7 +50,7 @@ exports.init = (client) => { exports.remove = (guild) => { if (!guildConfs.has(guild.id)) { - return console.log(`Attempting to remove ${guild.name} but it's not there.`); + return false; } fs.removeAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)); diff --git a/functions/loadFunctions.js b/functions/loadFunctions.js index d90e9a31..589edbe6 100644 --- a/functions/loadFunctions.js +++ b/functions/loadFunctions.js @@ -14,9 +14,10 @@ const loadFunctions = (client, baseDir, counts) => new Promise((resolve, reject) const file = f.split("."); if (file[0] === "loadFunctions") return; client.funcs[file[0]] = require(`${dir}/${f}`); - if (client.funcs[file[0]].init) { - client.funcs[file[0]].init(client); - } + // Currently disabled for workaround (See ready event) + // if (client.funcs[file[0]].init) { + // client.funcs[file[0]].init(client); + // } c++; }); resolve(c); diff --git a/package.json b/package.json index a5ccc342..ee4a17ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.12.4", + "version": "0.12.5", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From b6de8e9fb1c1bbed7b5f5f261298c3d326d006c6 Mon Sep 17 00:00:00 2001 From: Faith Date: Tue, 17 Jan 2017 00:40:42 +0100 Subject: [PATCH 06/56] [FEATURE] New Configuration System (#107) --- CHANGELOG.md | 5 + README.md | 12 +- app.js | 6 +- classes/Config.js | 598 +++++++++++++++++++++++++++++++++++ functions/confs.js | 254 --------------- functions/permissionLevel.js | 2 +- package.json | 2 +- 7 files changed, 617 insertions(+), 262 deletions(-) create mode 100644 classes/Config.js delete mode 100644 functions/confs.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 51b78a2e..d36578c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- New Beta Configuration (Needs heavy testing) + +### Removed +- Old Configuration System ## [0.12.4] - 2017-01-13 ### Added diff --git a/README.md b/README.md index 2853524a..1fde8200 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Komada -[![Discord](https://discordapp.com/api/guilds/234357395646578688/embed.png)](http://discord.gg/QnWkbXV) +[![Discord](https://discordapp.com/api/guilds/260202843686830080/embed.png)](https://discord.gg/dgs8263) [![npm](https://img.shields.io/npm/v/komada.svg?maxAge=3600)](https://www.npmjs.com/package/komada) [![npm](https://img.shields.io/npm/dt/komada.svg?maxAge=3600)](https://www.npmjs.com/package/komada) -[![Build Status](https://travis-ci.org/eslachance/komada.svg?branch=indev)](https://travis-ci.org/eslachance/komada) -[![David](https://img.shields.io/david/eslachance/komada.svg?maxAge=3600)](https://david-dm.org/eslachance/komada) +[![Build Status](https://travis-ci.org/dirigeants/komada.svg?branch=indev)](https://travis-ci.org/dirigeants/komada) +[![David](https://img.shields.io/david/dirigeants/komada.svg?maxAge=3600)](https://david-dm.org/dirigeants/komada) > "Stay a while, and listen!" @@ -44,7 +44,7 @@ npm install node app.js ``` -> Requires Node 6 or higher (because Discord.js requires that), also requires Discord.js v10, installed automatically with `npm install`. +> Requires Node 6 or higher (because Discord.js requires that), also requires Discord.js v11, installed automatically with `npm install`. ## Quick & Dirty Reference Guide > For creating your own pieces @@ -220,6 +220,9 @@ exports.run = (client, msg) => { }; ``` +> Note: Technically, this means that monitors are message events. You can use this trick +to get around the normal amount of message events in Komada.. *cough* + ### Using Methods Methods are just Discord.js native functions added to Komada, so that we may @@ -231,7 +234,6 @@ Current Methods are: Collections => `client.methods.Collection` Rich Embed Builder => `client.methods.Embed` Message Collector => `client.methods.MessageCollector` -ShardingManager => `client.methods.Shard` WebhookClient => `client.methods.Webhook` To use any of the methods, you follow this same structure: diff --git a/app.js b/app.js index d72bcc0c..9127a2fd 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,7 @@ const Discord = require("discord.js"); const chalk = require("chalk"); const loadFunctions = require("./functions/loadFunctions.js"); +const Config = require("./classes/Config.js"); const clk = new chalk.constructor({ enabled: true }); @@ -28,6 +29,8 @@ exports.start = (config) => { client.coreBaseDir = `${__dirname}/`; client.clientBaseDir = `${process.cwd()}/`; + client.guildConfs = Config.guildConfs; + client.configuration = Config; // Load core functions, then everything else loadFunctions(client).then(() => { @@ -42,6 +45,7 @@ exports.start = (config) => { client.once("ready", () => { client.config.prefixMention = new RegExp(`^<@!?${client.user.id}>`); + Config.initialize(client); for (const func in client.funcs) { if (client.funcs[func].init) client.funcs[func].init(client); } @@ -53,7 +57,7 @@ exports.start = (config) => { client.on("message", (msg) => { if (msg.author.bot) return; - const conf = client.funcs.confs.get(msg.guild); + const conf = Config.get(msg.guild); msg.guildConf = conf; client.i18n.use(conf.lang); client.funcs.runMessageMonitors(client, msg).catch(reason => msg.channel.sendMessage(reason).catch(console.error)); diff --git a/classes/Config.js b/classes/Config.js new file mode 100644 index 00000000..7b4a2261 --- /dev/null +++ b/classes/Config.js @@ -0,0 +1,598 @@ +/* eslint-disable no-restricted-syntax, no-underscore-dangle, no-unused-vars */ +const fs = require("fs-extra-promise"); +const path = require("path"); + +const guildConfs = new Map(); +let dataDir = ""; +const defaultFile = "default.json"; +let defaultConf = {}; + +/** The starting point for creating a String configuration key. */ +class StringConfig { + /** + * @param {Config} conf The guild configuration obtained from the guildConfs map. + * @param {Object} data The data you want to append to this String configuration key. + * @returns {StringConfig} + */ + constructor(conf, data) { + if (typeof data !== "string") this.data = ""; + else this.data = data; + this.type = "String"; + if (data.possibles) this.possibles = data.possibles; + else this.possibles = []; + Object.defineProperty(this, "_id", { value: conf._id }); + return this; + } + + /** + * Sets the value of a string configurations possibles. This takes into account the list of acceptable answers from the possibles array. + * @param {string} value The value you want to set this key to. + * @returns {StringConfig} + */ + set(value) { + if (!value) return "Please supply a value to set."; + if (this.possibles.length !== 0 && !this.possibles.includes(value)) return `That is not a valid option. Valid options: ${this.possibles.join(", ")}`; + this.data = value.toString(); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Adds a value(s) to list of acceptable answers for this key. Accepts one item or an array of items. + * @param {string|array} value The value(s) you want to add to this key. + * @returns {StringConfig} + */ + add(value) { + if (!value) return "Please supply a value to add to the possibles array."; + if (value instanceof Array) { + value.forEach((val) => { + if (this.possibles.includes(val)) return `The value ${val} is already in ${this.possibles}.`; + return this.possibles.push(val); + }); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + if (this.possibles.includes(value)) return `The value ${value} is already in ${this.possibles}.`; + this.possibles.push(value); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Deletes a value(s) from the string configurations possibles. Accepts one item or an array of items. + * @param {string|array} value The value(s) you want to delete from this key. + * @returns {StringConfig} + */ + del(value) { + if (!value) return "Please supply a value to add to the possibles array"; + if (value instanceof Array) { + value.forEach((val) => { + if (!this.possibles.includes(val)) return `The value ${value} is not in ${this.possibles}.`; + return this.possibles.splice(this.possibles.indexOf(val), 1); + }); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + if (!this.possibles.includes(value)) return `The value ${value} is not in ${this.possibles}.`; + this.possibles.splice(this.possibles.indexOf(value), 1); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } +} + +/** The starting point for creating an Array Configuration key. */ +class ArrayConfig { + /** + * @param {Config} conf The guild configuration obtained from the guildConfs map. + * @param {object} data The data you want to append to this Array configuration key. + * @returns {ArrayConfig} + */ + constructor(conf, data) { + if (!(data instanceof Array)) this.data = []; + else this.data = data; + this.type = "Array"; + Object.defineProperty(this, "_id", { value: conf._id }); + return this; + } + + /** + * Adds a value(s) to the array. Accepts a single value or an array of values. + * @param {string|array} value The value(s) to add to the array. + * @returns {ArrayConfig} + */ + add(value) { + if (!value) return "Please supply a value to add to the array."; + if (value instanceof Array) { + value.forEach((val) => { + if (!this.data.includes(val)) return "That value is not in the array."; + return this.data.splice(this.data.indexOf(value), 1); + }); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + if (this.data.includes(value)) return "That value is already in the array."; + this.data.push(value); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Deletes a value(s) from the array. Accepts a single value or an array of values. + * @param {string|array} value The value(s) to delete from the array. + * @returns {ArrayConfig} + */ + del(value) { + if (!value) return "Please supply a value to delete from the array"; + if (value instanceof Array) { + value.forEach((val) => { + if (!this.data.includes(val)) return "That value is not in the array."; + return this.data.splice(this.data.indexOf(value), 1); + }); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + if (!this.data.includes(value)) return "That value is not in the array."; + this.data.splice(this.data.indexOf(value), 1); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } +} + +/** The starting point for creating a Boolean configuration key. */ +class BooleanConfig { + /** + * @param {Config} conf A guilds configuration obtained from the guildConfs map. + * @param {object} data The data you want to append to this boolean key. + * @returns {BooleanConfig} + */ + constructor(conf, data) { + if (typeof data !== "boolean") this.data = false; + else this.data = data; + this.type = "Boolean"; + Object.defineProperty(this, "_id", { value: conf._id }); + return this; + } + + /** + * Toggles a boolean statement for the boolean key. + * @returns {BooleanConfig} + */ + toggle() { + if (this.data === true) this.data = false; + else this.data = true; + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } +} + +/** The starting point for creating a Number configuration key. */ +class NumberConfig { + /** + * @param {Config} conf A guilds configuration obtained from the guildConfs map. + * @param {object} data The data you want to append to this number key. + * @returns {NumberConfig} + */ + constructor(conf, data) { + if (typeof data !== "number") this.data = 0; + else this.data = data; + if (data.min) this.min = data.min; + if (data.max) this.max = data.max; + this.type = "Number"; + Object.defineProperty(this, "_id", { value: conf._id }); + return this; + } + + /** + * Sets the value for a number key, according to the minimum and maximum values if they apply. + * @param {number} value The value you want to set the key as. + * @returns {NumberConfig} + */ + set(value) { + if (!value) return `Error, value is ${value}. Please supply a value to set.`; + if (this.min && parseInt(value) < this.min) return `Error while setting the value. ${value} is less than ${this.min}`; + if (this.max && parseInt(value) > this.max) return `Error while setting the value. ${value} is more than ${this.max}`; + this.data = value; + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Sets the minimum value a number key can be. + * @param {number} value The value you want to set the minimum as. + * @returns {NumberConfig} + */ + setMin(value) { + this.min = value; + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Sets the maximum value a number key can bey. + * @param {number} value The value you want to set the maximum as. + * @returns {NumberConfig} + */ + setMax(value) { + this.max = value; + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + +} + +/** + * The starting point for creating a Guild configuration + */ +class Config { + /** + * @param {Client} client The Discord.js client. + * @param {GuildID} guildID The guild for which the configuration is being made. + * @param {Config} [config] The local config to add to the configuration. + */ + constructor(client, guildID, config = {}) { + /** + * The guild to create the configuration for. + * @type {GuildID} + * @readonly + */ + Object.defineProperty(this, "_id", { value: guildID }); + /** + * The default prefix to use for the bot if one is not in the configuration. + * @type {String} + */ + this.prefix = new StringConfig(this, client.config.prefix); + /** + * The array of disabled commands that are unable to be used in a guild. + * @type {Array} + */ + this.disabledCommands = new ArrayConfig(this, []); + /** + * The default role name to look for when assigning mod level permissions. + * @type {String} + */ + this.modRole = new StringConfig(this, "Mods"); + /** + * The default role name to look for when assigning admin level permissions. + * @type {String} + */ + this.adminRole = new StringConfig(this, "Devs"); + /** + * The default language to use for the bot. + * @type {String} + */ + this.lang = new StringConfig(this, "en"); + if (typeof config === "object") { + for (const prop in config) { + if (config[prop].type === "String") { + this[prop] = new StringConfig(this, config[prop].data); + } else if (config[prop].type === "Boolean") { + this[prop] = new BooleanConfig(this, config[prop].data); + } else if (config[prop].type === "Number") { + this[prop] = new NumberConfig(this, config[prop].data); + } else if (config[prop].type === "Array") { + this[prop] = new ArrayConfig(this, config[prop].data); + } else { + client.funcs.log("Invalid Key type inside of your configuration. Komada will ignore this key until it is fixed.", "warn"); + } + } + } + return this; + } + + + /** + * Allows you to add a key to a guild configuration. Note: This should never be called + * directly as it could cause unwanted side effects. + * @param {string} key The key to add to the configuration. + * @param {string|array|number|boolean} defaultValue The value for the key. + * @param {string} type The type of key you want to add. + * @returns {Config} + */ + addKey(key, defaultValue, type) { + if (type === "String") { + this[key] = new StringConfig(this, defaultValue); + } else if (type === "Boolean") { + this[key] = new BooleanConfig(this, defaultValue); + } else if (type === "Number") { + this[key] = new NumberConfig(this, defaultValue); + } else if (type === "Array") { + this[key] = new ArrayConfig(this, defaultValue); + } else { + console.log(`Invalid Key Type: Type: ${type}`); + } + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Deletes a key from the respected guild configuration. + * This should never be called directly. + * @param {string} key The key to delete from the configuration + * @returns {null} + */ + delKey(key) { + delete this[key]; + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return null; + } + + /** + * Resets a key for the respected guild configuration. + * @param {string} key The key to reset in the configuration. + * @returns {Config} + */ + reset(key) { + if (this[key].type === "String") { + this[key] = new StringConfig(this, defaultConf[key].data); + } else if (this[key].type === "Boolean") { + this[key] = new BooleanConfig(this, defaultConf[key].data); + } else if (this[key].type === "Number") { + this[key] = new NumberConfig(this, defaultConf[key].data); + } else if (this[key].type === "Array") { + this[key] = new ArrayConfig(this, defaultConf[key].data); + } + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this[key]; + } + + /** + * Checks the guild configuration for a key + * @param {string} key The key to check the guild configuration for. + * @returns {boolean} + */ + has(key) { + if (!key) return "Please supply a key."; + return (key in this); + } + + /** + * Simplifies the guild configuration for use in commands and modules. + * @param {Guild} guild The guild to get a configuration for. + * @returns {object} + * @static + * @example + * //Example of what this returns + * { prefix: '--', disabledCommands: [], modRole: 'Mods', adminRole: 'Devs', lang: 'en' } + */ + static get(guild) { + const conf = {}; + if (guild && guildConfs.has(guild.id)) { + const guildConf = guildConfs.get(guild.id); + for (const key in guildConf) { + if (guildConf[key]) conf[key] = guildConf[key].data; + else conf[key] = defaultConf[key].data; + } + } + return conf; + } + + /** + * Set the default value for a key in the default configuration. + * @param {string} key The key for which you want to change. + * @param {array|boolean|number|string} defaultValue The value you want to set as the default. + * @returns {object} Returns the new default configuration for the key. + * @static + */ + static set(key, defaultValue) { + if (!key || !defaultValue) return `You supplied ${key}, ${defaultValue}. Please supply both a key, and a default value.`; + if (!defaultConf[key]) return `The key ${key} does not seem to be present in the default configuration.`; + if (defaultConf[key].type === "Array") this.add(key, defaultValue); + if (defaultConf[key].type === "Boolean") this.toggle(key); + if (defaultConf[key].type === "Number" || defaultConf[key].type === "String") defaultConf[key].data = defaultValue; + else return "Unsupported Configuration Type! Cannot set the value."; + fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); + return defaultConf[key]; + } + + /** + * Sets the default minimum value for a Number key + * @param {string} key The Number key for which you want to set the minimum value for. + * @param {number} defaultMinValue The value you want to set as the "minimum" value. + * @returns {object} Returns the new default configuration for the key. + * @static + */ + static setMin(key, defaultMinValue) { + if (!key || !defaultMinValue) return `You supplied ${key}, ${defaultMinValue}. Please supply both a key, and a default min value.`; + if (!defaultConf[key].type !== "Number") return "You cannot use this method on non-Numeral configurations."; + defaultConf[key].min = defaultMinValue; + fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); + return defaultConf[key]; + } + + /** + * Sets the default maximum value for a Number key + * @param {string} key The Number key for which you want to set the maximum value for. + * @param {number} defaultMaxValue The value you want to set as the "maximum" value. + * @returns {object} Returns the new default configuration for the key. + * @static + */ + static setMax(key, defaultMaxValue) { + if (!key || !defaultMaxValue) return `You supplied ${key}, ${defaultMaxValue}. Please supply both a key, and a default max value.`; + if (!defaultConf[key].type !== "Number") return "You cannot use this method on non-Numeral configurations."; + defaultConf[key].min = defaultMaxValue; + fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); + return defaultConf[key]; + } + + /** + * Adds a value to the data array for an Array key. + * @param {string} key The Array key for which you want to add value(s) for. + * @param {string} defaultValue The value for which you want to add to the array. + * @returns {object} Returns the new default configuration for the key. + * @static + */ + static add(key, defaultValue) { + if (!key || !defaultValue) return `You supplied ${key}, ${defaultValue}. Please supply both a key, and a default value.`; + if (!defaultConf[key].type !== "Array") return "You cannot use this method on non-Array configuration options."; + if (defaultConf[key].data.includes(defaultValue)) return `The default value ${defaultValue} is already in ${defaultConf[key].data}.`; + defaultConf[key].data.push(defaultValue); + fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); + return defaultConf[key]; + } + + /** + * Deletes a value from the data array for an Array key. + * @param {string} key The array key for which you want to delete value(s) from. + * @param {string} defaultValue The value for which you want to remove from the array. + * @returns {object} Returns the new default configuration for the key. + * @static + */ + static del(key, defaultValue) { + if (!key || !defaultValue) return `You supplied ${key}, ${defaultValue}. Please supply both a key, and a default value.`; + if (!defaultConf[key].type !== "Array") return "You cannot use this method on non-Array configuration options."; + if (!defaultConf[key].data.includes(defaultValue)) return `The default value ${defaultValue} is not in ${defaultConf[key].data}.`; + defaultConf[key].data.splice(defaultConf[key].indexOf(defaultValue), 1); + fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); + return defaultConf[key]; + } + + /** + * Toggles the true/false statement for a Boolean key + * @param {string} key The boolean key for which you want to toggle the statement for. + * @returns {objct} Returns the new default configuration for the key. + * @static + */ + static toggle(key) { + if (!key) return "Please supply a key to toggle the value for."; + if (!defaultConf[key].type !== "Boolean") return "You cannot use this method on non-Boolean configuration options."; + if (defaultConf[key].data === true) defaultConf[key].data = false; + else defaultConf[key].data = false; + fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); + return defaultConf[key]; + } + + /** + * Checks if the guildConfs Map has the specified guild. + * @param {Guild} guild The guild to check the Map for. + * @returns {boolean} + * @static + */ + static has(guild) { + if (!guild) return "Please supply a guild."; + return (guildConfs.has(guild.id)); + } + + /** + * Checks if the default configuration has a specified key. + * @param {string} key The key for which to check the default configuration for. + * @returns {boolean} + * @static + */ + static hasKey(key) { + if (!key) return "Please supply a key to check for."; + return (key in defaultConf); + } + + /** + * Adds a key to the default configuration, and every guilds configuration. + * @param {string} key The key for which to add to the default and all guild configurations. + * @param {string|number|boolean|array} defaultValue The value for which you want to set as the default value. + * @param {string} [type] The type of key this will be. This can currently be Strings, Numbers, Arrays, or Booleans. + * @returns {object} Returns the entire default configuration + * @static + */ + static addKey(key, defaultValue, type = defaultValue.constructor.name) { + if (!key || !defaultValue) return `You supplied ${key}, ${defaultValue}. Please provide both.`; + if (defaultConf[key]) return "There's no reason to add this key, it already exists."; + if (["TextChannel", "GuildChannel", "Message", "User", "GuildMember", "Guild", "Role", "VoiceChannel", "Emoji", "Invite"].includes(type)) { + defaultValue = defaultValue.id; + } + if (defaultValue.constructor.name !== type && defaultValue.constructor.name !== null) { + return "Invalid Value was provided"; + } + defaultConf[key] = { type: defaultValue.constructor.name, data: defaultValue }; + guildConfs.forEach((config) => { + config.addKey(key, defaultValue, type); + }); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`), defaultConf); + return defaultConf; + } + + /** + * Deletes a key from the default configuration, and every guilds configuration. + * @param {string} key The key for which to add to the default and all guild configurations. + * @returns The new default configuration + * @static + */ + static delKey(key) { + if (!key) return "Please supply a key to delete from the default configuration."; + if (!defaultConf[key]) return `The key ${key} does not seem to be present in the default configuration.`; + if (["modRole", "adminRole", "disabledCommands", "prefix", "lang"].includes(key)) { + return `The key ${key} is core and cannot be deleted.`; + } + delete defaultConf[key]; + fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); + this.guildConfs.forEach((config) => { + config.delKey(key); + }); + return defaultConf; + } + + /** + * Inserts a guild into the guildConfs map and deletes the configuration JSON. + * This should never be called by anyone, this is purely for the guildCreate event. + * @param {Client} client The Discord.js Client + * @param {Guild} guild The Guild being inserted into the map. + * @returns {String} + * @static + */ + static insert(client, guild) { + if (!guild) return "Please specify a guild to remove."; + guildConfs.set(guild.id, new Config(client, guild.id, defaultConf)); + fs.outputJSONAsync(`${dataDir}${path.sep}${guild.id}.json`, guildConfs.get(guild.id)); + return `Inserted ${guild.name} succesfully.`; + } + + /** + * Removes a guild from the guildConfs map and deletes the configuration JSON. + * This should never be called by anyone, this is purely for the guildDelete event. + * @param {Guild} guild The guild being removed from the map. + * @returns {String} + * @static + */ + static remove(guild) { + if (!guild) return "Please specify a guild to remove."; + guildConfs.delete(guild.id); + fs.removeAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)); + return `Removed ${guild.name} succesfully.`; + } + + /** + * The motherboard of our Configuration system. + * There's no reason to ever call this as it's called internally upon startup. + * @param {Client} client The Discord.js Client + * @returns {null} + * @static + */ + static initialize(client) { + defaultConf = { + prefix: { type: "String", data: client.config.prefix }, + disabledCommands: { type: "Array", data: [] }, + modRole: { type: "String", data: "Mods" }, + adminRole: { type: "String", data: "Devs" }, + lang: { type: "String", data: "en" }, + }; + dataDir = path.resolve(`${client.clientBaseDir}${path.sep}bwd${path.sep}conf`); + fs.ensureFileAsync(`${dataDir}${path.sep}${defaultFile}`) + .then(() => { + fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`)) + .then((conf) => { + defaultConf = conf; + }).catch(() => { + fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); + }); + client.guilds.forEach((guild) => { + fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)) + .then((thisConf) => { + guildConfs.set(guild.id, new Config(client, guild.id, thisConf)); + }).catch(() => { + guildConfs.set(guild.id, new Config(client, guild.id)); + }); + }); + return null; + }); + } +} + +module.exports = Config; +module.exports.guildConfs = guildConfs; diff --git a/functions/confs.js b/functions/confs.js deleted file mode 100644 index 8a15a040..00000000 --- a/functions/confs.js +++ /dev/null @@ -1,254 +0,0 @@ -const fs = require("fs-extra-promise"); -const path = require("path"); - -const guildConfs = new Map(); -let dataDir = ""; -const defaultFile = "default.json"; -let defaultConf = {}; - -exports.init = (client) => { - dataDir = path.resolve(`${client.clientBaseDir}${path.sep}bwd${path.sep}conf`); - - defaultConf = { - prefix: { type: "String", data: client.config.prefix }, - disabledCommands: { type: "Array", data: [] }, - modRole: { type: "String", data: "Mods" }, - adminRole: { type: "String", data: "Devs" }, - lang: { type: "String", data: "en" }, - }; - - fs.ensureFileAsync(`${dataDir}${path.sep}${defaultFile}`) - .then(() => { - fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`)) - .then((currentDefaultConf) => { - Object.keys(defaultConf).forEach((key) => { - if (!currentDefaultConf.hasOwnProperty(key)) currentDefaultConf[key] = defaultConf[key]; - }); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`), currentDefaultConf) - .then(() => { - defaultConf = currentDefaultConf; - }); - }).catch(() => { - fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); - }); - fs.walk(dataDir) - .on("data", (item) => { - const fileinfo = path.parse(item.path); - if (!fileinfo.ext) return; - if (fileinfo.name === "default") return; - const guildID = fileinfo.name; - fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${fileinfo.base}`)) - .then((thisConf) => { - guildConfs.set(guildID, thisConf); - }).catch(err => client.funcs.log(err, "error")); - }) - .on("end", () => { - client.funcs.log("Guild Confs have finished loading", "log"); - }); - }); -}; - -exports.remove = (guild) => { - if (!guildConfs.has(guild.id)) { - return false; - } - - fs.removeAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)); - return `${guild.name} has been successfully removed!`; -}; - -exports.has = guild => guildConfs.has(guild.id); - -exports.get = (guild) => { - const conf = {}; - if (!!guild && guildConfs.has(guild.id)) { - const guildConf = guildConfs.get(guild.id); - for (const key in guildConf) { - if (guildConf[key]) conf[key] = guildConf[key].data; - else conf[key] = defaultConf[key].data; - } - } - for (const key in defaultConf) { - if (!conf[key]) conf[key] = defaultConf[key].data; - } - return conf; -}; - -exports.getRaw = (guild) => { - const conf = {}; - if (guild && guildConfs.has(guild.id)) { - const guildConf = guildConfs.get(guild.id); - for (const key in guildConf) { - if (guildConf[key]) conf[key] = guildConf[key]; - else conf[key] = defaultConf[key]; - } - } - for (const key in defaultConf) { - if (!conf[key]) conf[key] = defaultConf[key]; - } - return conf; -}; - -exports.addKey = (key, defaultValue, min = null, max = null) => { - const type = defaultValue.constructor.name; - if (["TextChannel", "GuildChannel", "Message", "User", "GuildMember", "Guild", "Role", "VoiceChannel", "Emoji", "Invite"].includes(type)) { - defaultValue = defaultValue.id; - } - if (defaultValue.constructor.name !== type && defaultValue.constructor.name !== null) { - return "Invalid Value was provided"; - } - defaultConf[key] = { type: defaultValue.constructor.name, data: defaultValue }; - if (type === "Number") { - defaultConf[key].min = min; - defaultConf[key].max = max; - } - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`), defaultConf); - return "Key has been successfully added."; -}; - -exports.setKey = (key, defaultValue) => { - if (!(key in defaultConf)) { - throw new Error(`The key ${key} does not seem to be present in the default configuration.`); - } - switch (defaultConf[key].type) { - case "Array": { - const dataArray = []; - if (defaultConf[key]) { - dataArray.splice(dataArray.indexOf(defaultValue), 1); - defaultValue = dataArray; - } else { - dataArray.push(defaultValue); - defaultValue = dataArray; - } - break; - } - case "Boolean": - if (defaultValue === "true") { - defaultValue = true; - } else if (defaultValue === "false") { - defaultValue = false; - } else { - throw new Error(`The value ${defaultValue} does not correspond to the type boolean.`); - } - break; - case "Number": - defaultValue = parseInt(defaultValue); - if (isNaN(defaultValue)) { - throw new Error(`The value ${defaultValue} does not correspond to the type integer.`); - } else if (defaultConf[key].max !== null && defaultValue > defaultConf[key].max) { - throw new Error(`The value ${defaultValue} is bigger than the max value ${defaultConf[key].max}`); - } else if (defaultConf[key].min !== null && defaultValue < defaultConf[key].min) { - throw new Error(`The value ${defaultValue} is smaller than the min value ${defaultConf[key].min}`); - } - break; - default: - defaultValue = defaultValue.toString(); - } - defaultConf[key].data = defaultValue; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`), defaultConf); - return defaultConf; -}; - -exports.resetKey = (guild, ...keys) => { - keys.forEach((key, element) => { // eslint-disable-line no-unused-vars - if (!(key in defaultConf)) { - throw new Error(`The key ${key} does not seem to be present in the default configuration.`); - } - }); - if (!guildConfs.has(guild.id)) { - throw new Error(`The guild ${guild.name}(${guild.id}) not found while trying to reset ${keys.join(", ")}`); - } - fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)) - .then((thisConf) => { - keys.forEach((key, element) => { // eslint-disable-line no-unused-vars - if (!(key in thisConf)) { - throw new Error(`The key ${key} does not seem to be present in the server configuration.`); - } - delete thisConf[key]; - }); - if (Object.keys(thisConf).length > 0) { - guildConfs.set(guild.id, thisConf); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`), thisConf); - return; - } - guildConfs.set(guild.id, defaultConf); - fs.removeAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)); - }); -}; - -exports.delKey = (key) => { - if (!(key in defaultConf)) { - throw new Error(`The key ${key} does not seem to be present in the default configuration.`); - } - if (["modRole", "adminRole", "disabledCommands", "prefix", "lang"].includes(key)) { - throw new Error(`The key ${key} is core and cannot be deleted.`); - } - delete defaultConf[key]; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`), defaultConf) - .then(() => { - const MapIter = guildConfs.keys(); - guildConfs.forEach((conf) => { - delete conf[key]; - if (Object.keys(conf).length > 0) { - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${MapIter.next().value}.json`), conf); - return; - } - fs.removeAsync(path.resolve(`${dataDir}${path.sep}${MapIter.next().value}.json`)); - }); - }); -}; - -exports.hasKey = key => (key in defaultConf); - -exports.set = (guild, key, value) => { - let thisConf = {}; - if (guildConfs.has(guild.id)) { - thisConf = guildConfs.get(guild.id); - } - - if (!(key in defaultConf)) { - throw new Error(`The key ${key} is not valid according to the Default Configuration.`); - } - - switch (defaultConf[key].type) { - case "Array": { - const dataArray = []; - if (thisConf[key]) { - dataArray.splice(dataArray.indexOf(value), 1); - value = dataArray; - } else { - dataArray.push(value); - value = dataArray; - } - break; - } - case "Boolean": - if (value === "true") { - value = true; - } else if (value === "false") { - value = false; - } else { - throw new Error(`The value ${value} does not correspond to the Boolean type.`); - } - break; - case "Number": - value = parseInt(value); - if (isNaN(value)) { - throw new Error(`The value ${value} does not correspond to the Number type.`); - } else if (defaultConf[key].max !== null && value > defaultConf[key].max) { - throw new Error(`The value ${value} is bigger than the max value ${defaultConf[key].max}`); - } else if (defaultConf[key].min !== null && value < defaultConf[key].min) { - throw new Error(`The value ${value} is smaller than the min value ${defaultConf[key].min}`); - } - break; - default: - value = value.toString(); - } - - thisConf[key] = { data: value, type: defaultConf[key].type }; - - guildConfs.set(guild.id, thisConf); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`), thisConf); - - return thisConf; -}; diff --git a/functions/permissionLevel.js b/functions/permissionLevel.js index f32b8107..19ca4a45 100644 --- a/functions/permissionLevel.js +++ b/functions/permissionLevel.js @@ -1,7 +1,7 @@ module.exports = (client, user, guild) => new Promise((resolve, reject) => { let permlvl = 0; if (guild) { - const guildConf = client.funcs.confs.get(guild); + const guildConf = client.configuration.get(guild); try { const modRole = guild.roles.find("name", guildConf.modRole); guild.fetchMember(user).then((member) => { diff --git a/package.json b/package.json index ee4a17ec..1709dd98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.12.5", + "version": "0.13.0", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 3a2bd49c443894a792f2c314d50fab65dcc4a843 Mon Sep 17 00:00:00 2001 From: Faith Date: Wed, 18 Jan 2017 01:15:46 +0100 Subject: [PATCH 07/56] [BUGFIX] Many Fixes/Additions (#108) * Fixed Conf.js to use new Configuration System * Changelog updated * Events Added For Configuration System * Clean function now properly removes client.user.email --- CHANGELOG.md | 2 ++ commands/System/conf.js | 22 +++++++--------------- events/guildCreate.js | 4 ++++ events/guildDelete.js | 2 +- functions/clean.js | 4 +--- 5 files changed, 15 insertions(+), 19 deletions(-) create mode 100644 events/guildCreate.js diff --git a/CHANGELOG.md b/CHANGELOG.md index d36578c3..9fabbd41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - New Beta Configuration (Needs heavy testing) +### Changed +- Confs.js uses new configuration system now ### Removed - Old Configuration System diff --git a/commands/System/conf.js b/commands/System/conf.js index 233232d8..f24425c8 100644 --- a/commands/System/conf.js +++ b/commands/System/conf.js @@ -12,25 +12,17 @@ exports.run = (client, msg, [action, key, ...value]) => { if (action === "set") { if (!key || value[0] === undefined) return msg.reply("Please provide both a key and value!"); - const type = value[0].constructor.name; - if (["TextChannel", "GuildChannel", "Message", "User", "GuildMember", "Guild", "Role", "VoiceChannel", "Emoji", "Invite"].includes(type)) { - value = value[0].id; - } else { - value = value.join(" ").toString(); - } - client.funcs.confs.set(msg.guild, key, value); - if (msg.guildConf[key].constructor.name === "Array") { - if (msg.guildConf[key].includes(value)) { - return msg.reply(`The value ${value} for ${key} has been added.`); - } - return msg.reply(`The value ${value} for ${key} has been removed.`); - } - return msg.reply(`The value for ${key} has been set to: ${value}`); + const conf = client.guildConfs.get(msg.guild.id); + if (conf[key].type === "Boolean") conf[key].toggle(); + if (conf[key].type === "String") conf[key].set(value[0]); + if (conf[key].type === "Number") conf[key].set(value[0]); + if (conf[key].type === "Array") conf[key].add(value); + return msg.reply(`The new value for ${key} is: ${conf[key].data}`); } else if (action === "reset") { if (!key) return msg.reply("Please provide a key you wish to reset"); - client.funcs.confs.resetKey(msg.guild, key); + client.guildConfs.get(msg.guild.id).reset(key); return msg.reply("The key has been reset."); } return false; diff --git a/events/guildCreate.js b/events/guildCreate.js new file mode 100644 index 00000000..40346557 --- /dev/null +++ b/events/guildCreate.js @@ -0,0 +1,4 @@ +exports.run = (client, guild) => { + if (!guild.available) return; + client.configuration.insert(client, guild); +}; diff --git a/events/guildDelete.js b/events/guildDelete.js index c999e175..b5785e11 100644 --- a/events/guildDelete.js +++ b/events/guildDelete.js @@ -1,4 +1,4 @@ exports.run = (client, guild) => { if (!guild.available) return; - client.funcs.confs.remove(guild); + client.configuration.remove(guild); }; diff --git a/functions/clean.js b/functions/clean.js index a7e6bce5..f57ac85e 100644 --- a/functions/clean.js +++ b/functions/clean.js @@ -4,6 +4,7 @@ function sensitivePattern(client) { if (client.token) pattern += client.token; if (client.token) pattern += (pattern.length > 0 ? "|" : "") + client.token; if (client.email) pattern += (pattern.length > 0 ? "|" : "") + client.email; + if (client.user.email) pattern += (pattern.length > 0 ? "|" : "") + client.user.email; if (client.password) pattern += (pattern.length > 0 ? "|" : "") + client.password; this.sensitivePattern = new RegExp(pattern, "gi"); } @@ -12,9 +13,6 @@ function sensitivePattern(client) { module.exports = (client, text) => { if (typeof (text) === "string") { - if (client.user.email) { - text.replace(client.user.email, "「redacted」"); - } return text.replace(sensitivePattern(client), "「redacted」").replace(/`/g, `\`${String.fromCharCode(8203)}`).replace(/@/g, `@${String.fromCharCode(8203)}`); } return text; From 146bae0dd7a26bb93949c21b1243d2237bfa1b30 Mon Sep 17 00:00:00 2001 From: Faith Date: Thu, 19 Jan 2017 10:04:35 +0100 Subject: [PATCH 08/56] [FEATURE] Configuration Update (#110) * Broke down Configuration into smaller, manageable parts. * Changelog && Patch Bump --- CHANGELOG.md | 2 + classes/Config.js | 219 +------------------------ classes/Configuration Types/Array.js | 66 ++++++++ classes/Configuration Types/Boolean.js | 35 ++++ classes/Configuration Types/Number.js | 62 +++++++ classes/Configuration Types/String.js | 81 +++++++++ package.json | 2 +- 7 files changed, 252 insertions(+), 215 deletions(-) create mode 100644 classes/Configuration Types/Array.js create mode 100644 classes/Configuration Types/Boolean.js create mode 100644 classes/Configuration Types/Number.js create mode 100644 classes/Configuration Types/String.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fabbd41..c9fa810a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Confs.js uses new configuration system now +- Configuration now split into smaller parts as requested. + ### Removed - Old Configuration System diff --git a/classes/Config.js b/classes/Config.js index 7b4a2261..1ee90f08 100644 --- a/classes/Config.js +++ b/classes/Config.js @@ -1,225 +1,16 @@ /* eslint-disable no-restricted-syntax, no-underscore-dangle, no-unused-vars */ const fs = require("fs-extra-promise"); const path = require("path"); +const ArrayConfig = require("./Configuration Types/Array.js"); +const BooleanConfig = require("./Configuration Types/Boolean.js"); +const NumberConfig = require("./Configuration Types/Number.js"); +const StringConfig = require("./Configuration Types/String.js"); const guildConfs = new Map(); let dataDir = ""; const defaultFile = "default.json"; let defaultConf = {}; -/** The starting point for creating a String configuration key. */ -class StringConfig { - /** - * @param {Config} conf The guild configuration obtained from the guildConfs map. - * @param {Object} data The data you want to append to this String configuration key. - * @returns {StringConfig} - */ - constructor(conf, data) { - if (typeof data !== "string") this.data = ""; - else this.data = data; - this.type = "String"; - if (data.possibles) this.possibles = data.possibles; - else this.possibles = []; - Object.defineProperty(this, "_id", { value: conf._id }); - return this; - } - - /** - * Sets the value of a string configurations possibles. This takes into account the list of acceptable answers from the possibles array. - * @param {string} value The value you want to set this key to. - * @returns {StringConfig} - */ - set(value) { - if (!value) return "Please supply a value to set."; - if (this.possibles.length !== 0 && !this.possibles.includes(value)) return `That is not a valid option. Valid options: ${this.possibles.join(", ")}`; - this.data = value.toString(); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - - /** - * Adds a value(s) to list of acceptable answers for this key. Accepts one item or an array of items. - * @param {string|array} value The value(s) you want to add to this key. - * @returns {StringConfig} - */ - add(value) { - if (!value) return "Please supply a value to add to the possibles array."; - if (value instanceof Array) { - value.forEach((val) => { - if (this.possibles.includes(val)) return `The value ${val} is already in ${this.possibles}.`; - return this.possibles.push(val); - }); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - if (this.possibles.includes(value)) return `The value ${value} is already in ${this.possibles}.`; - this.possibles.push(value); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - - /** - * Deletes a value(s) from the string configurations possibles. Accepts one item or an array of items. - * @param {string|array} value The value(s) you want to delete from this key. - * @returns {StringConfig} - */ - del(value) { - if (!value) return "Please supply a value to add to the possibles array"; - if (value instanceof Array) { - value.forEach((val) => { - if (!this.possibles.includes(val)) return `The value ${value} is not in ${this.possibles}.`; - return this.possibles.splice(this.possibles.indexOf(val), 1); - }); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - if (!this.possibles.includes(value)) return `The value ${value} is not in ${this.possibles}.`; - this.possibles.splice(this.possibles.indexOf(value), 1); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } -} - -/** The starting point for creating an Array Configuration key. */ -class ArrayConfig { - /** - * @param {Config} conf The guild configuration obtained from the guildConfs map. - * @param {object} data The data you want to append to this Array configuration key. - * @returns {ArrayConfig} - */ - constructor(conf, data) { - if (!(data instanceof Array)) this.data = []; - else this.data = data; - this.type = "Array"; - Object.defineProperty(this, "_id", { value: conf._id }); - return this; - } - - /** - * Adds a value(s) to the array. Accepts a single value or an array of values. - * @param {string|array} value The value(s) to add to the array. - * @returns {ArrayConfig} - */ - add(value) { - if (!value) return "Please supply a value to add to the array."; - if (value instanceof Array) { - value.forEach((val) => { - if (!this.data.includes(val)) return "That value is not in the array."; - return this.data.splice(this.data.indexOf(value), 1); - }); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - if (this.data.includes(value)) return "That value is already in the array."; - this.data.push(value); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - - /** - * Deletes a value(s) from the array. Accepts a single value or an array of values. - * @param {string|array} value The value(s) to delete from the array. - * @returns {ArrayConfig} - */ - del(value) { - if (!value) return "Please supply a value to delete from the array"; - if (value instanceof Array) { - value.forEach((val) => { - if (!this.data.includes(val)) return "That value is not in the array."; - return this.data.splice(this.data.indexOf(value), 1); - }); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - if (!this.data.includes(value)) return "That value is not in the array."; - this.data.splice(this.data.indexOf(value), 1); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } -} - -/** The starting point for creating a Boolean configuration key. */ -class BooleanConfig { - /** - * @param {Config} conf A guilds configuration obtained from the guildConfs map. - * @param {object} data The data you want to append to this boolean key. - * @returns {BooleanConfig} - */ - constructor(conf, data) { - if (typeof data !== "boolean") this.data = false; - else this.data = data; - this.type = "Boolean"; - Object.defineProperty(this, "_id", { value: conf._id }); - return this; - } - - /** - * Toggles a boolean statement for the boolean key. - * @returns {BooleanConfig} - */ - toggle() { - if (this.data === true) this.data = false; - else this.data = true; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } -} - -/** The starting point for creating a Number configuration key. */ -class NumberConfig { - /** - * @param {Config} conf A guilds configuration obtained from the guildConfs map. - * @param {object} data The data you want to append to this number key. - * @returns {NumberConfig} - */ - constructor(conf, data) { - if (typeof data !== "number") this.data = 0; - else this.data = data; - if (data.min) this.min = data.min; - if (data.max) this.max = data.max; - this.type = "Number"; - Object.defineProperty(this, "_id", { value: conf._id }); - return this; - } - - /** - * Sets the value for a number key, according to the minimum and maximum values if they apply. - * @param {number} value The value you want to set the key as. - * @returns {NumberConfig} - */ - set(value) { - if (!value) return `Error, value is ${value}. Please supply a value to set.`; - if (this.min && parseInt(value) < this.min) return `Error while setting the value. ${value} is less than ${this.min}`; - if (this.max && parseInt(value) > this.max) return `Error while setting the value. ${value} is more than ${this.max}`; - this.data = value; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - - /** - * Sets the minimum value a number key can be. - * @param {number} value The value you want to set the minimum as. - * @returns {NumberConfig} - */ - setMin(value) { - this.min = value; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - - /** - * Sets the maximum value a number key can bey. - * @param {number} value The value you want to set the maximum as. - * @returns {NumberConfig} - */ - setMax(value) { - this.max = value; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); - return this; - } - -} - /** * The starting point for creating a Guild configuration */ @@ -279,7 +70,6 @@ class Config { return this; } - /** * Allows you to add a key to a guild configuration. Note: This should never be called * directly as it could cause unwanted side effects. @@ -596,3 +386,4 @@ class Config { module.exports = Config; module.exports.guildConfs = guildConfs; +module.exports.dataDir = dataDir; diff --git a/classes/Configuration Types/Array.js b/classes/Configuration Types/Array.js new file mode 100644 index 00000000..86253de4 --- /dev/null +++ b/classes/Configuration Types/Array.js @@ -0,0 +1,66 @@ +/* eslint-disable no-restricted-syntax, no-underscore-dangle, no-unused-vars */ + +const fs = require("fs-extra-promise"); +const path = require("path"); +const dataDir = require("../Config.js").dataDir; +const guildConfs = require("../Config.js").guildConfs; + +/** The starting point for creating an Array Configuration key. */ +class ArrayConfig { + /** + * @param {Config} conf The guild configuration obtained from the guildConfs map. + * @param {object} data The data you want to append to this Array configuration key. + * @returns {ArrayConfig} + */ + constructor(conf, data) { + if (!(data instanceof Array)) this.data = []; + else this.data = data; + this.type = "Array"; + Object.defineProperty(this, "_id", { value: conf._id }); + return this; + } + + /** + * Adds a value(s) to the array. Accepts a single value or an array of values. + * @param {string|array} value The value(s) to add to the array. + * @returns {ArrayConfig} + */ + add(value) { + if (!value) return "Please supply a value to add to the array."; + if (value instanceof Array) { + value.forEach((val) => { + if (!this.data.includes(val)) return "That value is not in the array."; + return this.data.splice(this.data.indexOf(value), 1); + }); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + if (this.data.includes(value)) return "That value is already in the array."; + this.data.push(value); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Deletes a value(s) from the array. Accepts a single value or an array of values. + * @param {string|array} value The value(s) to delete from the array. + * @returns {ArrayConfig} + */ + del(value) { + if (!value) return "Please supply a value to delete from the array"; + if (value instanceof Array) { + value.forEach((val) => { + if (!this.data.includes(val)) return "That value is not in the array."; + return this.data.splice(this.data.indexOf(value), 1); + }); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + if (!this.data.includes(value)) return "That value is not in the array."; + this.data.splice(this.data.indexOf(value), 1); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } +} + +module.exports = ArrayConfig; diff --git a/classes/Configuration Types/Boolean.js b/classes/Configuration Types/Boolean.js new file mode 100644 index 00000000..7b5fce48 --- /dev/null +++ b/classes/Configuration Types/Boolean.js @@ -0,0 +1,35 @@ +/* eslint-disable no-restricted-syntax, no-underscore-dangle, no-unused-vars */ + +const fs = require("fs-extra-promise"); +const path = require("path"); +const dataDir = require("../Config.js").dataDir; +const guildConfs = require("../Config.js").guildConfs; + +/** The starting point for creating a Boolean configuration key. */ +class BooleanConfig { + /** + * @param {Config} conf A guilds configuration obtained from the guildConfs map. + * @param {object} data The data you want to append to this boolean key. + * @returns {BooleanConfig} + */ + constructor(conf, data) { + if (typeof data !== "boolean") this.data = false; + else this.data = data; + this.type = "Boolean"; + Object.defineProperty(this, "_id", { value: conf._id }); + return this; + } + + /** + * Toggles a boolean statement for the boolean key. + * @returns {BooleanConfig} + */ + toggle() { + if (this.data === true) this.data = false; + else this.data = true; + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } +} + +module.exports = BooleanConfig; diff --git a/classes/Configuration Types/Number.js b/classes/Configuration Types/Number.js new file mode 100644 index 00000000..22cbad05 --- /dev/null +++ b/classes/Configuration Types/Number.js @@ -0,0 +1,62 @@ +/* eslint-disable no-restricted-syntax, no-underscore-dangle, no-unused-vars */ + +const fs = require("fs-extra-promise"); +const path = require("path"); +const dataDir = require("../Config.js").dataDir; +const guildConfs = require("../Config.js").guildConfs; + +/** The starting point for creating a Number configuration key. */ +class NumberConfig { + /** + * @param {Config} conf A guilds configuration obtained from the guildConfs map. + * @param {object} data The data you want to append to this number key. + * @returns {NumberConfig} + */ + constructor(conf, data) { + if (typeof data !== "number") this.data = 0; + else this.data = data; + if (data.min) this.min = data.min; + if (data.max) this.max = data.max; + this.type = "Number"; + Object.defineProperty(this, "_id", { value: conf._id }); + return this; + } + + /** + * Sets the value for a number key, according to the minimum and maximum values if they apply. + * @param {number} value The value you want to set the key as. + * @returns {NumberConfig} + */ + set(value) { + if (!value) return `Error, value is ${value}. Please supply a value to set.`; + if (this.min && parseInt(value) < this.min) return `Error while setting the value. ${value} is less than ${this.min}`; + if (this.max && parseInt(value) > this.max) return `Error while setting the value. ${value} is more than ${this.max}`; + this.data = value; + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Sets the minimum value a number key can be. + * @param {number} value The value you want to set the minimum as. + * @returns {NumberConfig} + */ + setMin(value) { + this.min = value; + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Sets the maximum value a number key can bey. + * @param {number} value The value you want to set the maximum as. + * @returns {NumberConfig} + */ + setMax(value) { + this.max = value; + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } +} + +module.exports = NumberConfig; diff --git a/classes/Configuration Types/String.js b/classes/Configuration Types/String.js new file mode 100644 index 00000000..54e78c04 --- /dev/null +++ b/classes/Configuration Types/String.js @@ -0,0 +1,81 @@ +/* eslint-disable no-restricted-syntax, no-underscore-dangle, no-unused-vars */ + +const fs = require("fs-extra-promise"); +const path = require("path"); +const dataDir = require("../Config.js").dataDir; +const guildConfs = require("../Config.js").guildConfs; + +/** The starting point for creating a String configuration key. */ +class StringConfig { + /** + * @param {Config} conf The guild configuration obtained from the guildConfs map. + * @param {Object} data The data you want to append to this String configuration key. + * @returns {StringConfig} + */ + constructor(conf, data) { + if (typeof data !== "string") this.data = ""; + else this.data = data; + this.type = "String"; + if (data.possibles) this.possibles = data.possibles; + else this.possibles = []; + Object.defineProperty(this, "_id", { value: conf._id }); + return this; + } + + /** + * Sets the value of a string configurations possibles. This takes into account the list of acceptable answers from the possibles array. + * @param {string} value The value you want to set this key to. + * @returns {StringConfig} + */ + set(value) { + if (!value) return "Please supply a value to set."; + if (this.possibles.length !== 0 && !this.possibles.includes(value)) return `That is not a valid option. Valid options: ${this.possibles.join(", ")}`; + this.data = value.toString(); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Adds a value(s) to list of acceptable answers for this key. Accepts one item or an array of items. + * @param {string|array} value The value(s) you want to add to this key. + * @returns {StringConfig} + */ + add(value) { + if (!value) return "Please supply a value to add to the possibles array."; + if (value instanceof Array) { + value.forEach((val) => { + if (this.possibles.includes(val)) return `The value ${val} is already in ${this.possibles}.`; + return this.possibles.push(val); + }); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + if (this.possibles.includes(value)) return `The value ${value} is already in ${this.possibles}.`; + this.possibles.push(value); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + + /** + * Deletes a value(s) from the string configurations possibles. Accepts one item or an array of items. + * @param {string|array} value The value(s) you want to delete from this key. + * @returns {StringConfig} + */ + del(value) { + if (!value) return "Please supply a value to add to the possibles array"; + if (value instanceof Array) { + value.forEach((val) => { + if (!this.possibles.includes(val)) return `The value ${value} is not in ${this.possibles}.`; + return this.possibles.splice(this.possibles.indexOf(val), 1); + }); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } + if (!this.possibles.includes(value)) return `The value ${value} is not in ${this.possibles}.`; + this.possibles.splice(this.possibles.indexOf(value), 1); + fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + return this; + } +} + +module.exports = StringConfig; diff --git a/package.json b/package.json index 1709dd98..6aa97eed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.13.0", + "version": "0.13.1", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 2dd3f696c46d6f1a0e228fa4bf3494b43fbd0236 Mon Sep 17 00:00:00 2001 From: Faith Date: Fri, 20 Jan 2017 03:01:15 +0100 Subject: [PATCH 09/56] [FEATURE] New Argument Prompting System (#111) * New Argument Prompting System * [PR] Changelog && Patch Bump * [PR] Actual Patch Bump * [PR] loadCommands fix * [PR] Changelog - loadCommands * [PR] Fix for My Idiotic Mistake * [PR] Fix for My Idiotic Mistake * [PR] Configuration Client Option to Disable Prompts * [PR] Abort is now case-insensitive * [PR] Fix for DMs * [PR] Changelog for DMs * [PR] Translation Fixed for DMs * [PR] Config.prefix now accepts a String or an Array of Strings * [PR] Reload.js Bugfix && Changelog Update * [PR] Prefix.length error fixed --- CHANGELOG.md | 6 ++ app.js | 14 +++- classes/Config.js | 26 ++++--- functions/awaitMessage.js | 27 +++++++ functions/loadCommands.js | 17 +++-- functions/reload.js | 3 +- functions/runCommandInhibitors.js | 4 +- inhibitors/permissions.js | 4 +- inhibitors/usage.js | 122 ++++++++++++++++++++++++++++-- package.json | 2 +- 10 files changed, 195 insertions(+), 30 deletions(-) create mode 100644 functions/awaitMessage.js diff --git a/CHANGELOG.md b/CHANGELOG.md index c9fa810a..14879d3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added - New Beta Configuration (Needs heavy testing) +- New Argument Prompting for Commands ### Changed - Confs.js uses new configuration system now - Configuration now split into smaller parts as requested. +### Fixed +- loadCommands no longer counts/loads "Ghost" commands. +- DMs throwing errors with new Config System && permLevel +- Fixed Reload not erroring on new commands that aren't found + ### Removed - Old Configuration System diff --git a/app.js b/app.js index 9127a2fd..d018c2a6 100644 --- a/app.js +++ b/app.js @@ -61,8 +61,17 @@ exports.start = (config) => { msg.guildConf = conf; client.i18n.use(conf.lang); client.funcs.runMessageMonitors(client, msg).catch(reason => msg.channel.sendMessage(reason).catch(console.error)); - if (!msg.content.startsWith(conf.prefix) && (client.config.prefixMention && !client.config.prefixMention.test(msg.content))) return; - let prefixLength = conf.prefix.length; + let thisPrefix; + if (conf.prefix instanceof Array) { + conf.prefix.forEach((prefix) => { + if (msg.content.startsWith(prefix)) thisPrefix = prefix; + }); + } else if (msg.content.startsWith(conf.prefix)) { + thisPrefix = conf.prefix; + } + if (!thisPrefix) return; + if (client.config.prefixMention && !client.config.prefixMention.test(msg.content)) return; + let prefixLength = thisPrefix.length; if (client.config.prefixMention && client.config.prefixMention.test(msg.content)) prefixLength = client.config.prefixMention.exec(msg.content)[0].length + 1; const command = msg.content.slice(prefixLength).split(" ")[0].toLowerCase(); const suffix = msg.content.slice(prefixLength).split(" ").slice(1).join(" "); @@ -102,5 +111,6 @@ exports.start = (config) => { }; process.on("unhandledRejection", (err) => { + if (!err) return; console.error(`Uncaught Promise Error: \n${err.stack}`); }); diff --git a/classes/Config.js b/classes/Config.js index 1ee90f08..28506afa 100644 --- a/classes/Config.js +++ b/classes/Config.js @@ -144,17 +144,21 @@ class Config { * //Example of what this returns * { prefix: '--', disabledCommands: [], modRole: 'Mods', adminRole: 'Devs', lang: 'en' } */ - static get(guild) { - const conf = {}; - if (guild && guildConfs.has(guild.id)) { - const guildConf = guildConfs.get(guild.id); - for (const key in guildConf) { - if (guildConf[key]) conf[key] = guildConf[key].data; - else conf[key] = defaultConf[key].data; - } - } - return conf; - } + static get(guild) { + const conf = {}; + if (guild && guildConfs.has(guild.id)) { + const guildConf = guildConfs.get(guild.id); + for (const key in guildConf) { + if (guildConf[key]) conf[key] = guildConf[key].data; + else conf[key] = defaultConf[key].data; + } + } else { + for (const key in defaultConf) { + conf[key] = defaultConf[key].data; + } + } + return conf; + } /** * Set the default value for a key in the default configuration. diff --git a/functions/awaitMessage.js b/functions/awaitMessage.js new file mode 100644 index 00000000..b0c76a98 --- /dev/null +++ b/functions/awaitMessage.js @@ -0,0 +1,27 @@ +module.exports = (client, msg, cmd, args, error) => { + msg.channel.sendMessage(`<@!${msg.member.id}> | **${error}** | You have **30** seconds to respond to this prompt with a valid argument. Type **"ABORT"** to abort this prompt.`).then((message) => { + msg.channel.awaitMessages(response => response.member.id === msg.author.id && response.id !== message.id, { + max: 1, + time: 30000, + errors: ["time"], + }) + .then((param) => { + message.delete(); + if (param.first().content.toLowerCase() === "abort") return; + args.push(param.first().content); + client.funcs.runCommandInhibitors(client, msg, cmd, args) + .then((params) => { + cmd.run(client, msg, params); + }); + }) + .catch((reason) => { + if (reason) { + if (reason.stack) client.funcs.log(reason.stack, "error"); + msg.channel.sendCode("", reason).catch(console.error); + } + }) + .catch(() => { + msg.channel.sendMessage("No message was sent before the 30 second mark. Aborting command."); + }); + }); +}; diff --git a/functions/loadCommands.js b/functions/loadCommands.js index 154eba31..46e31aa7 100644 --- a/functions/loadCommands.js +++ b/functions/loadCommands.js @@ -51,10 +51,17 @@ module.exports = (client) => { client.commands.clear(); client.aliases.clear(); const count = [0, 0]; - loadCommands(client, client.coreBaseDir, count).then((counts) => { - loadCommands(client, client.clientBaseDir, counts).then((countss) => { - const [c, a] = countss; - client.funcs.log(`Loaded ${c} commands, with ${a} aliases.`); + if (client.coreBaseDir !== client.clientBaseDir) { + loadCommands(client, client.coreBaseDir, count).then((counts) => { + loadCommands(client, client.clientBaseDir, counts).then((countss) => { + const [c, a] = countss; + client.funcs.log(`Loaded ${c} commands, with ${a} aliases.`); + }); }); - }); + } else { + loadCommands(client, client.coreBaseDir, count).then((counts) => { + const [c, a] = counts; + client.funcs.log(`Loaded ${c} commands with ${a} aliases.`); + }); + } }; diff --git a/functions/reload.js b/functions/reload.js index c2061b70..4b101613 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -261,9 +261,10 @@ exports.command = (client, dir, commandName) => new Promise((resolve, reject) => .catch((e) => { reject(e); }); + resolve(`Succesfully loaded a new command called ${commandName}`); }); }); - resolve(`Succesully loaded a new command called ${commandName}`); + reject(`Could not locate a new **${command}** in ${dir}`); } else { client.funcs.loadSingleCommand(client, command, true) .then(() => { diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index 9fe33f78..38ae70c9 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -1,11 +1,11 @@ -module.exports = (client, msg, cmd, selective = false) => new Promise((resolve, reject) => { +module.exports = (client, msg, cmd, args, selective = false) => new Promise((resolve, reject) => { const mps = [true]; let i = 1; let usage; client.commandInhibitors.forEach((mProc, key) => { if (key === "usage") usage = i; if (!mProc.conf.spamProtection || !selective) { - mps.push(mProc.run(client, msg, cmd)); + mps.push(mProc.run(client, msg, cmd, args)); } i++; }); diff --git a/inhibitors/permissions.js b/inhibitors/permissions.js index 3d670e1d..6c1b3d8a 100644 --- a/inhibitors/permissions.js +++ b/inhibitors/permissions.js @@ -6,7 +6,9 @@ exports.conf = { exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { client.funcs.permissionLevel(client, msg.author, msg.guild) .then((permlvl) => { - msg.member.permLevel = permlvl; + if (msg.guild) { + msg.member.permLevel = permlvl; + } if (permlvl >= cmd.conf.permLevel) { resolve(); } else { diff --git a/inhibitors/usage.js b/inhibitors/usage.js index f336766d..f96895c1 100644 --- a/inhibitors/usage.js +++ b/inhibitors/usage.js @@ -5,20 +5,21 @@ exports.conf = { spamProtection: true, }; -exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { +exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reject) => { const usage = client.funcs.parseUsage(cmd.help.usage); let prefixLength = msg.guildConf.prefix.length; if (client.config.prefixMention.test(msg.content)) prefixLength = client.config.prefixMention.exec(msg.content)[0].length + 1; - let args = msg.content.slice(prefixLength) + if (args === undefined) { + args = msg.content.slice(prefixLength) .split(" ") .slice(1) .join(" ") .split(cmd.help.usageDelim !== "" ? cmd.help.usageDelim : null); + } if (args[0] === "") args = []; let currentUsage; let repeat = false; if (usage.length === 0) return resolve(); - (function validateArgs(i) { if (i >= usage.length && i >= args.length) { return resolve(args); @@ -34,14 +35,17 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { } if (currentUsage.type === "optional" && (args[i] === undefined || args[i] === "")) { // Handle if args length < required usage length if (usage.slice(i).some(u => u.type === "required")) { + if (client.config.cmdPrompt) return reject(client.funcs.awaitMessage(client, msg, cmd, args, "Missing one or more required arguments after end of input.")); return reject("Missing one or more required arguments after end of input."); } return resolve(args); } else if (currentUsage.type === "required" && args[i] === undefined) { + if (client.config.cmdPrompt) return reject(client.funcs.awaitMessage(client, msg, cmd, args, currentUsage.possibles.length === 1 ? `${currentUsage.possibles[0].name} is a required argument.` : `Missing a required option: (${currentUsage.possibles.map(p => p.name).join(", ")})`)); return reject(currentUsage.possibles.length === 1 ? `${currentUsage.possibles[0].name} is a required argument.` : `Missing a required option: (${currentUsage.possibles.map(p => p.name).join(", ")})`); } else if (currentUsage.possibles.length === 1) { switch (currentUsage.possibles[0].type) { case "literal": + console.log(args[i]); if (args[i].toLowerCase() === currentUsage.possibles[0].name.toLowerCase()) { args[i] = args[i].toLowerCase(); validateArgs(++i); @@ -49,7 +53,11 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(`Your option did not litterally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")})\nThis is likely caused by a mistake in the usage string.`); + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `Your option did not litterally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`)); + } + return reject(`Your option did not litterally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`); } break; case "msg": @@ -66,6 +74,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid message id.`)); + } return reject(`${currentUsage.possibles[0].name} must be a valid message id.`); } }); @@ -80,6 +92,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid message id.`)); + } return reject(`${currentUsage.possibles[0].name} must be a valid message id.`); } }); @@ -88,6 +104,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid message id.`)); + } return reject(`${currentUsage.possibles[0].name} must be a valid message id.`); } break; @@ -100,6 +120,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a mention or valid user id.`)); + } return reject(`${currentUsage.possibles[0].name} must be a mention or valid user id.`); } break; @@ -115,6 +139,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be true or false.`)); + } return reject(`${currentUsage.possibles[0].name} must be true or false.`); } break; @@ -126,6 +154,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a mention or valid user id.`)); + } return reject(`${currentUsage.possibles[0].name} must be a mention or valid user id.`); } break; @@ -137,6 +169,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a channel tag or valid channel id.`)); + } return reject(`${currentUsage.possibles[0].name} must be a channel tag or valid channel id.`); } break; @@ -148,6 +184,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid guild id.`)); + } return reject(`${currentUsage.possibles[0].name} must be a valid guild id.`); } break; @@ -159,6 +199,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a role mention or role id.`)); + } return reject(`${currentUsage.possibles[0].name} must be a role mention or role id.`); } break; @@ -170,8 +214,16 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else if (currentUsage.possibles[0].min === currentUsage.possibles[0].max) { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min} characters.`)); + } return reject(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min} characters.`); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max} characters.`)); + } return reject(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max} characters.`); } } else { @@ -183,6 +235,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be longer than ${currentUsage.possibles[0].min} characters.`)); + } return reject(`${currentUsage.possibles[0].name} must be longer than ${currentUsage.possibles[0].min} characters.`); } } else { @@ -194,6 +250,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be shorter than ${currentUsage.possibles[0].max} characters.`)); + } return reject(`${currentUsage.possibles[0].name} must be shorter than ${currentUsage.possibles[0].max} characters.`); } } else { @@ -210,6 +270,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be an integer.`)); + } return reject(`${currentUsage.possibles[0].name} must be an integer.`); } } else if (currentUsage.possibles[0].min && currentUsage.possibles[0].max) { @@ -220,12 +284,20 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}\nSo why didn't the dev use a literal?`); + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`)); + } + return reject(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`); } } else if (currentUsage.type === "optional" && !repeat) { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`)); + } return reject(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`); } } else { @@ -238,6 +310,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`)); + } return reject(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`); } } else { @@ -250,6 +326,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`)); + } return reject(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`); } } else { @@ -268,6 +348,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid number.`)); + } return reject(`${currentUsage.possibles[0].name} must be a valid number.`); } } else if (currentUsage.possibles[0].min && currentUsage.possibles[0].max) { @@ -278,12 +362,20 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}\nSo why didn't the dev use a literal?`); + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`)); + } + return reject(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`); } } else if (currentUsage.type === "optional" && !repeat) { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`)); + } return reject(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`); } } else { @@ -296,6 +388,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`)); + } return reject(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`); } } else { @@ -308,6 +404,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`)); + } return reject(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`); } } else { @@ -325,6 +425,10 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { + if (client.config.cmdPrompt) { + args.shift(); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid url.`)); + } return reject(`${currentUsage.possibles[0].name} must be a valid url.`); } } else { @@ -347,7 +451,11 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(`Your option didn't match any of the possibilities: (${currentUsage.possibles.map(possibles => possibles.name).join(", ")})`); + if (client.config.cmdPrompt) { + args.shift(); + reject(client.funcs.awaitMessage(client, msg, cmd, args, `Your option didn't match any of the possibilities: (${currentUsage.possibles.map(possibles => possibles.name).join(", ")})`)); + } + reject(`Your option didn't match any of the possibilities: (${currentUsage.possibles.map(possibles => possibles.name).join(", ")})`); } return; } diff --git a/package.json b/package.json index 6aa97eed..85d214e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.13.1", + "version": "0.13.2", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 7b6d2642566fe088e0ed303eff862ec0ab71da6d Mon Sep 17 00:00:00 2001 From: Faith Date: Fri, 20 Jan 2017 13:49:51 +0100 Subject: [PATCH 10/56] [BUGFIX] Fixed Selfbot Prefix Mentioning Crashing with CMD Prompts (#112) * Fixed Selfbot Prefix Mentioning Crashing with CMD Prompts * [PR] Changelog --- CHANGELOG.md | 1 + app.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14879d3f..f54f7018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - loadCommands no longer counts/loads "Ghost" commands. - DMs throwing errors with new Config System && permLevel - Fixed Reload not erroring on new commands that aren't found +- Fixed Bug on Selfbot mentions introduced with the new Argument Prompts ### Removed - Old Configuration System diff --git a/app.js b/app.js index d018c2a6..179a8077 100644 --- a/app.js +++ b/app.js @@ -65,12 +65,12 @@ exports.start = (config) => { if (conf.prefix instanceof Array) { conf.prefix.forEach((prefix) => { if (msg.content.startsWith(prefix)) thisPrefix = prefix; + else thisPrefix = prefix[0]; }); - } else if (msg.content.startsWith(conf.prefix)) { + } else { thisPrefix = conf.prefix; } - if (!thisPrefix) return; - if (client.config.prefixMention && !client.config.prefixMention.test(msg.content)) return; + if (msg.content.startsWith(thisPrefix) && client.config.prefixMention && !client.config.prefixMention.test(msg.content)) return; let prefixLength = thisPrefix.length; if (client.config.prefixMention && client.config.prefixMention.test(msg.content)) prefixLength = client.config.prefixMention.exec(msg.content)[0].length + 1; const command = msg.content.slice(prefixLength).split(" ")[0].toLowerCase(); From aa6e44fa7406b756a93bf33581eb4385da44a7a3 Mon Sep 17 00:00:00 2001 From: Motion Date: Fri, 20 Jan 2017 18:42:42 -0600 Subject: [PATCH 11/56] Motiond3v/helpdm (#114) * Help command is now a Direct Message * Help command is now a Direct Message * Update help.js * Update package.json --- CHANGELOG.md | 1 + commands/System/help.js | 5 +++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f54f7018..f19a2be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Confs.js uses new configuration system now - Configuration now split into smaller parts as requested. +- Help command is now a Direct Message. ### Fixed - loadCommands no longer counts/loads "Ghost" commands. diff --git a/commands/System/help.js b/commands/System/help.js index 1a1e4b25..8b95aefc 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -11,11 +11,12 @@ exports.run = (client, msg, [cmd]) => { } helpMessage.push("```\n\u200b"); } - msg.channel.sendMessage(helpMessage, { split: { char: "\u200b" } }).catch((e) => { console.error(e); }); + msg.author.sendMessage(helpMessage, { split: { char: "\u200b" } }).catch((e) => { console.error(e); }); + msg.reply('Commands have been sent to your DMs.'); }); } else if (client.commands.has(cmd)) { cmd = client.commands.get(cmd); - msg.channel.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}`); + msg.author.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}`); } }; diff --git a/package.json b/package.json index 85d214e0..b574aee3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.13.2", + "version": "0.13.3", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 644e6c7452267a7faea73b6cef61c8b19d2c6ba9 Mon Sep 17 00:00:00 2001 From: Faith Date: Sat, 21 Jan 2017 01:56:19 +0100 Subject: [PATCH 12/56] Fixed Bugs and Patch Bumped (#113) * Fixed Bugs and Patch Bumped * Update package.json --- CHANGELOG.md | 1 + app.js | 2 +- commands/System/help.js | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f19a2be9..a964dc12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - DMs throwing errors with new Config System && permLevel - Fixed Reload not erroring on new commands that aren't found - Fixed Bug on Selfbot mentions introduced with the new Argument Prompts +- Fixed Bug on Help not showing all commands with new Argument System ### Removed - Old Configuration System diff --git a/app.js b/app.js index 179a8077..4bcd0924 100644 --- a/app.js +++ b/app.js @@ -70,7 +70,7 @@ exports.start = (config) => { } else { thisPrefix = conf.prefix; } - if (msg.content.startsWith(thisPrefix) && client.config.prefixMention && !client.config.prefixMention.test(msg.content)) return; + if (!msg.content.startsWith(thisPrefix) && client.config.prefixMention && !client.config.prefixMention.test(msg.content)) return; let prefixLength = thisPrefix.length; if (client.config.prefixMention && client.config.prefixMention.test(msg.content)) prefixLength = client.config.prefixMention.exec(msg.content)[0].length + 1; const command = msg.content.slice(prefixLength).split(" ")[0].toLowerCase(); diff --git a/commands/System/help.js b/commands/System/help.js index 8b95aefc..19228979 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -45,7 +45,7 @@ const buildHelp = (client, msg) => new Promise((resolve) => { client.commands.forEach((command) => { mps.push(new Promise((res) => { - client.funcs.runCommandInhibitors(client, msg, command, true) + client.funcs.runCommandInhibitors(client, msg, command, [], true) .then(() => { const cat = command.help.category; const subcat = command.help.subCategory; diff --git a/package.json b/package.json index b574aee3..713a5502 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.13.3", + "version": "0.13.4", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 71a414e4cb6e66ad02113f74a32e8904bf734a79 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Sat, 21 Jan 2017 17:27:15 +1100 Subject: [PATCH 13/56] Fixed selfbot's not being able to DM themselves (#115) [BUG-FIX] Fixed selfbot's not being able to DM themselves --- commands/System/help.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/commands/System/help.js b/commands/System/help.js index 19228979..05b4a104 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -11,12 +11,21 @@ exports.run = (client, msg, [cmd]) => { } helpMessage.push("```\n\u200b"); } - msg.author.sendMessage(helpMessage, { split: { char: "\u200b" } }).catch((e) => { console.error(e); }); - msg.reply('Commands have been sent to your DMs.'); + if (!client.config.selfbot) { + msg.author.sendMessage(helpMessage, { split: { char: "\u200b" } }).catch((e) => { console.error(e); }); + msg.reply("Commands have been sent to your DMs."); + } else { + msg.channel.sendMessage(helpMessage, { split: { char: "\u200b" } }) + .catch((e) => { console.error(e); }); + } }); } else if (client.commands.has(cmd)) { cmd = client.commands.get(cmd); - msg.author.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}`); + if (!client.config.selfbot) { + msg.author.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}`); + } else { + msg.channel.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}`); + } } }; From b3daef66f946ba133efa1ce34f653b0d88bd19eb Mon Sep 17 00:00:00 2001 From: Faith Date: Sat, 21 Jan 2017 15:38:13 +0100 Subject: [PATCH 14/56] [BUGFIXES] Bugfixes (#116) * Bugfix && Changelog * [PR] Reload.js Bug Fix * [PR] Changelog * [PR] Reload Clarification/Fix * [PR] Patch Bump * [PR] Cyber is a bish --- CHANGELOG.md | 2 ++ functions/awaitMessage.js | 49 +++++++++++++++++++++------------------ functions/reload.js | 10 ++++---- package.json | 2 +- 4 files changed, 35 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a964dc12..22c9fb01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed Reload not erroring on new commands that aren't found - Fixed Bug on Selfbot mentions introduced with the new Argument Prompts - Fixed Bug on Help not showing all commands with new Argument System +- Fixed another bug introduced with the new Argument System where Permissions weren't finalized before Prompts +- Fixed Bug within reload.js that prevented new commands from being loaded ### Removed - Old Configuration System diff --git a/functions/awaitMessage.js b/functions/awaitMessage.js index b0c76a98..1ba19c3a 100644 --- a/functions/awaitMessage.js +++ b/functions/awaitMessage.js @@ -1,27 +1,32 @@ module.exports = (client, msg, cmd, args, error) => { - msg.channel.sendMessage(`<@!${msg.member.id}> | **${error}** | You have **30** seconds to respond to this prompt with a valid argument. Type **"ABORT"** to abort this prompt.`).then((message) => { - msg.channel.awaitMessages(response => response.member.id === msg.author.id && response.id !== message.id, { - max: 1, - time: 30000, - errors: ["time"], - }) - .then((param) => { - message.delete(); - if (param.first().content.toLowerCase() === "abort") return; - args.push(param.first().content); - client.funcs.runCommandInhibitors(client, msg, cmd, args) - .then((params) => { - cmd.run(client, msg, params); + client.funcs.permissionLevel(client, msg.author, msg.guild).then((permLvl) => { + if (cmd.conf.permLevel > permLvl) return msg.channel.sendCode("", "You do not have enough permission to use this command."); + msg.channel.sendMessage(`<@!${msg.member.id}> | **${error}** | You have **30** seconds to respond to this prompt with a valid argument. Type **"ABORT"** to abort this prompt.`).then((message) => { + msg.channel.awaitMessages(response => response.member.id === msg.author.id && response.id !== message.id, { + max: 1, + time: 30000, + errors: ["time"], + }) + .then((param) => { + message.delete(); + if (param.first().content.toLowerCase() === "abort") return; + args.push(param.first().content); + client.funcs.runCommandInhibitors(client, msg, cmd, args) + .then((params) => { + cmd.run(client, msg, params); + }); + }) + .catch((reason) => { + if (reason) { + if (reason.stack) client.funcs.log(reason.stack, "error"); + msg.channel.sendCode("", reason).catch(console.error); + } + }) + .catch(() => { + msg.channel.sendMessage("No message was sent before the 30 second mark. Aborting command."); }); - }) - .catch((reason) => { - if (reason) { - if (reason.stack) client.funcs.log(reason.stack, "error"); - msg.channel.sendCode("", reason).catch(console.error); - } - }) - .catch(() => { - msg.channel.sendMessage("No message was sent before the 30 second mark. Aborting command."); }); + }).catch((err) => { + console.error(err); }); }; diff --git a/functions/reload.js b/functions/reload.js index 4b101613..2c59fec4 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -255,20 +255,20 @@ exports.command = (client, dir, commandName) => new Promise((resolve, reject) => if (!command) { client.funcs.getFileListing(client, client.coreBaseDir, "commands") .then((files) => { - const newCommands = files.filter(f => f.name === command); + const newCommands = files.filter(f => f.name === commandName); newCommands.forEach((file) => { - client.funcs.loadSingleCommand(client, command, false, `${file.path}${path.sep}${file.base}`) + client.funcs.loadSingleCommand(client, commandName, false, `${file.path}${path.sep}${file.base}`) .catch((e) => { reject(e); }); - resolve(`Succesfully loaded a new command called ${commandName}`); + resolve(`Successfully loaded a new command called ${commandName}`); }); + reject(`Couldn't find a new command called ${commandName}`); }); - reject(`Could not locate a new **${command}** in ${dir}`); } else { client.funcs.loadSingleCommand(client, command, true) .then(() => { - resolve(`Succesfully reloaded the command ${commandName}`); + resolve(`Successfully reloaded the command ${commandName}`); }) .catch((e) => { reject(e); diff --git a/package.json b/package.json index 713a5502..f381ccc8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.13.4", + "version": "0.13.5", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From e02e4fde28d9f73ac8a743e7460248e1038e0285 Mon Sep 17 00:00:00 2001 From: Faith Date: Sat, 21 Jan 2017 23:20:55 +0100 Subject: [PATCH 15/56] [NODE] Async/Await all the things (#117) * Async/Await all the things * Re-enabled disconnect event * [PR] Fixed all commands that didnt have a usage * [PR] New Initialize Function * [PR] Fixed Wrong Shorthands * [PR] Added Initialize Function to Changelog --- CHANGELOG.md | 3 + app.js | 38 ++- commands/System/invite.js | 7 +- commands/System/ping.js | 8 +- commands/System/reload.js | 66 ++--- commands/System/stats.js | 2 +- functions/awaitMessage.js | 51 ++-- functions/getFileListing.js | 18 +- functions/initialize.js | 18 ++ functions/loadCommandInhibitors.js | 77 ++--- functions/loadCommands.js | 57 +--- functions/loadDataProviders.js | 58 ---- functions/loadEvents.js | 44 +-- functions/loadFunctions.js | 80 ++---- functions/loadMessageMonitors.js | 75 ++--- functions/loadProviders.js | 36 +++ functions/loadSingleCommand.js | 8 +- functions/permissionLevel.js | 33 ++- functions/reload.js | 441 ++++++++++++++--------------- functions/runCommandInhibitors.js | 4 +- functions/runMessageMonitors.js | 13 +- inhibitors/permissions.js | 22 +- inhibitors/usage.js | 1 - package.json | 4 +- 24 files changed, 485 insertions(+), 679 deletions(-) create mode 100644 functions/initialize.js delete mode 100644 functions/loadDataProviders.js create mode 100644 functions/loadProviders.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 22c9fb01..2c64c406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - New Beta Configuration (Needs heavy testing) - New Argument Prompting for Commands +- New Initialize Function to aleviate undefined errors ### Changed - Confs.js uses new configuration system now - Configuration now split into smaller parts as requested. - Help command is now a Direct Message. +- Async/Await for all pieces && app.js +- dataProviders renamed to Providers ### Fixed - loadCommands no longer counts/loads "Ghost" commands. diff --git a/app.js b/app.js index 4bcd0924..d1c683c3 100644 --- a/app.js +++ b/app.js @@ -5,7 +5,7 @@ const Config = require("./classes/Config.js"); const clk = new chalk.constructor({ enabled: true }); -exports.start = (config) => { +exports.start = async (config) => { const client = new Discord.Client(config.clientOptions); client.config = config; @@ -18,7 +18,7 @@ exports.start = (config) => { client.aliases = new Discord.Collection(); client.commandInhibitors = new Discord.Collection(); client.messageMonitors = new Discord.Collection(); - client.dataProviders = new Discord.Collection(); + client.providers = new Discord.Collection(); // Extend Client with Native Discord.js Functions for use in our pieces. client.methods = {}; @@ -33,34 +33,30 @@ exports.start = (config) => { client.configuration = Config; // Load core functions, then everything else - loadFunctions(client).then(() => { - client.funcs.loadDataProviders(client); - client.funcs.loadCommands(client); - client.funcs.loadCommandInhibitors(client); - client.funcs.loadMessageMonitors(client); - client.funcs.loadEvents(client); - client.i18n = client.funcs.loadLocalizations; - client.i18n.init(client); - }); + await loadFunctions(client); + client.funcs.loadProviders(client); + client.funcs.loadCommands(client); + client.funcs.loadCommandInhibitors(client); + client.funcs.loadMessageMonitors(client); + client.funcs.loadEvents(client); + client.i18n = client.funcs.loadLocalizations; + client.i18n.init(client); client.once("ready", () => { client.config.prefixMention = new RegExp(`^<@!?${client.user.id}>`); - Config.initialize(client); - for (const func in client.funcs) { - if (client.funcs[func].init) client.funcs[func].init(client); - } + client.funcs.initialize(client); }); client.on("error", e => client.funcs.log(e, "error")); client.on("warn", w => client.funcs.log(w, "warning")); client.on("disconnect", e => client.funcs.log(e, "error")); - client.on("message", (msg) => { + client.on("message", async (msg) => { if (msg.author.bot) return; const conf = Config.get(msg.guild); msg.guildConf = conf; client.i18n.use(conf.lang); - client.funcs.runMessageMonitors(client, msg).catch(reason => msg.channel.sendMessage(reason).catch(console.error)); + await client.funcs.runMessageMonitors(client, msg); let thisPrefix; if (conf.prefix instanceof Array) { conf.prefix.forEach((prefix) => { @@ -93,17 +89,15 @@ exports.start = (config) => { cmd = client.commands.get(client.aliases.get(command)); } if (!cmd) return; - client.funcs.runCommandInhibitors(client, msg, cmd) - .then((params) => { - client.funcs.log(commandLog); - cmd.run(client, msg, params); - }) + const params = await client.funcs.runCommandInhibitors(client, msg, cmd) .catch((reason) => { if (reason) { if (reason.stack) client.funcs.log(reason.stack, "error"); msg.channel.sendCode("", reason).catch(console.error); } }); + client.funcs.log(commandLog); + if (params || cmd.help.usage.length === 0) cmd.run(client, msg, params); }); client.login(client.config.botToken); diff --git a/commands/System/invite.js b/commands/System/invite.js index f4d2c92f..aba0c9f0 100644 --- a/commands/System/invite.js +++ b/commands/System/invite.js @@ -1,6 +1,9 @@ exports.run = (client, msg) => { - client.funcs.log("Join Server Link"); - msg.channel.sendMessage(`To add ${client.user.username} to your discord guild: \n https://discordapp.com/oauth2/authorize?&client_id=${client.config.clientID}&scope=bot&permissions=${client.funcs.botPermissions(client)}\n\`\`\`The above link is generated requesting the minimum permissions required to use every command currently. I know not all permissions are right for every server, so don't be afraid to uncheck any of the boxes. If you try to use a command that requires more permissions than the bot is granted, it will let you know.\`\`\`\nPlease file an issue at if you find any bugs.`); + if (!client.config.selfbot) { + msg.channel.sendMessage(`To add ${client.user.username} to your discord guild: \n https://discordapp.com/oauth2/authorize?&client_id=${client.config.clientID}&scope=bot&permissions=${client.funcs.botPermissions(client)}\n\`\`\`The above link is generated requesting the minimum permissions required to use every command currently. I know not all permissions are right for every server, so don't be afraid to uncheck any of the boxes. If you try to use a command that requires more permissions than the bot is granted, it will let you know.\`\`\`\nPlease file an issue at if you find any bugs.`); + } else { + msg.reply("Why would you need an invite link for a selfbot..."); + } }; exports.help = { diff --git a/commands/System/ping.js b/commands/System/ping.js index 5afd7285..394910a5 100644 --- a/commands/System/ping.js +++ b/commands/System/ping.js @@ -1,8 +1,6 @@ -exports.run = (client, msg) => { - msg.channel.sendMessage("Ping?") - .then((message) => { - message.edit(`Pong! (took: ${message.createdTimestamp - msg.createdTimestamp}ms)`); - }); +exports.run = async (client, msg) => { + const message = await msg.channel.sendMessage("Ping?").catch(err => client.funcs.log(err, "error")); + message.edit(`Pong! (took: ${message.createdTimestamp - msg.createdTimestamp}ms)`); }; exports.conf = { diff --git a/commands/System/reload.js b/commands/System/reload.js index b295aa7c..87857115 100644 --- a/commands/System/reload.js +++ b/commands/System/reload.js @@ -1,64 +1,42 @@ -exports.run = (client, msg, [type, name]) => { +exports.run = async (client, msg, [type, name]) => { + let m; + let message; switch (type) { case "function": - msg.channel.sendMessage(`Attemping to reload function ${name}`).then((m) => { - client.funcs.reload.function(client, client.clientBaseDir, name).then((message) => { - m.edit(`:white_check_mark: ${message}`); - }).catch((e) => { - m.edit(`:x: ${e}`); - }); - }); + m = await msg.channel.sendMessage(`Attemping to reload function ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.function(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; case "inhibitor": - msg.channel.sendMessage(`Attempting to reload inhibitor ${name}`).then((m) => { - client.funcs.reload.inhibitor(client, client.clientBaseDir, name).then((message) => { - m.edit(`:white_check_mark: ${message}`); - }).catch((e) => { - m.edit(`:x: ${e}`); - }); - }); + m = await msg.channel.sendMessage(`Attempting to reload inhibitor ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark ${message}`); break; case "monitor": - msg.channel.sendMessage(`Attempting to reload monitor ${name}`).then((m) => { - client.funcs.reload.monitor(client, client.clientBaseDir, name).then((message) => { - m.edit(`:white_check_mark: ${message}`); - }).catch((e) => { - m.edit(`:x: ${e}`); - }); - }); + m = await msg.channel.sendMessage(`Attempting to reload monitor ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.monitor(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; case "provider": - msg.channel.sendMessage(`Attempting to reload provider ${name}`).then((m) => { - client.funcs.reload.provider(client, client.clientBaseDir, name).then((message) => { - m.edit(`:white_check_mark: ${message}`); - }).catch((e) => { - m.edit(`:x: ${e}`); - }); - }); + m = await msg.channel.sendMessage(`Attempting to reload provider ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.provider(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; case "event": - msg.channel.sendMessage(`Attempting to reload event ${name}`).then((m) => { - client.funcs.reload.event(client, name).then((message) => { - m.edit(`:white_check_mark: ${message}`); - }).catch((e) => { - m.edit(`:x: ${e}`); - }); - }); + m = await msg.channel.sendMessage(`Attempting to reload event ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.event(client, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; case "command": switch (name) { case "all": - client.funcs.loadCommands(client); + await client.funcs.loadCommands(client); msg.channel.sendMessage(":white_check_mark: Reloaded all commands."); break; default: - msg.channel.sendMessage(`Attempting to reload command ${name}`).then((m) => { - client.funcs.reload.command(client, client.clientBaseDir, name).then((message) => { - m.edit(`:white_check_mark: ${message}`); - }).catch((e) => { - m.edit(`:x: ${e}`); - }); - }); + m = await msg.channel.sendMessage(`Attempting to reload command ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.command(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; } break; diff --git a/commands/System/stats.js b/commands/System/stats.js index a62d3d00..d22b2fe3 100644 --- a/commands/System/stats.js +++ b/commands/System/stats.js @@ -12,7 +12,7 @@ • Users :: ${client.users.size.toLocaleString()} • Servers :: ${client.guilds.size.toLocaleString()} • Channels :: ${client.channels.size.toLocaleString()} -• Komada :: ${komada.version} +• Komada :: v${komada.version} • Discord.js :: v${Discord.version}`); }; diff --git a/functions/awaitMessage.js b/functions/awaitMessage.js index 1ba19c3a..0c2c674d 100644 --- a/functions/awaitMessage.js +++ b/functions/awaitMessage.js @@ -1,32 +1,23 @@ -module.exports = (client, msg, cmd, args, error) => { - client.funcs.permissionLevel(client, msg.author, msg.guild).then((permLvl) => { - if (cmd.conf.permLevel > permLvl) return msg.channel.sendCode("", "You do not have enough permission to use this command."); - msg.channel.sendMessage(`<@!${msg.member.id}> | **${error}** | You have **30** seconds to respond to this prompt with a valid argument. Type **"ABORT"** to abort this prompt.`).then((message) => { - msg.channel.awaitMessages(response => response.member.id === msg.author.id && response.id !== message.id, { - max: 1, - time: 30000, - errors: ["time"], - }) - .then((param) => { - message.delete(); - if (param.first().content.toLowerCase() === "abort") return; - args.push(param.first().content); - client.funcs.runCommandInhibitors(client, msg, cmd, args) - .then((params) => { - cmd.run(client, msg, params); - }); - }) - .catch((reason) => { - if (reason) { - if (reason.stack) client.funcs.log(reason.stack, "error"); - msg.channel.sendCode("", reason).catch(console.error); - } - }) - .catch(() => { - msg.channel.sendMessage("No message was sent before the 30 second mark. Aborting command."); - }); - }); - }).catch((err) => { - console.error(err); +const options = { + max: 1, + time: 30000, + errors: ["time"], +}; + +module.exports = async (client, msg, cmd, args, error) => { + const permLvl = await client.funcs.permissionLevel(client, msg.author, msg.guild).catch(err => client.funcs.log(err, "error")); + if (cmd.conf.permLevel > permLvl) return msg.channel.sendCode("", "You do not have enough permission to use this command.").catch(err => client.funcs.log(err, "error")); + const message = await msg.channel.sendMessage(`<@!${msg.member.id}> | **${error}** | You have **30** seconds to respond to this prompt with a valid argument. Type **"ABORT"** to abort this prompt.`).catch(err => client.funcs.log(err, "error")); + const param = await msg.channel.awaitMessages(response => response.member.id === msg.author.id && response.id !== message.id, options).catch(err => client.funcs.log(err, "error")); + message.delete(); + if (param.first().content.toLowerCase() === "abort") return "Aborted"; + args.push(param.first().content); + const params = client.funcs.runCommandInhibitors(client, msg, cmd, args) + .catch((reason) => { + if (reason) { + if (reason.stack) client.funcs.log(reason.stack, "error"); + msg.channel.sendCode("", reason).catch(console.error); + } }); + return cmd.run(client, msg, params); }; diff --git a/functions/getFileListing.js b/functions/getFileListing.js index 65f3ff6d..7148116a 100644 --- a/functions/getFileListing.js +++ b/functions/getFileListing.js @@ -8,7 +8,7 @@ module.exports = (client, baseDir, type) => new Promise((resolve, reject) => { fs.walk(dir) .on("data", (item) => { const fileinfo = path.parse(item.path); - if (!fileinfo.ext) return; + if (!fileinfo.ext || fileinfo.ext !== ".js") return; files.push({ path: fileinfo.dir, name: fileinfo.name, @@ -27,19 +27,7 @@ module.exports = (client, baseDir, type) => new Promise((resolve, reject) => { }); next(); }); - } catch (e) { - if (e.code === "MODULE_NOT_FOUND") { - const module = /'[^']+'/g.exec(e.toString()); - client.funcs.installNPM(module[0].slice(1, -1)) - .then(() => { - client.funcs.loadCommands(client); - }) - .catch((err) => { - console.error(err); - process.exit(); - }); - } else { - reject(e); - } + } catch (err) { + reject(err); } }); diff --git a/functions/initialize.js b/functions/initialize.js new file mode 100644 index 00000000..b581df2e --- /dev/null +++ b/functions/initialize.js @@ -0,0 +1,18 @@ +module.exports = (client) => { + for (const func in client.funcs) { + if (client.funcs[func].init) client.funcs[func].init(client); + } + client.providers.forEach((prov) => { + if (prov.init) prov.init(client); + }); + client.configuration.initialize(client); + client.commandInhibitors.forEach((inhib) => { + if (inhib.init) inhib.init(client); + }); + client.messageMonitors.forEach((mon) => { + if (mon.init) mon.init(client); + }); + client.commands.forEach((cmd) => { + if (cmd.init) cmd.init(client); + }); +}; diff --git a/functions/loadCommandInhibitors.js b/functions/loadCommandInhibitors.js index d2bdc4b3..16a03808 100644 --- a/functions/loadCommandInhibitors.js +++ b/functions/loadCommandInhibitors.js @@ -1,63 +1,36 @@ const fs = require("fs-extra-promise"); const path = require("path"); -const loadCommandInhibitors = (client, baseDir, counts) => new Promise((resolve, reject) => { +const loadCommandInhibitors = (client, baseDir) => new Promise(async (resolve, reject) => { const dir = path.resolve(`${baseDir}./inhibitors/`); - fs.ensureDirAsync(dir) - .then(() => { - fs.readdirAsync(dir) - .then((files) => { - let c = counts; - try { - files = files.filter(f => f.slice(-3) === ".js"); - files.forEach((f) => { - const file = f.split("."); - const props = require(`${dir}/${f}`); - client.commandInhibitors.set(file[0], props); - if (props.init) { - props.init(client); - } - c++; - }); - } catch (e) { - if (e.code === "MODULE_NOT_FOUND") { - const module = /'[^']+'/g.exec(e.toString()); - client.funcs.installNPM(module[0].slice(1, -1)) - .then(() => { - client.funcs.loadCommandInhibitors(client); - }) - .catch((err) => { - console.error(err); - process.exit(); - }); - } else { - reject(e); - } - } - resolve(c); - }) - .catch((err) => { - client.funcs.log(err, "error"); + await fs.ensureDirAsync(dir); + const files = await client.funcs.getFileListing(client, baseDir, "inhibitors").catch(err => client.funcs.log(err, "error")); + try { + files.forEach((f) => { + const props = require(`${f.path}/${f.base}`); + client.commandInhibitors.set(f.name, props); }); - }) -.catch(err => client.funcs.log(err, "error")); + resolve(); + } catch (e) { + if (e.code === "MODULE_NOT_FOUND") { + const module = /'[^']+'/g.exec(e.toString()); + await client.funcs.installNPM(module[0].slice(1, -1)) + .catch((err) => { + console.error(err); + process.exit(); + }); + client.funcs.loadCommandInhibitors(client); + } else { + reject(e); + } + } }); - -module.exports = (client) => { +module.exports = async (client) => { client.commandInhibitors.clear(); - const count = 0; + await loadCommandInhibitors(client, client.coreBaseDir).catch(err => client.funcs.log(err, "error")); if (client.coreBaseDir !== client.clientBaseDir) { - loadCommandInhibitors(client, client.coreBaseDir, count).then((counts) => { - loadCommandInhibitors(client, client.clientBaseDir, counts).then((countss) => { - const c = countss; - client.funcs.log(`Loaded ${c} command inhibitors.`); - }); - }); - } else { - loadCommandInhibitors(client, client.coreBaseDir, count).then((counts) => { - const c = counts; - client.funcs.log(`Loaded ${c} command inhibitors.`); - }); + await loadCommandInhibitors(client, client.clientBaseDir).catch(err => client.funcs.log(err, "error")); } + client.funcs.log(`Loaded ${client.commandInhibitors.size} command inhibitors.`); }; diff --git a/functions/loadCommands.js b/functions/loadCommands.js index 46e31aa7..61fd79b2 100644 --- a/functions/loadCommands.js +++ b/functions/loadCommands.js @@ -1,67 +1,36 @@ const fs = require("fs-extra-promise"); const path = require("path"); -const loadCommands = (client, baseDir, counts) => new Promise((resolve, reject) => { +const loadCommands = (client, baseDir) => new Promise(async (resolve, reject) => { const dir = path.resolve(`${baseDir}./commands/`); - let [c, a] = counts; try { - fs.ensureDirAsync(dir) - .then(() => { - fs.walk(dir) - .on("data", (item) => { - const fileinfo = path.parse(item.path); - const fileDir = fileinfo.dir; - const name = fileinfo.name; - const ext = fileinfo.ext; - - if (!ext || ext !== ".js") return; - - client.funcs.loadSingleCommand(client, name, false, `${fileDir}${path.sep}${fileinfo.base}`).then((cmd) => { - c++; - cmd.conf.aliases.forEach(() => { - a++; - }); - }) - .catch((e) => { - client.funcs.log(e, "Error"); - }); - }) - .on("end", () => { - resolve([c, a]); - }); - }).catch(err => reject(err)); + await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, baseDir, "commands").catch(err => client.funcs.log(err, "error")); + files.forEach(async (f) => { + await client.funcs.loadSingleCommand(client, `${f.name}`, false, `${f.path}${path.sep}${f.base}`).catch(err => client.funcs.log(err, "error")); + }); + resolve(); } catch (e) { if (e.code === "MODULE_NOT_FOUND") { const module = /'[^']+'/g.exec(e.toString()); - client.funcs.installNPM(module[0].slice(1, -1)) - .then(() => { - client.funcs.loadCommands(client); - }) + await client.funcs.installNPM(module[0].slice(1, -1)) .catch((err) => { console.error(err); process.exit(); }); + client.funcs.loadCommands(client); } else { reject(e); } } }); -module.exports = (client) => { +module.exports = async (client) => { client.commands.clear(); client.aliases.clear(); - const count = [0, 0]; + await loadCommands(client, client.coreBaseDir).catch(err => client.funcs.log(err, "error")); if (client.coreBaseDir !== client.clientBaseDir) { - loadCommands(client, client.coreBaseDir, count).then((counts) => { - loadCommands(client, client.clientBaseDir, counts).then((countss) => { - const [c, a] = countss; - client.funcs.log(`Loaded ${c} commands, with ${a} aliases.`); - }); - }); - } else { - loadCommands(client, client.coreBaseDir, count).then((counts) => { - const [c, a] = counts; - client.funcs.log(`Loaded ${c} commands with ${a} aliases.`); - }); + await loadCommands(client, client.clientBaseDir).catch(err => client.funcs.log(err, "error")); } + client.funcs.log(`Loaded ${client.commands.size} commands, with ${client.aliases.size} aliases.`); }; diff --git a/functions/loadDataProviders.js b/functions/loadDataProviders.js deleted file mode 100644 index 75d1c5dc..00000000 --- a/functions/loadDataProviders.js +++ /dev/null @@ -1,58 +0,0 @@ -const fs = require("fs-extra-promise"); -const path = require("path"); - -const loadDataProviders = (client, baseDir, counts) => new Promise((resolve, reject) => { - const dir = path.resolve(`${baseDir}./dataProviders/`); - fs.ensureDirAsync(dir) - .then(() => { - fs.readdirAsync(dir) - .then((files) => { - let c = counts; - try { - files = files.filter(f => f.slice(-3) === ".js"); - files.forEach((f) => { - const file = f.split("."); - const props = require(`${dir}/${f}`); - client.dataProviders.set(file[0], props); - if (props.init) { - props.init(client); - } - c++; - }); - } catch (e) { - if (e.code === "MODULE_NOT_FOUND") { - const module = /'[^']+'/g.exec(e.toString()); - client.funcs.installNPM(module[0].slice(1, -1)) - .then(() => { - client.funcs.loadDataProviders(client, baseDir, counts); - }) - .catch((err) => { - console.error(err); - process.exit(); - }); - } else { - reject(e); - } - } - resolve(c); - }).catch(err => client.funcs.log(err, "error")); - }).catch(err => client.funcs.log(err, "error")); -}); - -module.exports = (client) => { - client.dataProviders.clear(); - const count = 0; - if (client.coreBaseDir !== client.clientBaseDir) { - loadDataProviders(client, client.coreBaseDir, count).then((counts) => { - loadDataProviders(client, client.clientBaseDir, counts).then((countss) => { - const c = countss; - client.funcs.log(`Loaded ${c} database handlers.`); - }); - }); - } else { - loadDataProviders(client, client.coreBaseDir, count).then((counts) => { - const c = counts; - client.funcs.log(`Loaded ${c} database handlers.`); - }); - } -}; diff --git a/functions/loadEvents.js b/functions/loadEvents.js index ac8b54d5..1026efec 100644 --- a/functions/loadEvents.js +++ b/functions/loadEvents.js @@ -5,38 +5,24 @@ let events = require("discord.js/src/util/Constants.js").Events; events = Object.keys(events).map(k => events[k]); -const loadEvents = (client, baseDir, count) => new Promise((resolve, reject) => { +const loadEvents = (client, baseDir, counts) => new Promise(async (resolve) => { const dir = path.resolve(`${baseDir}./events/`); - fs.ensureDirAsync(dir) - .then(() => { - fs.readdirAsync(dir) - .then((files) => { - let e = count; - files = files.filter((f) => { - const name = f.split(".")[0]; - return events.includes(name); - }); - files.forEach((f) => { - const name = f.split(".")[0]; - client.on(name, (...args) => require(`${dir}/${f}`).run(client, ...args)); - e++; - }); - resolve(e); - }).catch(err => reject(err)); - }).catch(err => reject(err)); + await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); + let files = await client.funcs.getFileListing(client, baseDir, "events").catch(err => client.funcs.log(err, "error")); + files = files.filter(f => events.includes(f.name)); + files.forEach((f) => { + client.on(f.name, (...args) => require(`${dir}/${f}`).run(client, ...args)); + counts++; + }); + resolve(counts); }); -module.exports = (client) => { - const count = 0; + +module.exports = async (client) => { + let counts = 0; + counts = await loadEvents(client, client.coreBaseDir, counts).catch(err => client.funcs.log(err, "error")); if (client.coreBaseDir !== client.clientBaseDir) { - loadEvents(client, client.coreBaseDir, count).then((counts) => { - loadEvents(client, client.clientBaseDir, counts).then((countss) => { - client.funcs.log(`Loaded ${countss} events`); - }); - }); - } else { - loadEvents(client, client.coreBaseDir, count).then((counts) => { - client.funcs.log(`Loaded ${counts} events`); - }); + counts = await loadEvents(client, client.clientBaseDir, counts).catch(err => client.funcs.log(err, "error")); } + client.funcs.log(`Loaded ${counts} events`); }; diff --git a/functions/loadFunctions.js b/functions/loadFunctions.js index 589edbe6..7f47f5fd 100644 --- a/functions/loadFunctions.js +++ b/functions/loadFunctions.js @@ -1,60 +1,38 @@ const fs = require("fs-extra-promise"); const path = require("path"); -const loadFunctions = (client, baseDir, counts) => new Promise((resolve, reject) => { +const loadFunctions = (client, baseDir) => new Promise(async (resolve, reject) => { const dir = path.resolve(`${baseDir}./functions/`); - fs.ensureDirAsync(dir) - .then(() => { - fs.readdirAsync(dir) - .then((files) => { - files = files.filter(f => f.slice(-3) === ".js"); - let c = counts; - try { - files.forEach((f) => { - const file = f.split("."); - if (file[0] === "loadFunctions") return; - client.funcs[file[0]] = require(`${dir}/${f}`); - // Currently disabled for workaround (See ready event) - // if (client.funcs[file[0]].init) { - // client.funcs[file[0]].init(client); - // } - c++; - }); - resolve(c); - } catch (e) { - if (e.code === "MODULE_NOT_FOUND") { - const module = /'[^']+'/g.exec(e.toString()); - client.funcs.installNPM(module[0].slice(1, -1)) - .then(() => { - client.funcs.loadFunctions(client); - }) - .catch((error) => { - console.error(error); - process.exit(); - }); - } else { - reject(e); - } - } - }).catch(err => client.funcs.log(err, "error")); - }).catch(err => client.funcs.log(err, "error")); + await fs.ensureDirAsync(dir).catch(err => console.error(err)); + let files = await fs.readdirAsync(dir).catch(err => console.err(err)); + files = files.filter(f => f.slice(-3) === ".js"); + try { + files.forEach((f) => { + const file = f.split("."); + if (file[0] === "loadFunctions") return; + client.funcs[file[0]] = require(`${dir}/${f}`); + }); + resolve(); + } catch (e) { + if (e.code === "MODULE_NOT_FOUND") { + const module = /'[^']+'/g.exec(e.toString()); + await client.funcs.installNPM(module[0].slice(1, -1)) + .catch((error) => { + console.error(error); + process.exit(); + }); + client.funcs.loadFunctions(client); + } else { + reject(e); + } + } }); -module.exports = client => new Promise((resolve, reject) => { - const count = 0; +module.exports = client => new Promise(async (resolve, reject) => { + await loadFunctions(client, client.coreBaseDir).catch(reject); if (client.coreBaseDir !== client.clientBaseDir) { - loadFunctions(client, client.coreBaseDir, count).then((counts) => { - loadFunctions(client, client.clientBaseDir, counts).then((countss) => { - const c = countss; - client.funcs.log(`Loaded ${c} functions.`); - resolve(); - }); - }).catch(reject); - } else { - loadFunctions(client, client.coreBaseDir, count).then((counts) => { - const c = counts; - client.funcs.log(`Loaded ${c} functions.`); - resolve(); - }).catch(reject); + await loadFunctions(client, client.clientBaseDir).catch(reject); } + client.funcs.log(`Loaded ${Object.keys(client.funcs).length} functions.`); + resolve(); }); diff --git a/functions/loadMessageMonitors.js b/functions/loadMessageMonitors.js index d6893aaf..35eb0c0e 100644 --- a/functions/loadMessageMonitors.js +++ b/functions/loadMessageMonitors.js @@ -1,59 +1,36 @@ const fs = require("fs-extra-promise"); const path = require("path"); -const loadMessageMonitors = (client, baseDir, counts) => new Promise((resolve, reject) => { +const loadMessageMonitors = (client, baseDir) => new Promise(async (resolve, reject) => { const dir = path.resolve(`${baseDir}./monitors/`); - fs.ensureDirAsync(dir) - .then(() => { - fs.readdirAsync(dir) - .then((files) => { - let c = counts; - try { - files = files.filter(f => f.slice(-3) === ".js"); - files.forEach((f) => { - const file = f.split("."); - const props = require(`${dir}/${f}`); - client.messageMonitors.set(file[0], props); - if (props.init) { - props.init(client); - } - c++; - }); - } catch (e) { - if (e.code === "MODULE_NOT_FOUND") { - const module = /'[^']+'/g.exec(e.toString()); - client.funcs.installNPM(module[0].slice(1, -1)) - .then(() => { - client.funcs.loadMessageMonitors(client); - }) - .catch((err) => { - console.error(err); - process.exit(); - }); - } else { - reject(e); - } - } - resolve(c); - }).catch(err => client.funcs.log(err, "error")); - }) - .catch(err => client.funcs.log(err, "error")); + await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, baseDir, "monitors").catch(err => client.funcs.log(err, "error")); + try { + files.forEach((f) => { + const props = require(`${f.path}/${f.base}`); + client.messageMonitors.set(f.name, props); + }); + resolve(); + } catch (e) { + if (e.code === "MODULE_NOT_FOUND") { + const module = /'[^']+'/g.exec(e.toString()); + await client.funcs.installNPM(module[0].slice(1, -1)) + .catch((err) => { + console.error(err); + process.exit(); + }); + client.funcs.loadMessageMonitors(client); + } else { + reject(e); + } + } }); -module.exports = (client) => { +module.exports = async (client) => { client.messageMonitors.clear(); - const count = 0; + await loadMessageMonitors(client, client.coreBaseDir).catch(err => client.funcs.log(err, "error")); if (client.coreBaseDir !== client.clientBaseDir) { - loadMessageMonitors(client, client.coreBaseDir, count).then((counts) => { - loadMessageMonitors(client, client.clientBaseDir, counts).then((countss) => { - const c = countss; - client.funcs.log(`Loaded ${c} command monitors.`); - }); - }); - } else { - loadMessageMonitors(client, client.coreBaseDir, count).then((counts) => { - const c = counts; - client.funcs.log(`Loaded ${c} command monitors.`); - }); + await loadMessageMonitors(client, client.clientBaseDir).catch(err => client.funcs.log(err, "error")); } + client.funcs.log(`Loaded ${client.messageMonitors.size} command monitors.`); }; diff --git a/functions/loadProviders.js b/functions/loadProviders.js new file mode 100644 index 00000000..969f2df3 --- /dev/null +++ b/functions/loadProviders.js @@ -0,0 +1,36 @@ +const fs = require("fs-extra-promise"); +const path = require("path"); + +const loadProviders = (client, baseDir) => new Promise(async (resolve, reject) => { + const dir = path.resolve(`${baseDir}./providers/`); + await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, baseDir, "providers").catch(err => client.funcs.log(err, "error")); + try { + files.forEach((f) => { + const file = f.split("."); + const props = require(`${dir}/${f}`); + client.providers.set(file[0], props); + }); + resolve(); + } catch (e) { + if (e.code === "MODULE_NOT_FOUND") { + const module = /'[^']+'/g.exec(e.toString()); + await client.funcs.installNPM(module[0].slice(1, -1)) + .catch((err) => { + console.error(err); + process.exit(); + }); + } else { + reject(e); + } + } +}); + +module.exports = async (client) => { + client.providers.clear(); + await loadProviders(client, client.coreBaseDir).catch(err => client.funcs.log(err, "error")); + if (client.coreBaseDir !== client.clientBaseDir) { + await loadProviders(client, client.clientBaseDir).catch(err => client.funcs.log(err, "error")); + } + client.funcs.log(`Loaded ${client.providers.size} providers.`); +}; diff --git a/functions/loadSingleCommand.js b/functions/loadSingleCommand.js index b0c562ab..5a4677f4 100644 --- a/functions/loadSingleCommand.js +++ b/functions/loadSingleCommand.js @@ -1,6 +1,6 @@ const path = require("path"); -module.exports = (client, command, reload = false, loadPath = null) => new Promise((resolve, reject) => { +module.exports = (client, command, reload = false, loadPath = null) => new Promise(async (resolve, reject) => { let category; let subCategory; let cmd; @@ -41,14 +41,12 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi } catch (e) { if (e.code === "MODULE_NOT_FOUND") { const module = /'[^']+'/g.exec(e.toString()); - client.funcs.installNPM(module[0].slice(1, -1)) - .then(() => { - client.funcs.loadSingleCommand(client, command, false, loadPath); - }) + await client.funcs.installNPM(module[0].slice(1, -1)) .catch((err) => { console.error(err); process.exit(); }); + client.funcs.loadSingleCommand(client, command, false, loadPath); } else { reject(`Could not load new command data: ${e.stack}`); } diff --git a/functions/permissionLevel.js b/functions/permissionLevel.js index 19ca4a45..eb2b46a3 100644 --- a/functions/permissionLevel.js +++ b/functions/permissionLevel.js @@ -1,25 +1,24 @@ -module.exports = (client, user, guild) => new Promise((resolve, reject) => { +module.exports = (client, user, guild) => new Promise(async (resolve, reject) => { let permlvl = 0; if (guild) { const guildConf = client.configuration.get(guild); try { const modRole = guild.roles.find("name", guildConf.modRole); - guild.fetchMember(user).then((member) => { - if (modRole && member.roles.has(modRole.id)) { - permlvl = 2; - } - const adminRole = guild.roles.find("name", guildConf.adminRole); - if (adminRole && member.roles.has(adminRole.id)) { - permlvl = 3; - } - if (member === guild.owner) { - permlvl = 4; - } - if (user.id === client.config.ownerID) { - permlvl = 10; - } - resolve(permlvl); - }); + const member = await guild.fetchMember(user).catch(err => client.funcs.log(err, "error")); + if (modRole && member.roles.has(modRole.id)) { + permlvl = 2; + } + const adminRole = guild.roles.find("name", guildConf.adminRole); + if (adminRole && member.roles.has(adminRole.id)) { + permlvl = 3; + } + if (member === guild.owner) { + permlvl = 4; + } + if (user.id === client.config.ownerID) { + permlvl = 10; + } + resolve(permlvl); } catch (e) { reject(e); } diff --git a/functions/reload.js b/functions/reload.js index 2c59fec4..e6514cc1 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -1,251 +1,246 @@ const path = require("path"); /* eslint-disable import/no-dynamic-require, global-require */ -exports.function = (client, dir, funcName) => new Promise((resolve, reject) => { - client.funcs.getFileListing(client, dir, "functions").then((files) => { - if (client.funcs.hasOwnProperty(funcName)) { - const oldFunction = files.filter(f => f.name === funcName); - if (oldFunction[0]) { - try { - oldFunction.forEach((file) => { - client.funcs[funcName] = ""; - delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; - client.funcs[funcName] = require(`${file.path}${path.sep}${file.base}`); - if (client.funcs[funcName].init) { - client.funcs[funcName].init(client); - } - }); - } catch (error) { - reject(error); - return; - } - resolve(`Successfully reloaded the function ${funcName}.`); - } else { - reject(`The function **${funcName}** does not reside in ${dir}functions`); - } - } else { - const newFunction = files.filter(f => f.name === funcName); - if (newFunction[0]) { - try { - newFunction.forEach((file) => { - client.funcs[funcName] = require(`${file.path}${path.sep}${file.base}`); - if (client.funcs[funcName].init) { - client.funcs[funcName].init(client); - } - }); - resolve(`Successfully loaded a new function called ${funcName}.`); - } catch (error) { - if (error.code === "MODULE_NOT_FOUND") { - const module = /'[^']+'/g.exec(error.toString()); - client.funcs.installNPM(module[0].slice(1, -1)).then(() => { - client.funcs.reload.function(client, dir, funcName); - }).catch((e) => { - console.error(e); - process.exit(); - }); - } else { - reject(`Could not load new function data: ${error}`); +exports.function = (client, dir, funcName) => new Promise(async (resolve, reject) => { + const files = await client.funcs.getFileListing(client, dir, "functions").catch(err => client.funcs.log(err, "error")); + if (client.funcs.hasOwnProperty(funcName)) { + const oldFunction = files.filter(f => f.name === funcName); + if (oldFunction[0]) { + try { + oldFunction.forEach((file) => { + client.funcs[funcName] = ""; + delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; + client.funcs[funcName] = require(`${file.path}${path.sep}${file.base}`); + if (client.funcs[funcName].init) { + client.funcs[funcName].init(client); } - } - } else { - reject(`Could not locate a new function ${funcName} in ${dir}functions`); + }); + } catch (error) { + reject(error); + return; } + resolve(`Successfully reloaded the function ${funcName}.`); + } else { + reject(`The function **${funcName}** does not reside in ${dir}functions`); } - }); -}); - -exports.inhibitor = (client, dir, inhibName) => new Promise((resolve, reject) => { - client.funcs.getFileListing(client, dir, "inhibitors").then((files) => { - if (client.commandInhibitors.has(inhibName)) { - const oldInhibitor = files.filter(f => f.name === inhibName); - if (oldInhibitor[0]) { - try { - oldInhibitor.forEach((file) => { - client.commandInhibitors.delete(file.name); - delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; - const props = require(`${file.path}${path.sep}${file.base}`); - client.commandInhibitors.set(file.name, props); - if (props.init) { - props.init(client); - } + } else { + const newFunction = files.filter(f => f.name === funcName); + if (newFunction[0]) { + try { + newFunction.forEach((file) => { + client.funcs[funcName] = require(`${file.path}${path.sep}${file.base}`); + if (client.funcs[funcName].init) { + client.funcs[funcName].init(client); + } + }); + resolve(`Successfully loaded a new function called ${funcName}.`); + } catch (error) { + if (error.code === "MODULE_NOT_FOUND") { + const module = /'[^']+'/g.exec(error.toString()); + client.funcs.installNPM(module[0].slice(1, -1)).then(() => { + client.funcs.reload.function(client, dir, funcName); + }).catch((e) => { + console.error(e); + process.exit(); }); - } catch (error) { - reject(error); - return; + } else { + reject(`Could not load new function data: ${error}`); } - resolve(`Successfully reloaded the inhibitor ${inhibName}`); - } else { - reject(`The inhibitor **${inhibName}** does not seem to reside in ${dir}inhibitors`); } } else { - const newInhibitor = files.filter(f => f.name === inhibName); - if (newInhibitor[0]) { - try { - newInhibitor.forEach((file) => { - const props = require(`${file.path}${path.sep}${file.base}`); - client.commandInhibitors.set(file.name, props); - if (props.init) { - props.init(client); - } - }); - resolve(`Successfully loaded a new inhibitor called ${inhibName}.`); - } catch (error) { - if (error.code === "MODULE_NOT_FOUND") { - const module = /'[^']+'/g.exec(error.toString()); - client.funcs.installNPM(module[0].slice(1, -1)).then(() => { - client.funcs.reload.inhibitor(client, dir, inhibName); - }).catch((e) => { - console.error(e); - process.exit(); - }); - } else { - reject(`Could not load new inhibitor data: ${error}`); - } - } - } else { - reject(`Could not locate a new inhibitor ${inhibName} in ${dir}inhibitors`); - } + reject(`Could not locate a new function ${funcName} in ${dir}functions`); } - }); + } }); -exports.monitor = (client, dir, monitName) => new Promise((resolve, reject) => { - client.funcs.getFileListing(client, dir, "monitors").then((files) => { - if (client.messageMonitors.has(monitName)) { - const oldMonitor = files.filter(f => f.name === monitName); - if (oldMonitor[0]) { - try { - oldMonitor.forEach((file) => { - client.messageMonitors.delete(file.name); - delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; - const props = require(`${file.path}${path.sep}${file.base}`); - client.messageMonitors.set(file.name, props); - if (props.init) { - props.init(client); - } - }); - } catch (error) { - reject(error); - return; - } - resolve(`Succesfully reloaded the monitor ${monitName}.`); - } else { - reject(`The monitor **${monitName}** does not reside in ${dir}monitors`); +exports.inhibitor = (client, dir, inhibName) => new Promise(async (resolve, reject) => { + const files = await client.funcs.getFileListing(client, dir, "inhibitors").catch(err => client.funcs.log(err, "error")); + if (client.commandInhibitors.has(inhibName)) { + const oldInhibitor = files.filter(f => f.name === inhibName); + if (oldInhibitor[0]) { + try { + oldInhibitor.forEach((file) => { + client.commandInhibitors.delete(file.name); + delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; + const props = require(`${file.path}${path.sep}${file.base}`); + client.commandInhibitors.set(file.name, props); + if (props.init) { + props.init(client); + } + }); + } catch (error) { + reject(error); + return; } + resolve(`Successfully reloaded the inhibitor ${inhibName}`); } else { - const newMonitor = files.filter(f => f.name === monitName); - if (newMonitor[0]) { - try { - newMonitor.forEach((file) => { - const props = require(`${file.path}${path.sep}${file.base}`); - client.messageMonitors.set(file.name, props); - if (props.init) { - props.init(client); - } - }); - resolve(`Successfully loaded a new monitor called ${monitName}.`); - } catch (error) { - if (error.code === "MODULE_NOT_FOUND") { - const module = /'[^']+'/g.exec(error.toString()); - client.funcs.installNPM(module[0].slice(1, -1)).then(() => { - client.funcs.reload.monitor(client, dir, monitName); - }).catch((e) => { - console.error(e); - process.exit(); - }); - } else { - reject(`Could not load new monitor data: ${error}`); + reject(`The inhibitor **${inhibName}** does not seem to reside in ${dir}inhibitors`); + } + } else { + const newInhibitor = files.filter(f => f.name === inhibName); + if (newInhibitor[0]) { + try { + newInhibitor.forEach((file) => { + const props = require(`${file.path}${path.sep}${file.base}`); + client.commandInhibitors.set(file.name, props); + if (props.init) { + props.init(client); } + }); + resolve(`Successfully loaded a new inhibitor called ${inhibName}.`); + } catch (error) { + if (error.code === "MODULE_NOT_FOUND") { + const module = /'[^']+'/g.exec(error.toString()); + client.funcs.installNPM(module[0].slice(1, -1)).then(() => { + client.funcs.reload.inhibitor(client, dir, inhibName); + }).catch((e) => { + console.error(e); + process.exit(); + }); + } else { + reject(`Could not load new inhibitor data: ${error}`); } - } else { - reject(`Could not locate a new monitor ${monitName} in ${dir}monitors`); } + } else { + reject(`Could not locate a new inhibitor ${inhibName} in ${dir}inhibitors`); } - }); + } }); -exports.provider = (client, dir, providerName) => new Promise((resolve, reject) => { - client.funcs.getFileListing(client, dir, "dataProviders").then((files) => { - if (client.dataProviders.has(providerName)) { - const oldProvider = files.filter(f => f.name === providerName); - if (oldProvider[0]) { - try { - oldProvider.forEach((file) => { - client.dataProviders.delete(file.name); - delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; - const props = require(`${file.path}${path.sep}${file.base}`); - client.dataProviders.set(file.name, props); - if (props.init) { - props.init(client); - } - }); - } catch (error) { - reject(error); - return; - } - resolve(`Successfully reloaded the provider ${providerName}.`); - } else { - reject(`The provider **${providerName}** does not seem to reside in ${dir}dataProviders`); +exports.monitor = (client, dir, monitName) => new Promise(async (resolve, reject) => { + const files = await client.funcs.getFileListing(client, dir, "monitors").catch(err => client.funcs.log(err, "error")); + if (client.messageMonitors.has(monitName)) { + const oldMonitor = files.filter(f => f.name === monitName); + if (oldMonitor[0]) { + try { + oldMonitor.forEach((file) => { + client.messageMonitors.delete(file.name); + delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; + const props = require(`${file.path}${path.sep}${file.base}`); + client.messageMonitors.set(file.name, props); + if (props.init) { + props.init(client); + } + }); + } catch (error) { + reject(error); + return; } + resolve(`Succesfully reloaded the monitor ${monitName}.`); } else { - const newProvider = files.filter(f => f.name === providerName); - if (newProvider[0]) { - try { - newProvider.forEach((file) => { - const props = require(`${file.path}${path.sep}${file.base}`); - client.dataProviders.set(file.name, props); - if (props.init) { - props.init(client); - } - }); - resolve(`Successfully loaded a new provider called ${providerName}.`); - } catch (error) { - if (error.code === "MODULE_NOT_FOUND") { - const module = /'[^']+'/g.exec(error.toString()); - client.funcs.installNPM(module[0].slice(1, -1)).then(() => { - client.funcs.reload.provider(client, dir, providerName); - }).catch((e) => { - console.error(e); - process.exit(); - }); - } else { - reject(`Could not load new provider data: ${error}`); + reject(`The monitor **${monitName}** does not reside in ${dir}monitors`); + } + } else { + const newMonitor = files.filter(f => f.name === monitName); + if (newMonitor[0]) { + try { + newMonitor.forEach((file) => { + const props = require(`${file.path}${path.sep}${file.base}`); + client.messageMonitors.set(file.name, props); + if (props.init) { + props.init(client); } + }); + resolve(`Successfully loaded a new monitor called ${monitName}.`); + } catch (error) { + if (error.code === "MODULE_NOT_FOUND") { + const module = /'[^']+'/g.exec(error.toString()); + client.funcs.installNPM(module[0].slice(1, -1)).then(() => { + client.funcs.reload.monitor(client, dir, monitName); + }).catch((e) => { + console.error(e); + process.exit(); + }); + } else { + reject(`Could not load new monitor data: ${error}`); } - } else { - reject(`Could not locate a new provider ${providerName} in ${dir}providers`); } + } else { + reject(`Could not locate a new monitor ${monitName} in ${dir}monitors`); } - }); + } }); -exports.event = (client, eventName) => new Promise((resolve, reject) => { - client.funcs.getFileListing(client, client.clientBaseDir, "events").then((files) => { - const oldEvent = files.filter(f => f.name === eventName); - if (oldEvent[0] && oldEvent[0].name === eventName) { - let listener; - if (client._events[eventName].length !== 0) { - listener = client._events[eventName][1]; - } else { - listener = client._events[eventName]; - } - client.removeListener(eventName, listener); +exports.provider = (client, dir, providerName) => new Promise(async (resolve, reject) => { + const files = await client.funcs.getFileListing(client, dir, "dataProviders").catch(err => client.funcs.log(err, "error")); + if (client.dataProviders.has(providerName)) { + const oldProvider = files.filter(f => f.name === providerName); + if (oldProvider[0]) { try { - oldEvent.forEach((file) => { + oldProvider.forEach((file) => { + client.dataProviders.delete(file.name); delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; - client.on(file.name, (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args)); + const props = require(`${file.path}${path.sep}${file.base}`); + client.dataProviders.set(file.name, props); + if (props.init) { + props.init(client); + } }); } catch (error) { reject(error); return; } - resolve(`Successfully reloaded the event ${eventName}`); + resolve(`Successfully reloaded the provider ${providerName}.`); + } else { + reject(`The provider **${providerName}** does not seem to reside in ${dir}dataProviders`); + } + } else { + const newProvider = files.filter(f => f.name === providerName); + if (newProvider[0]) { + try { + newProvider.forEach((file) => { + const props = require(`${file.path}${path.sep}${file.base}`); + client.dataProviders.set(file.name, props); + if (props.init) { + props.init(client); + } + }); + resolve(`Successfully loaded a new provider called ${providerName}.`); + } catch (error) { + if (error.code === "MODULE_NOT_FOUND") { + const module = /'[^']+'/g.exec(error.toString()); + client.funcs.installNPM(module[0].slice(1, -1)).then(() => { + client.funcs.reload.provider(client, dir, providerName); + }).catch((e) => { + console.error(e); + process.exit(); + }); + } else { + reject(`Could not load new provider data: ${error}`); + } + } } else { - reject(`The event **${eventName}** does not seem to exist!`); + reject(`Could not locate a new provider ${providerName} in ${dir}providers`); } - }); + } }); -exports.command = (client, dir, commandName) => new Promise((resolve, reject) => { +exports.event = (client, eventName) => new Promise(async (resolve, reject) => { + const files = await client.funcs.getFileListing(client, client.clientBaseDir, "events").catch(err => client.funcs.log(err, "error")); + const oldEvent = files.filter(f => f.name === eventName); + if (oldEvent[0] && oldEvent[0].name === eventName) { + let listener; + if (client._events[eventName].length !== 0) { + listener = client._events[eventName][1]; + } else { + listener = client._events[eventName]; + } + client.removeListener(eventName, listener); + try { + oldEvent.forEach((file) => { + delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; + client.on(file.name, (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args)); + }); + } catch (error) { + reject(error); + return; + } + resolve(`Successfully reloaded the event ${eventName}`); + } else { + reject(`The event **${eventName}** does not seem to exist!`); + } +}); + +exports.command = (client, dir, commandName) => new Promise(async (resolve, reject) => { let command; if (client.commands.has(commandName)) { command = commandName; @@ -253,25 +248,15 @@ exports.command = (client, dir, commandName) => new Promise((resolve, reject) => command = client.aliases.get(commandName); } if (!command) { - client.funcs.getFileListing(client, client.coreBaseDir, "commands") - .then((files) => { - const newCommands = files.filter(f => f.name === commandName); - newCommands.forEach((file) => { - client.funcs.loadSingleCommand(client, commandName, false, `${file.path}${path.sep}${file.base}`) - .catch((e) => { - reject(e); - }); - resolve(`Successfully loaded a new command called ${commandName}`); - }); - reject(`Couldn't find a new command called ${commandName}`); - }); + const files = await client.funcs.getFileListing(client, client.coreBaseDir, "commands").catch(err => client.funcs.log(err, "error")); + const newCommands = files.filter(f => f.name === commandName); + newCommands.forEach(async (file) => { + await client.funcs.loadSingleCommand(client, commandName, false, `${file.path}${path.sep}${file.base}`).catch(e => reject(e)); + resolve(`Successfully loaded a new command called ${commandName}`); + }); + reject(`Couldn't find a new command called ${commandName}`); } else { - client.funcs.loadSingleCommand(client, command, true) - .then(() => { - resolve(`Successfully reloaded the command ${commandName}`); - }) - .catch((e) => { - reject(e); - }); + await client.funcs.loadSingleCommand(client, command, true).catch(e => reject(e)); + resolve(`Successfully reloaded the command ${commandName}`); } }); diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index 38ae70c9..9fe33f78 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -1,11 +1,11 @@ -module.exports = (client, msg, cmd, args, selective = false) => new Promise((resolve, reject) => { +module.exports = (client, msg, cmd, selective = false) => new Promise((resolve, reject) => { const mps = [true]; let i = 1; let usage; client.commandInhibitors.forEach((mProc, key) => { if (key === "usage") usage = i; if (!mProc.conf.spamProtection || !selective) { - mps.push(mProc.run(client, msg, cmd, args)); + mps.push(mProc.run(client, msg, cmd)); } i++; }); diff --git a/functions/runMessageMonitors.js b/functions/runMessageMonitors.js index 7b00481e..a1c480e0 100644 --- a/functions/runMessageMonitors.js +++ b/functions/runMessageMonitors.js @@ -1,14 +1,7 @@ -module.exports = (client, msg) => new Promise((resolve, reject) => { - const mps = [true]; +module.exports = (client, msg) => { client.messageMonitors.forEach((mProc) => { if (mProc.conf.enabled) { - mps.push(mProc.run(client, msg)); + mProc.run(client, msg); } }); - Promise.all(mps) - .then((value) => { - resolve(value); - }, (reason) => { - reject(reason); - }); -}); +}; diff --git a/inhibitors/permissions.js b/inhibitors/permissions.js index 6c1b3d8a..da77cef9 100644 --- a/inhibitors/permissions.js +++ b/inhibitors/permissions.js @@ -3,16 +3,14 @@ exports.conf = { spamProtection: false, }; -exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { - client.funcs.permissionLevel(client, msg.author, msg.guild) - .then((permlvl) => { - if (msg.guild) { - msg.member.permLevel = permlvl; - } - if (permlvl >= cmd.conf.permLevel) { - resolve(); - } else { - reject("You do not have permission to use this command."); - } - }); +exports.run = (client, msg, cmd) => new Promise(async (resolve, reject) => { + const permlvl = await client.funcs.permissionLevel(client, msg.author, msg.guild).catch(err => client.funcs.log(err, "error")); + if (msg.guild) { + msg.member.permLevel = permlvl; + } + if (permlvl >= cmd.conf.permLevel) { + resolve(); + } else { + reject("You do not have permission to use this command."); + } }); diff --git a/inhibitors/usage.js b/inhibitors/usage.js index f96895c1..e91da3dc 100644 --- a/inhibitors/usage.js +++ b/inhibitors/usage.js @@ -45,7 +45,6 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje } else if (currentUsage.possibles.length === 1) { switch (currentUsage.possibles[0].type) { case "literal": - console.log(args[i]); if (args[i].toLowerCase() === currentUsage.possibles[0].name.toLowerCase()) { args[i] = args[i].toLowerCase(); validateArgs(++i); diff --git a/package.json b/package.json index f381ccc8..5e3ccd45 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.13.5", + "version": "0.14.0", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", @@ -27,7 +27,7 @@ "node-i18n": "github:eslachance/node-i18n" }, "engines": { - "node": ">=6.0.0" + "node": ">=7.0.0" }, "devDependencies": { "eslint": "^3.10.2", From d451a2273ca916e8baa457203425fe8519620d27 Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 22 Jan 2017 18:42:37 -0500 Subject: [PATCH 16/56] [BUG-FIX] More bug fixes (#118) * Selfbot BugFixes * [PR] New Download Command Linked to Pieces * [PR] Changelog && Patch Bump * [PR] Evies Changes * [PR] Fixed spelling error * [PR] Client.destroy() removed permanently. * [PR] Changelog * [PR] Features && Bug Fixes * [PR] Help Command Fix * [PR] Disable Typo Fixed * [PR] Download Fix --- CHANGELOG.md | 13 +- app.js | 18 +- classes/Config.js | 44 ++-- classes/Configuration Types/Array.js | 12 +- classes/Configuration Types/Boolean.js | 6 +- classes/Configuration Types/Number.js | 11 +- classes/Configuration Types/String.js | 14 +- commands/System/disable.js | 2 +- commands/System/download.js | 328 ++++++++++++++++--------- commands/System/help.js | 4 +- functions/confs.js | 83 +++++++ functions/initialize.js | 26 +- functions/loadProviders.js | 3 +- functions/loadSingleCommand.js | 3 - functions/reload.js | 27 +- inhibitors/selfBot.js | 16 -- package.json | 2 +- 17 files changed, 398 insertions(+), 214 deletions(-) create mode 100644 functions/confs.js delete mode 100644 inhibitors/selfBot.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c64c406..01d49fdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,16 +8,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - New Beta Configuration (Needs heavy testing) - New Argument Prompting for Commands -- New Initialize Function to aleviate undefined errors +- New Initialize Function to alleviate undefined errors ### Changed +- Old Configuration system now points to the new configuration system, to ease the trouble of updating to newer versions of Komada +- Pieces now have specific order they load in. (Functions, Providers, Commands, Inhibitors, Monitors, Events) - Confs.js uses new configuration system now - Configuration now split into smaller parts as requested. - Help command is now a Direct Message. - Async/Await for all pieces && app.js - dataProviders renamed to Providers +- New Download Command ### Fixed +- Fixed Typo in disable +- Fixed Help Command sending extra message when DMed +- New Configuration system fixed and outputs files correctly now. +- No longer able to kill komada with Client.destroy() +- All Pieces should now initialize in the correct order. - loadCommands no longer counts/loads "Ghost" commands. - DMs throwing errors with new Config System && permLevel - Fixed Reload not erroring on new commands that aren't found @@ -25,9 +33,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed Bug on Help not showing all commands with new Argument System - Fixed another bug introduced with the new Argument System where Permissions weren't finalized before Prompts - Fixed Bug within reload.js that prevented new commands from being loaded +- More Selfbot Bugs Fixed +- More Reload function fixes for commands ### Removed - Old Configuration System +- Selfbot Inhibitor ## [0.12.4] - 2017-01-13 ### Added diff --git a/app.js b/app.js index d1c683c3..378e5af4 100644 --- a/app.js +++ b/app.js @@ -34,17 +34,18 @@ exports.start = async (config) => { // Load core functions, then everything else await loadFunctions(client); - client.funcs.loadProviders(client); - client.funcs.loadCommands(client); - client.funcs.loadCommandInhibitors(client); - client.funcs.loadMessageMonitors(client); - client.funcs.loadEvents(client); + await client.funcs.loadProviders(client); + await client.funcs.loadCommands(client); + await client.funcs.loadCommandInhibitors(client); + await client.funcs.loadMessageMonitors(client); + await client.funcs.loadEvents(client); client.i18n = client.funcs.loadLocalizations; client.i18n.init(client); - client.once("ready", () => { + client.once("ready", async () => { client.config.prefixMention = new RegExp(`^<@!?${client.user.id}>`); - client.funcs.initialize(client); + await client.funcs.initialize(client); + client.destroy = () => "You cannot use this within Komada, use process.exit() instead."; }); client.on("error", e => client.funcs.log(e, "error")); @@ -57,6 +58,9 @@ exports.start = async (config) => { msg.guildConf = conf; client.i18n.use(conf.lang); await client.funcs.runMessageMonitors(client, msg); + if (client.config.selfbot) { + if (msg.author.id !== client.user.id) return; + } let thisPrefix; if (conf.prefix instanceof Array) { conf.prefix.forEach((prefix) => { diff --git a/classes/Config.js b/classes/Config.js index 28506afa..b255b587 100644 --- a/classes/Config.js +++ b/classes/Config.js @@ -21,12 +21,24 @@ class Config { * @param {Config} [config] The local config to add to the configuration. */ constructor(client, guildID, config = {}) { + /** + * The client that created this configuration + * @type {Client} + * @readonly + */ + Object.defineProperty(this, "_client", { value: client }); /** * The guild to create the configuration for. * @type {GuildID} * @readonly */ Object.defineProperty(this, "_id", { value: guildID }); + /** + * The location where we will be storing this data. + * @type {String} + * @readonly + */ + Object.defineProperty(this, "_dataDir", { value: dataDir }); /** * The default prefix to use for the bot if one is not in the configuration. * @type {String} @@ -144,21 +156,21 @@ class Config { * //Example of what this returns * { prefix: '--', disabledCommands: [], modRole: 'Mods', adminRole: 'Devs', lang: 'en' } */ - static get(guild) { - const conf = {}; - if (guild && guildConfs.has(guild.id)) { - const guildConf = guildConfs.get(guild.id); - for (const key in guildConf) { - if (guildConf[key]) conf[key] = guildConf[key].data; - else conf[key] = defaultConf[key].data; - } - } else { - for (const key in defaultConf) { - conf[key] = defaultConf[key].data; - } - } - return conf; - } + static get(guild) { + const conf = {}; + if (guild && guildConfs.has(guild.id)) { + const guildConf = guildConfs.get(guild.id); + for (const key in guildConf) { + if (guildConf[key]) conf[key] = guildConf[key].data; + else conf[key] = defaultConf[key].data; + } + } else { + for (const key in defaultConf) { + conf[key] = defaultConf[key].data; + } + } + return conf; + } /** * Set the default value for a key in the default configuration. @@ -390,4 +402,4 @@ class Config { module.exports = Config; module.exports.guildConfs = guildConfs; -module.exports.dataDir = dataDir; +module.exports.defaultConf = defaultConf; diff --git a/classes/Configuration Types/Array.js b/classes/Configuration Types/Array.js index 86253de4..857bea87 100644 --- a/classes/Configuration Types/Array.js +++ b/classes/Configuration Types/Array.js @@ -2,8 +2,6 @@ const fs = require("fs-extra-promise"); const path = require("path"); -const dataDir = require("../Config.js").dataDir; -const guildConfs = require("../Config.js").guildConfs; /** The starting point for creating an Array Configuration key. */ class ArrayConfig { @@ -17,6 +15,8 @@ class ArrayConfig { else this.data = data; this.type = "Array"; Object.defineProperty(this, "_id", { value: conf._id }); + Object.defineProperty(this, "_dataDir", { value: conf._dataDir }); + Object.defineProperty(this, "_client", { value: conf._client }); return this; } @@ -32,12 +32,12 @@ class ArrayConfig { if (!this.data.includes(val)) return "That value is not in the array."; return this.data.splice(this.data.indexOf(value), 1); }); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } if (this.data.includes(value)) return "That value is already in the array."; this.data.push(value); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } @@ -53,12 +53,12 @@ class ArrayConfig { if (!this.data.includes(val)) return "That value is not in the array."; return this.data.splice(this.data.indexOf(value), 1); }); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } if (!this.data.includes(value)) return "That value is not in the array."; this.data.splice(this.data.indexOf(value), 1); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } } diff --git a/classes/Configuration Types/Boolean.js b/classes/Configuration Types/Boolean.js index 7b5fce48..26a47b70 100644 --- a/classes/Configuration Types/Boolean.js +++ b/classes/Configuration Types/Boolean.js @@ -2,8 +2,6 @@ const fs = require("fs-extra-promise"); const path = require("path"); -const dataDir = require("../Config.js").dataDir; -const guildConfs = require("../Config.js").guildConfs; /** The starting point for creating a Boolean configuration key. */ class BooleanConfig { @@ -17,6 +15,8 @@ class BooleanConfig { else this.data = data; this.type = "Boolean"; Object.defineProperty(this, "_id", { value: conf._id }); + Object.defineProperty(this, "_dataDir", { value: conf._dataDir }); + Object.defineProperty(this, "_client", { value: conf._client }); return this; } @@ -27,7 +27,7 @@ class BooleanConfig { toggle() { if (this.data === true) this.data = false; else this.data = true; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } } diff --git a/classes/Configuration Types/Number.js b/classes/Configuration Types/Number.js index 22cbad05..d9ca203b 100644 --- a/classes/Configuration Types/Number.js +++ b/classes/Configuration Types/Number.js @@ -2,8 +2,7 @@ const fs = require("fs-extra-promise"); const path = require("path"); -const dataDir = require("../Config.js").dataDir; -const guildConfs = require("../Config.js").guildConfs; + /** The starting point for creating a Number configuration key. */ class NumberConfig { @@ -19,6 +18,8 @@ class NumberConfig { if (data.max) this.max = data.max; this.type = "Number"; Object.defineProperty(this, "_id", { value: conf._id }); + Object.defineProperty(this, "_dataDir", { value: conf._dataDir }); + Object.defineProperty(this, "_client", { value: conf._client }); return this; } @@ -32,7 +33,7 @@ class NumberConfig { if (this.min && parseInt(value) < this.min) return `Error while setting the value. ${value} is less than ${this.min}`; if (this.max && parseInt(value) > this.max) return `Error while setting the value. ${value} is more than ${this.max}`; this.data = value; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } @@ -43,7 +44,7 @@ class NumberConfig { */ setMin(value) { this.min = value; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } @@ -54,7 +55,7 @@ class NumberConfig { */ setMax(value) { this.max = value; - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } } diff --git a/classes/Configuration Types/String.js b/classes/Configuration Types/String.js index 54e78c04..fadc9663 100644 --- a/classes/Configuration Types/String.js +++ b/classes/Configuration Types/String.js @@ -2,8 +2,6 @@ const fs = require("fs-extra-promise"); const path = require("path"); -const dataDir = require("../Config.js").dataDir; -const guildConfs = require("../Config.js").guildConfs; /** The starting point for creating a String configuration key. */ class StringConfig { @@ -19,6 +17,8 @@ class StringConfig { if (data.possibles) this.possibles = data.possibles; else this.possibles = []; Object.defineProperty(this, "_id", { value: conf._id }); + Object.defineProperty(this, "_dataDir", { value: conf._dataDir }); + Object.defineProperty(this, "_client", { value: conf._client }); return this; } @@ -31,7 +31,7 @@ class StringConfig { if (!value) return "Please supply a value to set."; if (this.possibles.length !== 0 && !this.possibles.includes(value)) return `That is not a valid option. Valid options: ${this.possibles.join(", ")}`; this.data = value.toString(); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } @@ -47,12 +47,12 @@ class StringConfig { if (this.possibles.includes(val)) return `The value ${val} is already in ${this.possibles}.`; return this.possibles.push(val); }); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } if (this.possibles.includes(value)) return `The value ${value} is already in ${this.possibles}.`; this.possibles.push(value); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } @@ -68,12 +68,12 @@ class StringConfig { if (!this.possibles.includes(val)) return `The value ${value} is not in ${this.possibles}.`; return this.possibles.splice(this.possibles.indexOf(val), 1); }); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } if (!this.possibles.includes(value)) return `The value ${value} is not in ${this.possibles}.`; this.possibles.splice(this.possibles.indexOf(value), 1); - fs.outputJSONAsync(path.resolve(`${dataDir}${path.sep}${this._id}.json`), guildConfs.get(this._id)); + fs.outputJSONAsync(path.resolve(`${this._dataDir}${path.sep}${this._id}.json`), this._client.guildConfs.get(this._id)); return this; } } diff --git a/commands/System/disable.js b/commands/System/disable.js index 05f7e478..a93f79ec 100644 --- a/commands/System/disable.js +++ b/commands/System/disable.js @@ -51,7 +51,7 @@ exports.conf = { exports.help = { name: "disable", - description: "Temporarily disables the inhibtor/monitor/command. Resets upon reboot.", + description: "Temporarily disables the inhibitor/monitor/command. Resets upon reboot.", usage: " ", usageDelim: " ", }; diff --git a/commands/System/download.js b/commands/System/download.js index b02334a7..2e1a1c6e 100644 --- a/commands/System/download.js +++ b/commands/System/download.js @@ -1,152 +1,240 @@ const request = require("superagent"); const vm = require("vm"); -const exec = require("child_process").exec; -const fs = require("fs-extra"); +const fs = require("fs-extra-promise"); const path = require("path"); +const url = require("url"); -exports.run = (client, msg, [url, folder = "Downloaded"]) => { - request.get(url, (err, res) => { - if (err) console.log(err); - - // Load Command Data +exports.run = (client, msg, [link, piece, folder = "Downloaded"]) => { + const proposedURL = url.parse(link); + const piecesURL = "https://raw.githubusercontent.com/dirigeants/komada-pieces/master/"; + let newURL; + if (!proposedURL.protocol && !proposedURL.hostname) { + if (!piece) return msg.channel.sendMessage(`<@!${msg.author.id}> | You provided an invalid url, and no piece name to look up in the Pieces repository.`); + if (link === "command") { + piece = piece.split("/"); + if (piece.length < 0 || piece.length > 2) return msg.channel.sendMessage(`<@!${msg.author.id}> | You provided an invalid or no subfolder for a command. Please provide a valid folder name from the Pieces Repo. Example: Misc/test`); + newURL = `${piecesURL}${link}/${piece.join("/")}.js`; + } else { + newURL = `${piecesURL}${link}/${piece}.js`; + } + } else { + newURL = proposedURL; + } + request.get(newURL, (error, res) => { + if (error) { + if (error.message === "Unexpected token <") return msg.channel.sendMessage(`<@!${msg.author.id}> | An error has occured: **${error.message}** | This typically happens when you try to download a file from a link that isn't raw github information. Try a raw link instead!`); + return msg.channel.sendMessage(`<@!${msg.author.id}> | An error has occured: **${error}** | We're not sure what happened here... Report this our Developers to get it checked out!`); + } const mod = { exports: {}, }; - - const code = - `(function(require) { - ${res.text}; - })`; - try { - vm.runInNewContext(code, { module: mod, exports: mod.exports }, { timeout: 500 })(require); - } catch (e) { - if (e.message.startsWith("Cannot find module ")) { - msg.channel.sendMessage("Couldn't find module... Attempting to download.").then((m) => { - const moduleName = e.message.substring(19).replace(/'/g, ""); - client.funcs.installNPM(moduleName).then(() => { - m.edit(`Downloaded **${moduleName}!** I will make an attempt to load the command now.`); - if (client.tempmodules === undefined) client.tempmodules = []; - client.tempmodules.push(moduleName); - client.commands.get("download").run(client, msg, [url, folder]); - }).catch((error) => { - console.log(error); - }); - }); - } else if (e.message.startsWith("ENOENT: no such file or directory, open ")) { - msg.channel.sendMessage("Couldn't find module... Attempting to download."); - const string = e.message.substring(e.message.indexOf("node_modules"), e.message.lastIndexOf("\\")); - const moduleName = string.substring(string.indexOf("\\") + 1, string.length); - client.funcs.installNPM(moduleName).then(() => { - msg.edit(`Downloaded **${moduleName}!** I will make an attempt to load the command now.`); - if (client.tempmodules === undefined) client.tempmodules = []; - client.tempmodules.push(moduleName); - client.commands.get("download").run(client, msg, [url, folder]); - }).catch((error) => { - console.log(error); - }); - return; - } else { - msg.reply(`URL command not valid: ${e}`); - return; + vm.runInNewContext(res.text, { module: mod, exports: mod.exports }, { timeout: 500 }); + } catch (err) { + if (err.message === "Unexpected identifier") { + return msg.channel.sendMessage(`<@!${msg.author.id}> | An error has occured: **${err.message}** | This typically happens when you try to download a file that uses Node 7's new \`Async/await\` feature, or the creator of the piece messed up the code.`); + } else if (err.message === "require is not defined") { + return msg.channel.sendMessage(`<@!${msg.author.id}> | An error has occured: **${err.message}** | This typically happens when you try to download a file that has a require outside of an \`exports\`. Ask the Developer to fix it or download it as a file and then load it.`); } - return; + return console.log(err.message); } - const name = mod.exports.help.name; - const description = mod.exports.help.description; + const description = mod.exports.help.description || "No description provided."; + const type = link; + const modules = mod.exports.conf.requiredModules || "No required modules.. Yay!"; - if (client.commands.has(name)) { - msg.reply(`The command \`${name}\` already exists in the bot!`); - delete client.tempmodules; - return; - } + if (!name) return msg.channel.sendMessage(`<@!${msg.author.id}> | I have stopped the load of this piece because it does not have a name value, and I cannot determine the file name without it. Please ask the Developer of this piece to add it.`); + if (!type) return msg.channel.sendMessage(`<@!${msg.author.id}> | I have stopped the load of this piece because it does not have a type value, and I cannot determine the type without it. Please ask the Developer of the piece to add it.`); + if (!["command", "function", "inhibitor", "monitor", "provider"].includes(type)) return msg.channel.sendMessage(`<@!${msg.author.id}> | I have stopped the loading of this piece because its type value doesn't match those we accept. Please ask the Developer of the piece to fix it.`); - if (mod.exports.conf.selfbot && !client.config.selfbot) { - msg.channel.sendMessage(`The command \`${name}\` is only usable in selfbots! I'll uninstall the modules for you.`).then((m) => { - exec(`npm uninstall ${client.tempmodules.join(" ")}`, (e, stdout, stderr) => { - if (e) { - msg.channel.sendMessage("Failed uninstalling the modules.. Sorry about that."); - console.log(e); - } else { - console.log(stdout); - console.log(stderr); - m.edit(`Succesfully uninstalled : **${client.tempmodules.join(", ")}**`); - client.tempmodules.forEach((module) => { - delete require.cache[require.resolve(module)]; - }); - delete client.tempmodules; - } - }); - }); - return; + switch (type) { + case "command": + if (client.commands.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id}> | That command already exists in your bot. Aborting the load.`); + break; + case "function": + if (client.funcs[name]) return msg.channel.sendMessage(`<@!${msg.author.id}> | That function already exists in your bot. Aborting the load.`); + break; + case "inhibitor": + if (client.commandInhibitors.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id}> | That command inhibitor already exists in your bot. Aborting the load.`); + break; + case "monitor": + if (client.messageMonitors.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id}> | That message monitor already exists in your bot. Aborting the load.`); + break; + case "provider": + if (client.providers.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id} | That provider already exists in your bot. Aborting the load.`); + break; + default: + return "This will never trigger"; } - msg.channel.sendMessage(`Are you sure you want to load the following command into your bot? + if (mod.exports.conf.selfbot && !client.config.selfbot) return msg.reply(`I am not a selfbot, so I cannot download nor use ${name}.`); + msg.channel.sendMessage(`Are you sure you want to load the following ${type} into your bot? This will also install all required modules. This prompt will abort after 20 seconds. \`\`\`asciidoc === NAME === ${name} === DESCRIPTION === ${description} -\`\`\``); - const collector = msg.channel.createCollector(m => m.author === msg.author, { - time: 8000, - }); +=== REQUIRED MODULES === +${modules} +\`\`\``); + const collector = msg.channel.createCollector(m => m.author === msg.author, { time: 20000 }); collector.on("message", (m) => { if (m.content.toLowerCase() === "no") collector.stop("aborted"); if (m.content.toLowerCase() === "yes") collector.stop("success"); }); - collector.on("end", (collected, reason) => { - if (reason === "aborted" || reason === "time") { - if (client.tempmodules !== undefined) { - msg.channel.sendMessage(":no_mobile_phones: Load Aborted. Command not installed. Lemme remove those useless modules for you :smile:").then((m) => { - exec(`npm uninstall ${client.tempmodules.join(" ")}`, (e, stdout, stderr) => { - if (e) { - msg.channel.sendMessage("Failed uninstalling the modules.. Sorry about that, the modules you need to uninstall are in `client.tempmodules`"); - console.log(e); - } else { - console.log(stdout); - console.log(stderr); - m.edit(`Succesfully uninstalled : **${client.tempmodules.join(", ")}**`); - client.tempmodules.forEach((module) => { - delete require.cache[require.resolve(module)]; - }); - delete client.tempmodules; - } - }); + collector.on("end", async (collected, reason) => { + if (reason === "aborted") { + return msg.channel.sendMessage(`:no_mobile_phones: Load aborted, ${type} not installed.`); + } else if (reason === "time") { + return msg.channel.sendMessage(`:timer: Load aborted, ${type} not installed. You ran out of time.`); + } else if (reason === "success") { + const m = await msg.channel.sendMessage(`:inbox_tray: \`Loading ${type}\``).catch(err => client.funcs.log(err, "error")); + if (Array.isArray(modules) && modules.length > 0) { + await client.funcs.installNPM(modules.join(" ")) + .catch((err) => { + console.error(err); + process.exit(); }); + const category = mod.exports.help.category ? mod.exports.help.category : client.funcs.toTitleCase(folder); + let message; + switch (type) { + case "command": { + const dir = path.resolve(`${client.clientBaseDir}/commands/${category}/`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); + fs.writeFileSync(`${dir}${path.sep}${name}.js`, res.text); + message = await client.funcs.reload.command(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Command load failed ${name}\n\`\`\`${response}\`\`\``); + return fs.unlinkSync(`${dir}/${name}.js`); + }); + if (message) m.edit(`:inbox_tray: ${message}`); + break; + } + case "function": { + const dir = path.resolve(`${client.clientBaseDir}/functions/`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.writeFileAsync(`${dir}${path.sep}${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.function(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Function load failed ${name}\n\`\`\`${response}\`\`\``); + return fs.unlinkSync(`${dir}/${name}.js`); + }); + m.edit(`:inbox_tray: ${message}`); + break; + } + case "inhibitor": { + const dir = path.resolve(`${client.clientBaseDir}/inhibitors/`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Inhibitor load failed ${name}\n\`\`\`${response}\`\`\``); + return fs.unlinkSync(`${dir}/${name}.js`); + }); + m.edit(`:inbox_tray: ${message}`); + break; + } + case "monitor": { + const dir = path.resolve(`${client.clientBaseDir}/monitors/`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.monitor(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Monitor load failed ${name}\n\`\`\`${response}\`\`\``); + return fs.unlinkSync(`${dir}/${name}.js`); + }); + m.edit(`:inbox_tray: ${message}`); + break; + } + case "provider": { + const dir = path.resolve(`${client.clientBaseDir}/providers/`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.provider(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Provider load failed ${name}\n\`\`\`${response}\`\`\``); + return fs.unlinkSync(`${dir}/${name}.js`); + }); + m.edit(`:inbox_tray: ${message}`); + break; + } + default: + return "Will never trigger"; + } } else { - msg.channel.sendMessage(":no_mobile_phones: Load Aborted. Command not installed."); - return; - } - } - if (reason === "success") { - msg.channel.sendMessage(":inbox_tray: `Loading Command...`").then((m) => { const category = mod.exports.help.category ? mod.exports.help.category : client.funcs.toTitleCase(folder); - const dir = path.resolve(`${client.clientBaseDir}/commands/${category}/`); - m.edit(`:inbox_tray: \`Loading Command into ${dir}/${name}.js...\``); - - fs.ensureDir(dir, (error) => { - if (error) console.error(err); - fs.writeFile(`${dir}/${name}.js`, res.text, (errorn) => { - if (errorn) console.error(errorn); - client.funcs.loadSingleCommand(client, name, false, `${dir}/${name}.js`) - .then((cmd) => { - m.edit(`:inbox_tray: Successfully Loaded: ${cmd.help.name}`); - delete client.tempmodules; - }) - .catch((e) => { - m.edit(`:no_mobile_phones: Command load failed: ${name}\n\`\`\`${e.stack}\`\`\``); - fs.unlink(`${dir}/${name}.js`); - delete client.tempmodules; - }); - }); - }); - }); + let message; + switch (type) { + case "command": { + const dir = path.resolve(`${client.clientBaseDir}/commands/${category}`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); + await fs.writeFileAsync(`${dir}${path.sep}${name}.js`, res.text); + message = await client.funcs.reload.command(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Command load failed ${name}\n\`\`\`${response}\`\`\``); + fs.unlinkSync(`${dir}/${name}.js`); + }); + if (message) m.edit(`:inbox_tray: ${message}`); + break; + } + case "function": { + const dir = path.resolve(`${client.clientBaseDir}/functions/`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.function(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Function load failed ${name}\n\`\`\`${response}\`\`\``); + return fs.unlinkSync(`${dir}/${name}.js`); + }); + m.edit(`:inbox_tray: ${message}`); + break; + } + case "inhibitor": { + const dir = path.resolve(`${client.clientBaseDir}/inhibitors/`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Inhibitor load failed ${name}\n\`\`\`${response}\`\`\``); + return fs.unlinkSync(`${dir}/${name}.js`); + }); + m.edit(`:inbox_tray: ${message}`); + break; + } + case "monitor": { + const dir = path.resolve(`${client.clientBaseDir}/monitors/`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.monitor(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Monitor load failed ${name}\n\`\`\`${response}\`\`\``); + return fs.unlinkSync(`${dir}/${name}.js`); + }); + m.edit(`:inbox_tray: ${message}`); + break; + } + case "provider": { + const dir = path.resolve(`${client.clientBaseDir}/providers/`); + m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); + await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.provider(client, client.clientBaseDir, name) + .catch((response) => { + m.edit(`:no_mobile_phones: Provider load failed ${name}\n\`\`\`${response}\`\`\``); + return fs.unlinkSync(`${dir}/${name}.js`); + }); + m.edit(`:inbox_tray: ${message}`); + break; + } + default: + return "Will never trigger"; + } + } } }); }); @@ -163,7 +251,7 @@ exports.conf = { exports.help = { name: "download", - description: "Downloads a command and installs it to Komada", - usage: " [folder:str]", + description: "Downloads a piece, either from a link or our Pieces Repository, and installs it.", + usage: " [location:str] [folder:str]", usageDelim: " ", }; diff --git a/commands/System/help.js b/commands/System/help.js index 05b4a104..5fb734a4 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -13,7 +13,9 @@ exports.run = (client, msg, [cmd]) => { } if (!client.config.selfbot) { msg.author.sendMessage(helpMessage, { split: { char: "\u200b" } }).catch((e) => { console.error(e); }); - msg.reply("Commands have been sent to your DMs."); + if (msg.channel.type.toLowerCase() !== "dm") { + msg.reply("Commands have been sent to your DMs."); + } } else { msg.channel.sendMessage(helpMessage, { split: { char: "\u200b" } }) .catch((e) => { console.error(e); }); diff --git a/functions/confs.js b/functions/confs.js new file mode 100644 index 00000000..c7d51af2 --- /dev/null +++ b/functions/confs.js @@ -0,0 +1,83 @@ +const Config = require("../classes/Config.js"); +const ArrayConfig = require("../classes/Configuration Types/Array.js"); +const BooleanConfig = require("../classes/Configuration Types/Boolean.js"); +const NumberConfig = require("../classes/Configuration Types/Number.js"); +const StringConfig = require("../classes/Configuration Types/String.js"); + +exports.remove = guild => Config.remove(guild); + +exports.has = guild => Config.guildConfs.has(guild.id); + +exports.get = guild => Config.get(guild); + +exports.addKey = (key, defaultValue, type = defaultValue.constructor.name) => Config.addKey(key, defaultValue, type); + +exports.setKey = (key, defaultValue) => Config.setKey(key, defaultValue); + +exports.resetKey = (guild, keys) => { + const conf = Config.guildConfs.get(guild.id); + if (keys instanceof Array) { + keys.forEach((key) => { + conf.reset(key); + }); + } else { + conf.reset(keys); + } + return Config.get(guild); +}; + +exports.delKey = key => Config.delKey(key); + +exports.hasKey = key => Config.hasKey(key); + +exports.set = (guild, key, value) => { + if (!guild || !key || !value) return `You supplied: ${guild}, ${key}, ${value}. Please provide all three.`; + const conf = Config.guildConfs.get(guild.id); + if (conf[key] instanceof ArrayConfig) { + if (!conf[key].data.includes(value)) return conf[key].add(value); + return conf[key].del(value); + } else if (conf[key] instanceof BooleanConfig) { + return conf[key].toggle(); + } else if (conf[key] instanceof NumberConfig) { + return conf[key].set(value); + } else if (conf[key] instanceof StringConfig) { + return conf[key].set(value); + } + return "Invalid Key or Invalid configuration type provided."; +}; + +exports.setMin = (guild, key, value) => { + if (!guild || !key || !value) return `You supplied: ${guild}, ${key}, ${value}. Please provide all three.`; + const conf = Config.guildConfs.get(guild.id); + if (!(conf[key] instanceof NumberConfig)) return "You cannot use this function on a key that isn't a number key."; + return conf[key].setMin(value); +}; + +exports.setMax = (guild, key, value) => { + if (!guild || !key || !value) return `You supplied: ${guild}, ${key}, ${value}. Please provide all three.`; + const conf = Config.guildConfs.get(guild.id); + if (!(conf[key] instanceof NumberConfig)) return "You cannot use this function on a key that isn't a number key."; + return conf[key].setMax(value); +}; + +exports.add = (guild, key, value) => { + if (!guild || !key || !value) return `You supplied: ${guild}, ${key}, ${value}. Please provide all three.`; + const conf = Config.guildConfs.get(guild.id); + if (conf[key] instanceof StringConfig) { + conf[key].add(value); + } else if (conf[key] instanceof ArrayConfig) { + conf[key].add(value); + } + return "You cannot use this function on a key that isn't an array or string key."; +}; + +exports.del = (guild, key, value) => { + if (!guild || !key || !value) return `You supplied: ${guild}, ${key}, ${value}. Please provide all three.`; + const conf = Config.guildConfs.get(guild.id); + if (conf[key] instanceof StringConfig) { + conf[key].del(value); + } else if (conf[key] instanceof ArrayConfig) { + conf[key].del(value); + } + return "You cannot use this function on a key that isn't an array or string key."; +}; diff --git a/functions/initialize.js b/functions/initialize.js index b581df2e..ac00d300 100644 --- a/functions/initialize.js +++ b/functions/initialize.js @@ -1,18 +1,18 @@ -module.exports = (client) => { - for (const func in client.funcs) { - if (client.funcs[func].init) client.funcs[func].init(client); - } - client.providers.forEach((prov) => { - if (prov.init) prov.init(client); +module.exports = async (client) => { + Object.keys(client.funcs).forEach(async (func) => { + if (client.funcs[func].init) await client.funcs[func].init(client); }); - client.configuration.initialize(client); - client.commandInhibitors.forEach((inhib) => { - if (inhib.init) inhib.init(client); + client.providers.forEach(async (prov) => { + if (prov.init) await prov.init(client); }); - client.messageMonitors.forEach((mon) => { - if (mon.init) mon.init(client); + await client.configuration.initialize(client); + client.commandInhibitors.forEach(async (inhib) => { + if (inhib.init) await inhib.init(client); }); - client.commands.forEach((cmd) => { - if (cmd.init) cmd.init(client); + client.messageMonitors.forEach(async (mon) => { + if (mon.init) await mon.init(client); + }); + client.commands.forEach(async (cmd) => { + if (cmd.init) await cmd.init(client); }); }; diff --git a/functions/loadProviders.js b/functions/loadProviders.js index 969f2df3..50da5988 100644 --- a/functions/loadProviders.js +++ b/functions/loadProviders.js @@ -7,9 +7,8 @@ const loadProviders = (client, baseDir) => new Promise(async (resolve, reject) = const files = await client.funcs.getFileListing(client, baseDir, "providers").catch(err => client.funcs.log(err, "error")); try { files.forEach((f) => { - const file = f.split("."); const props = require(`${dir}/${f}`); - client.providers.set(file[0], props); + client.providers.set(f.name, props); }); resolve(); } catch (e) { diff --git a/functions/loadSingleCommand.js b/functions/loadSingleCommand.js index 5a4677f4..759a3de0 100644 --- a/functions/loadSingleCommand.js +++ b/functions/loadSingleCommand.js @@ -19,9 +19,6 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi }); delete require.cache[require.resolve(loadPath)]; cmd = require(loadPath); - if (cmd.init) { - cmd.init(client); - } } catch (e) { reject(`Could not load existing command data: ${e.stack}`); } diff --git a/functions/reload.js b/functions/reload.js index e6514cc1..0242a708 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -161,16 +161,16 @@ exports.monitor = (client, dir, monitName) => new Promise(async (resolve, reject }); exports.provider = (client, dir, providerName) => new Promise(async (resolve, reject) => { - const files = await client.funcs.getFileListing(client, dir, "dataProviders").catch(err => client.funcs.log(err, "error")); - if (client.dataProviders.has(providerName)) { + const files = await client.funcs.getFileListing(client, dir, "providers").catch(err => client.funcs.log(err, "error")); + if (client.providers.has(providerName)) { const oldProvider = files.filter(f => f.name === providerName); if (oldProvider[0]) { try { oldProvider.forEach((file) => { - client.dataProviders.delete(file.name); + client.providers.delete(file.name); delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; const props = require(`${file.path}${path.sep}${file.base}`); - client.dataProviders.set(file.name, props); + client.providers.set(file.name, props); if (props.init) { props.init(client); } @@ -181,7 +181,7 @@ exports.provider = (client, dir, providerName) => new Promise(async (resolve, re } resolve(`Successfully reloaded the provider ${providerName}.`); } else { - reject(`The provider **${providerName}** does not seem to reside in ${dir}dataProviders`); + reject(`The provider **${providerName}** does not seem to reside in ${dir}providers`); } } else { const newProvider = files.filter(f => f.name === providerName); @@ -189,7 +189,7 @@ exports.provider = (client, dir, providerName) => new Promise(async (resolve, re try { newProvider.forEach((file) => { const props = require(`${file.path}${path.sep}${file.base}`); - client.dataProviders.set(file.name, props); + client.providers.set(file.name, props); if (props.init) { props.init(client); } @@ -248,13 +248,16 @@ exports.command = (client, dir, commandName) => new Promise(async (resolve, reje command = client.aliases.get(commandName); } if (!command) { - const files = await client.funcs.getFileListing(client, client.coreBaseDir, "commands").catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, dir, "commands").catch(err => client.funcs.log(err, "error")); const newCommands = files.filter(f => f.name === commandName); - newCommands.forEach(async (file) => { - await client.funcs.loadSingleCommand(client, commandName, false, `${file.path}${path.sep}${file.base}`).catch(e => reject(e)); - resolve(`Successfully loaded a new command called ${commandName}`); - }); - reject(`Couldn't find a new command called ${commandName}`); + if (newCommands[0]) { + newCommands.forEach(async (file) => { + await client.funcs.loadSingleCommand(client, commandName, false, `${file.path}${path.sep}${file.base}`).catch(e => reject(e)); + resolve(`Successfully loaded a new command called ${commandName}`); + }); + } else { + reject(`Couldn't find a new command called ${commandName}`); + } } else { await client.funcs.loadSingleCommand(client, command, true).catch(e => reject(e)); resolve(`Successfully reloaded the command ${commandName}`); diff --git a/inhibitors/selfBot.js b/inhibitors/selfBot.js deleted file mode 100644 index a0f8ec94..00000000 --- a/inhibitors/selfBot.js +++ /dev/null @@ -1,16 +0,0 @@ -exports.conf = { - enabled: true, - spamProtection: false, -}; - -exports.run = (client, msg) => new Promise((resolve, reject) => { - if (client.config.selfbot) { - if (msg.author === client.user) { - resolve(); - } else { - reject(); - } - } else { - resolve(); - } -}); diff --git a/package.json b/package.json index 5e3ccd45..283ffdf6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.0", + "version": "0.14.1", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From ccc4db05859377122d310e8e69958fea699a7168 Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 22 Jan 2017 19:39:35 -0500 Subject: [PATCH 17/56] Download.js Fix (#120) --- commands/System/download.js | 42 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/commands/System/download.js b/commands/System/download.js index 2e1a1c6e..da047503 100644 --- a/commands/System/download.js +++ b/commands/System/download.js @@ -23,7 +23,8 @@ exports.run = (client, msg, [link, piece, folder = "Downloaded"]) => { request.get(newURL, (error, res) => { if (error) { if (error.message === "Unexpected token <") return msg.channel.sendMessage(`<@!${msg.author.id}> | An error has occured: **${error.message}** | This typically happens when you try to download a file from a link that isn't raw github information. Try a raw link instead!`); - return msg.channel.sendMessage(`<@!${msg.author.id}> | An error has occured: **${error}** | We're not sure what happened here... Report this our Developers to get it checked out!`); + if (error.message === "Not Found") return msg.channel.sendMessage(`<@!${msg.author.id}> | An error has occured: **${error.message}** | This typically happens when you try to download a piece that doesn't exist. Try verifying it exists.`); + return msg.channel.sendMessage(`<@!${msg.author.id}> | An error has occured: **${error}** | We're not sure what happened here... Report this to our Developers to get it checked out!`); } const mod = { exports: {}, @@ -38,6 +39,7 @@ exports.run = (client, msg, [link, piece, folder = "Downloaded"]) => { } return console.log(err.message); } + const name = mod.exports.help.name; const description = mod.exports.help.description || "No description provided."; const type = link; @@ -45,22 +47,22 @@ exports.run = (client, msg, [link, piece, folder = "Downloaded"]) => { if (!name) return msg.channel.sendMessage(`<@!${msg.author.id}> | I have stopped the load of this piece because it does not have a name value, and I cannot determine the file name without it. Please ask the Developer of this piece to add it.`); if (!type) return msg.channel.sendMessage(`<@!${msg.author.id}> | I have stopped the load of this piece because it does not have a type value, and I cannot determine the type without it. Please ask the Developer of the piece to add it.`); - if (!["command", "function", "inhibitor", "monitor", "provider"].includes(type)) return msg.channel.sendMessage(`<@!${msg.author.id}> | I have stopped the loading of this piece because its type value doesn't match those we accept. Please ask the Developer of the piece to fix it.`); + if (!["commands", "functions", "inhibitors", "monitors", "providers"].includes(type)) return msg.channel.sendMessage(`<@!${msg.author.id}> | I have stopped the loading of this piece because its type value doesn't match those we accept. Please ask the Developer of the piece to fix it.`); switch (type) { - case "command": + case "commands": if (client.commands.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id}> | That command already exists in your bot. Aborting the load.`); break; - case "function": + case "functions": if (client.funcs[name]) return msg.channel.sendMessage(`<@!${msg.author.id}> | That function already exists in your bot. Aborting the load.`); break; - case "inhibitor": + case "inhibitors": if (client.commandInhibitors.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id}> | That command inhibitor already exists in your bot. Aborting the load.`); break; - case "monitor": + case "monitors": if (client.messageMonitors.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id}> | That message monitor already exists in your bot. Aborting the load.`); break; - case "provider": + case "providers": if (client.providers.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id} | That provider already exists in your bot. Aborting the load.`); break; default: @@ -102,7 +104,7 @@ ${modules} const category = mod.exports.help.category ? mod.exports.help.category : client.funcs.toTitleCase(folder); let message; switch (type) { - case "command": { + case "commands": { const dir = path.resolve(`${client.clientBaseDir}/commands/${category}/`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); @@ -115,7 +117,7 @@ ${modules} if (message) m.edit(`:inbox_tray: ${message}`); break; } - case "function": { + case "functions": { const dir = path.resolve(`${client.clientBaseDir}/functions/`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.writeFileAsync(`${dir}${path.sep}${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); @@ -124,10 +126,10 @@ ${modules} m.edit(`:no_mobile_phones: Function load failed ${name}\n\`\`\`${response}\`\`\``); return fs.unlinkSync(`${dir}/${name}.js`); }); - m.edit(`:inbox_tray: ${message}`); + if (message) m.edit(`:inbox_tray: ${message}`); break; } - case "inhibitor": { + case "inhibitors": { const dir = path.resolve(`${client.clientBaseDir}/inhibitors/`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); @@ -136,10 +138,10 @@ ${modules} m.edit(`:no_mobile_phones: Inhibitor load failed ${name}\n\`\`\`${response}\`\`\``); return fs.unlinkSync(`${dir}/${name}.js`); }); - m.edit(`:inbox_tray: ${message}`); + if (message) m.edit(`:inbox_tray: ${message}`); break; } - case "monitor": { + case "monitors": { const dir = path.resolve(`${client.clientBaseDir}/monitors/`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); @@ -148,10 +150,10 @@ ${modules} m.edit(`:no_mobile_phones: Monitor load failed ${name}\n\`\`\`${response}\`\`\``); return fs.unlinkSync(`${dir}/${name}.js`); }); - m.edit(`:inbox_tray: ${message}`); + if (message) m.edit(`:inbox_tray: ${message}`); break; } - case "provider": { + case "providers": { const dir = path.resolve(`${client.clientBaseDir}/providers/`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); @@ -170,7 +172,7 @@ ${modules} const category = mod.exports.help.category ? mod.exports.help.category : client.funcs.toTitleCase(folder); let message; switch (type) { - case "command": { + case "commands": { const dir = path.resolve(`${client.clientBaseDir}/commands/${category}`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); @@ -183,7 +185,7 @@ ${modules} if (message) m.edit(`:inbox_tray: ${message}`); break; } - case "function": { + case "functions": { const dir = path.resolve(`${client.clientBaseDir}/functions/`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); @@ -195,7 +197,7 @@ ${modules} m.edit(`:inbox_tray: ${message}`); break; } - case "inhibitor": { + case "inhibitors": { const dir = path.resolve(`${client.clientBaseDir}/inhibitors/`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); @@ -207,7 +209,7 @@ ${modules} m.edit(`:inbox_tray: ${message}`); break; } - case "monitor": { + case "monitors": { const dir = path.resolve(`${client.clientBaseDir}/monitors/`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); @@ -219,7 +221,7 @@ ${modules} m.edit(`:inbox_tray: ${message}`); break; } - case "provider": { + case "providers": { const dir = path.resolve(`${client.clientBaseDir}/providers/`); m.edit(`:inbox_tray: \`Loading ${type} into ${dir}/${name}.js...\``); await fs.writeFileAsync(`${dir}/${name}.js`, res.text).catch(err => client.funcs.log(err, "error")); From 14f882fb44145ab6c9428d5ea788755c05abcbb1 Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 22 Jan 2017 21:50:03 -0500 Subject: [PATCH 18/56] Indev (#121) * Download.js Fix * Fixes * No longer failed --- CHANGELOG.md | 2 ++ commands/System/info.js | 3 +-- functions/runCommandInhibitors.js | 25 +++++++++++-------------- inhibitors/disable.js | 1 + inhibitors/guildOnly.js | 1 + inhibitors/missingBotPermissions.js | 1 + inhibitors/permissions.js | 1 + inhibitors/requiredFuncs.js | 1 + inhibitors/usage.js | 1 + package.json | 2 +- 10 files changed, 21 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d49fdf..c4821147 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Initialize Function to alleviate undefined errors ### Changed +- Changed info to no longer mention Evie because she said she was tired of it.. kek +- New runCommandInhibitors should be much faster and prioritizes inhibitors via a prioritiy configuration setting. - Old Configuration system now points to the new configuration system, to ease the trouble of updating to newer versions of Komada - Pieces now have specific order they load in. (Functions, Providers, Commands, Inhibitors, Monitors, Events) - Confs.js uses new configuration system now diff --git a/commands/System/info.js b/commands/System/info.js index 96f88439..db3988ad 100644 --- a/commands/System/info.js +++ b/commands/System/info.js @@ -1,6 +1,5 @@ exports.run = (client, msg) => { - msg.channel.sendMessage(`This bot is built on the Komada framework, a plug-and-play bot builder made by <@139412744439988224> (LuckyEvie#4611 , aka \`root\`). -For more information visit: `); + msg.channel.sendMessage("This bot is built on the Komada framework, a plug-and-play bot builder made by Dirigeant's team of dedicated developers. For more information visit: "); }; exports.conf = { diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index 9fe33f78..84c89efd 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -1,18 +1,15 @@ -module.exports = (client, msg, cmd, selective = false) => new Promise((resolve, reject) => { - const mps = [true]; - let i = 1; +module.exports = (client, msg, cmd) => new Promise((resolve, reject) => { let usage; - client.commandInhibitors.forEach((mProc, key) => { - if (key === "usage") usage = i; - if (!mProc.conf.spamProtection || !selective) { - mps.push(mProc.run(client, msg, cmd)); - } - i++; - }); - Promise.all(mps) + const priority = client.commandInhibitors.array(); + const sorted = priority.sort((a, b) => a.conf.priority > b.conf.priority); + sorted.forEach((inhib) => { + inhib.run(client, msg, cmd) .then((value) => { - resolve(value[usage]); - }, (reason) => { - reject(reason); + if (value) usage = value; + }) + .catch((error) => { + reject(error); }); + }); + resolve(usage); }); diff --git a/inhibitors/disable.js b/inhibitors/disable.js index d000fdc0..4a8cc5e2 100644 --- a/inhibitors/disable.js +++ b/inhibitors/disable.js @@ -1,6 +1,7 @@ exports.conf = { enabled: true, spamProtection: false, + priority: 9, }; exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { diff --git a/inhibitors/guildOnly.js b/inhibitors/guildOnly.js index cb25618d..d8a49947 100644 --- a/inhibitors/guildOnly.js +++ b/inhibitors/guildOnly.js @@ -1,6 +1,7 @@ exports.conf = { enabled: true, spamProtection: false, + priority: 8, }; exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { diff --git a/inhibitors/missingBotPermissions.js b/inhibitors/missingBotPermissions.js index 36a923d8..87e77da1 100644 --- a/inhibitors/missingBotPermissions.js +++ b/inhibitors/missingBotPermissions.js @@ -1,6 +1,7 @@ exports.conf = { enabled: true, spamProtection: false, + priority: 7, }; exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { diff --git a/inhibitors/permissions.js b/inhibitors/permissions.js index da77cef9..4fac1098 100644 --- a/inhibitors/permissions.js +++ b/inhibitors/permissions.js @@ -1,6 +1,7 @@ exports.conf = { enabled: true, spamProtection: false, + priority: 10, }; exports.run = (client, msg, cmd) => new Promise(async (resolve, reject) => { diff --git a/inhibitors/requiredFuncs.js b/inhibitors/requiredFuncs.js index 71d3e3df..9e45c89f 100644 --- a/inhibitors/requiredFuncs.js +++ b/inhibitors/requiredFuncs.js @@ -1,6 +1,7 @@ exports.conf = { enabled: true, spamProtection: false, + priority: 6, }; exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { diff --git a/inhibitors/usage.js b/inhibitors/usage.js index e91da3dc..ebb9fd1a 100644 --- a/inhibitors/usage.js +++ b/inhibitors/usage.js @@ -3,6 +3,7 @@ const url = require("url"); exports.conf = { enabled: true, spamProtection: true, + priority: 5, }; exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reject) => { diff --git a/package.json b/package.json index 283ffdf6..abc7df97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.1", + "version": "0.14.2", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 17229e5cd587e762226d05385ad96d0164a2ac45 Mon Sep 17 00:00:00 2001 From: Faith Date: Mon, 23 Jan 2017 15:57:50 -0500 Subject: [PATCH 19/56] [BUGFIXES] More Bugfixes (#122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bunch of Bugfixes * [PR] Bunch of Command/inhibitor fixes * ESLint お願いします --- CHANGELOG.md | 1 + app.js | 8 +++++--- commands/System/help.js | 13 ++++++++----- functions/loadCommandInhibitors.js | 2 +- functions/loadFunctions.js | 2 +- functions/loadMessageMonitors.js | 2 +- functions/loadProviders.js | 2 +- functions/loadSingleCommand.js | 6 +++--- functions/runCommandInhibitors.js | 8 +++++--- package.json | 2 +- 10 files changed, 27 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4821147..ef94b14c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Fixed +- Bunch of fixes for Inhibitors/Commands - Fixed Typo in disable - Fixed Help Command sending extra message when DMed - New Configuration system fixed and outputs files correctly now. diff --git a/app.js b/app.js index 378e5af4..ca78b0fd 100644 --- a/app.js +++ b/app.js @@ -93,15 +93,17 @@ exports.start = async (config) => { cmd = client.commands.get(client.aliases.get(command)); } if (!cmd) return; - const params = await client.funcs.runCommandInhibitors(client, msg, cmd) + client.funcs.runCommandInhibitors(client, msg, cmd).then((params) => { + console.log(params); + client.funcs.log(commandLog); + cmd.run(client, msg, params); + }) .catch((reason) => { if (reason) { if (reason.stack) client.funcs.log(reason.stack, "error"); msg.channel.sendCode("", reason).catch(console.error); } }); - client.funcs.log(commandLog); - if (params || cmd.help.usage.length === 0) cmd.run(client, msg, params); }); client.login(client.config.botToken); diff --git a/commands/System/help.js b/commands/System/help.js index 5fb734a4..b458da81 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -58,11 +58,14 @@ const buildHelp = (client, msg) => new Promise((resolve) => { mps.push(new Promise((res) => { client.funcs.runCommandInhibitors(client, msg, command, [], true) .then(() => { - const cat = command.help.category; - const subcat = command.help.subCategory; - if (!help.hasOwnProperty(cat)) help[cat] = {}; - if (!help[cat].hasOwnProperty(subcat)) help[cat][subcat] = []; - help[cat][subcat].push(`${msg.guildConf.prefix}${command.help.name}${" ".repeat(longest - command.help.name.length)} :: ${command.help.description}`); + if (command.conf.permLevel <= msg.author.permLevel) { + const cat = command.help.category; + const subcat = command.help.subCategory; + if (!help.hasOwnProperty(cat)) help[cat] = {}; + if (!help[cat].hasOwnProperty(subcat)) help[cat][subcat] = []; + help[cat][subcat].push(`${msg.guildConf.prefix}${command.help.name}${" ".repeat(longest - command.help.name.length)} :: ${command.help.description}`); + res(); + } res(); }) .catch(() => { diff --git a/functions/loadCommandInhibitors.js b/functions/loadCommandInhibitors.js index 16a03808..d2410e73 100644 --- a/functions/loadCommandInhibitors.js +++ b/functions/loadCommandInhibitors.js @@ -7,7 +7,7 @@ const loadCommandInhibitors = (client, baseDir) => new Promise(async (resolve, r const files = await client.funcs.getFileListing(client, baseDir, "inhibitors").catch(err => client.funcs.log(err, "error")); try { files.forEach((f) => { - const props = require(`${f.path}/${f.base}`); + const props = require(`${f.path}${path.sep}${f.base}`); client.commandInhibitors.set(f.name, props); }); resolve(); diff --git a/functions/loadFunctions.js b/functions/loadFunctions.js index 7f47f5fd..14cd3d3b 100644 --- a/functions/loadFunctions.js +++ b/functions/loadFunctions.js @@ -10,7 +10,7 @@ const loadFunctions = (client, baseDir) => new Promise(async (resolve, reject) = files.forEach((f) => { const file = f.split("."); if (file[0] === "loadFunctions") return; - client.funcs[file[0]] = require(`${dir}/${f}`); + client.funcs[file[0]] = require(`${dir}${path.sep}${f}`); }); resolve(); } catch (e) { diff --git a/functions/loadMessageMonitors.js b/functions/loadMessageMonitors.js index 35eb0c0e..ff7bd48d 100644 --- a/functions/loadMessageMonitors.js +++ b/functions/loadMessageMonitors.js @@ -7,7 +7,7 @@ const loadMessageMonitors = (client, baseDir) => new Promise(async (resolve, rej const files = await client.funcs.getFileListing(client, baseDir, "monitors").catch(err => client.funcs.log(err, "error")); try { files.forEach((f) => { - const props = require(`${f.path}/${f.base}`); + const props = require(`${f.path}${path.sep}${f.base}`); client.messageMonitors.set(f.name, props); }); resolve(); diff --git a/functions/loadProviders.js b/functions/loadProviders.js index 50da5988..54ce37cd 100644 --- a/functions/loadProviders.js +++ b/functions/loadProviders.js @@ -7,7 +7,7 @@ const loadProviders = (client, baseDir) => new Promise(async (resolve, reject) = const files = await client.funcs.getFileListing(client, baseDir, "providers").catch(err => client.funcs.log(err, "error")); try { files.forEach((f) => { - const props = require(`${dir}/${f}`); + const props = require(`${dir}${path.sep}${f}`); client.providers.set(f.name, props); }); resolve(); diff --git a/functions/loadSingleCommand.js b/functions/loadSingleCommand.js index 759a3de0..bd4cc007 100644 --- a/functions/loadSingleCommand.js +++ b/functions/loadSingleCommand.js @@ -19,6 +19,9 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi }); delete require.cache[require.resolve(loadPath)]; cmd = require(loadPath); + if (cmd.init) { + cmd.init(client); + } } catch (e) { reject(`Could not load existing command data: ${e.stack}`); } @@ -28,9 +31,6 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi if (cmd.conf.selfbot && !client.config.selfbot) { return reject(`The command \`${cmd.help.name}\` is only usable in selfbots!`); } - if (cmd.init) { - cmd.init(client); - } let pathParts = loadPath.split(path.sep); pathParts = pathParts.slice(pathParts.indexOf("commands") + 1); category = client.funcs.toTitleCase(cmd.help.category ? cmd.help.category : (pathParts[0] && pathParts[0].length > 0 && pathParts[0].indexOf(".") === -1 ? pathParts[0] : "General")); diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index 84c89efd..ca371134 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -1,15 +1,17 @@ -module.exports = (client, msg, cmd) => new Promise((resolve, reject) => { +module.exports = (client, msg, cmd, args, selective = false) => new Promise((resolve, reject) => { let usage; const priority = client.commandInhibitors.array(); const sorted = priority.sort((a, b) => a.conf.priority > b.conf.priority); sorted.forEach((inhib) => { - inhib.run(client, msg, cmd) + if (!cmd.conf.spamProtection && !selective) { + inhib.run(client, msg, cmd, args) .then((value) => { if (value) usage = value; }) .catch((error) => { reject(error); }); + } }); - resolve(usage); + setTimeout(() => { resolve(usage); }, 1); }); diff --git a/package.json b/package.json index abc7df97..257c671b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.2", + "version": "0.14.3", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From d6aac33773b4d6f0af52760a2db46ba193290430 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Tue, 24 Jan 2017 10:59:17 +1100 Subject: [PATCH 20/56] [PR] Provider Requires --- functions/loadProviders.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/loadProviders.js b/functions/loadProviders.js index 54ce37cd..99880a6f 100644 --- a/functions/loadProviders.js +++ b/functions/loadProviders.js @@ -7,7 +7,7 @@ const loadProviders = (client, baseDir) => new Promise(async (resolve, reject) = const files = await client.funcs.getFileListing(client, baseDir, "providers").catch(err => client.funcs.log(err, "error")); try { files.forEach((f) => { - const props = require(`${dir}${path.sep}${f}`); + const props = require(`${f.path}${path.sep}${f.base}`); client.providers.set(f.name, props); }); resolve(); From 5c5aef59211d995261487b93ab7be40b1e5639cc Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Tue, 24 Jan 2017 13:29:48 +1100 Subject: [PATCH 21/56] [BUG-FIX] User Count now correctly displays. (#123) SEMVER: Patch Why: * Allows Komada installations that do not fetchAllMembers to be able to have an accurate user count.. --- CHANGELOG.md | 1 + app.js | 6 ++++-- commands/System/stats.js | 46 ++++++++++++++++++++-------------------- package.json | 2 +- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef94b14c..4ee47179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Initialize Function to alleviate undefined errors ### Changed +- Stats command now uses `.reduce` to correctly determine User Count when fetchAllMembers is false - Changed info to no longer mention Evie because she said she was tired of it.. kek - New runCommandInhibitors should be much faster and prioritizes inhibitors via a prioritiy configuration setting. - Old Configuration system now points to the new configuration system, to ease the trouble of updating to newer versions of Komada diff --git a/app.js b/app.js index ca78b0fd..a196b92a 100644 --- a/app.js +++ b/app.js @@ -1,5 +1,7 @@ const Discord = require("discord.js"); const chalk = require("chalk"); +const path = require("path"); + const loadFunctions = require("./functions/loadFunctions.js"); const Config = require("./classes/Config.js"); @@ -27,8 +29,8 @@ exports.start = async (config) => { client.methods.MessageCollector = Discord.MessageCollector; client.methods.Webhook = Discord.WebhookClient; - client.coreBaseDir = `${__dirname}/`; - client.clientBaseDir = `${process.cwd()}/`; + client.coreBaseDir = `${__dirname}${path.sep}`; + client.clientBaseDir = `${process.cwd()}${path.sep}`; client.guildConfs = Config.guildConfs; client.configuration = Config; diff --git a/commands/System/stats.js b/commands/System/stats.js index d22b2fe3..beab64e1 100644 --- a/commands/System/stats.js +++ b/commands/System/stats.js @@ -1,33 +1,33 @@ - const Discord = require("discord.js"); - const moment = require("moment"); - const komada = require("../../package.json"); - require("moment-duration-format"); +const Discord = require("discord.js"); +const moment = require("moment"); +require("moment-duration-format"); - exports.run = (client, msg) => { - const duration = moment.duration(client.uptime).format(" D [days], H [hrs], m [mins], s [secs]"); - msg.channel.sendCode("asciidoc", `= STATISTICS = +exports.run = (client, msg) => { + const komada = require(`${client.coreBaseDir}/package.json`); // eslint-disable-line + const duration = moment.duration(client.uptime).format(" D [days], H [hrs], m [mins], s [secs]"); + msg.channel.sendCode("asciidoc", `= STATISTICS = • Mem Usage :: ${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB • Uptime :: ${duration} -• Users :: ${client.users.size.toLocaleString()} +• Users :: ${client.guilds.reduce((a, b) => a + b.memberCount, 0).toLocaleString()} • Servers :: ${client.guilds.size.toLocaleString()} • Channels :: ${client.channels.size.toLocaleString()} • Komada :: v${komada.version} • Discord.js :: v${Discord.version}`); - }; +}; - exports.conf = { - enabled: true, - guildOnly: false, - aliases: ["details", "what"], - permLevel: 0, - botPerms: [], - requiredFuncs: [], - }; +exports.conf = { + enabled: true, + guildOnly: false, + aliases: ["details", "what"], + permLevel: 0, + botPerms: [], + requiredFuncs: [], +}; - exports.help = { - name: "stats", - description: "Provides some details about the bot and stats.", - usage: "", - usageDelim: "", - }; +exports.help = { + name: "stats", + description: "Provides some details about the bot and stats.", + usage: "", + usageDelim: "", +}; diff --git a/package.json b/package.json index 257c671b..3ccfc232 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.3", + "version": "0.14.4", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 2163ea2654bf2f276c5a5324ad41f89f422d172b Mon Sep 17 00:00:00 2001 From: CyberiumShadow Date: Tue, 24 Jan 2017 15:01:30 +1100 Subject: [PATCH 22/56] [BUG-FIX] Help Command is fixed SEMVER: Patch Why: * A Fix to bring the Help Command back into working order This change addresses the need by: * Changing the buildHelp code from `msg.author.permlvl` to `msg.member.permlvl` --- CHANGELOG.md | 1 + commands/System/help.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ee47179..25a21896 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Fixed +- Help command is now working. `msg.author.permlvl => msg.member.permlvl`. - Bunch of fixes for Inhibitors/Commands - Fixed Typo in disable - Fixed Help Command sending extra message when DMed diff --git a/commands/System/help.js b/commands/System/help.js index b458da81..82da434b 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -58,7 +58,7 @@ const buildHelp = (client, msg) => new Promise((resolve) => { mps.push(new Promise((res) => { client.funcs.runCommandInhibitors(client, msg, command, [], true) .then(() => { - if (command.conf.permLevel <= msg.author.permLevel) { + if (command.conf.permLevel <= msg.member.permLevel) { const cat = command.help.category; const subcat = command.help.subCategory; if (!help.hasOwnProperty(cat)) help[cat] = {}; diff --git a/package.json b/package.json index 3ccfc232..ed054124 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.4", + "version": "0.14.5", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 2777e26ed28b7b200defddbf6c497f9e01983438 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Tue, 24 Jan 2017 15:33:33 +1100 Subject: [PATCH 23/56] Removal of console.log(params) --- app.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app.js b/app.js index a196b92a..4d236d61 100644 --- a/app.js +++ b/app.js @@ -96,7 +96,6 @@ exports.start = async (config) => { } if (!cmd) return; client.funcs.runCommandInhibitors(client, msg, cmd).then((params) => { - console.log(params); client.funcs.log(commandLog); cmd.run(client, msg, params); }) From 740470efe0fff196ffc94eda92201ded528188c4 Mon Sep 17 00:00:00 2001 From: Faith Date: Tue, 24 Jan 2017 18:25:16 -0500 Subject: [PATCH 24/56] [BUG-FIX] Prompts & Help (#124) * Prompts Fix * [PR] Msg.member.permLevel -> msg.author.permLevel * [PR] Changelog for Prompts * More AwaitMessage/Prompt Fixes * [PR] Priority Fix for Inhibs * [PR] Cmd.Rejected * [PR] Fix for cmd.Rejected * [PR] .some() Change * [PR] Eslint Disable --- CHANGELOG.md | 4 ++++ app.js | 1 + commands/System/help.js | 2 +- functions/awaitMessage.js | 6 ++++-- functions/runCommandInhibitors.js | 5 +++-- inhibitors/permissions.js | 4 +--- package.json | 2 +- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25a21896..4d631a97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Initialize Function to alleviate undefined errors ### Changed +- Inhibitors now use .some() to determine if more inhibitors should be ran. - Stats command now uses `.reduce` to correctly determine User Count when fetchAllMembers is false - Changed info to no longer mention Evie because she said she was tired of it.. kek - New runCommandInhibitors should be much faster and prioritizes inhibitors via a prioritiy configuration setting. @@ -24,6 +25,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Fixed +- Inhibitors now running in the correct order +- Fixed Prompts sending an extra message. +- Help command back to msg.author... - Help command is now working. `msg.author.permlvl => msg.member.permlvl`. - Bunch of fixes for Inhibitors/Commands - Fixed Typo in disable diff --git a/app.js b/app.js index 4d236d61..9bcb2bd8 100644 --- a/app.js +++ b/app.js @@ -101,6 +101,7 @@ exports.start = async (config) => { }) .catch((reason) => { if (reason) { + if (reason instanceof Promise) return; if (reason.stack) client.funcs.log(reason.stack, "error"); msg.channel.sendCode("", reason).catch(console.error); } diff --git a/commands/System/help.js b/commands/System/help.js index 82da434b..b458da81 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -58,7 +58,7 @@ const buildHelp = (client, msg) => new Promise((resolve) => { mps.push(new Promise((res) => { client.funcs.runCommandInhibitors(client, msg, command, [], true) .then(() => { - if (command.conf.permLevel <= msg.member.permLevel) { + if (command.conf.permLevel <= msg.author.permLevel) { const cat = command.help.category; const subcat = command.help.subCategory; if (!help.hasOwnProperty(cat)) help[cat] = {}; diff --git a/functions/awaitMessage.js b/functions/awaitMessage.js index 0c2c674d..5c57ee4f 100644 --- a/functions/awaitMessage.js +++ b/functions/awaitMessage.js @@ -10,14 +10,16 @@ module.exports = async (client, msg, cmd, args, error) => { const message = await msg.channel.sendMessage(`<@!${msg.member.id}> | **${error}** | You have **30** seconds to respond to this prompt with a valid argument. Type **"ABORT"** to abort this prompt.`).catch(err => client.funcs.log(err, "error")); const param = await msg.channel.awaitMessages(response => response.member.id === msg.author.id && response.id !== message.id, options).catch(err => client.funcs.log(err, "error")); message.delete(); + if (!param) return "Aborted"; if (param.first().content.toLowerCase() === "abort") return "Aborted"; args.push(param.first().content); - const params = client.funcs.runCommandInhibitors(client, msg, cmd, args) + client.funcs.runCommandInhibitors(client, msg, cmd, args) + .then(params => cmd.run(client, msg, params)) .catch((reason) => { if (reason) { + if (reason instanceof Promise) return; if (reason.stack) client.funcs.log(reason.stack, "error"); msg.channel.sendCode("", reason).catch(console.error); } }); - return cmd.run(client, msg, params); }; diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index ca371134..1eb6f9e0 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -1,8 +1,8 @@ module.exports = (client, msg, cmd, args, selective = false) => new Promise((resolve, reject) => { let usage; const priority = client.commandInhibitors.array(); - const sorted = priority.sort((a, b) => a.conf.priority > b.conf.priority); - sorted.forEach((inhib) => { + const sorted = priority.sort((a, b) => a.conf.priority < b.conf.priority); + sorted.some((inhib) => { // eslint-disable-line if (!cmd.conf.spamProtection && !selective) { inhib.run(client, msg, cmd, args) .then((value) => { @@ -10,6 +10,7 @@ module.exports = (client, msg, cmd, args, selective = false) => new Promise((res }) .catch((error) => { reject(error); + return true; }); } }); diff --git a/inhibitors/permissions.js b/inhibitors/permissions.js index 4fac1098..29539dd5 100644 --- a/inhibitors/permissions.js +++ b/inhibitors/permissions.js @@ -6,9 +6,7 @@ exports.conf = { exports.run = (client, msg, cmd) => new Promise(async (resolve, reject) => { const permlvl = await client.funcs.permissionLevel(client, msg.author, msg.guild).catch(err => client.funcs.log(err, "error")); - if (msg.guild) { - msg.member.permLevel = permlvl; - } + msg.author.permLevel = permlvl; if (permlvl >= cmd.conf.permLevel) { resolve(); } else { diff --git a/package.json b/package.json index ed054124..a83a4eaa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.5", + "version": "0.14.6", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 330fcf67a6c469dcde2a922aa843270c1b4f7233 Mon Sep 17 00:00:00 2001 From: Faith Date: Tue, 24 Jan 2017 18:39:51 -0500 Subject: [PATCH 25/56] [BUG-FIX] Lotsa Bugs (#125) * Prompts Fix * [PR] Msg.member.permLevel -> msg.author.permLevel * [PR] Changelog for Prompts * More AwaitMessage/Prompt Fixes * [PR] Priority Fix for Inhibs * [PR] Cmd.Rejected * [PR] Fix for cmd.Rejected * [PR] .some() Change * [PR] Eslint Disable * Events Crucial Fix --- functions/loadEvents.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/loadEvents.js b/functions/loadEvents.js index 1026efec..55dca720 100644 --- a/functions/loadEvents.js +++ b/functions/loadEvents.js @@ -11,7 +11,7 @@ const loadEvents = (client, baseDir, counts) => new Promise(async (resolve) => { let files = await client.funcs.getFileListing(client, baseDir, "events").catch(err => client.funcs.log(err, "error")); files = files.filter(f => events.includes(f.name)); files.forEach((f) => { - client.on(f.name, (...args) => require(`${dir}/${f}`).run(client, ...args)); + client.on(f.name, (...args) => require(`${f.path}${path.sep}${f.name}`).run(client, ...args)); counts++; }); resolve(counts); From afe48fd007b946c9106d3630fd7ce294790e72f8 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Thu, 26 Jan 2017 21:47:39 +1100 Subject: [PATCH 26/56] [PR] Fixed Event Loading (#126) * [PR] Fixed Event Loading * Update CHANGELOG.md * Update package.json --- CHANGELOG.md | 1 + functions/loadEvents.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d631a97..3894c651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Fixed +- Events.... now should be fixed - Inhibitors now running in the correct order - Fixed Prompts sending an extra message. - Help command back to msg.author... diff --git a/functions/loadEvents.js b/functions/loadEvents.js index 55dca720..b3e55b42 100644 --- a/functions/loadEvents.js +++ b/functions/loadEvents.js @@ -11,7 +11,7 @@ const loadEvents = (client, baseDir, counts) => new Promise(async (resolve) => { let files = await client.funcs.getFileListing(client, baseDir, "events").catch(err => client.funcs.log(err, "error")); files = files.filter(f => events.includes(f.name)); files.forEach((f) => { - client.on(f.name, (...args) => require(`${f.path}${path.sep}${f.name}`).run(client, ...args)); + client.on(f.name, (...args) => require(`${f.path}${path.sep}${f.base}`).run(client, ...args)); counts++; }); resolve(counts); diff --git a/package.json b/package.json index a83a4eaa..6e2849b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.6", + "version": "0.14.7", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From f465d5ad08cd1c43188e26d22c11aea2b2faaaa5 Mon Sep 17 00:00:00 2001 From: Faith Date: Thu, 26 Jan 2017 20:16:14 -0500 Subject: [PATCH 27/56] You've encountered a wild Buggerfree! (#127) --- CHANGELOG.md | 2 ++ classes/Config.js | 53 +++++++------------------------ functions/runCommandInhibitors.js | 2 +- package.json | 2 +- 4 files changed, 15 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3894c651..752d6145 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Fixed +- Fixed Default configuration not being read before guild configurations are created +- Inhibitors now are correctly 'disabled' when set to be. - Events.... now should be fixed - Inhibitors now running in the correct order - Fixed Prompts sending an extra message. diff --git a/classes/Config.js b/classes/Config.js index b255b587..c138ffc3 100644 --- a/classes/Config.js +++ b/classes/Config.js @@ -39,31 +39,6 @@ class Config { * @readonly */ Object.defineProperty(this, "_dataDir", { value: dataDir }); - /** - * The default prefix to use for the bot if one is not in the configuration. - * @type {String} - */ - this.prefix = new StringConfig(this, client.config.prefix); - /** - * The array of disabled commands that are unable to be used in a guild. - * @type {Array} - */ - this.disabledCommands = new ArrayConfig(this, []); - /** - * The default role name to look for when assigning mod level permissions. - * @type {String} - */ - this.modRole = new StringConfig(this, "Mods"); - /** - * The default role name to look for when assigning admin level permissions. - * @type {String} - */ - this.adminRole = new StringConfig(this, "Devs"); - /** - * The default language to use for the bot. - * @type {String} - */ - this.lang = new StringConfig(this, "en"); if (typeof config === "object") { for (const prop in config) { if (config[prop].type === "String") { @@ -370,7 +345,7 @@ class Config { * @returns {null} * @static */ - static initialize(client) { + static async initialize(client) { defaultConf = { prefix: { type: "String", data: client.config.prefix }, disabledCommands: { type: "Array", data: [] }, @@ -379,24 +354,18 @@ class Config { lang: { type: "String", data: "en" }, }; dataDir = path.resolve(`${client.clientBaseDir}${path.sep}bwd${path.sep}conf`); - fs.ensureFileAsync(`${dataDir}${path.sep}${defaultFile}`) - .then(() => { - fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`)) - .then((conf) => { - defaultConf = conf; - }).catch(() => { - fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf); - }); - client.guilds.forEach((guild) => { - fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)) - .then((thisConf) => { - guildConfs.set(guild.id, new Config(client, guild.id, thisConf)); - }).catch(() => { - guildConfs.set(guild.id, new Config(client, guild.id)); - }); + await fs.ensureFileAsync(`${dataDir}${path.sep}${defaultFile}`).catch(err => client.funcs.log(err, "error")); + const conf = await fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`)).catch(() => fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`)); + if (conf) defaultConf = conf; + client.guilds.forEach((guild) => { + fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)) + .then((thisConf) => { + guildConfs.set(guild.id, new Config(client, guild.id, thisConf)); + }).catch(() => { + guildConfs.set(guild.id, new Config(client, guild.id, defaultConf)); }); - return null; }); + return null; } } diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index 1eb6f9e0..7cfc9b57 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -3,7 +3,7 @@ module.exports = (client, msg, cmd, args, selective = false) => new Promise((res const priority = client.commandInhibitors.array(); const sorted = priority.sort((a, b) => a.conf.priority < b.conf.priority); sorted.some((inhib) => { // eslint-disable-line - if (!cmd.conf.spamProtection && !selective) { + if (!inhib.conf.spamProtection && !selective && inhib.conf.enabled) { inhib.run(client, msg, cmd, args) .then((value) => { if (value) usage = value; diff --git a/package.json b/package.json index 6e2849b5..fee977fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.7", + "version": "0.14.8", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 81aec48cc01c54e59a7d8be04af90c97974c2d97 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Fri, 27 Jan 2017 12:29:22 +1100 Subject: [PATCH 28/56] You encountered a lone Buggerfree. --- functions/runCommandInhibitors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index 7cfc9b57..5967f593 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -3,7 +3,7 @@ module.exports = (client, msg, cmd, args, selective = false) => new Promise((res const priority = client.commandInhibitors.array(); const sorted = priority.sort((a, b) => a.conf.priority < b.conf.priority); sorted.some((inhib) => { // eslint-disable-line - if (!inhib.conf.spamProtection && !selective && inhib.conf.enabled) { + if (!inhib.conf.spamProtection && (!selective && inhib.conf.enabled)) { inhib.run(client, msg, cmd, args) .then((value) => { if (value) usage = value; From eb19e3aef63113476e08582af47e9351bc413374 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Fri, 27 Jan 2017 12:48:13 +1100 Subject: [PATCH 29/56] You saw nothing Faith --- functions/runCommandInhibitors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index 5967f593..ee0c65b5 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -3,7 +3,7 @@ module.exports = (client, msg, cmd, args, selective = false) => new Promise((res const priority = client.commandInhibitors.array(); const sorted = priority.sort((a, b) => a.conf.priority < b.conf.priority); sorted.some((inhib) => { // eslint-disable-line - if (!inhib.conf.spamProtection && (!selective && inhib.conf.enabled)) { + if (!selective && inhib.conf.enabled) { inhib.run(client, msg, cmd, args) .then((value) => { if (value) usage = value; From f18ae73fb73b7181564e49e3ca4152c09daa6a8f Mon Sep 17 00:00:00 2001 From: CyberiumShadow Date: Sun, 29 Jan 2017 12:00:14 +1100 Subject: [PATCH 30/56] [BUG-FIX] Fixes Dep Installation on Client Functions SEMVER: N/A --- CHANGELOG.md | 1 + functions/loadFunctions.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 752d6145..ae0b1f19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Fixed +- LoadFunctions now calls itself when installing a new dependency in a client function - Fixed Default configuration not being read before guild configurations are created - Inhibitors now are correctly 'disabled' when set to be. - Events.... now should be fixed diff --git a/functions/loadFunctions.js b/functions/loadFunctions.js index 14cd3d3b..a541e34d 100644 --- a/functions/loadFunctions.js +++ b/functions/loadFunctions.js @@ -21,7 +21,7 @@ const loadFunctions = (client, baseDir) => new Promise(async (resolve, reject) = console.error(error); process.exit(); }); - client.funcs.loadFunctions(client); + loadFunctions(client); } else { reject(e); } From 733212975579bf2e79f994513f80faf9edae2da0 Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 29 Jan 2017 06:26:12 -0500 Subject: [PATCH 31/56] Extended help (#128) * Extended Help added * [PR] Inhibitors changed to await * [PR] Usage Spell checking * [PR] Bump && Changelog --- CHANGELOG.md | 3 +++ commands/System/help.js | 4 ++-- functions/runCommandInhibitors.js | 4 ++-- inhibitors/usage.js | 4 ++-- package.json | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae0b1f19..fd9f5664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- New extended help feature added. - New Beta Configuration (Needs heavy testing) - New Argument Prompting for Commands - New Initialize Function to alleviate undefined errors @@ -25,6 +26,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Fixed +- Inhibitors now await +- Usage typos fixed - LoadFunctions now calls itself when installing a new dependency in a client function - Fixed Default configuration not being read before guild configurations are created - Inhibitors now are correctly 'disabled' when set to be. diff --git a/commands/System/help.js b/commands/System/help.js index b458da81..c4d05914 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -24,9 +24,9 @@ exports.run = (client, msg, [cmd]) => { } else if (client.commands.has(cmd)) { cmd = client.commands.get(cmd); if (!client.config.selfbot) { - msg.author.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}`); + msg.author.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}Extended Help ::\n${cmd.help.extendedHelp ? cmd.help.extendedHelp : "No extended help available."}`); } else { - msg.channel.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}`); + msg.channel.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}\nExtended Help ::\n${cmd.help.extendedHelp ? cmd.help.extendedHelp : "No extended help available."}`); } } }; diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index ee0c65b5..9543ff7f 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -2,9 +2,9 @@ module.exports = (client, msg, cmd, args, selective = false) => new Promise((res let usage; const priority = client.commandInhibitors.array(); const sorted = priority.sort((a, b) => a.conf.priority < b.conf.priority); - sorted.some((inhib) => { // eslint-disable-line + sorted.some(async (inhib) => { // eslint-disable-line if (!selective && inhib.conf.enabled) { - inhib.run(client, msg, cmd, args) + await inhib.run(client, msg, cmd, args) .then((value) => { if (value) usage = value; }) diff --git a/inhibitors/usage.js b/inhibitors/usage.js index ebb9fd1a..8752f617 100644 --- a/inhibitors/usage.js +++ b/inhibitors/usage.js @@ -55,9 +55,9 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje } else { if (client.config.cmdPrompt) { args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `Your option did not litterally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`)); + return reject(client.funcs.awaitMessage(client, msg, cmd, args, `Your option did not literally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`)); } - return reject(`Your option did not litterally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`); + return reject(`Your option did not literally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`); } break; case "msg": diff --git a/package.json b/package.json index fee977fc..9f7a4ac8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.8", + "version": "0.14.9", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 6b8eac0f8224ccc2cb21106eb3c62af33f2f0f47 Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 29 Jan 2017 07:20:57 -0500 Subject: [PATCH 32/56] Potential Inhib fix (#129) --- functions/runCommandInhibitors.js | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index 9543ff7f..ba712e5f 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -1,18 +1,26 @@ module.exports = (client, msg, cmd, args, selective = false) => new Promise((resolve, reject) => { let usage; + let error; const priority = client.commandInhibitors.array(); const sorted = priority.sort((a, b) => a.conf.priority < b.conf.priority); - sorted.some(async (inhib) => { // eslint-disable-line - if (!selective && inhib.conf.enabled) { - await inhib.run(client, msg, cmd, args) - .then((value) => { - if (value) usage = value; - }) - .catch((error) => { - reject(error); - return true; - }); + sorted.some((inhib) => { // eslint-disable-line + if (!msg.cmdInhibited) { + if (!selective && inhib.conf.enabled) { + inhib.run(client, msg, cmd, args) + .then((value) => { + if (value) usage = value; + }) + .catch((err) => { + if (err) { + error = err; + msg.cmdInhibited = true; + } + }); + } + } else { + return true; } }); - setTimeout(() => { resolve(usage); }, 1); + if (msg.cmdInhibited) return setTimeout(() => { reject(error); }, 1); + return setTimeout(() => { resolve(usage); }, 1); }); From f2ae17021265ed4b66cde9e568b228a2cedc70bb Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 29 Jan 2017 16:22:25 -0500 Subject: [PATCH 33/56] Inhibitor Fix (#130) * Inhibitor Fix * Removed useless async * [PR] Help Fix --- commands/System/help.js | 2 +- functions/runCommandInhibitors.js | 28 +++++++++++----------------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/commands/System/help.js b/commands/System/help.js index c4d05914..9151fd05 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -24,7 +24,7 @@ exports.run = (client, msg, [cmd]) => { } else if (client.commands.has(cmd)) { cmd = client.commands.get(cmd); if (!client.config.selfbot) { - msg.author.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}Extended Help ::\n${cmd.help.extendedHelp ? cmd.help.extendedHelp : "No extended help available."}`); + msg.author.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}\nExtended Help ::\n${cmd.help.extendedHelp ? cmd.help.extendedHelp : "No extended help available."}`); } else { msg.channel.sendCode("asciidoc", `= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}\nExtended Help ::\n${cmd.help.extendedHelp ? cmd.help.extendedHelp : "No extended help available."}`); } diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index ba712e5f..2a768d01 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -1,26 +1,20 @@ module.exports = (client, msg, cmd, args, selective = false) => new Promise((resolve, reject) => { let usage; - let error; const priority = client.commandInhibitors.array(); const sorted = priority.sort((a, b) => a.conf.priority < b.conf.priority); sorted.some((inhib) => { // eslint-disable-line - if (!msg.cmdInhibited) { - if (!selective && inhib.conf.enabled) { - inhib.run(client, msg, cmd, args) - .then((value) => { - if (value) usage = value; - }) - .catch((err) => { - if (err) { - error = err; - msg.cmdInhibited = true; - } - }); - } - } else { - return true; + if (!selective && inhib.conf.enabled) { + inhib.run(client, msg, cmd, args) + .then((params) => { + if (params) usage = params; + }) + .catch((err) => { + if (err) { + reject(err); + return true; + } + }); } }); - if (msg.cmdInhibited) return setTimeout(() => { reject(error); }, 1); return setTimeout(() => { resolve(usage); }, 1); }); From b1ab7881e6391c9fa0144b7b7e960114afb25cfd Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Mon, 30 Jan 2017 10:17:40 +1100 Subject: [PATCH 34/56] [Feature] New Command & Inhibitor (#131) * [FEATURE] Transfer Command Added SEMVER: N/A Why: * A transfer command that copies "pieces" from Core directories to client directories has been added. * [FEATURE] Added runIn Inhibitor SEMVER: Minor Why: * This now allows a better variety in how/where commands can be run This change addresses the need by: * changing the boolean-based guildOnly property to an array of channel types * Sounds better --- CHANGELOG.md | 2 ++ commands/System/conf.js | 2 +- commands/System/disable.js | 2 +- commands/System/download.js | 2 +- commands/System/enable.js | 2 +- commands/System/eval.js | 2 +- commands/System/help.js | 2 +- commands/System/info.js | 2 +- commands/System/invite.js | 2 +- commands/System/ping.js | 2 +- commands/System/reboot.js | 2 +- commands/System/reload.js | 2 +- commands/System/stats.js | 2 +- commands/System/transfer.js | 48 +++++++++++++++++++++++++++ inhibitors/{guildOnly.js => runIn.js} | 4 +-- package.json | 2 +- 16 files changed, 65 insertions(+), 15 deletions(-) create mode 100644 commands/System/transfer.js rename inhibitors/{guildOnly.js => runIn.js} (57%) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd9f5664..a6123630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- New Core Command "Transfer" - New extended help feature added. - New Beta Configuration (Needs heavy testing) - New Argument Prompting for Commands - New Initialize Function to alleviate undefined errors ### Changed +- guildOnly Inhibitor is now a `runIn` Inhibitor. - Inhibitors now use .some() to determine if more inhibitors should be ran. - Stats command now uses `.reduce` to correctly determine User Count when fetchAllMembers is false - Changed info to no longer mention Evie because she said she was tired of it.. kek diff --git a/commands/System/conf.js b/commands/System/conf.js index f24425c8..4c29f183 100644 --- a/commands/System/conf.js +++ b/commands/System/conf.js @@ -30,7 +30,7 @@ exports.run = (client, msg, [action, key, ...value]) => { exports.conf = { enabled: true, - guildOnly: true, + runIn: ["text"], aliases: [], permLevel: 3, botPerms: [], diff --git a/commands/System/disable.js b/commands/System/disable.js index a93f79ec..64493d89 100644 --- a/commands/System/disable.js +++ b/commands/System/disable.js @@ -42,7 +42,7 @@ exports.run = (client, msg, [type, name]) => { exports.conf = { enabled: true, - guildOnly: false, + runIn: ["text", "dm", "group"], aliases: [], permLevel: 10, botPerms: [], diff --git a/commands/System/download.js b/commands/System/download.js index da047503..a5eac7c3 100644 --- a/commands/System/download.js +++ b/commands/System/download.js @@ -244,7 +244,7 @@ ${modules} exports.conf = { enabled: true, - guildOnly: true, + runIn: ["text"], aliases: [], permLevel: 5, botPerms: [], diff --git a/commands/System/enable.js b/commands/System/enable.js index 76c50b3f..a79319c3 100644 --- a/commands/System/enable.js +++ b/commands/System/enable.js @@ -42,7 +42,7 @@ exports.run = (client, msg, [type, name]) => { exports.conf = { enabled: true, - guildOnly: false, + runIn: ["text", "dm", "group"], aliases: [], permLevel: 10, botPerms: [], diff --git a/commands/System/eval.js b/commands/System/eval.js index aae2cb25..b8946824 100644 --- a/commands/System/eval.js +++ b/commands/System/eval.js @@ -17,7 +17,7 @@ exports.run = (client, msg, [code]) => { exports.conf = { enabled: true, - guildOnly: false, + runIn: ["text", "dm", "group"], aliases: ["ev"], permLevel: 10, botPerms: [], diff --git a/commands/System/help.js b/commands/System/help.js index 9151fd05..4c588dcd 100644 --- a/commands/System/help.js +++ b/commands/System/help.js @@ -33,7 +33,7 @@ exports.run = (client, msg, [cmd]) => { exports.conf = { enabled: true, - guildOnly: false, + runIn: ["text", "dm", "group"], aliases: [], permLevel: 0, botPerms: [], diff --git a/commands/System/info.js b/commands/System/info.js index db3988ad..5889baa4 100644 --- a/commands/System/info.js +++ b/commands/System/info.js @@ -4,7 +4,7 @@ exports.run = (client, msg) => { exports.conf = { enabled: true, - guildOnly: false, + runIn: ["text", "dm", "group"], aliases: ["details", "what"], permLevel: 0, botPerms: [], diff --git a/commands/System/invite.js b/commands/System/invite.js index aba0c9f0..c2ab0f39 100644 --- a/commands/System/invite.js +++ b/commands/System/invite.js @@ -15,7 +15,7 @@ exports.help = { exports.conf = { enabled: true, - guildOnly: true, + runIn: ["text"], aliases: [], permLevel: 0, botPerms: [], diff --git a/commands/System/ping.js b/commands/System/ping.js index 394910a5..7e3c99aa 100644 --- a/commands/System/ping.js +++ b/commands/System/ping.js @@ -5,7 +5,7 @@ exports.run = async (client, msg) => { exports.conf = { enabled: true, - guildOnly: false, + runIn: ["text", "dm", "group"], aliases: [], permLevel: 0, botPerms: [], diff --git a/commands/System/reboot.js b/commands/System/reboot.js index 56287a77..17335163 100644 --- a/commands/System/reboot.js +++ b/commands/System/reboot.js @@ -10,7 +10,7 @@ exports.run = (client, msg) => { exports.conf = { enabled: true, - guildOnly: false, + runIn: ["text", "dm", "group"], aliases: [], permLevel: 10, botPerms: [], diff --git a/commands/System/reload.js b/commands/System/reload.js index 87857115..6901f0ef 100644 --- a/commands/System/reload.js +++ b/commands/System/reload.js @@ -46,7 +46,7 @@ exports.run = async (client, msg, [type, name]) => { exports.conf = { enabled: true, - guildOnly: false, + runIn: ["text", "dm", "group"], aliases: ["r", "load"], permLevel: 10, botPerms: [], diff --git a/commands/System/stats.js b/commands/System/stats.js index beab64e1..aeb22871 100644 --- a/commands/System/stats.js +++ b/commands/System/stats.js @@ -18,7 +18,7 @@ exports.run = (client, msg) => { exports.conf = { enabled: true, - guildOnly: false, + runIn: ["text", "dm", "group"], aliases: ["details", "what"], permLevel: 0, botPerms: [], diff --git a/commands/System/transfer.js b/commands/System/transfer.js new file mode 100644 index 00000000..463e627f --- /dev/null +++ b/commands/System/transfer.js @@ -0,0 +1,48 @@ +const fs = require("fs-extra-promise"); +const path = require("path"); + +exports.run = (client, msg, [type, name]) => { + copy(client, msg, type, name); +}; + +exports.conf = { + enabled: true, + runIn: ["text", "dm", "group"], + aliases: [], + permLevel: 10, + botPerms: [], + requiredFuncs: [], +}; + +exports.help = { + name: "transfer", + description: "Transfers a core command to the user folders", + usage: " ", + usageDelim: " ", +}; + +function copy(client, msg, type, name) { + const coreDir = client.coreBaseDir; + const clientDir = client.clientBaseDir; + if (type !== "command") { + fs.copyAsync(path.resolve(`${coreDir}/${type}s/${name}.js`), path.resolve(`${clientDir}/${type}s/${name}.js`)) + .then(() => { + client.funcs.reload[type](client, client.clientBaseDir, name).catch(response => msg.edit(`:x: ${response}`)); + msg.channel.send(`:white_check_mark: Successfully Transferred ${type}: ${name}`); + }) + .catch((err) => { + msg.channel.send(`Transfer of ${type}: ${name} to Client has failed. Please check your Console.`); + client.funcs.log(err.stack, "error"); + }); + } else { + fs.copyAsync(path.resolve(`${coreDir}/${type}s/System/${name}.js`), path.resolve(`${clientDir}/${type}s/${name}.js`)) + .then(() => { + client.funcs.reload[type](client, client.clientBaseDir, name).catch(response => msg.edit(`:x: ${response}`)); + msg.channel.send(`:white_check_mark: Successfully Transferred ${type}: ${name}`); + }) + .catch((err) => { + msg.channel.send(`Transfer of ${type}: ${name} to Client has failed. Please check your Console.`); + client.funcs.log(err.stack, "error"); + }); + } +} diff --git a/inhibitors/guildOnly.js b/inhibitors/runIn.js similarity index 57% rename from inhibitors/guildOnly.js rename to inhibitors/runIn.js index d8a49947..a91e9cb9 100644 --- a/inhibitors/guildOnly.js +++ b/inhibitors/runIn.js @@ -5,9 +5,9 @@ exports.conf = { }; exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { - if (msg.guild || !cmd.conf.guildOnly) { + if (cmd.conf.runIn.includes(msg.channel.type)) { resolve(); } else { - reject("This command is only available in a guild."); + reject(`This command is only avaliable in ${cmd.conf.runIn.join(" ")} channels`); } }); diff --git a/package.json b/package.json index 9f7a4ac8..92c01fa1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.14.9", + "version": "0.15.0", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 7dbbfdc172288fec322c8349de6f4b205027c34e Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 29 Jan 2017 19:08:16 -0500 Subject: [PATCH 35/56] Removal of Async/Await... not working properly (#132) --- classes/Config.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/classes/Config.js b/classes/Config.js index c138ffc3..4511b68f 100644 --- a/classes/Config.js +++ b/classes/Config.js @@ -345,7 +345,7 @@ class Config { * @returns {null} * @static */ - static async initialize(client) { + static initialize(client) { defaultConf = { prefix: { type: "String", data: client.config.prefix }, disabledCommands: { type: "Array", data: [] }, @@ -354,9 +354,12 @@ class Config { lang: { type: "String", data: "en" }, }; dataDir = path.resolve(`${client.clientBaseDir}${path.sep}bwd${path.sep}conf`); - await fs.ensureFileAsync(`${dataDir}${path.sep}${defaultFile}`).catch(err => client.funcs.log(err, "error")); - const conf = await fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`)).catch(() => fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`)); - if (conf) defaultConf = conf; + fs.ensureFileAsync(`${dataDir}${path.sep}${defaultFile}`).catch(err => client.funcs.log(err, "error")); + fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${defaultFile}`)) + .then((conf) => { + if (conf) defaultConf = conf; + }) + .catch(() => fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`)); client.guilds.forEach((guild) => { fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)) .then((thisConf) => { From b1484fd3240522109f4201138a01fd17e9147284 Mon Sep 17 00:00:00 2001 From: Faith Date: Mon, 30 Jan 2017 20:08:39 -0500 Subject: [PATCH 36/56] [FEATURE] New error handling && Command Handler (#133) * New Errors && Command Handlers.. and other stuff * [PR] Minor Bump && Changelog * [PR] Changelog && uncaughtPromise fix * [PR] Fix for Client being undefined * [PR] Update README --- CHANGELOG.md | 5 + README.md | 222 +++-------------------------- app.js | 67 +++------ functions/awaitMessage.js | 26 ++-- functions/commandHandler.js | 23 +++ functions/getFileListing.js | 1 - functions/loadCommandInhibitors.js | 6 +- functions/loadCommands.js | 8 +- functions/loadEvents.js | 8 +- functions/loadFunctions.js | 4 +- functions/loadMessageMonitors.js | 8 +- functions/loadProviders.js | 8 +- functions/log.js | 9 +- functions/newError.js | 13 ++ functions/permissionLevel.js | 2 +- functions/reload.js | 12 +- inhibitors/requiredFuncs.js | 1 + inhibitors/runIn.js | 1 + inhibitors/usage.js | 162 ++++----------------- package.json | 2 +- 20 files changed, 152 insertions(+), 436 deletions(-) create mode 100644 functions/commandHandler.js create mode 100644 functions/newError.js diff --git a/CHANGELOG.md b/CHANGELOG.md index a6123630..0d74613f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- All Bad Requests/Forbiddens.. etc, now properly give a human readable error in console or chat, depending on the error. +- New Error Creator +- New CommandHandler (Removed it from message event) - New Core Command "Transfer" - New extended help feature added. - New Beta Configuration (Needs heavy testing) @@ -13,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Initialize Function to alleviate undefined errors ### Changed +- Many Files to use the new Error creator - guildOnly Inhibitor is now a `runIn` Inhibitor. - Inhibitors now use .some() to determine if more inhibitors should be ran. - Stats command now uses `.reduce` to correctly determine User Count when fetchAllMembers is false @@ -28,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Fixed +- CmdPrompts should now be fixed completely - Inhibitors now await - Usage typos fixed - LoadFunctions now calls itself when installing a new dependency in a client function diff --git a/README.md b/README.md index 1fde8200..7e82bb5a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Komada +# Komada Framework Documentation [![Discord](https://discordapp.com/api/guilds/260202843686830080/embed.png)](https://discord.gg/dgs8263) [![npm](https://img.shields.io/npm/v/komada.svg?maxAge=3600)](https://www.npmjs.com/package/komada) @@ -6,10 +6,12 @@ [![Build Status](https://travis-ci.org/dirigeants/komada.svg?branch=indev)](https://travis-ci.org/dirigeants/komada) [![David](https://img.shields.io/david/dirigeants/komada.svg?maxAge=3600)](https://david-dm.org/dirigeants/komada) -> "Stay a while, and listen!" - Komada is a modular framework for bots built on top of [Discord.js](https://github.com/hydrabolt/dicord.js). It offers an extremely easy installation, downloadable commands, and a framework to build your own commands, modules, and functions. +## What's with the name? + +Komada is the Croatian word for "pieces", such as puzzle pieces. As usual to find my software name I just shove english words in a translator and see what comes out. But why "pieces"? Because Komada is modular, meaning each "piece" is a standalone part that can be easily replaced, enhanced, reloaded, removed. + ## Installing Komada Time to take the plunge! Komada is on NPM and can be easily installed. @@ -35,213 +37,23 @@ komada.start({ }); ``` -> For all you selfbot users out there, you can add a option ('selfbot': true) to have Komada enabled for selfbot usage. i.e. only respond to commands from you. - -Then, run the following in your folder: - -``` -npm install -node app.js -``` - -> Requires Node 6 or higher (because Discord.js requires that), also requires Discord.js v11, installed automatically with `npm install`. - -## Quick & Dirty Reference Guide -> For creating your own pieces - -Essentially, the way Komada works is that we have *core* pieces (functions, events, commands, etc.) loaded automatically. -But you can add your own pieces easily by adding files to your *local* folders (which are created on first load). - -These pieces are: -- **commands** which add in-chat functionality to your bot. -- **functions** which can be used by other pieces or anywhere in the bot. -- **inhibitors** which are used to check if a command should be run or not. -- **monitors** which are used to check a message before it's a command. -- **events** which are triggered based on what happens in Discord. -- **dataProviders** which are database connectors (in progress at the moment). -- **methods** which are native Discord.js functions - -### Creating a new command - -New commands are created in the `./commands/` folder, where subfolders are -the categories offered in the help command. For instance adding `./commands/Misc/test.js` -will create a command named `test` in the `Misc` category. Subcategories can -also be created by adding a second folder level. - -> If a command is present both in the *core* folders and your client folders, -your command will override the core one. This can let you modify the core -behavior. Note also that you cannot have more than one command with the same name. - -```js -exports.run = (client, msg, [...args]) => { - // Place Code Here -}; - -exports.conf = { - enabled: true, - guildOnly: true, - aliases: [], - permLevel: 0, - botPerms: [], - requiredFuncs: [] -}; - -exports.help = { - name: "name", - description: "Command Description", - usage: "", - usageDelim: "" -}; -``` -> Tip: If you need something created before the command is ever ran, you can specify -exports.init = (client) => {...} to make Komada run that portion of code beforehand. - -`[...args]` represents a variable number of arguments give when the command is -run. The name of the arguments in the array (and their count) is determined -by the `usage` property and its given arguments. - -**Non-obvious options**: -- **enabled**: Set to false to completely disable this command, it cannot be forcefully enabled. -- **aliases**: Array of aliases for the command, which will *also* trigger it. -- **permLevel**: Permission level, controlled via `./functions/permissionLevel.js`. -- **selfbot**: Set to true to only load this command if the bot is configured to be a selfbot. -- **botPerms**: An array of permission strings (such as `"MANAGE_MESSAGES"`) required for the command to run. -- **requiredFuncs**: An array of function names required for this command to execute (dependency). -- **usage**: The usage string as determined by the Argument Usage (see below). - -#### Command Arguments - -**Usage Structure** - -`<>` required argument, `[]` optional argument -`` - -- **Name** Mostly used for debugging message, unless the type is Literal in which it compares the argument to the name. -- **Type** The type of variable you are expecting. -- **Min, Max** Minimum or Maximum for a giving variable (works on strings in terms of length, and on all types of numbers in terms of value) You are allowed to define any combination of min and max. Omit for none, `{min}` for min, `{,max}` for max. -- **Special Repeat Tag** `[...]` will repeat the last usage optionally until you run out of arguments. Useful for doing something like ` [...]` which will allow you to take as many search terms as you want, per your Usage Delimiter. - -**Usage Types** - -- `literal` : Literally equal to the Name. This is the default type if none is defined. -- `str`, `string` : Strings. -- `int`, `integer` : Integers. -- `num`, `number`, `Float` : Floating point numbers. -- `boolean`, : A true or false statement. -- `url` : A URL. -- `msg`, `message` : A message object returned from the message ID (now using fetchMessage as of d3d498c99d5eca98b5cbcefb9838fa7d96f17c93). -- `role` : A role object returned from the role ID or mention. -- `channel` : A channel object returned from the channel ID or channel tag. -- `guild` : A guild object returned from the guild ID. -- `user`, `mention` : A user object returned from the user ID or mention. - -### Creating an event - -Events are placed in `./events/` and their filename must be `eventName.js`. -If a conflicting event is present in both the core and your client, *both* are -loaded and will run when that event is triggered. - -Their structure is the following : - -```js -exports.run = (client, [...args]) => { - // event contents -}; -``` - -Where `[...args]` are arguments you would *normally* get from those events. -For example, while the `ready` event would only have `(client)`, the -`guildMemberAdd` event would be `(guild, member)`. - -### Creating a function - -Functions are available throughout the system, from anywhere. Since they are the -first thing loaded, every other piece can access them. Functions are loaded as -core first, and if your code contains a function of the same name it overrides -the core function. - -Their structure is somewhat freeform, in that they can contain a single function, -or they may be a module composed of more than one functions as a module. It's -not supposed to, but let's keep it between you and me, alright? - -```js -module.exports = (str) => { - return str.replace(/\w\S*/g, function(txt) { - return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); - }); -}; -``` - -The arguments are arbitrary - just like a regular function. It may, or may not, -return anything. Basically any functions. You know what I mean. - -### Creating Inhibitors - -Inhibitors are only ran on commands. They are used to check a variety of conditions -before a command is ever ran, such as checking if a user has the right amount of permissions -to use a command. Inhibitors are loaded as core first, and if your code contains a inhibitor -of the same name it overrides the core inhibitor. - -Their structure is restricted, meaning to work they must be defined exactly like this. +### Configuration Options -```js -exports.conf = { - enabled: true, - spamProtection: false, -}; - -exports.run = (client, msg, cmd) => { - // code here -} -``` +- **botToken**: The MFA token for your bot. To get this, please see [This discord.js Getting Started Guide](https://eslachance.gitbooks.io/discord-js-bot-guide/content/getting-started/the-long-version.html), which explains how to create the bot and get the token. +- **ownerID**: The User ID of the bot owner - you. This gives you the highest possible access to the bot's default commands, including eval! To obtain it, enable Developer Mode in Discord, right-click your name and do "Copy ID". +- **clientID**: The bot's client ID obtained from the same place you got the token. It's at the top of the page! +- **prefix**: The default prefix when the bot first boots up. This option becomes useless after first boot, since the prefix is written to the default configuration system. +- **clientOptions**: These are passed directly to the discord.js library. They are optional. For more information on which options are available, see [ClientOptions in the discord.js docs](https://discord.js.org/#/docs/main/stable/typedef/ClientOptions). -> Note: The order does not matter. - -### Creating Monitors +> For all you selfbot users out there, you can add a option ('selfbot': true) to have Komada enabled for selfbot usage. i.e. only respond to commands from you. -Monitors are special in that they will always run on any message. This is particularly -useful when you need to do checking on the message, such as checking if a message -contains a vulgar word (profanity filter). They are almost completely identical to -inhibitors, the only difference between one is ran on the message, and the other -is ran on the command. Monitors are loaded as core first, and if your code contains -a monitor of the same name it overrides the core monitor. +## Running the bot -Their structure is identical to inhibitors, being the only difference is that you -don't pass a command parameter to them. +Then, run the following in your folder: -```js -exports.conf = { - enabled: true, - spamProtection: false, -}; - -exports.run = (client, msg) => { - // code here -}; ``` - -> Note: Technically, this means that monitors are message events. You can use this trick -to get around the normal amount of message events in Komada.. *cough* - -### Using Methods - -Methods are just Discord.js native functions added to Komada, so that we may -export them to any other piece that we may need them in. For example, if your bot -a larger bot and you need to make use of the shardingManager, but can't do since -it's a native Discord.js function, well now you can. - -Current Methods are: -Collections => `client.methods.Collection` -Rich Embed Builder => `client.methods.Embed` -Message Collector => `client.methods.MessageCollector` -WebhookClient => `client.methods.Webhook` - -To use any of the methods, you follow this same structure: -```js -let method = new client.methods.(OptionalMethodProperties); +npm install +node --harmony app.js ``` -So if you need to create a Message Collector, you will do: -```js -let messageCollector = new client.methods.MessageCollector(channelid, filter, options); -``` +> **Requirements**: Requires Node 7 or higher to run. Depends on Discord.js v11 or higher (the appropriate version is automatically installed). diff --git a/app.js b/app.js index 9bcb2bd8..bd0be74c 100644 --- a/app.js +++ b/app.js @@ -1,19 +1,18 @@ const Discord = require("discord.js"); -const chalk = require("chalk"); const path = require("path"); const loadFunctions = require("./functions/loadFunctions.js"); const Config = require("./classes/Config.js"); -const clk = new chalk.constructor({ enabled: true }); +let client; exports.start = async (config) => { - const client = new Discord.Client(config.clientOptions); + if (typeof config !== "object") throw new TypeError("Configuration for Komada must be an object."); + client = new Discord.Client(config.clientOptions); client.config = config; // Extend client - client.config.init = []; client.funcs = {}; client.helpStructure = new Map(); client.commands = new Discord.Collection(); @@ -56,56 +55,22 @@ exports.start = async (config) => { client.on("message", async (msg) => { if (msg.author.bot) return; - const conf = Config.get(msg.guild); - msg.guildConf = conf; - client.i18n.use(conf.lang); + msg.guildConf = Config.get(msg.guild); + client.i18n.use(msg.guildConf.lang); await client.funcs.runMessageMonitors(client, msg); - if (client.config.selfbot) { - if (msg.author.id !== client.user.id) return; - } - let thisPrefix; - if (conf.prefix instanceof Array) { - conf.prefix.forEach((prefix) => { - if (msg.content.startsWith(prefix)) thisPrefix = prefix; - else thisPrefix = prefix[0]; - }); - } else { - thisPrefix = conf.prefix; - } - if (!msg.content.startsWith(thisPrefix) && client.config.prefixMention && !client.config.prefixMention.test(msg.content)) return; - let prefixLength = thisPrefix.length; - if (client.config.prefixMention && client.config.prefixMention.test(msg.content)) prefixLength = client.config.prefixMention.exec(msg.content)[0].length + 1; - const command = msg.content.slice(prefixLength).split(" ")[0].toLowerCase(); - const suffix = msg.content.slice(prefixLength).split(" ").slice(1).join(" "); - let commandLog; - switch (msg.channel.type) { - case "dm": - commandLog = `${clk.black.bgYellow(`${msg.author.username}<@${msg.author.id}>`)} : ${clk.bold(command)}('${suffix}') => ${clk.bgMagenta("[Direct Messages]")}`; - break; - case "group": // works for selfbots only - commandLog = `${clk.black.bgYellow(`${msg.author.username}<@${msg.author.id}>`)} : ${clk.bold(command)}('${suffix}') => ${clk.bgCyan(`${msg.channel.owner.username}[${msg.channel.owner.id}] in [GroupDM]`)}`; - break; - default: // text channels - commandLog = `${clk.black.bgYellow(`${msg.author.username}<@${msg.author.id}>`)} : ${clk.bold(command)}('${suffix}') => ${clk.bgGreen(`${msg.guild.name}[${msg.guild.id}]`)}`; - } - let cmd; - if (client.commands.has(command)) { - cmd = client.commands.get(command); - } else if (client.aliases.has(command)) { - cmd = client.commands.get(client.aliases.get(command)); - } + if (client.config.selfbot && msg.author.id !== client.user.id) return; + const cmd = client.funcs.commandHandler(client, msg); if (!cmd) return; - client.funcs.runCommandInhibitors(client, msg, cmd).then((params) => { - client.funcs.log(commandLog); + try { + const params = await client.funcs.runCommandInhibitors(client, msg, cmd); cmd.run(client, msg, params); - }) - .catch((reason) => { - if (reason) { - if (reason instanceof Promise) return; - if (reason.stack) client.funcs.log(reason.stack, "error"); - msg.channel.sendCode("", reason).catch(console.error); + } catch (error) { + if (error) { + if (error.code === 1 && client.config.cmdPrompt) client.funcs.awaitMessage(client, msg, cmd, [], error.message); + if (error.stack) client.emit("error", error.stack); + msg.channel.sendCode("JSON", error.message).catch(err => client.emit("error", err)); } - }); + } }); client.login(client.config.botToken); @@ -114,5 +79,5 @@ exports.start = async (config) => { process.on("unhandledRejection", (err) => { if (!err) return; - console.error(`Uncaught Promise Error: \n${err.stack}`); + console.error(`Uncaught Promise Error: \n${client.funcs.newError(err, 999)}`); }); diff --git a/functions/awaitMessage.js b/functions/awaitMessage.js index 5c57ee4f..e8a63214 100644 --- a/functions/awaitMessage.js +++ b/functions/awaitMessage.js @@ -5,21 +5,23 @@ const options = { }; module.exports = async (client, msg, cmd, args, error) => { - const permLvl = await client.funcs.permissionLevel(client, msg.author, msg.guild).catch(err => client.funcs.log(err, "error")); - if (cmd.conf.permLevel > permLvl) return msg.channel.sendCode("", "You do not have enough permission to use this command.").catch(err => client.funcs.log(err, "error")); - const message = await msg.channel.sendMessage(`<@!${msg.member.id}> | **${error}** | You have **30** seconds to respond to this prompt with a valid argument. Type **"ABORT"** to abort this prompt.`).catch(err => client.funcs.log(err, "error")); - const param = await msg.channel.awaitMessages(response => response.member.id === msg.author.id && response.id !== message.id, options).catch(err => client.funcs.log(err, "error")); + args.shift(); + const permLvl = await client.funcs.permissionLevel(client, msg.author, msg.guild).catch(err => client.emit("error", client.funcs.newError(err))); + if (cmd.conf.permLevel > permLvl) return msg.channel.sendCode("", "You do not have enough permission to use this command.").catch(err => client.emit("error", client.funcs.newError(err))); + const message = await msg.channel.sendMessage(`<@!${msg.member.id}> | **${error}** | You have **30** seconds to respond to this prompt with a valid argument. Type **"ABORT"** to abort this prompt.`).catch(err => client.emit("error", client.funcs.newError(err))); + const param = await msg.channel.awaitMessages(response => response.member.id === msg.author.id && response.id !== message.id, options).catch(err => client.emit("error", client.funcs.newError(err))); message.delete(); if (!param) return "Aborted"; if (param.first().content.toLowerCase() === "abort") return "Aborted"; args.push(param.first().content); - client.funcs.runCommandInhibitors(client, msg, cmd, args) - .then(params => cmd.run(client, msg, params)) - .catch((reason) => { - if (reason) { - if (reason instanceof Promise) return; - if (reason.stack) client.funcs.log(reason.stack, "error"); - msg.channel.sendCode("", reason).catch(console.error); + try { + const params = await client.funcs.runCommandInhibitors(client, msg, cmd, args); + cmd.run(client, msg, params); + } catch (err) { + if (err) { + if (err.code === 1 && client.config.cmdPrompt) return client.funcs.awaitMessage(client, msg, cmd, args, err.message); + if (err.stack) client.emit("error", err.stack); + msg.channel.sendCode("JSON", error.message).catch(errs => client.emit("error", errs)); } - }); + } }; diff --git a/functions/commandHandler.js b/functions/commandHandler.js new file mode 100644 index 00000000..e0515a87 --- /dev/null +++ b/functions/commandHandler.js @@ -0,0 +1,23 @@ +module.exports = (client, msg) => { + let thisPrefix; + if (msg.guildConf.prefix instanceof Array) { + msg.guildConf.prefix.forEach((prefix) => { + if (msg.content.startsWith(prefix)) thisPrefix = prefix; + else thisPrefix = prefix[0]; + }); + } else { + thisPrefix = msg.guildConf.prefix; + } + if (!msg.content.startsWith(thisPrefix) && client.config.prefixMention && !client.config.prefixMention.test(msg.content)) return false; + let prefixLength = thisPrefix.length; + if (client.config.prefixMention && client.config.prefixMention.test(msg.content)) prefixLength = client.config.prefixMention.exec(msg.content)[0].length + 1; + const command = msg.content.slice(prefixLength).split(" ")[0].toLowerCase(); + let cmd; + if (client.commands.has(command)) { + cmd = client.commands.get(command); + } else if (client.aliases.has(command)) { + cmd = client.commands.get(client.aliases.get(command)); + } + if (!cmd) return false; + return cmd; +}; diff --git a/functions/getFileListing.js b/functions/getFileListing.js index 7148116a..560065d3 100644 --- a/functions/getFileListing.js +++ b/functions/getFileListing.js @@ -17,7 +17,6 @@ module.exports = (client, baseDir, type) => new Promise((resolve, reject) => { }); }) .on("end", () => { - // client.funcs.log(`Modules: ${categories.join(",")}`); resolve(files); }) .on("errors", (root, nodeStatsArray, next) => { diff --git a/functions/loadCommandInhibitors.js b/functions/loadCommandInhibitors.js index d2410e73..b550a0d5 100644 --- a/functions/loadCommandInhibitors.js +++ b/functions/loadCommandInhibitors.js @@ -4,7 +4,7 @@ const path = require("path"); const loadCommandInhibitors = (client, baseDir) => new Promise(async (resolve, reject) => { const dir = path.resolve(`${baseDir}./inhibitors/`); await fs.ensureDirAsync(dir); - const files = await client.funcs.getFileListing(client, baseDir, "inhibitors").catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, baseDir, "inhibitors").catch(err => client.emit("error", client.funcs.newError(err))); try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); @@ -28,9 +28,9 @@ const loadCommandInhibitors = (client, baseDir) => new Promise(async (resolve, r module.exports = async (client) => { client.commandInhibitors.clear(); - await loadCommandInhibitors(client, client.coreBaseDir).catch(err => client.funcs.log(err, "error")); + await loadCommandInhibitors(client, client.coreBaseDir).catch(err => client.emit("error", client.funcs.newError(err))); if (client.coreBaseDir !== client.clientBaseDir) { - await loadCommandInhibitors(client, client.clientBaseDir).catch(err => client.funcs.log(err, "error")); + await loadCommandInhibitors(client, client.clientBaseDir).catch(err => client.emit("error", client.funcs.newError(err))); } client.funcs.log(`Loaded ${client.commandInhibitors.size} command inhibitors.`); }; diff --git a/functions/loadCommands.js b/functions/loadCommands.js index 61fd79b2..a0b2c154 100644 --- a/functions/loadCommands.js +++ b/functions/loadCommands.js @@ -5,9 +5,9 @@ const loadCommands = (client, baseDir) => new Promise(async (resolve, reject) => const dir = path.resolve(`${baseDir}./commands/`); try { await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); - const files = await client.funcs.getFileListing(client, baseDir, "commands").catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, baseDir, "commands").catch(err => client.emit("error", client.funcs.newError(err))); files.forEach(async (f) => { - await client.funcs.loadSingleCommand(client, `${f.name}`, false, `${f.path}${path.sep}${f.base}`).catch(err => client.funcs.log(err, "error")); + await client.funcs.loadSingleCommand(client, `${f.name}`, false, `${f.path}${path.sep}${f.base}`).catch(err => client.emit("error", client.funcs.newError(err))); }); resolve(); } catch (e) { @@ -28,9 +28,9 @@ const loadCommands = (client, baseDir) => new Promise(async (resolve, reject) => module.exports = async (client) => { client.commands.clear(); client.aliases.clear(); - await loadCommands(client, client.coreBaseDir).catch(err => client.funcs.log(err, "error")); + await loadCommands(client, client.coreBaseDir).catch(err => client.emit("error", client.funcs.newError(err))); if (client.coreBaseDir !== client.clientBaseDir) { - await loadCommands(client, client.clientBaseDir).catch(err => client.funcs.log(err, "error")); + await loadCommands(client, client.clientBaseDir).catch(err => client.emit("error", client.funcs.newError(err))); } client.funcs.log(`Loaded ${client.commands.size} commands, with ${client.aliases.size} aliases.`); }; diff --git a/functions/loadEvents.js b/functions/loadEvents.js index b3e55b42..e2c274e5 100644 --- a/functions/loadEvents.js +++ b/functions/loadEvents.js @@ -7,8 +7,8 @@ events = Object.keys(events).map(k => events[k]); const loadEvents = (client, baseDir, counts) => new Promise(async (resolve) => { const dir = path.resolve(`${baseDir}./events/`); - await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); - let files = await client.funcs.getFileListing(client, baseDir, "events").catch(err => client.funcs.log(err, "error")); + await fs.ensureDirAsync(dir).catch(err => client.emit("error", client.funcs.newError(err))); + let files = await client.funcs.getFileListing(client, baseDir, "events").catch(err => client.emit("error", client.funcs.newError(err))); files = files.filter(f => events.includes(f.name)); files.forEach((f) => { client.on(f.name, (...args) => require(`${f.path}${path.sep}${f.base}`).run(client, ...args)); @@ -20,9 +20,9 @@ const loadEvents = (client, baseDir, counts) => new Promise(async (resolve) => { module.exports = async (client) => { let counts = 0; - counts = await loadEvents(client, client.coreBaseDir, counts).catch(err => client.funcs.log(err, "error")); + counts = await loadEvents(client, client.coreBaseDir, counts).catch(err => client.emit("error", client.funcs.newError(err))); if (client.coreBaseDir !== client.clientBaseDir) { - counts = await loadEvents(client, client.clientBaseDir, counts).catch(err => client.funcs.log(err, "error")); + counts = await loadEvents(client, client.clientBaseDir, counts).catch(err => client.emit("error", client.funcs.newError(err))); } client.funcs.log(`Loaded ${counts} events`); }; diff --git a/functions/loadFunctions.js b/functions/loadFunctions.js index a541e34d..d3c6a124 100644 --- a/functions/loadFunctions.js +++ b/functions/loadFunctions.js @@ -3,8 +3,8 @@ const path = require("path"); const loadFunctions = (client, baseDir) => new Promise(async (resolve, reject) => { const dir = path.resolve(`${baseDir}./functions/`); - await fs.ensureDirAsync(dir).catch(err => console.error(err)); - let files = await fs.readdirAsync(dir).catch(err => console.err(err)); + await fs.ensureDirAsync(dir).catch(err => client.emit("error", client.funcs.newError(err))); + let files = await fs.readdirAsync(dir).catch(err => client.emit("error", client.funcs.newError(err))); files = files.filter(f => f.slice(-3) === ".js"); try { files.forEach((f) => { diff --git a/functions/loadMessageMonitors.js b/functions/loadMessageMonitors.js index ff7bd48d..f33de96a 100644 --- a/functions/loadMessageMonitors.js +++ b/functions/loadMessageMonitors.js @@ -3,8 +3,8 @@ const path = require("path"); const loadMessageMonitors = (client, baseDir) => new Promise(async (resolve, reject) => { const dir = path.resolve(`${baseDir}./monitors/`); - await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); - const files = await client.funcs.getFileListing(client, baseDir, "monitors").catch(err => client.funcs.log(err, "error")); + await fs.ensureDirAsync(dir).catch(err => client.emit("error", client.funcs.newError(err))); + const files = await client.funcs.getFileListing(client, baseDir, "monitors").catch(err => client.emit("error", client.funcs.newError(err))); try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); @@ -28,9 +28,9 @@ const loadMessageMonitors = (client, baseDir) => new Promise(async (resolve, rej module.exports = async (client) => { client.messageMonitors.clear(); - await loadMessageMonitors(client, client.coreBaseDir).catch(err => client.funcs.log(err, "error")); + await loadMessageMonitors(client, client.coreBaseDir).catch(err => client.emit("error", client.funcs.newError(err))); if (client.coreBaseDir !== client.clientBaseDir) { - await loadMessageMonitors(client, client.clientBaseDir).catch(err => client.funcs.log(err, "error")); + await loadMessageMonitors(client, client.clientBaseDir).catch(err => client.emit("error", client.funcs.newError(err))); } client.funcs.log(`Loaded ${client.messageMonitors.size} command monitors.`); }; diff --git a/functions/loadProviders.js b/functions/loadProviders.js index 99880a6f..6f0a7475 100644 --- a/functions/loadProviders.js +++ b/functions/loadProviders.js @@ -3,8 +3,8 @@ const path = require("path"); const loadProviders = (client, baseDir) => new Promise(async (resolve, reject) => { const dir = path.resolve(`${baseDir}./providers/`); - await fs.ensureDirAsync(dir).catch(err => client.funcs.log(err, "error")); - const files = await client.funcs.getFileListing(client, baseDir, "providers").catch(err => client.funcs.log(err, "error")); + await fs.ensureDirAsync(dir).catch(err => client.emit("error", client.funcs.newError(err))); + const files = await client.funcs.getFileListing(client, baseDir, "providers").catch(err => client.emit("error", client.funcs.newError(err))); try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); @@ -27,9 +27,9 @@ const loadProviders = (client, baseDir) => new Promise(async (resolve, reject) = module.exports = async (client) => { client.providers.clear(); - await loadProviders(client, client.coreBaseDir).catch(err => client.funcs.log(err, "error")); + await loadProviders(client, client.coreBaseDir).catch(err => client.emit("error", client.funcs.newError(err))); if (client.coreBaseDir !== client.clientBaseDir) { - await loadProviders(client, client.clientBaseDir).catch(err => client.funcs.log(err, "error")); + await loadProviders(client, client.clientBaseDir).catch(err => client.emit("error", client.funcs.newError(err))); } client.funcs.log(`Loaded ${client.providers.size} providers.`); }; diff --git a/functions/log.js b/functions/log.js index 183f8e72..8ac7d538 100644 --- a/functions/log.js +++ b/functions/log.js @@ -1,21 +1,22 @@ const moment = require("moment"); const chalk = require("chalk"); +const inspect = require("util").inspect; const clk = new chalk.constructor({ enabled: true }); module.exports = (data, type = "log") => { switch (type.toLowerCase()) { case "debug": - console.log(`${clk.bgMagenta(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); + console.log(`${clk.bgMagenta(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data)}`); break; case "warn": - console.warn(`${clk.black.bgYellow(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); + console.warn(`${clk.black.bgYellow(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 5 })}`); break; case "error": - console.error(`${clk.bgRed(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); + console.error(`${clk.bgRed(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 5 })}`); break; case "log": - console.log(`${clk.bgBlue(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); + console.log(`${clk.bgBlue(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 0 })}`); break; // no default } diff --git a/functions/newError.js b/functions/newError.js new file mode 100644 index 00000000..20ade93d --- /dev/null +++ b/functions/newError.js @@ -0,0 +1,13 @@ +module.exports = (error, code) => { + if (error.status) { + this.statusCode = error.response.res.statusCode; + this.statusMessage = error.response.res.statusMessage; + this.code = error.response.body.code; + this.message = error.response.body.message; + return this; + } + this.code = code || null; + this.message = error; + this.stack = error.stack || null; + return this; +}; diff --git a/functions/permissionLevel.js b/functions/permissionLevel.js index eb2b46a3..f476271a 100644 --- a/functions/permissionLevel.js +++ b/functions/permissionLevel.js @@ -4,7 +4,7 @@ module.exports = (client, user, guild) => new Promise(async (resolve, reject) => const guildConf = client.configuration.get(guild); try { const modRole = guild.roles.find("name", guildConf.modRole); - const member = await guild.fetchMember(user).catch(err => client.funcs.log(err, "error")); + const member = await guild.fetchMember(user).catch(err => client.emit("error", client.funcs.newError(err))); if (modRole && member.roles.has(modRole.id)) { permlvl = 2; } diff --git a/functions/reload.js b/functions/reload.js index 0242a708..7081c08f 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -1,7 +1,7 @@ const path = require("path"); /* eslint-disable import/no-dynamic-require, global-require */ exports.function = (client, dir, funcName) => new Promise(async (resolve, reject) => { - const files = await client.funcs.getFileListing(client, dir, "functions").catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, dir, "functions").catch(err => client.emit("error", client.funcs.newError(err))); if (client.funcs.hasOwnProperty(funcName)) { const oldFunction = files.filter(f => f.name === funcName); if (oldFunction[0]) { @@ -53,7 +53,7 @@ exports.function = (client, dir, funcName) => new Promise(async (resolve, reject }); exports.inhibitor = (client, dir, inhibName) => new Promise(async (resolve, reject) => { - const files = await client.funcs.getFileListing(client, dir, "inhibitors").catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, dir, "inhibitors").catch(err => client.emit("error", client.funcs.newError(err))); if (client.commandInhibitors.has(inhibName)) { const oldInhibitor = files.filter(f => f.name === inhibName); if (oldInhibitor[0]) { @@ -107,7 +107,7 @@ exports.inhibitor = (client, dir, inhibName) => new Promise(async (resolve, reje }); exports.monitor = (client, dir, monitName) => new Promise(async (resolve, reject) => { - const files = await client.funcs.getFileListing(client, dir, "monitors").catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, dir, "monitors").catch(err => client.emit("error", client.funcs.newError(err))); if (client.messageMonitors.has(monitName)) { const oldMonitor = files.filter(f => f.name === monitName); if (oldMonitor[0]) { @@ -161,7 +161,7 @@ exports.monitor = (client, dir, monitName) => new Promise(async (resolve, reject }); exports.provider = (client, dir, providerName) => new Promise(async (resolve, reject) => { - const files = await client.funcs.getFileListing(client, dir, "providers").catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, dir, "providers").catch(err => client.emit("error", client.funcs.newError(err))); if (client.providers.has(providerName)) { const oldProvider = files.filter(f => f.name === providerName); if (oldProvider[0]) { @@ -215,7 +215,7 @@ exports.provider = (client, dir, providerName) => new Promise(async (resolve, re }); exports.event = (client, eventName) => new Promise(async (resolve, reject) => { - const files = await client.funcs.getFileListing(client, client.clientBaseDir, "events").catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, client.clientBaseDir, "events").catch(err => client.emit("error", client.funcs.newError(err))); const oldEvent = files.filter(f => f.name === eventName); if (oldEvent[0] && oldEvent[0].name === eventName) { let listener; @@ -248,7 +248,7 @@ exports.command = (client, dir, commandName) => new Promise(async (resolve, reje command = client.aliases.get(commandName); } if (!command) { - const files = await client.funcs.getFileListing(client, dir, "commands").catch(err => client.funcs.log(err, "error")); + const files = await client.funcs.getFileListing(client, dir, "commands").catch(err => client.emit("error", client.funcs.newError(err))); const newCommands = files.filter(f => f.name === commandName); if (newCommands[0]) { newCommands.forEach(async (file) => { diff --git a/inhibitors/requiredFuncs.js b/inhibitors/requiredFuncs.js index 9e45c89f..1af53bb9 100644 --- a/inhibitors/requiredFuncs.js +++ b/inhibitors/requiredFuncs.js @@ -5,6 +5,7 @@ exports.conf = { }; exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { + if (!cmd.conf.requiredFuncs) resolve(); cmd.conf.requiredFuncs.forEach((func) => { if (!client.funcs.hasOwnProperty(func)) reject(`The client is missing **${func}**, and cannot run.`); }); diff --git a/inhibitors/runIn.js b/inhibitors/runIn.js index a91e9cb9..e7772db3 100644 --- a/inhibitors/runIn.js +++ b/inhibitors/runIn.js @@ -5,6 +5,7 @@ exports.conf = { }; exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { + if (!cmd.conf.runIn) resolve(); if (cmd.conf.runIn.includes(msg.channel.type)) { resolve(); } else { diff --git a/inhibitors/usage.js b/inhibitors/usage.js index 8752f617..f35883c9 100644 --- a/inhibitors/usage.js +++ b/inhibitors/usage.js @@ -36,13 +36,11 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje } if (currentUsage.type === "optional" && (args[i] === undefined || args[i] === "")) { // Handle if args length < required usage length if (usage.slice(i).some(u => u.type === "required")) { - if (client.config.cmdPrompt) return reject(client.funcs.awaitMessage(client, msg, cmd, args, "Missing one or more required arguments after end of input.")); - return reject("Missing one or more required arguments after end of input."); + return reject(client.funcs.newError("Missing one or more required arguments after end of input.", 1)); } return resolve(args); } else if (currentUsage.type === "required" && args[i] === undefined) { - if (client.config.cmdPrompt) return reject(client.funcs.awaitMessage(client, msg, cmd, args, currentUsage.possibles.length === 1 ? `${currentUsage.possibles[0].name} is a required argument.` : `Missing a required option: (${currentUsage.possibles.map(p => p.name).join(", ")})`)); - return reject(currentUsage.possibles.length === 1 ? `${currentUsage.possibles[0].name} is a required argument.` : `Missing a required option: (${currentUsage.possibles.map(p => p.name).join(", ")})`); + return reject(client.funcs.newError(currentUsage.possibles.length === 1 ? `${currentUsage.possibles[0].name} is a required argument.` : `Missing a required option: (${currentUsage.possibles.map(p => p.name).join(", ")})`, 1)); } else if (currentUsage.possibles.length === 1) { switch (currentUsage.possibles[0].type) { case "literal": @@ -53,11 +51,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `Your option did not literally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`)); - } - return reject(`Your option did not literally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`); + return reject(client.funcs.newError(`Your option did not literally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`, 1)); } break; case "msg": @@ -74,11 +68,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid message id.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a valid message id.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid message id.`, 1)); } }); } else { @@ -92,11 +82,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid message id.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a valid message id.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid message id.`, 1)); } }); } @@ -104,11 +90,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid message id.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a valid message id.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid message id.`, 1)); } break; case "user": @@ -120,11 +102,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a mention or valid user id.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a mention or valid user id.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a mention or valid user id.`, 1)); } break; case "boolean": @@ -139,11 +117,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be true or false.`)); - } - return reject(`${currentUsage.possibles[0].name} must be true or false.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be true or false.`, 1)); } break; case "member": @@ -154,11 +128,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a mention or valid user id.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a mention or valid user id.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a mention or valid user id.`, 1)); } break; case "channel": @@ -169,11 +139,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a channel tag or valid channel id.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a channel tag or valid channel id.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a channel tag or valid channel id.`, 1)); } break; case "guild": @@ -184,11 +150,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid guild id.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a valid guild id.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid guild id.`, 1)); } break; case "role": @@ -199,11 +161,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a role mention or role id.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a role mention or role id.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a role mention or role id.`, 1)); } break; case "str": @@ -214,17 +172,9 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else if (currentUsage.possibles[0].min === currentUsage.possibles[0].max) { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min} characters.`)); - } - return reject(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min} characters.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min} characters.`, 1)); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max} characters.`)); - } - return reject(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max} characters.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max} characters.`, 1)); } } else { validateArgs(++i); @@ -235,11 +185,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be longer than ${currentUsage.possibles[0].min} characters.`)); - } - return reject(`${currentUsage.possibles[0].name} must be longer than ${currentUsage.possibles[0].min} characters.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be longer than ${currentUsage.possibles[0].min} characters.`, 1)); } } else { validateArgs(++i); @@ -250,11 +196,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be shorter than ${currentUsage.possibles[0].max} characters.`)); - } - return reject(`${currentUsage.possibles[0].name} must be shorter than ${currentUsage.possibles[0].max} characters.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be shorter than ${currentUsage.possibles[0].max} characters.`, 1)); } } else { validateArgs(++i); @@ -270,11 +212,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be an integer.`)); - } - return reject(`${currentUsage.possibles[0].name} must be an integer.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be an integer.`, 1)); } } else if (currentUsage.possibles[0].min && currentUsage.possibles[0].max) { args[i] = parseInt(args[i]); @@ -284,21 +222,13 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`)); - } - return reject(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`, 1)); } } else if (currentUsage.type === "optional" && !repeat) { args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`)); - } - return reject(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`, 1)); } } else { validateArgs(++i); @@ -310,11 +240,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`)); - } - return reject(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`, 1)); } } else { validateArgs(++i); @@ -326,11 +252,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`)); - } - return reject(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`, 1)); } } else { validateArgs(++i); @@ -348,11 +270,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid number.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a valid number.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid number.`, 1)); } } else if (currentUsage.possibles[0].min && currentUsage.possibles[0].max) { args[i] = parseFloat(args[i]); @@ -362,21 +280,13 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`)); - } - return reject(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`, 1)); } } else if (currentUsage.type === "optional" && !repeat) { args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`)); - } - return reject(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`, 1)); } } else { validateArgs(++i); @@ -388,11 +298,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`)); - } - return reject(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`, 1)); } } else { validateArgs(++i); @@ -404,11 +310,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`)); - } - return reject(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`, 1)); } } else { validateArgs(++i); @@ -425,11 +327,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - return reject(client.funcs.awaitMessage(client, msg, cmd, args, `${currentUsage.possibles[0].name} must be a valid url.`)); - } - return reject(`${currentUsage.possibles[0].name} must be a valid url.`); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid url.`, 1)); } } else { validateArgs(++i); @@ -451,11 +349,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - if (client.config.cmdPrompt) { - args.shift(); - reject(client.funcs.awaitMessage(client, msg, cmd, args, `Your option didn't match any of the possibilities: (${currentUsage.possibles.map(possibles => possibles.name).join(", ")})`)); - } - reject(`Your option didn't match any of the possibilities: (${currentUsage.possibles.map(possibles => possibles.name).join(", ")})`); + reject(client.funcs.newError(`Your option didn't match any of the possibilities: (${currentUsage.possibles.map(possibles => possibles.name).join(", ")})`, 1)); } return; } diff --git a/package.json b/package.json index 92c01fa1..284f70f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.15.0", + "version": "0.16.0", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 09ec58129503419a9f5f98254520ac594e736e32 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Tue, 31 Jan 2017 22:59:54 +1100 Subject: [PATCH 37/56] Holy fuck Depth: 5 are you kidding me? --- functions/log.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/log.js b/functions/log.js index 8ac7d538..9163e0d9 100644 --- a/functions/log.js +++ b/functions/log.js @@ -10,10 +10,10 @@ module.exports = (data, type = "log") => { console.log(`${clk.bgMagenta(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data)}`); break; case "warn": - console.warn(`${clk.black.bgYellow(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 5 })}`); + console.warn(`${clk.black.bgYellow(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 2 })}`); break; case "error": - console.error(`${clk.bgRed(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 5 })}`); + console.error(`${clk.bgRed(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 2 })}`); break; case "log": console.log(`${clk.bgBlue(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 0 })}`); From 7eb8bb13c0b7c3e9f8463f4a3c5740218080b77b Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Tue, 31 Jan 2017 23:15:02 +1100 Subject: [PATCH 38/56] [BUGFIX] Fixes dem nasty Empty Message errors. --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index bd0be74c..755c4383 100644 --- a/app.js +++ b/app.js @@ -68,7 +68,7 @@ exports.start = async (config) => { if (error) { if (error.code === 1 && client.config.cmdPrompt) client.funcs.awaitMessage(client, msg, cmd, [], error.message); if (error.stack) client.emit("error", error.stack); - msg.channel.sendCode("JSON", error.message).catch(err => client.emit("error", err)); + msg.channel.sendCode("JSON", (error.message || error)).catch(err => client.emit("error", err)); } } }); From 13dbe02f8b3bba338655b325cf9d633a1c152904 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Tue, 31 Jan 2017 23:42:20 +1100 Subject: [PATCH 39/56] [BUGFIX] Refer to previous commit Refer to #347 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 284f70f5..888105da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.16.0", + "version": "0.16.1", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 35e5770e37daa6ec22dca0747fa073a0ca1bcdfe Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Tue, 31 Jan 2017 23:42:52 +1100 Subject: [PATCH 40/56] [BUGFIX] Refer to #347 Refer to #347 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d74613f..570800d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Fixed +- Empty Message errors - CmdPrompts should now be fixed completely - Inhibitors now await - Usage typos fixed From 4fc7eb878937d6e6a795470a86a9c4c4de8c2b75 Mon Sep 17 00:00:00 2001 From: Faith Date: Tue, 31 Jan 2017 09:28:50 -0500 Subject: [PATCH 41/56] [BUGFIX] Error fixing (#134) * Fix for Cmd Prompts && Revert of Log.js * [PR] Changelog && Patch Bump --- CHANGELOG.md | 3 ++- app.js | 9 ++++++--- functions/awaitMessage.js | 9 ++++++--- functions/log.js | 9 ++++----- package.json | 2 +- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 570800d5..eb30ce00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Initialize Function to alleviate undefined errors ### Changed +- Reverted Log function for the time being. - Many Files to use the new Error creator - guildOnly Inhibitor is now a `runIn` Inhibitor. - Inhibitors now use .some() to determine if more inhibitors should be ran. @@ -33,7 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Empty Message errors -- CmdPrompts should now be fixed completely +- CmdPrompts should now be fixed completely (as of 0.16.2) - Inhibitors now await - Usage typos fixed - LoadFunctions now calls itself when installing a new dependency in a client function diff --git a/app.js b/app.js index 755c4383..54a22083 100644 --- a/app.js +++ b/app.js @@ -66,9 +66,12 @@ exports.start = async (config) => { cmd.run(client, msg, params); } catch (error) { if (error) { - if (error.code === 1 && client.config.cmdPrompt) client.funcs.awaitMessage(client, msg, cmd, [], error.message); - if (error.stack) client.emit("error", error.stack); - msg.channel.sendCode("JSON", (error.message || error)).catch(err => client.emit("error", err)); + if (error.code === 1 && client.config.cmdPrompt) { + client.funcs.awaitMessage(client, msg, cmd, [], error.message); + } else { + if (error.stack) client.emit("error", error.stack); + msg.channel.sendCode("JSON", (error.message || error)).catch(err => client.emit("error", err)); + } } } }); diff --git a/functions/awaitMessage.js b/functions/awaitMessage.js index e8a63214..9a6a6c2f 100644 --- a/functions/awaitMessage.js +++ b/functions/awaitMessage.js @@ -19,9 +19,12 @@ module.exports = async (client, msg, cmd, args, error) => { cmd.run(client, msg, params); } catch (err) { if (err) { - if (err.code === 1 && client.config.cmdPrompt) return client.funcs.awaitMessage(client, msg, cmd, args, err.message); - if (err.stack) client.emit("error", err.stack); - msg.channel.sendCode("JSON", error.message).catch(errs => client.emit("error", errs)); + if (err.code === 1 && client.config.cmdPrompt) { + client.funcs.awaitMessage(client, msg, cmd, args, err.message); + } else { + if (err.stack) client.emit("error", err.stack); + msg.channel.sendCode("JSON", (err.message || error)).catch(errs => client.emit("error", client.funcs.newError(errs))); + } } } }; diff --git a/functions/log.js b/functions/log.js index 9163e0d9..183f8e72 100644 --- a/functions/log.js +++ b/functions/log.js @@ -1,22 +1,21 @@ const moment = require("moment"); const chalk = require("chalk"); -const inspect = require("util").inspect; const clk = new chalk.constructor({ enabled: true }); module.exports = (data, type = "log") => { switch (type.toLowerCase()) { case "debug": - console.log(`${clk.bgMagenta(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data)}`); + console.log(`${clk.bgMagenta(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); break; case "warn": - console.warn(`${clk.black.bgYellow(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 2 })}`); + console.warn(`${clk.black.bgYellow(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); break; case "error": - console.error(`${clk.bgRed(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 2 })}`); + console.error(`${clk.bgRed(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); break; case "log": - console.log(`${clk.bgBlue(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${inspect(data, { depth: 0 })}`); + console.log(`${clk.bgBlue(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); break; // no default } diff --git a/package.json b/package.json index 888105da..60582655 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.16.1", + "version": "0.16.2", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From d510871052b77c9e12d7a70f7c12e262abe6ce8b Mon Sep 17 00:00:00 2001 From: Faith Date: Wed, 1 Feb 2017 05:40:25 -0500 Subject: [PATCH 42/56] Features (#135) * One Super Big Commit * [PR] Eslinting * [PR] Jsdocing of Config Types && Config.js * [PR] More jsdocing --- CHANGELOG.md | 15 ++++- app.js | 7 +-- classes/Config.js | 83 ++++++++++++-------------- classes/Configuration Types/Array.js | 6 +- classes/Configuration Types/Boolean.js | 2 +- classes/Configuration Types/Number.js | 8 +-- classes/Configuration Types/String.js | 6 +- commands/System/download.js | 2 +- commands/System/reload.js | 2 +- functions/awaitMessage.js | 8 +-- functions/newError.js | 3 +- functions/runCommandInhibitors.js | 33 +++++----- {inhibitors => functions}/usage.js | 83 +++++++++++++++++--------- inhibitors/disable.js | 11 ++-- inhibitors/missingBotPermissions.js | 11 ++-- inhibitors/permissions.js | 13 ++-- inhibitors/requiredFuncs.js | 10 ++-- inhibitors/runIn.js | 13 ++-- package.json | 6 +- 19 files changed, 167 insertions(+), 155 deletions(-) rename {inhibitors => functions}/usage.js (91%) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb30ce00..b95b5deb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added -- All Bad Requests/Forbiddens.. etc, now properly give a human readable error in console or chat, depending on the error. +- All Bad Requests/Forbiddens.. etc, now properly give a human readable error in console or chat, depending on the error. (Not as of (0.17.0).. must be fixed) *** - New Error Creator - New CommandHandler (Removed it from message event) - New Core Command "Transfer" @@ -14,8 +14,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Beta Configuration (Needs heavy testing) - New Argument Prompting for Commands - New Initialize Function to alleviate undefined errors +- New Download Command ### Changed +- newError changed to send arguments to awaitMessage when errors are from usage +- awaitMessage changed to work perfectly with the new system +- msg.author.permLevel is now available immediately on a message, instead of after inhibitors run correctly. +- Usage Inhibitor is now a function instead, which will help issues with racing and prompts. +- All Inhibitors now return values instead of using promises to resolve or reject. (Change will be reflected on Documentation soon) - Reverted Log function for the time being. - Many Files to use the new Error creator - guildOnly Inhibitor is now a `runIn` Inhibitor. @@ -30,11 +36,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Help command is now a Direct Message. - Async/Await for all pieces && app.js - dataProviders renamed to Providers -- New Download Command ### Fixed +- Unchanged Package.json links to the repository +- App.js uncaughtWarnings reverted (for now) +- Download.js Fix && Reload.js typo fix. +- Inhibitors now run one by one until one of them fails or all succeed. Fixes race conditions permanently. - Empty Message errors -- CmdPrompts should now be fixed completely (as of 0.16.2) +- CmdPrompts should now be fixed completely (as of 0.17.0) - Inhibitors now await - Usage typos fixed - LoadFunctions now calls itself when installing a new dependency in a client function diff --git a/app.js b/app.js index 54a22083..7f47388d 100644 --- a/app.js +++ b/app.js @@ -4,11 +4,9 @@ const path = require("path"); const loadFunctions = require("./functions/loadFunctions.js"); const Config = require("./classes/Config.js"); -let client; - exports.start = async (config) => { if (typeof config !== "object") throw new TypeError("Configuration for Komada must be an object."); - client = new Discord.Client(config.clientOptions); + const client = new Discord.Client(config.clientOptions); client.config = config; @@ -55,6 +53,7 @@ exports.start = async (config) => { client.on("message", async (msg) => { if (msg.author.bot) return; + msg.author.permLevel = await client.funcs.permissionLevels(client, msg.author, msg.guild); msg.guildConf = Config.get(msg.guild); client.i18n.use(msg.guildConf.lang); await client.funcs.runMessageMonitors(client, msg); @@ -82,5 +81,5 @@ exports.start = async (config) => { process.on("unhandledRejection", (err) => { if (!err) return; - console.error(`Uncaught Promise Error: \n${client.funcs.newError(err, 999)}`); + console.error(`Uncaught Promise Error: \n${err}`); }); diff --git a/classes/Config.js b/classes/Config.js index 4511b68f..4eb4d779 100644 --- a/classes/Config.js +++ b/classes/Config.js @@ -58,11 +58,10 @@ class Config { } /** - * Allows you to add a key to a guild configuration. Note: This should never be called - * directly as it could cause unwanted side effects. - * @param {string} key The key to add to the configuration. - * @param {string|array|number|boolean} defaultValue The value for the key. - * @param {string} type The type of key you want to add. + * Allows you to add a key to a guild configuration. Note: This should never be called directly as it could cause unwanted side effects. + * @param {String} key The key to add to the configuration. + * @param {String|Array|Number|Boolean} defaultValue The value for the key. + * @param {String} type The type of key you want to add. * @returns {Config} */ addKey(key, defaultValue, type) { @@ -82,9 +81,8 @@ class Config { } /** - * Deletes a key from the respected guild configuration. - * This should never be called directly. - * @param {string} key The key to delete from the configuration + * Deletes a key from the respected guild configuration. This should never be called directly. + * @param {String} key The key to delete from the configuration * @returns {null} */ delKey(key) { @@ -95,7 +93,7 @@ class Config { /** * Resets a key for the respected guild configuration. - * @param {string} key The key to reset in the configuration. + * @param {String} key The key to reset in the configuration. * @returns {Config} */ reset(key) { @@ -114,8 +112,8 @@ class Config { /** * Checks the guild configuration for a key - * @param {string} key The key to check the guild configuration for. - * @returns {boolean} + * @param {String} key The key to check the guild configuration for. + * @returns {Boolean} */ has(key) { if (!key) return "Please supply a key."; @@ -125,7 +123,7 @@ class Config { /** * Simplifies the guild configuration for use in commands and modules. * @param {Guild} guild The guild to get a configuration for. - * @returns {object} + * @returns {Object} * @static * @example * //Example of what this returns @@ -149,9 +147,9 @@ class Config { /** * Set the default value for a key in the default configuration. - * @param {string} key The key for which you want to change. - * @param {array|boolean|number|string} defaultValue The value you want to set as the default. - * @returns {object} Returns the new default configuration for the key. + * @param {String} key The key for which you want to change. + * @param {Array|Boolean|Number|String} defaultValue The value you want to set as the default. + * @returns {Object} Returns the new default configuration for the key. * @static */ static set(key, defaultValue) { @@ -167,9 +165,9 @@ class Config { /** * Sets the default minimum value for a Number key - * @param {string} key The Number key for which you want to set the minimum value for. - * @param {number} defaultMinValue The value you want to set as the "minimum" value. - * @returns {object} Returns the new default configuration for the key. + * @param {String} key The Number key for which you want to set the minimum value for. + * @param {Number} defaultMinValue The value you want to set as the "minimum" value. + * @returns {Object} Returns the new default configuration for the key. * @static */ static setMin(key, defaultMinValue) { @@ -182,9 +180,9 @@ class Config { /** * Sets the default maximum value for a Number key - * @param {string} key The Number key for which you want to set the maximum value for. - * @param {number} defaultMaxValue The value you want to set as the "maximum" value. - * @returns {object} Returns the new default configuration for the key. + * @param {String} key The Number key for which you want to set the maximum value for. + * @param {Number} defaultMaxValue The value you want to set as the "maximum" value. + * @returns {Object} Returns the new default configuration for the key. * @static */ static setMax(key, defaultMaxValue) { @@ -197,9 +195,9 @@ class Config { /** * Adds a value to the data array for an Array key. - * @param {string} key The Array key for which you want to add value(s) for. - * @param {string} defaultValue The value for which you want to add to the array. - * @returns {object} Returns the new default configuration for the key. + * @param {String} key The Array key for which you want to add value(s) for. + * @param {String} defaultValue The value for which you want to add to the array. + * @returns {Object} Returns the new default configuration for the key. * @static */ static add(key, defaultValue) { @@ -213,9 +211,9 @@ class Config { /** * Deletes a value from the data array for an Array key. - * @param {string} key The array key for which you want to delete value(s) from. - * @param {string} defaultValue The value for which you want to remove from the array. - * @returns {object} Returns the new default configuration for the key. + * @param {String} key The array key for which you want to delete value(s) from. + * @param {String} defaultValue The value for which you want to remove from the array. + * @returns {Object} Returns the new default configuration for the key. * @static */ static del(key, defaultValue) { @@ -229,8 +227,8 @@ class Config { /** * Toggles the true/false statement for a Boolean key - * @param {string} key The boolean key for which you want to toggle the statement for. - * @returns {objct} Returns the new default configuration for the key. + * @param {String} key The boolean key for which you want to toggle the statement for. + * @returns {Object} Returns the new default configuration for the key. * @static */ static toggle(key) { @@ -245,7 +243,7 @@ class Config { /** * Checks if the guildConfs Map has the specified guild. * @param {Guild} guild The guild to check the Map for. - * @returns {boolean} + * @returns {Boolean} * @static */ static has(guild) { @@ -255,8 +253,8 @@ class Config { /** * Checks if the default configuration has a specified key. - * @param {string} key The key for which to check the default configuration for. - * @returns {boolean} + * @param {String} key The key for which to check the default configuration for. + * @returns {Boolean} * @static */ static hasKey(key) { @@ -266,10 +264,10 @@ class Config { /** * Adds a key to the default configuration, and every guilds configuration. - * @param {string} key The key for which to add to the default and all guild configurations. - * @param {string|number|boolean|array} defaultValue The value for which you want to set as the default value. - * @param {string} [type] The type of key this will be. This can currently be Strings, Numbers, Arrays, or Booleans. - * @returns {object} Returns the entire default configuration + * @param {String} key The key for which to add to the default and all guild configurations. + * @param {String|Number|Boolean|Array} defaultValue The value for which you want to set as the default value. + * @param {String} [type] The type of key this will be. This can currently be Strings, Numbers, Arrays, or Booleans. + * @returns {Object} Returns the entire default configuration * @static */ static addKey(key, defaultValue, type = defaultValue.constructor.name) { @@ -291,8 +289,8 @@ class Config { /** * Deletes a key from the default configuration, and every guilds configuration. - * @param {string} key The key for which to add to the default and all guild configurations. - * @returns The new default configuration + * @param {String} key The key for which to add to the default and all guild configurations. + * @returns {Object} Returns the new default configuration * @static */ static delKey(key) { @@ -310,8 +308,7 @@ class Config { } /** - * Inserts a guild into the guildConfs map and deletes the configuration JSON. - * This should never be called by anyone, this is purely for the guildCreate event. + * Inserts a guild into the guildConfs map and deletes the configuration JSON. This should never be called by anyone, this is purely for the guildCreate event. * @param {Client} client The Discord.js Client * @param {Guild} guild The Guild being inserted into the map. * @returns {String} @@ -325,8 +322,7 @@ class Config { } /** - * Removes a guild from the guildConfs map and deletes the configuration JSON. - * This should never be called by anyone, this is purely for the guildDelete event. + * Removes a guild from the guildConfs map and deletes the configuration JSON. This should never be called by anyone, this is purely for the guildDelete event. * @param {Guild} guild The guild being removed from the map. * @returns {String} * @static @@ -339,8 +335,7 @@ class Config { } /** - * The motherboard of our Configuration system. - * There's no reason to ever call this as it's called internally upon startup. + * The motherboard of our Configuration system. There's no reason to ever call this as it's called internally upon startup. * @param {Client} client The Discord.js Client * @returns {null} * @static diff --git a/classes/Configuration Types/Array.js b/classes/Configuration Types/Array.js index 857bea87..785e1e63 100644 --- a/classes/Configuration Types/Array.js +++ b/classes/Configuration Types/Array.js @@ -7,7 +7,7 @@ const path = require("path"); class ArrayConfig { /** * @param {Config} conf The guild configuration obtained from the guildConfs map. - * @param {object} data The data you want to append to this Array configuration key. + * @param {Object} data The data you want to append to this Array configuration key. * @returns {ArrayConfig} */ constructor(conf, data) { @@ -22,7 +22,7 @@ class ArrayConfig { /** * Adds a value(s) to the array. Accepts a single value or an array of values. - * @param {string|array} value The value(s) to add to the array. + * @param {String|Array} value The value(s) to add to the array. * @returns {ArrayConfig} */ add(value) { @@ -43,7 +43,7 @@ class ArrayConfig { /** * Deletes a value(s) from the array. Accepts a single value or an array of values. - * @param {string|array} value The value(s) to delete from the array. + * @param {String|Array} value The value(s) to delete from the array. * @returns {ArrayConfig} */ del(value) { diff --git a/classes/Configuration Types/Boolean.js b/classes/Configuration Types/Boolean.js index 26a47b70..2aade731 100644 --- a/classes/Configuration Types/Boolean.js +++ b/classes/Configuration Types/Boolean.js @@ -7,7 +7,7 @@ const path = require("path"); class BooleanConfig { /** * @param {Config} conf A guilds configuration obtained from the guildConfs map. - * @param {object} data The data you want to append to this boolean key. + * @param {Object} data The data you want to append to this boolean key. * @returns {BooleanConfig} */ constructor(conf, data) { diff --git a/classes/Configuration Types/Number.js b/classes/Configuration Types/Number.js index d9ca203b..c89dd411 100644 --- a/classes/Configuration Types/Number.js +++ b/classes/Configuration Types/Number.js @@ -8,7 +8,7 @@ const path = require("path"); class NumberConfig { /** * @param {Config} conf A guilds configuration obtained from the guildConfs map. - * @param {object} data The data you want to append to this number key. + * @param {Object} data The data you want to append to this number key. * @returns {NumberConfig} */ constructor(conf, data) { @@ -25,7 +25,7 @@ class NumberConfig { /** * Sets the value for a number key, according to the minimum and maximum values if they apply. - * @param {number} value The value you want to set the key as. + * @param {Number} value The value you want to set the key as. * @returns {NumberConfig} */ set(value) { @@ -39,7 +39,7 @@ class NumberConfig { /** * Sets the minimum value a number key can be. - * @param {number} value The value you want to set the minimum as. + * @param {Number} value The value you want to set the minimum as. * @returns {NumberConfig} */ setMin(value) { @@ -50,7 +50,7 @@ class NumberConfig { /** * Sets the maximum value a number key can bey. - * @param {number} value The value you want to set the maximum as. + * @param {Number} value The value you want to set the maximum as. * @returns {NumberConfig} */ setMax(value) { diff --git a/classes/Configuration Types/String.js b/classes/Configuration Types/String.js index fadc9663..f3869005 100644 --- a/classes/Configuration Types/String.js +++ b/classes/Configuration Types/String.js @@ -24,7 +24,7 @@ class StringConfig { /** * Sets the value of a string configurations possibles. This takes into account the list of acceptable answers from the possibles array. - * @param {string} value The value you want to set this key to. + * @param {String} value The value you want to set this key to. * @returns {StringConfig} */ set(value) { @@ -37,7 +37,7 @@ class StringConfig { /** * Adds a value(s) to list of acceptable answers for this key. Accepts one item or an array of items. - * @param {string|array} value The value(s) you want to add to this key. + * @param {String|Array} value The value(s) you want to add to this key. * @returns {StringConfig} */ add(value) { @@ -58,7 +58,7 @@ class StringConfig { /** * Deletes a value(s) from the string configurations possibles. Accepts one item or an array of items. - * @param {string|array} value The value(s) you want to delete from this key. + * @param {String|Array} value The value(s) you want to delete from this key. * @returns {StringConfig} */ del(value) { diff --git a/commands/System/download.js b/commands/System/download.js index a5eac7c3..b125054b 100644 --- a/commands/System/download.js +++ b/commands/System/download.js @@ -10,7 +10,7 @@ exports.run = (client, msg, [link, piece, folder = "Downloaded"]) => { let newURL; if (!proposedURL.protocol && !proposedURL.hostname) { if (!piece) return msg.channel.sendMessage(`<@!${msg.author.id}> | You provided an invalid url, and no piece name to look up in the Pieces repository.`); - if (link === "command") { + if (link === "commands") { piece = piece.split("/"); if (piece.length < 0 || piece.length > 2) return msg.channel.sendMessage(`<@!${msg.author.id}> | You provided an invalid or no subfolder for a command. Please provide a valid folder name from the Pieces Repo. Example: Misc/test`); newURL = `${piecesURL}${link}/${piece.join("/")}.js`; diff --git a/commands/System/reload.js b/commands/System/reload.js index 6901f0ef..0964ab46 100644 --- a/commands/System/reload.js +++ b/commands/System/reload.js @@ -10,7 +10,7 @@ exports.run = async (client, msg, [type, name]) => { case "inhibitor": m = await msg.channel.sendMessage(`Attempting to reload inhibitor ${name}`).catch(err => client.funcs.log(err, "error")); message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark ${message}`); + m.edit(`:white_check_mark: ${message}`); break; case "monitor": m = await msg.channel.sendMessage(`Attempting to reload monitor ${name}`).catch(err => client.funcs.log(err, "error")); diff --git a/functions/awaitMessage.js b/functions/awaitMessage.js index 9a6a6c2f..d0c66db1 100644 --- a/functions/awaitMessage.js +++ b/functions/awaitMessage.js @@ -5,7 +5,6 @@ const options = { }; module.exports = async (client, msg, cmd, args, error) => { - args.shift(); const permLvl = await client.funcs.permissionLevel(client, msg.author, msg.guild).catch(err => client.emit("error", client.funcs.newError(err))); if (cmd.conf.permLevel > permLvl) return msg.channel.sendCode("", "You do not have enough permission to use this command.").catch(err => client.emit("error", client.funcs.newError(err))); const message = await msg.channel.sendMessage(`<@!${msg.member.id}> | **${error}** | You have **30** seconds to respond to this prompt with a valid argument. Type **"ABORT"** to abort this prompt.`).catch(err => client.emit("error", client.funcs.newError(err))); @@ -15,15 +14,12 @@ module.exports = async (client, msg, cmd, args, error) => { if (param.first().content.toLowerCase() === "abort") return "Aborted"; args.push(param.first().content); try { - const params = await client.funcs.runCommandInhibitors(client, msg, cmd, args); + const params = await client.funcs.usage.run(client, msg, cmd, args); cmd.run(client, msg, params); } catch (err) { if (err) { if (err.code === 1 && client.config.cmdPrompt) { - client.funcs.awaitMessage(client, msg, cmd, args, err.message); - } else { - if (err.stack) client.emit("error", err.stack); - msg.channel.sendCode("JSON", (err.message || error)).catch(errs => client.emit("error", client.funcs.newError(errs))); + client.funcs.awaitMessage(client, msg, cmd, err.args, err.message); } } } diff --git a/functions/newError.js b/functions/newError.js index 20ade93d..d905f498 100644 --- a/functions/newError.js +++ b/functions/newError.js @@ -1,4 +1,4 @@ -module.exports = (error, code) => { +module.exports = (error, code, args) => { if (error.status) { this.statusCode = error.response.res.statusCode; this.statusMessage = error.response.res.statusMessage; @@ -7,6 +7,7 @@ module.exports = (error, code) => { return this; } this.code = code || null; + if (this.code === 1) this.args = args; this.message = error; this.stack = error.stack || null; return this; diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index 2a768d01..db85f9d7 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -1,20 +1,19 @@ -module.exports = (client, msg, cmd, args, selective = false) => new Promise((resolve, reject) => { + +module.exports = (client, msg, cmd, selective = false) => new Promise(async (resolve, reject) => { let usage; - const priority = client.commandInhibitors.array(); - const sorted = priority.sort((a, b) => a.conf.priority < b.conf.priority); - sorted.some((inhib) => { // eslint-disable-line - if (!selective && inhib.conf.enabled) { - inhib.run(client, msg, cmd, args) - .then((params) => { - if (params) usage = params; - }) - .catch((err) => { - if (err) { - reject(err); - return true; - } - }); - } + const inhibitors = client.commandInhibitors.array().sort((a, b) => a.conf.priority < b.conf.priority); + inhibitors.some((inhib) => { + usage = inhib.run(client, msg, cmd); + if (usage) return true; + return false; }); - return setTimeout(() => { resolve(usage); }, 1); + if (usage) return reject(usage); + if (selective) { + try { + usage = await client.funcs.usage.run(client, msg, cmd); + } catch (err) { + return reject(err); + } + } + return resolve(usage); }); diff --git a/inhibitors/usage.js b/functions/usage.js similarity index 91% rename from inhibitors/usage.js rename to functions/usage.js index f35883c9..18446acb 100644 --- a/inhibitors/usage.js +++ b/functions/usage.js @@ -36,11 +36,13 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje } if (currentUsage.type === "optional" && (args[i] === undefined || args[i] === "")) { // Handle if args length < required usage length if (usage.slice(i).some(u => u.type === "required")) { - return reject(client.funcs.newError("Missing one or more required arguments after end of input.", 1)); + args.shift(); + return reject(client.funcs.newError("Missing one or more required arguments after end of input.", 1, args)); } return resolve(args); } else if (currentUsage.type === "required" && args[i] === undefined) { - return reject(client.funcs.newError(currentUsage.possibles.length === 1 ? `${currentUsage.possibles[0].name} is a required argument.` : `Missing a required option: (${currentUsage.possibles.map(p => p.name).join(", ")})`, 1)); + args.shift(); + return reject(client.funcs.newError(currentUsage.possibles.length === 1 ? `${currentUsage.possibles[0].name} is a required argument.` : `Missing a required option: (${currentUsage.possibles.map(p => p.name).join(", ")})`, 1, args)); } else if (currentUsage.possibles.length === 1) { switch (currentUsage.possibles[0].type) { case "literal": @@ -51,7 +53,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`Your option did not literally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`, 1)); + args.shift(); + return reject(client.funcs.newError(`Your option did not literally match the only possibility: (${currentUsage.possibles.map(p => p.name).join(", ")}).. This is likely caused by a mistake in the usage string.`, 1, args)); } break; case "msg": @@ -68,7 +71,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid message id.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid message id.`, 1, args)); } }); } else { @@ -82,7 +86,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid message id.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid message id.`, 1, args)); } }); } @@ -90,7 +95,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid message id.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid message id.`, 1, args)); } break; case "user": @@ -102,7 +108,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a mention or valid user id.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a mention or valid user id.`, 1, args)); } break; case "boolean": @@ -117,7 +124,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be true or false.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be true or false.`, 1, args)); } break; case "member": @@ -128,7 +136,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a mention or valid user id.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a mention or valid user id.`, 1, args)); } break; case "channel": @@ -139,7 +148,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a channel tag or valid channel id.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a channel tag or valid channel id.`, 1, args)); } break; case "guild": @@ -150,7 +160,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid guild id.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid guild id.`, 1, args)); } break; case "role": @@ -161,7 +172,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a role mention or role id.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a role mention or role id.`, 1, args)); } break; case "str": @@ -172,9 +184,11 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else if (currentUsage.possibles[0].min === currentUsage.possibles[0].max) { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min} characters.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min} characters.`, 1, args)); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max} characters.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max} characters.`, 1, args)); } } else { validateArgs(++i); @@ -185,7 +199,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be longer than ${currentUsage.possibles[0].min} characters.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be longer than ${currentUsage.possibles[0].min} characters.`, 1, args)); } } else { validateArgs(++i); @@ -196,7 +211,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be shorter than ${currentUsage.possibles[0].max} characters.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be shorter than ${currentUsage.possibles[0].max} characters.`, 1, args)); } } else { validateArgs(++i); @@ -212,7 +228,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be an integer.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be an integer.`, 1, args)); } } else if (currentUsage.possibles[0].min && currentUsage.possibles[0].max) { args[i] = parseInt(args[i]); @@ -222,13 +239,15 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`, 1, args)); } } else if (currentUsage.type === "optional" && !repeat) { args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`, 1, args)); } } else { validateArgs(++i); @@ -240,7 +259,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`, 1, args)); } } else { validateArgs(++i); @@ -252,7 +272,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`, 1, args)); } } else { validateArgs(++i); @@ -270,7 +291,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid number.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid number.`, 1, args)); } } else if (currentUsage.possibles[0].min && currentUsage.possibles[0].max) { args[i] = parseFloat(args[i]); @@ -280,13 +302,15 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be exactly ${currentUsage.possibles[0].min}... So why didn't the dev use a literal?`, 1, args)); } } else if (currentUsage.type === "optional" && !repeat) { args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be between ${currentUsage.possibles[0].min} and ${currentUsage.possibles[0].max}.`, 1, args)); } } else { validateArgs(++i); @@ -298,7 +322,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be greater than ${currentUsage.possibles[0].min}.`, 1, args)); } } else { validateArgs(++i); @@ -310,7 +335,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be less than ${currentUsage.possibles[0].max}.`, 1, args)); } } else { validateArgs(++i); @@ -327,7 +353,8 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid url.`, 1)); + args.shift(); + return reject(client.funcs.newError(`${currentUsage.possibles[0].name} must be a valid url.`, 1, args)); } } else { validateArgs(++i); @@ -349,7 +376,7 @@ exports.run = (client, msg, cmd, args = undefined) => new Promise((resolve, reje args.splice(i, 0, undefined); validateArgs(++i); } else { - reject(client.funcs.newError(`Your option didn't match any of the possibilities: (${currentUsage.possibles.map(possibles => possibles.name).join(", ")})`, 1)); + reject(client.funcs.newError(`Your option didn't match any of the possibilities: (${currentUsage.possibles.map(possibles => possibles.name).join(", ")})`, 1, args)); } return; } diff --git a/inhibitors/disable.js b/inhibitors/disable.js index 4a8cc5e2..8cb4df8a 100644 --- a/inhibitors/disable.js +++ b/inhibitors/disable.js @@ -4,10 +4,7 @@ exports.conf = { priority: 9, }; -exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { - if (cmd.conf.enabled && !msg.guildConf.disabledCommands.includes(cmd.help.name)) { - resolve(); - } else { - reject("This command is currently disabled"); - } -}); +exports.run = (client, msg, cmd) => { + if (cmd.conf.enabled && !msg.guildConf.disabledCommands.includes(cmd.help.name)) return false; + return "This command is currently disabled"; +}; diff --git a/inhibitors/missingBotPermissions.js b/inhibitors/missingBotPermissions.js index 87e77da1..1b66207b 100644 --- a/inhibitors/missingBotPermissions.js +++ b/inhibitors/missingBotPermissions.js @@ -4,7 +4,7 @@ exports.conf = { priority: 7, }; -exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { +exports.run = (client, msg, cmd) => { let missing = []; if (msg.channel.type === "text") { missing = msg.channel.permissionsFor(client.user).missingPermissions(cmd.conf.botPerms); @@ -14,9 +14,6 @@ exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { if (!impliedPermissions[perm]) missing.push(perm); }); } - if (missing.length > 0) { - reject(`Insufficient permissions, missing: **${client.funcs.toTitleCase(missing.join(", ").split("_").join(" "))}**`); - } else { - resolve(); - } -}); + if (missing.length > 0) return `Insufficient permissions, missing: **${client.funcs.toTitleCase(missing.join(", ").split("_").join(" "))}**`; + return false; +}; diff --git a/inhibitors/permissions.js b/inhibitors/permissions.js index 29539dd5..71413d33 100644 --- a/inhibitors/permissions.js +++ b/inhibitors/permissions.js @@ -4,12 +4,7 @@ exports.conf = { priority: 10, }; -exports.run = (client, msg, cmd) => new Promise(async (resolve, reject) => { - const permlvl = await client.funcs.permissionLevel(client, msg.author, msg.guild).catch(err => client.funcs.log(err, "error")); - msg.author.permLevel = permlvl; - if (permlvl >= cmd.conf.permLevel) { - resolve(); - } else { - reject("You do not have permission to use this command."); - } -}); +exports.run = (client, msg, cmd) => { + if (msg.author.permLevel >= cmd.conf.permLevel) return false; + return "You do not have permission to use this command."; +}; diff --git a/inhibitors/requiredFuncs.js b/inhibitors/requiredFuncs.js index 1af53bb9..15e513a9 100644 --- a/inhibitors/requiredFuncs.js +++ b/inhibitors/requiredFuncs.js @@ -4,10 +4,10 @@ exports.conf = { priority: 6, }; -exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { - if (!cmd.conf.requiredFuncs) resolve(); +exports.run = (client, msg, cmd) => { + if (!cmd.conf.requiredFuncs) return false; cmd.conf.requiredFuncs.forEach((func) => { - if (!client.funcs.hasOwnProperty(func)) reject(`The client is missing **${func}**, and cannot run.`); + if (!client.funcs.hasOwnProperty(func)) return `The client is missing **${func}**, and cannot run.`; }); - resolve(); -}); + return false; +}; diff --git a/inhibitors/runIn.js b/inhibitors/runIn.js index e7772db3..72370b9b 100644 --- a/inhibitors/runIn.js +++ b/inhibitors/runIn.js @@ -4,11 +4,8 @@ exports.conf = { priority: 8, }; -exports.run = (client, msg, cmd) => new Promise((resolve, reject) => { - if (!cmd.conf.runIn) resolve(); - if (cmd.conf.runIn.includes(msg.channel.type)) { - resolve(); - } else { - reject(`This command is only avaliable in ${cmd.conf.runIn.join(" ")} channels`); - } -}); +exports.run = (client, msg, cmd) => { + if (!cmd.conf.runIn) return false; + if (cmd.conf.runIn.includes(msg.channel.type)) return false; + return `This command is only avaliable in ${cmd.conf.runIn.join(" ")} channels`; +}; diff --git a/package.json b/package.json index 60582655..0eb13f2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.16.2", + "version": "0.17.0", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", @@ -11,10 +11,10 @@ }, "repository": { "type": "git", - "url": "https://github.com/eslachance/komada.git" + "url": "https://github.com/dirigeants/komada.git" }, "bugs": { - "url": "https://github.com/eslachance/komada/issues" + "url": "https://github.com/dirigeants/komada/issues" }, "license": "ISC", "dependencies": { From 5eb988ba09f3f8eaa9ad5ab7519e01f30b214a1e Mon Sep 17 00:00:00 2001 From: Faith Date: Wed, 1 Feb 2017 06:12:56 -0500 Subject: [PATCH 43/56] [BUGFIXES] Merge Indev (#136) * Bugfixes, Whoopsies * Changelog Updated --- CHANGELOG.md | 2 ++ app.js | 4 ++-- functions/runCommandInhibitors.js | 2 +- inhibitors/runIn.js | 5 ++++- package.json | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b95b5deb..78db48e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - dataProviders renamed to Providers ### Fixed +- Fixed Usage not working properly with selective +- permissionLevels -> permissionLevel - Unchanged Package.json links to the repository - App.js uncaughtWarnings reverted (for now) - Download.js Fix && Reload.js typo fix. diff --git a/app.js b/app.js index 7f47388d..d9e29793 100644 --- a/app.js +++ b/app.js @@ -53,7 +53,7 @@ exports.start = async (config) => { client.on("message", async (msg) => { if (msg.author.bot) return; - msg.author.permLevel = await client.funcs.permissionLevels(client, msg.author, msg.guild); + msg.author.permLevel = await client.funcs.permissionLevel(client, msg.author, msg.guild); msg.guildConf = Config.get(msg.guild); client.i18n.use(msg.guildConf.lang); await client.funcs.runMessageMonitors(client, msg); @@ -81,5 +81,5 @@ exports.start = async (config) => { process.on("unhandledRejection", (err) => { if (!err) return; - console.error(`Uncaught Promise Error: \n${err}`); + console.error(`Uncaught Promise Error: \n${err.stack || err}`); }); diff --git a/functions/runCommandInhibitors.js b/functions/runCommandInhibitors.js index db85f9d7..5c784f92 100644 --- a/functions/runCommandInhibitors.js +++ b/functions/runCommandInhibitors.js @@ -8,7 +8,7 @@ module.exports = (client, msg, cmd, selective = false) => new Promise(async (res return false; }); if (usage) return reject(usage); - if (selective) { + if (!selective || !client.funcs.usage.conf.spamProtection) { try { usage = await client.funcs.usage.run(client, msg, cmd); } catch (err) { diff --git a/inhibitors/runIn.js b/inhibitors/runIn.js index 72370b9b..c70975fa 100644 --- a/inhibitors/runIn.js +++ b/inhibitors/runIn.js @@ -5,7 +5,10 @@ exports.conf = { }; exports.run = (client, msg, cmd) => { - if (!cmd.conf.runIn) return false; + if (!cmd.conf.runIn) { + cmd.conf.runIn = ["text"]; + return false; + } if (cmd.conf.runIn.includes(msg.channel.type)) return false; return `This command is only avaliable in ${cmd.conf.runIn.join(" ")} channels`; }; diff --git a/package.json b/package.json index 0eb13f2d..79b52488 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.17.0", + "version": "0.17.1", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From eb041f385e78b9c03028557eb4e72585f210c129 Mon Sep 17 00:00:00 2001 From: Faith Date: Thu, 2 Feb 2017 23:23:55 -0500 Subject: [PATCH 44/56] [FEATURES] Destruction of App.js (#137) * The Destruction of App.js * [PR] Changelog && Patch Bump * [PR] Eslint && Array-prefixing fix --- CHANGELOG.md | 3 +++ app.js | 22 ++++---------------- commands/System/transfer.js | 2 +- functions/commandHandler.js | 23 --------------------- functions/handleCommand.js | 40 +++++++++++++++++++++++++++++++++++++ functions/handleMessage.js | 8 ++++++++ functions/parseCommand.js | 30 ++++++++++++++++++++++++++++ package.json | 2 +- 8 files changed, 87 insertions(+), 43 deletions(-) delete mode 100644 functions/commandHandler.js create mode 100644 functions/handleCommand.js create mode 100644 functions/handleMessage.js create mode 100644 functions/parseCommand.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 78db48e2..f9bf06d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- Added a bunch of unusable configuration options that'll make their debut soon. - All Bad Requests/Forbiddens.. etc, now properly give a human readable error in console or chat, depending on the error. (Not as of (0.17.0).. must be fixed) *** - New Error Creator - New CommandHandler (Removed it from message event) @@ -17,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Changed +- Broke down App.js Message Event into several smaller, changeable parts. - newError changed to send arguments to awaitMessage when errors are from usage - awaitMessage changed to work perfectly with the new system - msg.author.permLevel is now available immediately on a message, instead of after inhibitors run correctly. @@ -38,6 +40,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - dataProviders renamed to Providers ### Fixed +- Fixed Typo in transfer command - Fixed Usage not working properly with selective - permissionLevels -> permissionLevel - Unchanged Package.json links to the repository diff --git a/app.js b/app.js index d9e29793..be818a03 100644 --- a/app.js +++ b/app.js @@ -52,27 +52,13 @@ exports.start = async (config) => { client.on("disconnect", e => client.funcs.log(e, "error")); client.on("message", async (msg) => { - if (msg.author.bot) return; + await client.funcs.runMessageMonitors(client, msg); msg.author.permLevel = await client.funcs.permissionLevel(client, msg.author, msg.guild); msg.guildConf = Config.get(msg.guild); client.i18n.use(msg.guildConf.lang); - await client.funcs.runMessageMonitors(client, msg); - if (client.config.selfbot && msg.author.id !== client.user.id) return; - const cmd = client.funcs.commandHandler(client, msg); - if (!cmd) return; - try { - const params = await client.funcs.runCommandInhibitors(client, msg, cmd); - cmd.run(client, msg, params); - } catch (error) { - if (error) { - if (error.code === 1 && client.config.cmdPrompt) { - client.funcs.awaitMessage(client, msg, cmd, [], error.message); - } else { - if (error.stack) client.emit("error", error.stack); - msg.channel.sendCode("JSON", (error.message || error)).catch(err => client.emit("error", err)); - } - } - } + if (!client.funcs.handleMessage(client, msg)) return; + const command = client.funcs.parseCommand(client, msg); + client.funcs.handleCommand(client, msg, command); }); client.login(client.config.botToken); diff --git a/commands/System/transfer.js b/commands/System/transfer.js index 463e627f..7a6818a2 100644 --- a/commands/System/transfer.js +++ b/commands/System/transfer.js @@ -16,7 +16,7 @@ exports.conf = { exports.help = { name: "transfer", - description: "Transfers a core command to the user folders", + description: "Transfers a core piece to its respected folder", usage: " ", usageDelim: " ", }; diff --git a/functions/commandHandler.js b/functions/commandHandler.js deleted file mode 100644 index e0515a87..00000000 --- a/functions/commandHandler.js +++ /dev/null @@ -1,23 +0,0 @@ -module.exports = (client, msg) => { - let thisPrefix; - if (msg.guildConf.prefix instanceof Array) { - msg.guildConf.prefix.forEach((prefix) => { - if (msg.content.startsWith(prefix)) thisPrefix = prefix; - else thisPrefix = prefix[0]; - }); - } else { - thisPrefix = msg.guildConf.prefix; - } - if (!msg.content.startsWith(thisPrefix) && client.config.prefixMention && !client.config.prefixMention.test(msg.content)) return false; - let prefixLength = thisPrefix.length; - if (client.config.prefixMention && client.config.prefixMention.test(msg.content)) prefixLength = client.config.prefixMention.exec(msg.content)[0].length + 1; - const command = msg.content.slice(prefixLength).split(" ")[0].toLowerCase(); - let cmd; - if (client.commands.has(command)) { - cmd = client.commands.get(command); - } else if (client.aliases.has(command)) { - cmd = client.commands.get(client.aliases.get(command)); - } - if (!cmd) return false; - return cmd; -}; diff --git a/functions/handleCommand.js b/functions/handleCommand.js new file mode 100644 index 00000000..2552fe02 --- /dev/null +++ b/functions/handleCommand.js @@ -0,0 +1,40 @@ +module.exports = async (client, msg, command, args = undefined) => { + const validCommand = this.getCommand(client, command); + if (!validCommand) return; + const response = this.runInhibitors(client, msg, validCommand); + if (response) return msg.reply(response); + try { + const params = await client.funcs.usage.run(client, msg, validCommand, args); + validCommand.run(client, msg, params); + } catch (error) { + if (error) { + if (error.code === 1 && client.config.cmdPrompt) { + client.funcs.awaitMessage(client, msg, validCommand, [], error.message); + } else { + if (error.stack) client.emit("error", error.stack); + msg.channel.sendCode("JSON", (error.message || error)).catch(err => client.emit("error", err)); + } + } + } +}; + +exports.getCommand = (client, command) => { + if (client.commands.has(command)) { + return client.commands.get(command); + } else if (client.aliases.has(command)) { + return client.commands.get(client.aliases.get(command)); + } + return false; +}; + +exports.runInhibitors = (client, msg, command) => { + const priority = client.commandInhibitors.array().sort((low, high) => low.conf.priority < high.conf.priority); + let response; + priority.some((inhibitor) => { // eslint-disable-line + if (inhibitor.conf.enabled) { + response = inhibitor.run(client, msg, command); + if (response) return true; + } + }); + return response; +}; diff --git a/functions/handleMessage.js b/functions/handleMessage.js new file mode 100644 index 00000000..11793b4c --- /dev/null +++ b/functions/handleMessage.js @@ -0,0 +1,8 @@ +module.exports = (client, msg, edited = false) => { + if (client.config.ignoreBots && msg.author.bot) return false; // Ignore Bots if True + if (client.config.ignoreSelf && msg.author.id === client.user.id) return false; // Ignore Self if true + if (client.config.selfbot && msg.author.id !== client.user.id) return false; // Ignore other users if selfbot is true + if (!client.config.selfbot && msg.author.id === client.user.id) return false; // Ignore other users if selfbot but config option is false + if (!client.config.editableCommands && edited) return false; // Ignore message if owner doesn't allow editableCommands + return true; +} diff --git a/functions/parseCommand.js b/functions/parseCommand.js new file mode 100644 index 00000000..fad054ad --- /dev/null +++ b/functions/parseCommand.js @@ -0,0 +1,30 @@ +module.exports = (client, msg, usage = false) => { + const prefix = this.getPrefix(client, msg); + if (!prefix) return; + const prefixLength = this.getLength(client, msg, prefix); + if (usage) return prefixLength; + return msg.content.slice(prefixLength).split(" ")[0].toLowerCase(); +}; + +exports.getLength = (client, msg, prefix) => { + if (client.config.prefixMention === prefix) { + return prefix.exec(msg.content)[0].length + 1; + } + return prefix.exec(msg.content)[0].length; +}; + +exports.getPrefix = (client, msg) => { + if (client.config.prefixMention.test(msg.content)) { + return client.config.prefixMention; + } + const prefix = msg.guildConf.prefix; + if (prefix instanceof Array) { + prefix.forEach((prefix) => { + if (msg.content.startsWith(prefix)) prefix = RegExp(`^${prefix}`); + else prefix = false; + }); + return prefix; + } + if (msg.content.startsWith(prefix)) return new RegExp(`^${prefix}`); // eslint-disable-line + return false; +}; diff --git a/package.json b/package.json index 79b52488..fa04cf7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.17.1", + "version": "0.17.2", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 5d470b8d1e6c7b9ab181d96cbbd0e9562acb0d62 Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 5 Feb 2017 16:38:59 -0500 Subject: [PATCH 45/56] [BUGFIXES] Reload && Events (#139) * Bugfixes && Changes * [PR] Patch Bump --- CHANGELOG.md | 6 ++- commands/System/reload.js | 62 +++++++++++++++++++++--------- functions/initialize.js | 18 --------- functions/loadCommandInhibitors.js | 1 + functions/loadFunctions.js | 1 + functions/loadMessageMonitors.js | 1 + functions/loadProviders.js | 1 + functions/loadSingleCommand.js | 1 + functions/reload.js | 5 +++ package.json | 2 +- 10 files changed, 59 insertions(+), 39 deletions(-) delete mode 100644 functions/initialize.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f9bf06d3..ed6558c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New extended help feature added. - New Beta Configuration (Needs heavy testing) - New Argument Prompting for Commands -- New Initialize Function to alleviate undefined errors +- ~~New Initialize Function to alleviate undefined errors~~ Reverted in #139 - New Download Command ### Changed +- All pieces now initialize upon being loaded, in order. +- Changed Emojis to unicode variants in Reload.js - Broke down App.js Message Event into several smaller, changeable parts. - newError changed to send arguments to awaitMessage when errors are from usage - awaitMessage changed to work perfectly with the new system @@ -40,6 +42,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - dataProviders renamed to Providers ### Fixed +- Fixed Reloading Events not loading new events correctly. - Fixed Typo in transfer command - Fixed Usage not working properly with selective - permissionLevels -> permissionLevel @@ -76,6 +79,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - More Reload function fixes for commands ### Removed +- Old initialize system (Was borked) - Old Configuration System - Selfbot Inhibitor diff --git a/commands/System/reload.js b/commands/System/reload.js index 0964ab46..86259b84 100644 --- a/commands/System/reload.js +++ b/commands/System/reload.js @@ -3,40 +3,64 @@ exports.run = async (client, msg, [type, name]) => { let message; switch (type) { case "function": - m = await msg.channel.sendMessage(`Attemping to reload function ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.function(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload function ${name}`); + message = await client.funcs.reload.function(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "inhibitor": - m = await msg.channel.sendMessage(`Attempting to reload inhibitor ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload inhibitor ${name}`); + message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "monitor": - m = await msg.channel.sendMessage(`Attempting to reload monitor ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.monitor(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload monitor ${name}`); + message = await client.funcs.reload.monitor(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "provider": - m = await msg.channel.sendMessage(`Attempting to reload provider ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.provider(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload provider ${name}`); + message = await client.funcs.reload.provider(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "event": - m = await msg.channel.sendMessage(`Attempting to reload event ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.event(client, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload event ${name}`); + message = await client.funcs.reload.event(client, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "command": switch (name) { case "all": await client.funcs.loadCommands(client); - msg.channel.sendMessage(":white_check_mark: Reloaded all commands."); + msg.channel.sendMessage("✅ Reloaded all commands."); break; default: - m = await msg.channel.sendMessage(`Attempting to reload command ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.command(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload command ${name}`); + message = await client.funcs.reload.command(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; } break; diff --git a/functions/initialize.js b/functions/initialize.js deleted file mode 100644 index ac00d300..00000000 --- a/functions/initialize.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = async (client) => { - Object.keys(client.funcs).forEach(async (func) => { - if (client.funcs[func].init) await client.funcs[func].init(client); - }); - client.providers.forEach(async (prov) => { - if (prov.init) await prov.init(client); - }); - await client.configuration.initialize(client); - client.commandInhibitors.forEach(async (inhib) => { - if (inhib.init) await inhib.init(client); - }); - client.messageMonitors.forEach(async (mon) => { - if (mon.init) await mon.init(client); - }); - client.commands.forEach(async (cmd) => { - if (cmd.init) await cmd.init(client); - }); -}; diff --git a/functions/loadCommandInhibitors.js b/functions/loadCommandInhibitors.js index b550a0d5..817be170 100644 --- a/functions/loadCommandInhibitors.js +++ b/functions/loadCommandInhibitors.js @@ -8,6 +8,7 @@ const loadCommandInhibitors = (client, baseDir) => new Promise(async (resolve, r try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); + if (props.init) props.init(client); client.commandInhibitors.set(f.name, props); }); resolve(); diff --git a/functions/loadFunctions.js b/functions/loadFunctions.js index d3c6a124..10e16bd5 100644 --- a/functions/loadFunctions.js +++ b/functions/loadFunctions.js @@ -11,6 +11,7 @@ const loadFunctions = (client, baseDir) => new Promise(async (resolve, reject) = const file = f.split("."); if (file[0] === "loadFunctions") return; client.funcs[file[0]] = require(`${dir}${path.sep}${f}`); + if (client.funcs[file[0]].init) client.funcs[file[0]].init(client); }); resolve(); } catch (e) { diff --git a/functions/loadMessageMonitors.js b/functions/loadMessageMonitors.js index f33de96a..181f1ae5 100644 --- a/functions/loadMessageMonitors.js +++ b/functions/loadMessageMonitors.js @@ -8,6 +8,7 @@ const loadMessageMonitors = (client, baseDir) => new Promise(async (resolve, rej try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); + if (props.init) props.init(client); client.messageMonitors.set(f.name, props); }); resolve(); diff --git a/functions/loadProviders.js b/functions/loadProviders.js index 6f0a7475..94efe063 100644 --- a/functions/loadProviders.js +++ b/functions/loadProviders.js @@ -8,6 +8,7 @@ const loadProviders = (client, baseDir) => new Promise(async (resolve, reject) = try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); + if (props.init) props.init(client); client.providers.set(f.name, props); }); resolve(); diff --git a/functions/loadSingleCommand.js b/functions/loadSingleCommand.js index bd4cc007..1d382835 100644 --- a/functions/loadSingleCommand.js +++ b/functions/loadSingleCommand.js @@ -31,6 +31,7 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi if (cmd.conf.selfbot && !client.config.selfbot) { return reject(`The command \`${cmd.help.name}\` is only usable in selfbots!`); } + if (cmd.init) cmd.init(client); let pathParts = loadPath.split(path.sep); pathParts = pathParts.slice(pathParts.indexOf("commands") + 1); category = client.funcs.toTitleCase(cmd.help.category ? cmd.help.category : (pathParts[0] && pathParts[0].length > 0 && pathParts[0].indexOf(".") === -1 ? pathParts[0] : "General")); diff --git a/functions/reload.js b/functions/reload.js index 7081c08f..98b3b9eb 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -218,6 +218,11 @@ exports.event = (client, eventName) => new Promise(async (resolve, reject) => { const files = await client.funcs.getFileListing(client, client.clientBaseDir, "events").catch(err => client.emit("error", client.funcs.newError(err))); const oldEvent = files.filter(f => f.name === eventName); if (oldEvent[0] && oldEvent[0].name === eventName) { + if (!client.events[eventName]) { + const file = oldEvent[0]; + client.on(file.name, (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args)); + return; + } let listener; if (client._events[eventName].length !== 0) { listener = client._events[eventName][1]; diff --git a/package.json b/package.json index fa04cf7a..0777168d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.17.2", + "version": "0.17.3", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From c7228f22ade5c2d7ff1dea5e8f5b3df0f3524a0e Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 5 Feb 2017 16:42:06 -0500 Subject: [PATCH 46/56] Revert "[BUGFIXES] Reload && Events" (#140) --- CHANGELOG.md | 6 +-- commands/System/reload.js | 62 +++++++++--------------------- functions/initialize.js | 18 +++++++++ functions/loadCommandInhibitors.js | 1 - functions/loadFunctions.js | 1 - functions/loadMessageMonitors.js | 1 - functions/loadProviders.js | 1 - functions/loadSingleCommand.js | 1 - functions/reload.js | 5 --- package.json | 2 +- 10 files changed, 39 insertions(+), 59 deletions(-) create mode 100644 functions/initialize.js diff --git a/CHANGELOG.md b/CHANGELOG.md index ed6558c3..f9bf06d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,12 +14,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New extended help feature added. - New Beta Configuration (Needs heavy testing) - New Argument Prompting for Commands -- ~~New Initialize Function to alleviate undefined errors~~ Reverted in #139 +- New Initialize Function to alleviate undefined errors - New Download Command ### Changed -- All pieces now initialize upon being loaded, in order. -- Changed Emojis to unicode variants in Reload.js - Broke down App.js Message Event into several smaller, changeable parts. - newError changed to send arguments to awaitMessage when errors are from usage - awaitMessage changed to work perfectly with the new system @@ -42,7 +40,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - dataProviders renamed to Providers ### Fixed -- Fixed Reloading Events not loading new events correctly. - Fixed Typo in transfer command - Fixed Usage not working properly with selective - permissionLevels -> permissionLevel @@ -79,7 +76,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - More Reload function fixes for commands ### Removed -- Old initialize system (Was borked) - Old Configuration System - Selfbot Inhibitor diff --git a/commands/System/reload.js b/commands/System/reload.js index 86259b84..0964ab46 100644 --- a/commands/System/reload.js +++ b/commands/System/reload.js @@ -3,64 +3,40 @@ exports.run = async (client, msg, [type, name]) => { let message; switch (type) { case "function": - try { - m = await msg.channel.sendMessage(`Attempting to reload function ${name}`); - message = await client.funcs.reload.function(client, client.clientBaseDir, name); - m.edit(`✅ ${message}`); - } catch (err) { - m.edit(`❌ ${err}`); - } + m = await msg.channel.sendMessage(`Attemping to reload function ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.function(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; case "inhibitor": - try { - m = await msg.channel.sendMessage(`Attempting to reload inhibitor ${name}`); - message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name); - m.edit(`✅ ${message}`); - } catch (err) { - m.edit(`❌ ${err}`); - } + m = await msg.channel.sendMessage(`Attempting to reload inhibitor ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; case "monitor": - try { - m = await msg.channel.sendMessage(`Attempting to reload monitor ${name}`); - message = await client.funcs.reload.monitor(client, client.clientBaseDir, name); - m.edit(`✅ ${message}`); - } catch (err) { - m.edit(`❌ ${err}`); - } + m = await msg.channel.sendMessage(`Attempting to reload monitor ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.monitor(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; case "provider": - try { - m = await msg.channel.sendMessage(`Attempting to reload provider ${name}`); - message = await client.funcs.reload.provider(client, client.clientBaseDir, name); - m.edit(`✅ ${message}`); - } catch (err) { - m.edit(`❌ ${err}`); - } + m = await msg.channel.sendMessage(`Attempting to reload provider ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.provider(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; case "event": - try { - m = await msg.channel.sendMessage(`Attempting to reload event ${name}`); - message = await client.funcs.reload.event(client, name); - m.edit(`✅ ${message}`); - } catch (err) { - m.edit(`❌ ${err}`); - } + m = await msg.channel.sendMessage(`Attempting to reload event ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.event(client, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; case "command": switch (name) { case "all": await client.funcs.loadCommands(client); - msg.channel.sendMessage("✅ Reloaded all commands."); + msg.channel.sendMessage(":white_check_mark: Reloaded all commands."); break; default: - try { - m = await msg.channel.sendMessage(`Attempting to reload command ${name}`); - message = await client.funcs.reload.command(client, client.clientBaseDir, name); - m.edit(`✅ ${message}`); - } catch (err) { - m.edit(`❌ ${err}`); - } + m = await msg.channel.sendMessage(`Attempting to reload command ${name}`).catch(err => client.funcs.log(err, "error")); + message = await client.funcs.reload.command(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); + m.edit(`:white_check_mark: ${message}`); break; } break; diff --git a/functions/initialize.js b/functions/initialize.js new file mode 100644 index 00000000..ac00d300 --- /dev/null +++ b/functions/initialize.js @@ -0,0 +1,18 @@ +module.exports = async (client) => { + Object.keys(client.funcs).forEach(async (func) => { + if (client.funcs[func].init) await client.funcs[func].init(client); + }); + client.providers.forEach(async (prov) => { + if (prov.init) await prov.init(client); + }); + await client.configuration.initialize(client); + client.commandInhibitors.forEach(async (inhib) => { + if (inhib.init) await inhib.init(client); + }); + client.messageMonitors.forEach(async (mon) => { + if (mon.init) await mon.init(client); + }); + client.commands.forEach(async (cmd) => { + if (cmd.init) await cmd.init(client); + }); +}; diff --git a/functions/loadCommandInhibitors.js b/functions/loadCommandInhibitors.js index 817be170..b550a0d5 100644 --- a/functions/loadCommandInhibitors.js +++ b/functions/loadCommandInhibitors.js @@ -8,7 +8,6 @@ const loadCommandInhibitors = (client, baseDir) => new Promise(async (resolve, r try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); - if (props.init) props.init(client); client.commandInhibitors.set(f.name, props); }); resolve(); diff --git a/functions/loadFunctions.js b/functions/loadFunctions.js index 10e16bd5..d3c6a124 100644 --- a/functions/loadFunctions.js +++ b/functions/loadFunctions.js @@ -11,7 +11,6 @@ const loadFunctions = (client, baseDir) => new Promise(async (resolve, reject) = const file = f.split("."); if (file[0] === "loadFunctions") return; client.funcs[file[0]] = require(`${dir}${path.sep}${f}`); - if (client.funcs[file[0]].init) client.funcs[file[0]].init(client); }); resolve(); } catch (e) { diff --git a/functions/loadMessageMonitors.js b/functions/loadMessageMonitors.js index 181f1ae5..f33de96a 100644 --- a/functions/loadMessageMonitors.js +++ b/functions/loadMessageMonitors.js @@ -8,7 +8,6 @@ const loadMessageMonitors = (client, baseDir) => new Promise(async (resolve, rej try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); - if (props.init) props.init(client); client.messageMonitors.set(f.name, props); }); resolve(); diff --git a/functions/loadProviders.js b/functions/loadProviders.js index 94efe063..6f0a7475 100644 --- a/functions/loadProviders.js +++ b/functions/loadProviders.js @@ -8,7 +8,6 @@ const loadProviders = (client, baseDir) => new Promise(async (resolve, reject) = try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); - if (props.init) props.init(client); client.providers.set(f.name, props); }); resolve(); diff --git a/functions/loadSingleCommand.js b/functions/loadSingleCommand.js index 1d382835..bd4cc007 100644 --- a/functions/loadSingleCommand.js +++ b/functions/loadSingleCommand.js @@ -31,7 +31,6 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi if (cmd.conf.selfbot && !client.config.selfbot) { return reject(`The command \`${cmd.help.name}\` is only usable in selfbots!`); } - if (cmd.init) cmd.init(client); let pathParts = loadPath.split(path.sep); pathParts = pathParts.slice(pathParts.indexOf("commands") + 1); category = client.funcs.toTitleCase(cmd.help.category ? cmd.help.category : (pathParts[0] && pathParts[0].length > 0 && pathParts[0].indexOf(".") === -1 ? pathParts[0] : "General")); diff --git a/functions/reload.js b/functions/reload.js index 98b3b9eb..7081c08f 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -218,11 +218,6 @@ exports.event = (client, eventName) => new Promise(async (resolve, reject) => { const files = await client.funcs.getFileListing(client, client.clientBaseDir, "events").catch(err => client.emit("error", client.funcs.newError(err))); const oldEvent = files.filter(f => f.name === eventName); if (oldEvent[0] && oldEvent[0].name === eventName) { - if (!client.events[eventName]) { - const file = oldEvent[0]; - client.on(file.name, (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args)); - return; - } let listener; if (client._events[eventName].length !== 0) { listener = client._events[eventName][1]; diff --git a/package.json b/package.json index 0777168d..fa04cf7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.17.3", + "version": "0.17.2", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 10ed84d10a1db7bf327980d2640107ec6f69f132 Mon Sep 17 00:00:00 2001 From: Faith Date: Sun, 5 Feb 2017 17:23:18 -0500 Subject: [PATCH 47/56] [BUG FIXES] Reload && Events && Inits (#141) * Bugfixes && Changes * [PR] Patch Bump --- CHANGELOG.md | 6 ++- commands/System/reload.js | 62 +++++++++++++++++++++--------- functions/initialize.js | 18 --------- functions/loadCommandInhibitors.js | 1 + functions/loadFunctions.js | 1 + functions/loadMessageMonitors.js | 1 + functions/loadProviders.js | 1 + functions/loadSingleCommand.js | 1 + functions/reload.js | 5 +++ package.json | 2 +- 10 files changed, 59 insertions(+), 39 deletions(-) delete mode 100644 functions/initialize.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f9bf06d3..ed6558c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New extended help feature added. - New Beta Configuration (Needs heavy testing) - New Argument Prompting for Commands -- New Initialize Function to alleviate undefined errors +- ~~New Initialize Function to alleviate undefined errors~~ Reverted in #139 - New Download Command ### Changed +- All pieces now initialize upon being loaded, in order. +- Changed Emojis to unicode variants in Reload.js - Broke down App.js Message Event into several smaller, changeable parts. - newError changed to send arguments to awaitMessage when errors are from usage - awaitMessage changed to work perfectly with the new system @@ -40,6 +42,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - dataProviders renamed to Providers ### Fixed +- Fixed Reloading Events not loading new events correctly. - Fixed Typo in transfer command - Fixed Usage not working properly with selective - permissionLevels -> permissionLevel @@ -76,6 +79,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - More Reload function fixes for commands ### Removed +- Old initialize system (Was borked) - Old Configuration System - Selfbot Inhibitor diff --git a/commands/System/reload.js b/commands/System/reload.js index 0964ab46..86259b84 100644 --- a/commands/System/reload.js +++ b/commands/System/reload.js @@ -3,40 +3,64 @@ exports.run = async (client, msg, [type, name]) => { let message; switch (type) { case "function": - m = await msg.channel.sendMessage(`Attemping to reload function ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.function(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload function ${name}`); + message = await client.funcs.reload.function(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "inhibitor": - m = await msg.channel.sendMessage(`Attempting to reload inhibitor ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload inhibitor ${name}`); + message = await client.funcs.reload.inhibitor(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "monitor": - m = await msg.channel.sendMessage(`Attempting to reload monitor ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.monitor(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload monitor ${name}`); + message = await client.funcs.reload.monitor(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "provider": - m = await msg.channel.sendMessage(`Attempting to reload provider ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.provider(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload provider ${name}`); + message = await client.funcs.reload.provider(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "event": - m = await msg.channel.sendMessage(`Attempting to reload event ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.event(client, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload event ${name}`); + message = await client.funcs.reload.event(client, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; case "command": switch (name) { case "all": await client.funcs.loadCommands(client); - msg.channel.sendMessage(":white_check_mark: Reloaded all commands."); + msg.channel.sendMessage("✅ Reloaded all commands."); break; default: - m = await msg.channel.sendMessage(`Attempting to reload command ${name}`).catch(err => client.funcs.log(err, "error")); - message = await client.funcs.reload.command(client, client.clientBaseDir, name).catch(response => m.edit(`:x: ${response}`)); - m.edit(`:white_check_mark: ${message}`); + try { + m = await msg.channel.sendMessage(`Attempting to reload command ${name}`); + message = await client.funcs.reload.command(client, client.clientBaseDir, name); + m.edit(`✅ ${message}`); + } catch (err) { + m.edit(`❌ ${err}`); + } break; } break; diff --git a/functions/initialize.js b/functions/initialize.js deleted file mode 100644 index ac00d300..00000000 --- a/functions/initialize.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = async (client) => { - Object.keys(client.funcs).forEach(async (func) => { - if (client.funcs[func].init) await client.funcs[func].init(client); - }); - client.providers.forEach(async (prov) => { - if (prov.init) await prov.init(client); - }); - await client.configuration.initialize(client); - client.commandInhibitors.forEach(async (inhib) => { - if (inhib.init) await inhib.init(client); - }); - client.messageMonitors.forEach(async (mon) => { - if (mon.init) await mon.init(client); - }); - client.commands.forEach(async (cmd) => { - if (cmd.init) await cmd.init(client); - }); -}; diff --git a/functions/loadCommandInhibitors.js b/functions/loadCommandInhibitors.js index b550a0d5..817be170 100644 --- a/functions/loadCommandInhibitors.js +++ b/functions/loadCommandInhibitors.js @@ -8,6 +8,7 @@ const loadCommandInhibitors = (client, baseDir) => new Promise(async (resolve, r try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); + if (props.init) props.init(client); client.commandInhibitors.set(f.name, props); }); resolve(); diff --git a/functions/loadFunctions.js b/functions/loadFunctions.js index d3c6a124..10e16bd5 100644 --- a/functions/loadFunctions.js +++ b/functions/loadFunctions.js @@ -11,6 +11,7 @@ const loadFunctions = (client, baseDir) => new Promise(async (resolve, reject) = const file = f.split("."); if (file[0] === "loadFunctions") return; client.funcs[file[0]] = require(`${dir}${path.sep}${f}`); + if (client.funcs[file[0]].init) client.funcs[file[0]].init(client); }); resolve(); } catch (e) { diff --git a/functions/loadMessageMonitors.js b/functions/loadMessageMonitors.js index f33de96a..181f1ae5 100644 --- a/functions/loadMessageMonitors.js +++ b/functions/loadMessageMonitors.js @@ -8,6 +8,7 @@ const loadMessageMonitors = (client, baseDir) => new Promise(async (resolve, rej try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); + if (props.init) props.init(client); client.messageMonitors.set(f.name, props); }); resolve(); diff --git a/functions/loadProviders.js b/functions/loadProviders.js index 6f0a7475..94efe063 100644 --- a/functions/loadProviders.js +++ b/functions/loadProviders.js @@ -8,6 +8,7 @@ const loadProviders = (client, baseDir) => new Promise(async (resolve, reject) = try { files.forEach((f) => { const props = require(`${f.path}${path.sep}${f.base}`); + if (props.init) props.init(client); client.providers.set(f.name, props); }); resolve(); diff --git a/functions/loadSingleCommand.js b/functions/loadSingleCommand.js index bd4cc007..1d382835 100644 --- a/functions/loadSingleCommand.js +++ b/functions/loadSingleCommand.js @@ -31,6 +31,7 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi if (cmd.conf.selfbot && !client.config.selfbot) { return reject(`The command \`${cmd.help.name}\` is only usable in selfbots!`); } + if (cmd.init) cmd.init(client); let pathParts = loadPath.split(path.sep); pathParts = pathParts.slice(pathParts.indexOf("commands") + 1); category = client.funcs.toTitleCase(cmd.help.category ? cmd.help.category : (pathParts[0] && pathParts[0].length > 0 && pathParts[0].indexOf(".") === -1 ? pathParts[0] : "General")); diff --git a/functions/reload.js b/functions/reload.js index 7081c08f..98b3b9eb 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -218,6 +218,11 @@ exports.event = (client, eventName) => new Promise(async (resolve, reject) => { const files = await client.funcs.getFileListing(client, client.clientBaseDir, "events").catch(err => client.emit("error", client.funcs.newError(err))); const oldEvent = files.filter(f => f.name === eventName); if (oldEvent[0] && oldEvent[0].name === eventName) { + if (!client.events[eventName]) { + const file = oldEvent[0]; + client.on(file.name, (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args)); + return; + } let listener; if (client._events[eventName].length !== 0) { listener = client._events[eventName][1]; diff --git a/package.json b/package.json index fa04cf7a..0777168d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.17.2", + "version": "0.17.3", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 6c24fdb23754b5c167287e379dec06f32fce163e Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Mon, 6 Feb 2017 09:48:31 +1100 Subject: [PATCH 48/56] Continuation of #141 --- app.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app.js b/app.js index be818a03..b2d901c3 100644 --- a/app.js +++ b/app.js @@ -43,7 +43,6 @@ exports.start = async (config) => { client.once("ready", async () => { client.config.prefixMention = new RegExp(`^<@!?${client.user.id}>`); - await client.funcs.initialize(client); client.destroy = () => "You cannot use this within Komada, use process.exit() instead."; }); From 8332579a75148d50b3c1361956d945411a0d4cdf Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Mon, 6 Feb 2017 10:11:25 +1100 Subject: [PATCH 49/56] Continuation of #141 Part 2 --- app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app.js b/app.js index b2d901c3..6f110def 100644 --- a/app.js +++ b/app.js @@ -43,6 +43,7 @@ exports.start = async (config) => { client.once("ready", async () => { client.config.prefixMention = new RegExp(`^<@!?${client.user.id}>`); + client.configuration.initialize(client); client.destroy = () => "You cannot use this within Komada, use process.exit() instead."; }); From 0cbca65c462e81978fff0776e37411e968025b9b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 7 Feb 2017 00:20:07 +0000 Subject: [PATCH 50/56] [BUGFIX] Initialisation is now fixed. --- .gitignore | 2 ++ CHANGELOG.md | 2 +- app.js | 18 ++++++++---------- package.json | 3 ++- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index bcd9b685..816a41f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ + +.c9 # Logs logs *.log diff --git a/CHANGELOG.md b/CHANGELOG.md index ed6558c3..794754e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - New Download Command ### Changed -- All pieces now initialize upon being loaded, in order. +- ~~All pieces now initialize upon being loaded, in order.~~ ~~Reverted in 0.17.3~~ Reimplemented in 0.17.4 within `client.on("ready")` - Changed Emojis to unicode variants in Reload.js - Broke down App.js Message Event into several smaller, changeable parts. - newError changed to send arguments to awaitMessage when errors are from usage diff --git a/app.js b/app.js index 6f110def..1b160087 100644 --- a/app.js +++ b/app.js @@ -31,19 +31,17 @@ exports.start = async (config) => { client.guildConfs = Config.guildConfs; client.configuration = Config; - // Load core functions, then everything else - await loadFunctions(client); - await client.funcs.loadProviders(client); - await client.funcs.loadCommands(client); - await client.funcs.loadCommandInhibitors(client); - await client.funcs.loadMessageMonitors(client); - await client.funcs.loadEvents(client); - client.i18n = client.funcs.loadLocalizations; - client.i18n.init(client); - client.once("ready", async () => { client.config.prefixMention = new RegExp(`^<@!?${client.user.id}>`); client.configuration.initialize(client); + await loadFunctions(client); + await client.funcs.loadProviders(client); + await client.funcs.loadCommands(client); + await client.funcs.loadCommandInhibitors(client); + await client.funcs.loadMessageMonitors(client); + await client.funcs.loadEvents(client); + client.i18n = client.funcs.loadLocalizations; + client.i18n.init(client); client.destroy = () => "You cannot use this within Komada, use process.exit() instead."; }); diff --git a/package.json b/package.json index 0777168d..3c122eee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.17.3", + "version": "0.17.4", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", @@ -11,6 +11,7 @@ }, "repository": { "type": "git", + "url": "https://github.com/dirigeants/komada.git" }, "bugs": { From 5e9d830b6dc8ce3d13e92688e94d92e1f207f4e6 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 7 Feb 2017 01:43:07 +0000 Subject: [PATCH 51/56] [BUG-FIX] Booleans are now properly validated --- CHANGELOG.md | 1 + functions/validateData.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 794754e9..c4c08f96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - dataProviders renamed to Providers ### Fixed +- Fixed validateData Booleans. - Fixed Reloading Events not loading new events correctly. - Fixed Typo in transfer command - Fixed Usage not working properly with selective diff --git a/functions/validateData.js b/functions/validateData.js index 8bac80b2..4fa7806d 100644 --- a/functions/validateData.js +++ b/functions/validateData.js @@ -38,7 +38,7 @@ module.exports = (data, properties, values) => { } break; case "bool": - if (!["true", "false", true, false, 1, 0].includes(v.toLowerCase())) { + if (!["true", "false", true, false, 1, 0].includes(!!JSON.parse(v))) { throw new Error(`at value #${i + 1}: The 'bool' type must be 'true(1)' or 'false(0)', got '${v}`); } break; diff --git a/package.json b/package.json index 3c122eee..16fb04d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.17.4", + "version": "0.17.5", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From 21033e07bf8b4c0c255c29aaedd473d0168d8004 Mon Sep 17 00:00:00 2001 From: kyraNET Date: Thu, 9 Feb 2017 23:30:30 +0100 Subject: [PATCH 52/56] Fixed some typos in parseUsage (#142) * Fixed some typos * Update parseUsage.js --- functions/parseUsage.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/functions/parseUsage.js b/functions/parseUsage.js index cbbd2b67..e564c1b0 100644 --- a/functions/parseUsage.js +++ b/functions/parseUsage.js @@ -13,7 +13,7 @@ const parseTag = (tag, count) => { const result = /^([a-z0-9]+)(?::([a-z0-9]+)(?:{(?:(\d+(?:\.\d+)?))?(?:,(\d+(?:\.\d+)?))?})?)?$/i.exec(e); // I require to modify the regex if we wan't to handle invalid types instead of defaulting them - if (!result) { throw new Error(`at tag #${count} at bound #${i + 1}: Invalid syntax, non spesific`); } + if (!result) { throw new Error(`at tag #${count} at bound #${i + 1}: Invalid syntax, non specific`); } const fill = { name: result[1], @@ -22,7 +22,7 @@ const parseTag = (tag, count) => { if (result[3]) { if (fill.type === "literal") { - throw new Error(`at tag #${count} at bound #${i + 1} at the type length definition (min): you canno't set a length for a literal type`); + throw new Error(`at tag #${count} at bound #${i + 1} at the type length definition (min): you cannot set a length for a literal type`); } const temp = parseFloat(result[3]); @@ -36,7 +36,7 @@ const parseTag = (tag, count) => { if (result[4]) { if (fill.type === "literal") { - throw new Error(`at tag #${count} at bound #${i + 1} at the type length definition (max): you canno't set a length for a literal type`); + throw new Error(`at tag #${count} at bound #${i + 1} at the type length definition (max): you cannot set a length for a literal type`); } const temp = parseFloat(result[4]); @@ -45,7 +45,7 @@ const parseTag = (tag, count) => { } if (fill.type === "literal") { - if (literals.includes(fill.name)) { throw new Error(`at tag #${count} at bound #${i + 1}: there can't be two literals with the same text.`); } + if (literals.includes(fill.name)) { throw new Error(`at tag #${count} at bound #${i + 1}: there cannot be two literals with the same text.`); } literals.push(fill.name); } else if (members.length > 1) { @@ -53,7 +53,7 @@ const parseTag = (tag, count) => { throw new Error(`at tag #${count} at bound #${i + 1}: the String type is vague, you must specify it at the last bound`); } if (types.includes(fill.type)) { - throw new Error(`at tag #${count} at bound #${i + 1}: there can't be two bounds with the same type (${fill.type})`); + throw new Error(`at tag #${count} at bound #${i + 1}: there cannot be two bounds with the same type (${fill.type})`); } types.push(fill.type); } @@ -73,7 +73,7 @@ module.exports = (command) => { const cmd = command.split(""); cmd.forEach((c, i) => { if (last && c !== " ") { - throw new Error(`at char #${i + 1} '${c}': there can't be anything else after the repeat tag.`); + throw new Error(`at char #${i + 1} '${c}': there cannot be anything else after the repeat tag.`); } if (c === "<") { @@ -81,7 +81,7 @@ module.exports = (command) => { throw new Error(`at char #${i + 1} '<': you might not open a tag inside another tag.`); } if (current) { - throw new Error(`from char #${(i + 1) - current.length} to #${i + 1} '${current}': there can't be a literal outside a tag`); + throw new Error(`from char #${(i + 1) - current.length} to #${i + 1} '${current}': there cannot be a literal outside a tag`); } opened++; openReq = true; @@ -105,7 +105,7 @@ module.exports = (command) => { throw new Error(`at char #${i + 1} '[': you might not open a tag inside another tag.`); } if (current) { - throw new Error(`from char #${(i + 1) - current.length} to #${i + 1} '${current}': there can't be a literal outside a tag`); + throw new Error(`from char #${(i + 1) - current.length} to #${i + 1} '${current}': there cannot be a literal outside a tag`); } opened++; openReq = false; @@ -117,7 +117,7 @@ module.exports = (command) => { opened--; if (current === "...") { if (tags.length < 1) { - throw new Error(`from char #${i - 3} to #${i} '[...]': there can't be a loop at teh begining`); + throw new Error(`from char #${i - 3} to #${i} '[...]': there cannot be a loop at the begining`); } tags.push({ type: "repeat" }); last = true; @@ -132,7 +132,7 @@ module.exports = (command) => { } else if (c === " ") { if (opened) { throw new Error(`at char #${i + 1}: spaces aren't allowed inside a tag`); } if (current) { - throw new Error(`from char #${(i + 1) - current.length} to char #${i} '${current}': there can't be a literal outside a tag.`); + throw new Error(`from char #${(i + 1) - current.length} to char #${i} '${current}': there cannot be a literal outside a tag.`); } } else current += c; }); From 3b76d94a187fa139497624c46306165cf10d8d1d Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Thu, 16 Mar 2017 10:52:51 +1100 Subject: [PATCH 53/56] CHANGELOG --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4c08f96..b0ab378a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] + +## [0.18.0] - 2017-03-16 ### Added - Added a bunch of unusable configuration options that'll make their debut soon. - All Bad Requests/Forbiddens.. etc, now properly give a human readable error in console or chat, depending on the error. (Not as of (0.17.0).. must be fixed) *** @@ -176,11 +178,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Various Confs fixes from [UnseenFaith] - Usage Addition/ParseUsage fix from [UnseenFaith] -[Unreleased]: https://github.com/eslachance/komada/compare/0.12.4...indev +[Unreleased]: https://github.com/eslachance/komada/compare/0.18.0...indev [0.10.0]: https://github.com/eslachance/komada/compare/1627e6deb1d8c352d83e52ccd590f2330f5f8bb2...0.10.0 [0.11.0]: https://github.com/eslachance/komada/compare/0.10.0...0.11.0 [0.12.0]: https://github.com/eslachance/komada/compare/0.11.0...0.12.0 [0.12.4]: https://github.com/eslachance/komada/compare/0.12.0...0.12.4 +[0.18.0]: https://github.com/eslachance/komada/compare/0.12.4...0.18 [vzwGrey]: https://github.com/vzwGrey [eslachance]: https://github.com/eslachance From 709a4e9c8aca27b7f5df798b1c9a3e073927a440 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Thu, 16 Mar 2017 10:54:26 +1100 Subject: [PATCH 54/56] Bump to 0.18.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 16fb04d9..f66cb593 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "komada", - "version": "0.17.5", + "version": "0.18.0", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", From b1b65b2b8bbd7d3aca6de145eadcd4660dfa8775 Mon Sep 17 00:00:00 2001 From: Johnson Chen Date: Sat, 18 Mar 2017 09:05:08 +1100 Subject: [PATCH 55/56] [BUGFIX/Feature] Fixed Event Loading && Added Internal Utils (#143) * Moved loading to Utils * Time for more utils * FML * Faith fixed the regex * Typo out of 10 * regexp * Fixed Configs * ESlint * temp * ^^^^ * typo * We are not using initialize function again * Client.ready * Added Env Var support for clientDir * Removal of client.email from clean function * ESlint + Updating of ~Info Command * ... * Fix a typo on line 70 (#144) [FIX] Fix a typo on line 70 * reload all commands * whoops * Silent inhibitors (#146) * Added silent inhibitors If you have an inhibitor and you set `return true;`, it'll stop execution instead of sending an error. However, you are still able to do `return "This is a string"`, and it'll reply with the inserted string. * Added Silent Inhibitors * Update handleCommand.js * Update package.json * Fixed * Added the new Discord.js methods. (#147) * Update package.json * Update CHANGELOG.md * Added new Discord.js utils * Update .travis.yml * Sentry integration * Update app.js * grrr * Is this it? * ESLint * Update package.json * Update CHANGELOG.md * Update package.json * Fixed reload events * Update CHANGELOG.md * Fixed `'file' is not defined` * ESLint * Update package.json * Update app.js * Update package.json * Update CHANGELOG.md * ESLint * Fixed codeblocks and missing information (no error stack) * Fixed missing codeblocks * Update CHANGELOG.md * Add error.stack to case warn. * Disconnect event should print "Disconnected" now * Update CHANGELOG.md * Update app.js * util.inspect error, warn, and better disconnect message. * Update CHANGELOG.md * Update app.js --- .eslintrc.json => .eslintrc | 1 - .travis.yml | 9 +++- CHANGELOG.md | 27 +++++++++- app.js | 39 +++++++++----- classes/Config.js | 2 +- commands/System/download.js | 2 +- commands/System/info.js | 18 ++++++- commands/System/reload.js | 2 +- functions/clean.js | 1 - functions/handleCommand.js | 5 +- functions/handleMessage.js | 2 +- functions/loadSingleCommand.js | 5 +- functions/log.js | 2 +- functions/parseCommand.js | 11 ++-- functions/regExpEsc.js | 1 + functions/reload.js | 53 ++++++++----------- functions/validateData.js | 2 +- package.json | 6 +-- {functions => utils}/loadCommandInhibitors.js | 0 {functions => utils}/loadCommands.js | 0 {functions => utils}/loadEvents.js | 6 ++- {functions => utils}/loadFunctions.js | 0 {functions => utils}/loadMessageMonitors.js | 0 {functions => utils}/loadProviders.js | 0 24 files changed, 126 insertions(+), 68 deletions(-) rename .eslintrc.json => .eslintrc (97%) create mode 100644 functions/regExpEsc.js rename {functions => utils}/loadCommandInhibitors.js (100%) rename {functions => utils}/loadCommands.js (100%) rename {functions => utils}/loadEvents.js (78%) rename {functions => utils}/loadFunctions.js (100%) rename {functions => utils}/loadMessageMonitors.js (100%) rename {functions => utils}/loadProviders.js (100%) diff --git a/.eslintrc.json b/.eslintrc similarity index 97% rename from .eslintrc.json rename to .eslintrc index 94c48f07..57186d54 100644 --- a/.eslintrc.json +++ b/.eslintrc @@ -14,7 +14,6 @@ "no-shadow": "warn", "no-plusplus": "off", "radix": ["error", "as-needed"], - "max-len": "warn", "import/no-extraneous-dependencies": "off", "import/no-unresolved": "off", "import/no-dynamic-require": "warn", diff --git a/.travis.yml b/.travis.yml index 07720ee5..a5edc928 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,11 @@ language: node_js node_js: - - "6" + - "7" install: npm install +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/0926b82fc22ea0760ede + on_success: always # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always diff --git a/CHANGELOG.md b/CHANGELOG.md index b0ab378a..1a6bcb5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,30 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [0.18.1] - 2017-03-17 +### Added +- Sentry Integration for Error Tracking purposes direct to Komada Devs, +- Added the new utils from `Discord.js#master`: escapeMarkdown and splitMessage are now in `client.methods`. +- Added support for silent inhibitors (if `return true`, it won't send a reply). +- Added Environmental Variable support for clientDir. +- Added regExpEscape function. + +### Changed +- Add error.stack to the function log.js to avoid [object Object]. +- Disconnect event should now prints a more human readable error instead of `[object Object]`. +- error and warn event errors are now inspected with depth 0, for better debug. +- loading Functions are removed from Functions folder and moved to a Utils folder. (This folder will be there for future features as well.) + +### Fixed +- Reloading pieces should now return the error stack in a codeblock. +- Fixed function reload event. +- Fixed command reload all. underlying bug since 0.15.x days. +- Fixed typo in validateData function +- Fixed Default Conf initialize. (No longer outputs undefined) +- Fixed invalid regex for prefixes in parseCommand + +### Removed +- client.email redaction from the clean function. ## [0.18.0] - 2017-03-16 ### Added @@ -178,12 +202,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Various Confs fixes from [UnseenFaith] - Usage Addition/ParseUsage fix from [UnseenFaith] -[Unreleased]: https://github.com/eslachance/komada/compare/0.18.0...indev +[Unreleased]: https://github.com/eslachance/komada/compare/0.18.1...indev [0.10.0]: https://github.com/eslachance/komada/compare/1627e6deb1d8c352d83e52ccd590f2330f5f8bb2...0.10.0 [0.11.0]: https://github.com/eslachance/komada/compare/0.10.0...0.11.0 [0.12.0]: https://github.com/eslachance/komada/compare/0.11.0...0.12.0 [0.12.4]: https://github.com/eslachance/komada/compare/0.12.0...0.12.4 [0.18.0]: https://github.com/eslachance/komada/compare/0.12.4...0.18 +[0.18.1]: https://github.com/eslachance/komada/compare/0.12.4...0.18.1 [vzwGrey]: https://github.com/vzwGrey [eslachance]: https://github.com/eslachance diff --git a/app.js b/app.js index 1b160087..dfcbebe9 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,14 @@ const Discord = require("discord.js"); const path = require("path"); +const util = require("util"); + +const loadFunctions = require("./utils/loadFunctions.js"); +const loadEvents = require("./utils/loadEvents.js"); +const loadProviders = require("./utils/loadProviders.js"); +const loadCommands = require("./utils/loadCommands.js"); +const loadCommandInhibitors = require("./utils/loadCommandInhibitors.js"); +const loadMessageMonitors = require("./utils/loadMessageMonitors.js"); -const loadFunctions = require("./functions/loadFunctions.js"); const Config = require("./classes/Config.js"); exports.start = async (config) => { @@ -10,7 +17,7 @@ exports.start = async (config) => { client.config = config; - // Extend client + // Extend client client.funcs = {}; client.helpStructure = new Map(); client.commands = new Discord.Collection(); @@ -19,37 +26,42 @@ exports.start = async (config) => { client.messageMonitors = new Discord.Collection(); client.providers = new Discord.Collection(); - // Extend Client with Native Discord.js Functions for use in our pieces. + // Extend Client with Native Discord.js Functions for use in our pieces. client.methods = {}; client.methods.Collection = Discord.Collection; client.methods.Embed = Discord.RichEmbed; client.methods.MessageCollector = Discord.MessageCollector; client.methods.Webhook = Discord.WebhookClient; + client.methods.escapeMarkdown = Discord.escapeMarkdown; + client.methods.splitMessage = Discord.splitMessage; client.coreBaseDir = `${__dirname}${path.sep}`; - client.clientBaseDir = `${process.cwd()}${path.sep}`; + client.clientBaseDir = `${process.env.clientDir || process.cwd()}${path.sep}`; client.guildConfs = Config.guildConfs; client.configuration = Config; + await loadEvents(client); + client.once("ready", async () => { client.config.prefixMention = new RegExp(`^<@!?${client.user.id}>`); - client.configuration.initialize(client); + await client.configuration.initialize(client); await loadFunctions(client); - await client.funcs.loadProviders(client); - await client.funcs.loadCommands(client); - await client.funcs.loadCommandInhibitors(client); - await client.funcs.loadMessageMonitors(client); - await client.funcs.loadEvents(client); + await loadProviders(client); + await loadCommands(client); + await loadCommandInhibitors(client); + await loadMessageMonitors(client); client.i18n = client.funcs.loadLocalizations; client.i18n.init(client); client.destroy = () => "You cannot use this within Komada, use process.exit() instead."; + client.ready = true; }); - client.on("error", e => client.funcs.log(e, "error")); - client.on("warn", w => client.funcs.log(w, "warning")); - client.on("disconnect", e => client.funcs.log(e, "error")); + client.on("error", e => client.funcs.log(util.inspect(e, { depth: 0 }), "error")); + client.on("warn", w => client.funcs.log(util.inspect(w, { depth: 0 }), "warn")); + client.on("disconnect", e => client.funcs.log(`Disconnected | ${e.code}: ${e.reason}`, "error")); client.on("message", async (msg) => { + if (!client.ready) return; await client.funcs.runMessageMonitors(client, msg); msg.author.permLevel = await client.funcs.permissionLevel(client, msg.author, msg.guild); msg.guildConf = Config.get(msg.guild); @@ -62,7 +74,6 @@ exports.start = async (config) => { client.login(client.config.botToken); return client; }; - process.on("unhandledRejection", (err) => { if (!err) return; console.error(`Uncaught Promise Error: \n${err.stack || err}`); diff --git a/classes/Config.js b/classes/Config.js index 4eb4d779..ab4611c6 100644 --- a/classes/Config.js +++ b/classes/Config.js @@ -354,7 +354,7 @@ class Config { .then((conf) => { if (conf) defaultConf = conf; }) - .catch(() => fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`)); + .catch(() => fs.outputJSONAsync(`${dataDir}${path.sep}${defaultFile}`, defaultConf)); client.guilds.forEach((guild) => { fs.readJSONAsync(path.resolve(`${dataDir}${path.sep}${guild.id}.json`)) .then((thisConf) => { diff --git a/commands/System/download.js b/commands/System/download.js index b125054b..031d0845 100644 --- a/commands/System/download.js +++ b/commands/System/download.js @@ -63,7 +63,7 @@ exports.run = (client, msg, [link, piece, folder = "Downloaded"]) => { if (client.messageMonitors.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id}> | That message monitor already exists in your bot. Aborting the load.`); break; case "providers": - if (client.providers.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id} | That provider already exists in your bot. Aborting the load.`); + if (client.providers.has(name)) return msg.channel.sendMessage(`<@!${msg.author.id}> | That provider already exists in your bot. Aborting the load.`); break; default: return "This will never trigger"; diff --git a/commands/System/info.js b/commands/System/info.js index 5889baa4..9cdf56d5 100644 --- a/commands/System/info.js +++ b/commands/System/info.js @@ -1,5 +1,21 @@ exports.run = (client, msg) => { - msg.channel.sendMessage("This bot is built on the Komada framework, a plug-and-play bot builder made by Dirigeant's team of dedicated developers. For more information visit: "); + const information = ` +Komada is a 'plug-and-play' framework built on top of the Discord.js library. +Most of the code is modularized, which allows developers to edit Komada to suit their needs. + +Some features of Komada include: +• Fast Loading times with ES7 Support (Async/Await) +• Per-server settings for each guild, that can be extended with your own code +• Customizable Command system with automated usage parsing and easy to use reloading and downloading modules +• "Monitors" which can watch messages and act on them, like a normal message event (Swear Filters, Spam Protection, etc) +• "Inhibitors" which can prevent commands from running based on a set of parameters (Permissions, Blacklists, etc) +• "Providers" which allow you to connect with an outside database of your choosing **soon**:tm: +• Internal "Functions" which allow you to use functions anywhere where you have access to a client variable. + +We hope to be a 100% customizable framework that can cater to all audiences. We do frequent updates and bugfixes when available. +If you're interested in us, check us out at https://komada.js.org +`; + msg.channel.send(information); }; exports.conf = { diff --git a/commands/System/reload.js b/commands/System/reload.js index 86259b84..32db4a8a 100644 --- a/commands/System/reload.js +++ b/commands/System/reload.js @@ -50,7 +50,7 @@ exports.run = async (client, msg, [type, name]) => { case "command": switch (name) { case "all": - await client.funcs.loadCommands(client); + await require(`${client.coreBaseDir}utils/loadCommands.js`)(client); msg.channel.sendMessage("✅ Reloaded all commands."); break; default: diff --git a/functions/clean.js b/functions/clean.js index f57ac85e..3f5c5668 100644 --- a/functions/clean.js +++ b/functions/clean.js @@ -3,7 +3,6 @@ function sensitivePattern(client) { let pattern = ""; if (client.token) pattern += client.token; if (client.token) pattern += (pattern.length > 0 ? "|" : "") + client.token; - if (client.email) pattern += (pattern.length > 0 ? "|" : "") + client.email; if (client.user.email) pattern += (pattern.length > 0 ? "|" : "") + client.user.email; if (client.password) pattern += (pattern.length > 0 ? "|" : "") + client.password; this.sensitivePattern = new RegExp(pattern, "gi"); diff --git a/functions/handleCommand.js b/functions/handleCommand.js index 2552fe02..764c3dde 100644 --- a/functions/handleCommand.js +++ b/functions/handleCommand.js @@ -2,7 +2,10 @@ module.exports = async (client, msg, command, args = undefined) => { const validCommand = this.getCommand(client, command); if (!validCommand) return; const response = this.runInhibitors(client, msg, validCommand); - if (response) return msg.reply(response); + if (response) { + if (typeof response === "string") return msg.reply(response); + return; + } try { const params = await client.funcs.usage.run(client, msg, validCommand, args); validCommand.run(client, msg, params); diff --git a/functions/handleMessage.js b/functions/handleMessage.js index 11793b4c..c19de497 100644 --- a/functions/handleMessage.js +++ b/functions/handleMessage.js @@ -5,4 +5,4 @@ module.exports = (client, msg, edited = false) => { if (!client.config.selfbot && msg.author.id === client.user.id) return false; // Ignore other users if selfbot but config option is false if (!client.config.editableCommands && edited) return false; // Ignore message if owner doesn't allow editableCommands return true; -} +}; diff --git a/functions/loadSingleCommand.js b/functions/loadSingleCommand.js index 1d382835..756c97b8 100644 --- a/functions/loadSingleCommand.js +++ b/functions/loadSingleCommand.js @@ -23,7 +23,7 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi cmd.init(client); } } catch (e) { - reject(`Could not load existing command data: ${e.stack}`); + reject(`Could not load existing command data: \`\`\`js\n${e.stack}\`\`\``); } } else { try { @@ -31,6 +31,7 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi if (cmd.conf.selfbot && !client.config.selfbot) { return reject(`The command \`${cmd.help.name}\` is only usable in selfbots!`); } + delete require.cache[require.resolve(loadPath)]; if (cmd.init) cmd.init(client); let pathParts = loadPath.split(path.sep); pathParts = pathParts.slice(pathParts.indexOf("commands") + 1); @@ -46,7 +47,7 @@ module.exports = (client, command, reload = false, loadPath = null) => new Promi }); client.funcs.loadSingleCommand(client, command, false, loadPath); } else { - reject(`Could not load new command data: ${e.stack}`); + reject(`Could not load new command data: \`\`\`js\n${e.stack}\`\`\``); } } } diff --git a/functions/log.js b/functions/log.js index 183f8e72..3753019b 100644 --- a/functions/log.js +++ b/functions/log.js @@ -12,7 +12,7 @@ module.exports = (data, type = "log") => { console.warn(`${clk.black.bgYellow(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); break; case "error": - console.error(`${clk.bgRed(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); + console.error(`${clk.bgRed(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data.stack || data}`); break; case "log": console.log(`${clk.bgBlue(`[${moment().format("YYYY-MM-DD HH:mm:ss")}]`)} ${data}`); diff --git a/functions/parseCommand.js b/functions/parseCommand.js index fad054ad..77378bdf 100644 --- a/functions/parseCommand.js +++ b/functions/parseCommand.js @@ -17,14 +17,15 @@ exports.getPrefix = (client, msg) => { if (client.config.prefixMention.test(msg.content)) { return client.config.prefixMention; } - const prefix = msg.guildConf.prefix; + let prefix = msg.guildConf.prefix; + const escape = client.funcs.regExpEsc; if (prefix instanceof Array) { - prefix.forEach((prefix) => { - if (msg.content.startsWith(prefix)) prefix = RegExp(`^${prefix}`); - else prefix = false; + prefix.forEach((pref) => { + if (msg.content.startsWith(pref)) prefix = pref; + else pref = false; }); return prefix; } - if (msg.content.startsWith(prefix)) return new RegExp(`^${prefix}`); // eslint-disable-line + if (prefix && msg.content.startsWith(prefix)) return new RegExp(`^${escape(prefix)}`); // eslint-disable-line return false; }; diff --git a/functions/regExpEsc.js b/functions/regExpEsc.js new file mode 100644 index 00000000..be800b04 --- /dev/null +++ b/functions/regExpEsc.js @@ -0,0 +1 @@ +module.exports = str => str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); diff --git a/functions/reload.js b/functions/reload.js index 98b3b9eb..c42ab493 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -14,8 +14,8 @@ exports.function = (client, dir, funcName) => new Promise(async (resolve, reject client.funcs[funcName].init(client); } }); - } catch (error) { - reject(error); + } catch (e) { + reject(`Could not load new function data: \`\`\`js\n${e.stack}\`\`\``); return; } resolve(`Successfully reloaded the function ${funcName}.`); @@ -43,7 +43,7 @@ exports.function = (client, dir, funcName) => new Promise(async (resolve, reject process.exit(); }); } else { - reject(`Could not load new function data: ${error}`); + reject(`Could not load new function data: \`\`\`js\n${error.stack}\`\`\``); } } } else { @@ -67,8 +67,8 @@ exports.inhibitor = (client, dir, inhibName) => new Promise(async (resolve, reje props.init(client); } }); - } catch (error) { - reject(error); + } catch (e) { + reject(`Could not load new inhibitor data: \`\`\`js\n${e.stack}\`\`\``); return; } resolve(`Successfully reloaded the inhibitor ${inhibName}`); @@ -97,7 +97,7 @@ exports.inhibitor = (client, dir, inhibName) => new Promise(async (resolve, reje process.exit(); }); } else { - reject(`Could not load new inhibitor data: ${error}`); + reject(`Could not load new inhibitor data: \`\`\`js\n${error.stack}\`\`\``); } } } else { @@ -121,8 +121,8 @@ exports.monitor = (client, dir, monitName) => new Promise(async (resolve, reject props.init(client); } }); - } catch (error) { - reject(error); + } catch (e) { + reject(`Could not load new monitor data: \`\`\`js\n${e.stack}\`\`\``); return; } resolve(`Succesfully reloaded the monitor ${monitName}.`); @@ -151,7 +151,7 @@ exports.monitor = (client, dir, monitName) => new Promise(async (resolve, reject process.exit(); }); } else { - reject(`Could not load new monitor data: ${error}`); + reject(`Could not load new monitor data: \`\`\`js\n${error.stack}\`\`\``); } } } else { @@ -175,8 +175,8 @@ exports.provider = (client, dir, providerName) => new Promise(async (resolve, re props.init(client); } }); - } catch (error) { - reject(error); + } catch (e) { + reject(`Could not load new provider data: \`\`\`js\n${e.stack}\`\`\``); return; } resolve(`Successfully reloaded the provider ${providerName}.`); @@ -205,7 +205,7 @@ exports.provider = (client, dir, providerName) => new Promise(async (resolve, re process.exit(); }); } else { - reject(`Could not load new provider data: ${error}`); + reject(`Could not load new provider data: \`\`\`js\n${error.stack}\`\`\``); } } } else { @@ -216,27 +216,20 @@ exports.provider = (client, dir, providerName) => new Promise(async (resolve, re exports.event = (client, eventName) => new Promise(async (resolve, reject) => { const files = await client.funcs.getFileListing(client, client.clientBaseDir, "events").catch(err => client.emit("error", client.funcs.newError(err))); - const oldEvent = files.filter(f => f.name === eventName); - if (oldEvent[0] && oldEvent[0].name === eventName) { - if (!client.events[eventName]) { - const file = oldEvent[0]; - client.on(file.name, (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args)); + const file = files.find(f => f.name === eventName); + if (file && file.name === eventName) { + const runEvent = (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args); + if (!client._events[eventName]) { + client.on(file.name, runEvent); + resolve(`Successfully loaded a new event called ${eventName}.`); return; } - let listener; - if (client._events[eventName].length !== 0) { - listener = client._events[eventName][1]; - } else { - listener = client._events[eventName]; - } - client.removeListener(eventName, listener); + client.removeListener(eventName, runEvent); try { - oldEvent.forEach((file) => { - delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; - client.on(file.name, (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args)); - }); - } catch (error) { - reject(error); + delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; + client.on(file.name, runEvent); + } catch (e) { + reject(`Could not load new event data: \`\`\`js\n${e.stack}\`\`\``); return; } resolve(`Successfully reloaded the event ${eventName}`); diff --git a/functions/validateData.js b/functions/validateData.js index 4fa7806d..b6190a05 100644 --- a/functions/validateData.js +++ b/functions/validateData.js @@ -67,7 +67,7 @@ module.exports = (data, properties, values) => { case "string": break; default: - throw new Error("unsuported"); + throw new Error("unsupported"); } }); }; diff --git a/package.json b/package.json index f66cb593..662949fd 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,16 @@ { "name": "komada", - "version": "0.18.0", + "version": "0.18.1", "author": "Evelyne Lachance", "description": "Komada: Croatian for 'pieces', is a modular bot system including reloading modules and easy to use custom commands.", "main": "app.js", "scripts": { "start": "node app.js", - "test": "eslint commands functions inhibitors events monitors app.js", + "test": "eslint commands functions inhibitors events monitors utils app.js", "lint": "npm run test -- --fix" }, "repository": { "type": "git", - "url": "https://github.com/dirigeants/komada.git" }, "bugs": { @@ -21,6 +20,7 @@ "dependencies": { "chalk": "^1.1.3", "discord.js": "hydrabolt/discord.js", + "dotenv": "^4.0.0", "fs-extra": "^1.0.0", "fs-extra-promise": "^0.4.1", "moment": "^2.16.0", diff --git a/functions/loadCommandInhibitors.js b/utils/loadCommandInhibitors.js similarity index 100% rename from functions/loadCommandInhibitors.js rename to utils/loadCommandInhibitors.js diff --git a/functions/loadCommands.js b/utils/loadCommands.js similarity index 100% rename from functions/loadCommands.js rename to utils/loadCommands.js diff --git a/functions/loadEvents.js b/utils/loadEvents.js similarity index 78% rename from functions/loadEvents.js rename to utils/loadEvents.js index e2c274e5..0efd5b66 100644 --- a/functions/loadEvents.js +++ b/utils/loadEvents.js @@ -1,5 +1,7 @@ const fs = require("fs-extra-promise"); const path = require("path"); +const getFileListing = require("../functions/getFileListing.js"); +const log = require("../functions/log.js"); let events = require("discord.js/src/util/Constants.js").Events; @@ -8,7 +10,7 @@ events = Object.keys(events).map(k => events[k]); const loadEvents = (client, baseDir, counts) => new Promise(async (resolve) => { const dir = path.resolve(`${baseDir}./events/`); await fs.ensureDirAsync(dir).catch(err => client.emit("error", client.funcs.newError(err))); - let files = await client.funcs.getFileListing(client, baseDir, "events").catch(err => client.emit("error", client.funcs.newError(err))); + let files = await getFileListing(client, baseDir, "events").catch(err => client.emit("error", client.funcs.newError(err))); files = files.filter(f => events.includes(f.name)); files.forEach((f) => { client.on(f.name, (...args) => require(`${f.path}${path.sep}${f.base}`).run(client, ...args)); @@ -24,5 +26,5 @@ module.exports = async (client) => { if (client.coreBaseDir !== client.clientBaseDir) { counts = await loadEvents(client, client.clientBaseDir, counts).catch(err => client.emit("error", client.funcs.newError(err))); } - client.funcs.log(`Loaded ${counts} events`); + log(`Loaded ${counts} events`); }; diff --git a/functions/loadFunctions.js b/utils/loadFunctions.js similarity index 100% rename from functions/loadFunctions.js rename to utils/loadFunctions.js diff --git a/functions/loadMessageMonitors.js b/utils/loadMessageMonitors.js similarity index 100% rename from functions/loadMessageMonitors.js rename to utils/loadMessageMonitors.js diff --git a/functions/loadProviders.js b/utils/loadProviders.js similarity index 100% rename from functions/loadProviders.js rename to utils/loadProviders.js From e5b0293ca8237f1d74e8b3d612b202620cb8a701 Mon Sep 17 00:00:00 2001 From: CyberiumShadow Date: Sat, 18 Mar 2017 09:34:17 +1100 Subject: [PATCH 56/56] Oops --- functions/reload.js | 50 --------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/functions/reload.js b/functions/reload.js index 778d6586..c42ab493 100644 --- a/functions/reload.js +++ b/functions/reload.js @@ -43,11 +43,7 @@ exports.function = (client, dir, funcName) => new Promise(async (resolve, reject process.exit(); }); } else { -<<<<<<< HEAD reject(`Could not load new function data: \`\`\`js\n${error.stack}\`\`\``); -======= - reject(`Could not load new function data: ${error}`); ->>>>>>> master } } } else { @@ -71,13 +67,8 @@ exports.inhibitor = (client, dir, inhibName) => new Promise(async (resolve, reje props.init(client); } }); -<<<<<<< HEAD } catch (e) { reject(`Could not load new inhibitor data: \`\`\`js\n${e.stack}\`\`\``); -======= - } catch (error) { - reject(error); ->>>>>>> master return; } resolve(`Successfully reloaded the inhibitor ${inhibName}`); @@ -106,11 +97,7 @@ exports.inhibitor = (client, dir, inhibName) => new Promise(async (resolve, reje process.exit(); }); } else { -<<<<<<< HEAD reject(`Could not load new inhibitor data: \`\`\`js\n${error.stack}\`\`\``); -======= - reject(`Could not load new inhibitor data: ${error}`); ->>>>>>> master } } } else { @@ -134,13 +121,8 @@ exports.monitor = (client, dir, monitName) => new Promise(async (resolve, reject props.init(client); } }); -<<<<<<< HEAD } catch (e) { reject(`Could not load new monitor data: \`\`\`js\n${e.stack}\`\`\``); -======= - } catch (error) { - reject(error); ->>>>>>> master return; } resolve(`Succesfully reloaded the monitor ${monitName}.`); @@ -169,11 +151,7 @@ exports.monitor = (client, dir, monitName) => new Promise(async (resolve, reject process.exit(); }); } else { -<<<<<<< HEAD reject(`Could not load new monitor data: \`\`\`js\n${error.stack}\`\`\``); -======= - reject(`Could not load new monitor data: ${error}`); ->>>>>>> master } } } else { @@ -227,11 +205,7 @@ exports.provider = (client, dir, providerName) => new Promise(async (resolve, re process.exit(); }); } else { -<<<<<<< HEAD reject(`Could not load new provider data: \`\`\`js\n${error.stack}\`\`\``); -======= - reject(`Could not load new provider data: ${error}`); ->>>>>>> master } } } else { @@ -242,7 +216,6 @@ exports.provider = (client, dir, providerName) => new Promise(async (resolve, re exports.event = (client, eventName) => new Promise(async (resolve, reject) => { const files = await client.funcs.getFileListing(client, client.clientBaseDir, "events").catch(err => client.emit("error", client.funcs.newError(err))); -<<<<<<< HEAD const file = files.find(f => f.name === eventName); if (file && file.name === eventName) { const runEvent = (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args); @@ -257,29 +230,6 @@ exports.event = (client, eventName) => new Promise(async (resolve, reject) => { client.on(file.name, runEvent); } catch (e) { reject(`Could not load new event data: \`\`\`js\n${e.stack}\`\`\``); -======= - const oldEvent = files.filter(f => f.name === eventName); - if (oldEvent[0] && oldEvent[0].name === eventName) { - if (!client.events[eventName]) { - const file = oldEvent[0]; - client.on(file.name, (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args)); - return; - } - let listener; - if (client._events[eventName].length !== 0) { - listener = client._events[eventName][1]; - } else { - listener = client._events[eventName]; - } - client.removeListener(eventName, listener); - try { - oldEvent.forEach((file) => { - delete require.cache[require.resolve(`${file.path}${path.sep}${file.base}`)]; - client.on(file.name, (...args) => require(`${file.path}${path.sep}${file.base}`).run(client, ...args)); - }); - } catch (error) { - reject(error); ->>>>>>> master return; } resolve(`Successfully reloaded the event ${eventName}`);