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..93b783b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,110 @@ 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 +- 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) +- 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~~ Reverted in #139 +- New Download Command + +### Changed +- ~~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 +- 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. +- 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. +- 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 + +### Fixed +- Fixed validateData Booleans. +- Fixed Reloading Events not loading new events correctly. +- Fixed Typo in transfer command +- 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. +- 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.17.0) +- 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. +- Events.... now should be 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 +- 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 +- 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 +- More Selfbot Bugs Fixed +- More Reload function fixes for commands + +### Removed +- Old initialize system (Was borked) +- Old Configuration System +- Selfbot Inhibitor ## [0.18.0] - 2017-03-16 ### Added @@ -178,12 +282,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..a1b6c5a1 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) => { @@ -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..027ac41e 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/utils/loadCommandInhibitors.js b/utils/loadCommandInhibitors.js new file mode 100644 index 00000000..817be170 --- /dev/null +++ b/utils/loadCommandInhibitors.js @@ -0,0 +1,37 @@ +const fs = require("fs-extra-promise"); +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.emit("error", client.funcs.newError(err))); + 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(); + } 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 = async (client) => { + client.commandInhibitors.clear(); + 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.emit("error", client.funcs.newError(err))); + } + client.funcs.log(`Loaded ${client.commandInhibitors.size} command inhibitors.`); +}; diff --git a/utils/loadCommands.js b/utils/loadCommands.js new file mode 100644 index 00000000..a0b2c154 --- /dev/null +++ b/utils/loadCommands.js @@ -0,0 +1,36 @@ +const fs = require("fs-extra-promise"); +const path = require("path"); + +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.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.emit("error", client.funcs.newError(err))); + }); + 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.loadCommands(client); + } else { + reject(e); + } + } +}); + +module.exports = async (client) => { + client.commands.clear(); + client.aliases.clear(); + 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.emit("error", client.funcs.newError(err))); + } + client.funcs.log(`Loaded ${client.commands.size} commands, with ${client.aliases.size} aliases.`); +}; diff --git a/utils/loadEvents.js b/utils/loadEvents.js new file mode 100644 index 00000000..0efd5b66 --- /dev/null +++ b/utils/loadEvents.js @@ -0,0 +1,30 @@ +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; + +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 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)); + counts++; + }); + resolve(counts); +}); + + +module.exports = async (client) => { + let counts = 0; + 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.emit("error", client.funcs.newError(err))); + } + log(`Loaded ${counts} events`); +}; diff --git a/utils/loadFunctions.js b/utils/loadFunctions.js new file mode 100644 index 00000000..10e16bd5 --- /dev/null +++ b/utils/loadFunctions.js @@ -0,0 +1,39 @@ +const fs = require("fs-extra-promise"); +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 => 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) => { + 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) { + 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(); + }); + loadFunctions(client); + } else { + reject(e); + } + } +}); + +module.exports = client => new Promise(async (resolve, reject) => { + await loadFunctions(client, client.coreBaseDir).catch(reject); + if (client.coreBaseDir !== client.clientBaseDir) { + await loadFunctions(client, client.clientBaseDir).catch(reject); + } + client.funcs.log(`Loaded ${Object.keys(client.funcs).length} functions.`); + resolve(); +}); diff --git a/utils/loadMessageMonitors.js b/utils/loadMessageMonitors.js new file mode 100644 index 00000000..181f1ae5 --- /dev/null +++ b/utils/loadMessageMonitors.js @@ -0,0 +1,37 @@ +const fs = require("fs-extra-promise"); +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.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}`); + if (props.init) props.init(client); + 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 = async (client) => { + client.messageMonitors.clear(); + 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.emit("error", client.funcs.newError(err))); + } + client.funcs.log(`Loaded ${client.messageMonitors.size} command monitors.`); +}; diff --git a/utils/loadProviders.js b/utils/loadProviders.js new file mode 100644 index 00000000..94efe063 --- /dev/null +++ b/utils/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.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}`); + if (props.init) props.init(client); + client.providers.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(); + }); + } else { + reject(e); + } + } +}); + +module.exports = async (client) => { + client.providers.clear(); + 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.emit("error", client.funcs.newError(err))); + } + client.funcs.log(`Loaded ${client.providers.size} providers.`); +};