diff --git a/.github/workflows/CodeQL.yml b/.github/workflows/CodeQL.yml new file mode 100644 index 000000000..ae3edea79 --- /dev/null +++ b/.github/workflows/CodeQL.yml @@ -0,0 +1,31 @@ +name: CodeQL + +on: + push: + branches: + - dev + pull_request: + branches: + - dev + +jobs: + run: + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + languange: ['javascript'] + steps: + - name: Checkout repository + uses: actions/checkout@v2.3.4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + - name: Autobuild (Attempts to build any compiled languages) + uses: github/codeql-action/autobuild@v1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 \ No newline at end of file diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cce754dae..018f9ad5f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,25 +12,16 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Create Folder - run: mkdir docs - - name: Download - run: npm i jsdoc-to-markdown + run: npm i @discordjs/docgen - name: Build run: npm run docs:build - - name: Copy File - uses: canastro/copy-file-action@master - with: - source: "docs.md" - target: "docs/index.md" - - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.ACCESS_TOKEN }} publish_dir: ./docs publish_branch: docs - destination_dir: ./docs/docs/ \ No newline at end of file + destination_dir: ./docs/ \ No newline at end of file diff --git a/.github/workflows/lint_node12.yml b/.github/workflows/lint_node12.yml new file mode 100644 index 000000000..cb03d3b7e --- /dev/null +++ b/.github/workflows/lint_node12.yml @@ -0,0 +1,26 @@ +name: Lint (Node 12.x) + +on: + push: + branches: [ dev ] + pull_request: + branches: [ dev ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: 12.x + - name: Lint + uses: github/super-linter@v3 + env: + VALIDATE_ALL_CODEBASE: true + VALIDATE_JAVASCRIPT_ES: true + DEFAULT_BRANCH: master + GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/lint_node14.yml b/.github/workflows/lint_node14.yml new file mode 100644 index 000000000..d0bf60903 --- /dev/null +++ b/.github/workflows/lint_node14.yml @@ -0,0 +1,26 @@ +name: Lint (Node 14.x) + +on: + push: + branches: [ dev ] + pull_request: + branches: [ dev ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 14.x + uses: actions/setup-node@v1 + with: + node-version: 14.x + - name: Lint + uses: github/super-linter@v3 + env: + VALIDATE_ALL_CODEBASE: true + VALIDATE_JAVASCRIPT_ES: true + DEFAULT_BRANCH: master + GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/lint_node16.yml b/.github/workflows/lint_node16.yml new file mode 100644 index 000000000..e4ae6a6ab --- /dev/null +++ b/.github/workflows/lint_node16.yml @@ -0,0 +1,26 @@ +name: Lint (Node 16.x) + +on: + push: + branches: [ dev ] + pull_request: + branches: [ dev ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 16.x + uses: actions/setup-node@v1 + with: + node-version: 16.x + - name: Lint + uses: github/super-linter@v3 + env: + VALIDATE_ALL_CODEBASE: true + VALIDATE_JAVASCRIPT_ES: true + DEFAULT_BRANCH: master + GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/vuebuild.yml b/.github/workflows/vuebuild.yml deleted file mode 100644 index 35afe3eef..000000000 --- a/.github/workflows/vuebuild.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: "vue build | gcommands.js.org Build" - -on: - push: - branches: - - docs - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@main - - - name: Build and Deploy - uses: jenkey2011/vuepress-deploy@1.6.1 - env: - ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} - TARGET_REPO: Garlic-Team/GCommands - TARGET_BRANCH: master - BUILD_SCRIPT: yarn && yarn build - BUILD_DIR: docs/.vuepress/dist/ - CNAME: gcommands.js.org - COMMIT_MESSAGE: Deploy Docs diff --git a/.npmignore b/.npmignore new file mode 100644 index 000000000..b5962a597 --- /dev/null +++ b/.npmignore @@ -0,0 +1,18 @@ +# Packages +node_modules/ +yarn.lock + +# Log files +logs/ +*.log + +# Authentication +deploy/ + +# Miscellaneous +.tmp/ +.vscode/ +docs/ + +# NPM ignore +jsdoc.json \ No newline at end of file diff --git a/README.md b/README.md index 14a581bfd..0d0a0b311 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@
-

G Commands

+

GCommands

NPM version NPM downloads + Code Raiting

NPM Banner @@ -16,10 +17,12 @@ npm install gcommands yarn install gcommands -npm install github:Garlic-Team/GCommands#dev #dev build +# Dev Build +npm install Garlic-Team/GCommands#dev +yarn install Garlic-Team/GCommands#dev ``` -If you're updating from 3.x to 4.x, check https://gcommands.js.org/guide/additional/fromv3tov4.html +If you're updating from 4.x to 5.x, check https://gcommands.js.org/guide/additional/fromv4tov5.html ### 📜 | Setup ```js @@ -55,7 +58,7 @@ client.login("bot token") ``` ### ✍ | Examples -You can find everything in the [guide](https://gcommands.js.org).
+You can find everything in the [guide](https://gcommands.js.org/guide/) and [docs](https://gcommands.js.org/docs/).
Join our [discord server](https://discord.gg/AjKJSBbGm2), if you need help or have any questions. ### 👥 | Contact diff --git a/docs/index.yml b/docs/index.yml new file mode 100644 index 000000000..0aea5a7e4 --- /dev/null +++ b/docs/index.yml @@ -0,0 +1,5 @@ +- name: General + files: + - name: Welcome + id: welcome + path: ../../README.md \ No newline at end of file diff --git a/jsdoc.json b/jsdoc.json new file mode 100644 index 000000000..3929bca27 --- /dev/null +++ b/jsdoc.json @@ -0,0 +1,3 @@ +{ + "plugins": ["node_modules/jsdoc-strip-async-await"] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 75c93718d..db8efb54a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gcommands", - "version": "4.3.0", + "version": "5.2.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "gcommands", - "version": "4.3.0", + "version": "5.2.3", "license": "MIT", "dependencies": { "axios": "^0.21.1", diff --git a/package.json b/package.json index 45f2db27a..f4013f782 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,16 @@ { "name": "gcommands", - "version": "4.3.0", - "description": "Open-source slash/normal command handler", + "version": "5.2.3", + "description": "Powerful and flexible command handler that can do everything!", "main": "src/index.js", "types": "./typings/index.d.ts", "dependencies": { + "@gcommands/events": "^1.0.3", "axios": "^0.21.1", "ms": "^2.1.3" }, "devDependencies": { + "@discordjs/docgen": "^0.10.0", "@types/node": "^12.7.3" }, "repository": { @@ -17,13 +19,10 @@ }, "keywords": [ "gcommands", - "cmdhandler", - "slashcommandhandler", - "slashcmds", - "slash commands", - "handler slash commands", - "command handler", - "discord js slash" + "command", + "discord bot", + "handler", + "slash command handler" ], "author": "Garlic-Team", "license": "MIT", @@ -31,7 +30,11 @@ "url": "https://github.com/Garlic-Team/GCommands/issues" }, "scripts": { - "docs:build": "jsdoc2md ./src/**/** > docs.md" + "docs:build": "docgen --source src --custom docs/index.yml --output docs/main.json", + "docs:test": "docgen --source src" + }, + "engines": { + "node": ">=12.0.0" }, "homepage": "https://gcommands.js.org" } diff --git a/src/base/GCommands.js b/src/base/GCommands.js index 60ca18db2..82b7f178d 100644 --- a/src/base/GCommands.js +++ b/src/base/GCommands.js @@ -1,8 +1,7 @@ -const GCommandLoader = require("../managers/GCommandLoader"), Color = require("../structures/Color"), GCommandsBase = require("./GCommandsBase"), GCommandsDispatcher = require("./GCommandsDispatcher"), GEvents = require("./GEvents"), GEventLoader = require("../managers/GEventLoader"), GDatabaseLoader = require("../managers/GDatabaseLoader"), { Events } = require("../util/Constants"), GUpdater = require("../util/updater"), {msToSeconds} = require("../util/util"); -const { Collection, version, Client } = require('discord.js'); -const axios = require("axios"); -const fs = require("fs"); -const ms = require("ms"); +const GCommandLoader = require('../managers/GCommandLoader'), Color = require('../structures/Color'), GCommandsBase = require('./GCommandsBase'), GCommandsDispatcher = require('./GCommandsDispatcher'), { GEvents: GEventLoader } = require('@gcommands/events'), GEventHandling = require('../managers/GEventHandling'), GDatabaseLoader = require('../managers/GDatabaseLoader'), { Events } = require('../util/Constants'), GUpdater = require('../util/updater'), {msToSeconds} = require('../util/util'); +const { Collection, version } = require('discord.js'); +const fs = require('fs'); +const ms = require('ms'); /** * The main GCommands class @@ -10,112 +9,156 @@ const ms = require("ms"); class GCommands extends GCommandsBase { /** * The GCommands class - * @param {Object} client - Discord.js Client + * @param {Client} client - Discord.js Client * @param {Object} options - Options (cmdDir, eventDir etc) */ constructor(client, options = {}) { super(client, options) - if (typeof client != "object") return console.log(new Color("&d[GCommands] &cNo discord.js client provided!",{json:false}).getText()); - if (!Object.keys(options).length) return console.log(new Color("&d[GCommands] &cNo default options provided!",{json:false}).getText()); - if(!options.cmdDir) return console.log(new Color("&d[GCommands] &cNo default options provided! (cmdDir)",{json:false}).getText()); - if(!options.language) return console.log(new Color("&d[GCommands] &cNo default options provided! (language (english, spanish, portuguese, russian, german, czech, slovak, turkish))",{json:false}).getText()); + if (typeof client !== 'object') return console.log(new Color('&d[GCommands] &cNo discord.js client provided!',{json:false}).getText()); + if (!Object.keys(options).length) return console.log(new Color('&d[GCommands] &cNo default options provided!',{json:false}).getText()); + if(!options.cmdDir) return console.log(new Color('&d[GCommands] &cNo default options provided! (cmdDir)',{json:false}).getText()); + if(!options.language) return console.log(new Color('&d[GCommands] &cNo default options provided! (language (english, spanish, portuguese, russian, german, czech, slovak, turkish))',{json:false}).getText()); + /** + * GCommandsClient + * @type {GCommands} + */ this.GCommandsClient = this; + + /** + * client + * @type {Client} + */ this.client = client; + /** + * caseSensitiveCommands + * @type {Boolean} + * @default true + */ + this.caseSensitiveCommands = Boolean(options.caseSensitiveCommands) || true + + /** + * caseSensitivePrefixes + * @type {Boolean} + * @default true + */ + this.caseSensitivePrefixes = Boolean(options.caseSensitivePrefixes) || true + /** * CmdDir - * @property {String} cmdDir + * @type {String} */ this.cmdDir = options.cmdDir; /** * EventDir - * @property {String} eventDir + * @type {String} + * @default undefined */ this.eventDir = options.eventDir; this.client.discordjsversion = version; /** * unkownCommandMessage - * @property {String} unkownCommandMessage + * @type {Boolean} + * @default false */ this.unkownCommandMessage = options.unkownCommandMessage; /** * AutoTyping - * @property {Boolean} autoTyping + * @type {Boolean} + * @default false */ this.autoTyping = options.autoTyping; /** * ownLanguageFile - * @property {Object} ownLanguageFile + * @type {Object} */ - if(!options.ownLanguageFile) this.languageFile = require("../util/message.json"); + if(!options.ownLanguageFile) this.languageFile = require('../util/message.json'); else this.languageFile = options.ownLanguageFile; - this.language = options.language; - if(this.eventDir) { - new GEvents(this.GCommandsClient, { - eventDir: this.eventDir - }) - } + /** + * language + * @type {Object} language + */ + this.language = options.language; /** * database - * @property {Object} database + * @type {Object} database + * @default undefined */ - this.database = options.database || undefined; + this.database = options.database; + + /** + * gcategories + * @type {Array} + */ + this.client.gcategories = fs.readdirSync(`./${this.cmdDir}`) - this.client.categories = fs.readdirSync("./" + this.cmdDir ); + /** + * gcommands + * @type {Collection} + */ this.client.gcommands = new Collection(); + + /** + * galiases + * @type {Collection} + */ this.client.galiases = new Collection(); /** * Prefix - * @property {String} prefix + * @type {String} + * @default undefined */ - this.prefix = options.slash.prefix ? options.slash.prefix : undefined; + this.prefix = !Array.isArray(options.slash.prefix) ? [options.slash.prefix] : options.slash.prefix; /** * Slash - * @property {String} slash + * @type {String} + * @default false */ this.slash = options.slash.slash ? options.slash.slash : false; /** - * cooldownDefault - * @property {Number} cooldownDefault + * defaultCooldown + * @type {Number} + * @default 0 */ - this.cooldownDefault = options.defaultCooldown ? options.defaultCooldown : 0; + this.defaultCooldown = options.defaultCooldown ? options.defaultCooldown : 0; - this.GCommandsClient.unkownCommandMessage = this.unkownCommandMessage; - this.GCommandsClient.database = this.database; this.client.language = this.language; this.client.languageFile = this.languageFile; this.client.database = this.database - this.client.prefix = this.prefix; + this.client.prefixes = this.prefix; this.client.slash = this.slash; - this.client.cooldownDefault = this.cooldownDefault; + this.client.defaultCooldown = this.defaultCooldown; this.client.autoTyping = this.autoTyping ? msToSeconds(ms(this.autoTyping)) : null; - process.setMaxListeners(50); process.on('uncaughtException', (error) => { - this.emit(Events.LOG, new Color("&d[GCommands Errors] &eHandled: &a" + error + ` ${error.response ? error.response.data.message : ""} ${error.response ? error.response.data.code : ""} | use debug for full error`).getText()); + this.emit(Events.LOG, new Color('&d[GCommands Errors] &eHandled: &a' + error + ` ${error.response ? error.response.data.message : ''} ${error.response ? error.response.data.code : ''} | use debug for full error`).getText()); setTimeout(() => {this.emit(Events.DEBUG, error)}, 1000) }); this.client.dispatcher = new GCommandsDispatcher(this.client); - setTimeout(() => { - new GDatabaseLoader(this.GCommandsClient); - new GEventLoader(this.GCommandsClient); - new GCommandLoader(this.GCommandsClient); - }, 1000) + setTimeout(() => { this.loadSys() }, 1000) GUpdater.__updater(); } + + async loadSys() { + require('../structures/GMessage'); require('../structures/GGuild'); require('../structures/DMChannel'); require('../structures/NewsChannel'); require('../structures/TextChannel'); + new GDatabaseLoader(this.GCommandsClient) + new GEventHandling(this.GCommandsClient) + new GEventLoader(this.GCommandsClient) + new GCommandLoader(this.GCommandsClient) + }; } module.exports = GCommands; \ No newline at end of file diff --git a/src/base/GCommandsBase.js b/src/base/GCommandsBase.js index 95f4a69be..821cee153 100644 --- a/src/base/GCommandsBase.js +++ b/src/base/GCommandsBase.js @@ -1,4 +1,4 @@ -const EventEmitter = require("events"); +const EventEmitter = require('events'); /** * The GCommandsBase class diff --git a/src/base/GCommandsDispatcher.js b/src/base/GCommandsDispatcher.js index 8e5bd441c..0d1f5c5d4 100644 --- a/src/base/GCommandsDispatcher.js +++ b/src/base/GCommandsDispatcher.js @@ -1,21 +1,37 @@ -const { Collector, Collection } = require('discord.js'); -const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector'), Color = require("../structures/Color") -const updater = require("../util/updater"); -const ms = require("ms"); +const { Collector, Collection, User, Team } = require('discord.js'); +const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector'), Color = require('../structures/Color') +const ifDjsV13 = require('../util/updater').checkDjsVersion('13'); +const ms = require('ms'); /** * The GCommansDispatcher class */ class GCommandsDispatcher { + /** + * The GCommansDispatcher class + * @param {Client} client - Discord.js Client + */ constructor(client) { /** - * GCommandsDispatcher options - * @property {Object} client + * client + * @type {Client} client */ this.client = client; + + /** + * Inhibitors + * @type {Set} + */ this.client.inhibitors = new Set(); + + /** + * Cooldowns + * @type {Collection} + */ this.client.cooldowns = new Collection(); + + this.fetchClientApplication(); } /** @@ -39,24 +55,24 @@ class GCommandsDispatcher { * @returns {String} */ async getGuildPrefix(guildId, cache = true) { - if(!this.client.database) return this.client.prefix; - if(cache) return this.client.guilds.cache.get(guildId).prefix ? this.client.guilds.cache.get(guildId).prefix : this.client.prefix; + if(!this.client.database) return this.client.prefixes; + if(cache) return this.client.guilds.cache.get(guildId).prefix ? this.client.guilds.cache.get(guildId).prefix : this.client.prefixes; let guildData = await this.client.database.get(`guild_${guildId}`) - return guildData ? guildData.prefix : this.client.prefix + return guildData ? guildData.prefix : this.client.prefixes } /** * Internal method to getCooldown * @returns {String} */ - async getCooldown(guildId, userId, command) { - if(!command.cooldown) return { cooldown: false } ; + async getCooldown(guildId, userId, command) { + if(this.client.application.owners.some(user => user.id == userId)) return { cooldown: false }; let now = Date.now(); let cooldown; - if(typeof command.cooldown == "object") cooldown = command.cooldown ? ms(command.cooldown.cooldown) : 0; - else cooldown = command.cooldown ? ms(command.cooldown) : 0; + if(typeof command.cooldown == 'object') cooldown = command.cooldown ? ms(command.cooldown.cooldown) : ms(this.client.defaultCooldown); + else cooldown = command.cooldown ? ms(command.cooldown) : ms(this.client.defaultCooldown); if(cooldown < 1800000 || !this.client.database) { if (!this.client.cooldowns.has(command.name)) { @@ -64,14 +80,13 @@ class GCommandsDispatcher { } const timestamps = this.client.cooldowns.get(command.name); - const cooldownAmount = cooldown ? cooldown : ms(this.client.defaultCooldown); if (timestamps.has(userId)) { if (timestamps.has(userId)) { - const expirationTime = timestamps.get(userId) + cooldownAmount; + const expirationTime = timestamps.get(userId) + cooldown; if (now < expirationTime) { - if(typeof command.cooldown == "object" && command.cooldown.agressive) { + if(typeof command.cooldown == 'object' && command.cooldown.agressive) { this.client.cooldowns.set(command.name, new Collection()); return { cooldown: true, wait: ms(cooldown) } } @@ -84,10 +99,11 @@ class GCommandsDispatcher { } timestamps.set(userId, now); - setTimeout(() => timestamps.delete(userId), cooldownAmount); + setTimeout(() => timestamps.delete(userId), cooldown); + return { cooldown:false } } - if(!this.client.database) return 0; + if(!this.client.database || !command.cooldown) return { cooldown: false }; let guildData = await this.client.database.get(`guild_${guildId}`) || {} if(!guildData.users) guildData.users = {} @@ -103,7 +119,7 @@ class GCommandsDispatcher { } if(now < userInfo) { - if(typeof command.cooldown == "object" && command.cooldown.agressive) { + if(typeof command.cooldown == 'object' && command.cooldown.agressive) { guildData.users[userId][command.name] = ms(command.cooldown) + now userInfo = guildData.users[userId][command.name] @@ -149,7 +165,7 @@ class GCommandsDispatcher { * @param {Object} command * @returns {boolean} */ - async getGuildLanguage(guildId, cache = true) { + async getGuildLanguage(guildId, cache = true) { if(!this.client.database) return this.client.language; if(cache) return this.client.guilds.cache.get(guildId).language ? this.client.guilds.cache.get(guildId).language : this.client.language; @@ -157,6 +173,22 @@ class GCommandsDispatcher { return guildData ? guildData.language : this.client.language } + /** + * Internal method to fetchClientApplication + * @private + * @returns {Array} + */ + async fetchClientApplication() { + if(!ifDjsV13) this.client.application = await this.client.fetchApplication() + if(this.client.application.owner == null) return this.client.application.owners = []; + + if(this.client.application.owner instanceof Team) { + this.client.application.owners = this.client.application.owner.members.array().map(teamMember => teamMember.user) + } else this.client.application.owners = [this.client.application.owner] + + return this.client.application.owners; + } + /** * Internal method to addInhibitor * @param {Function} inhibitor @@ -185,7 +217,7 @@ class GCommandsDispatcher { * @returns {Collector} */ createButtonCollector(msg, filter, options = {}) { - if(updater.checkDjsVersion("13")) return new ButtonCollectorV13(msg, filter, options); + if(ifDjsV13) return new ButtonCollectorV13(msg, filter, options); else return new ButtonCollectorV12(msg, filter, options); } @@ -215,7 +247,7 @@ class GCommandsDispatcher { * @returns {Collector} */ createSelectMenuCollector(msg, filter, options = {}) { - if(updater.checkDjsVersion("13")) return new SelectMenuCollectorV13(msg, filter, options); + if(ifDjsV13) return new SelectMenuCollectorV13(msg, filter, options); else return new SelectMenuCollectorV12(msg, filter, options); } diff --git a/src/base/GEvents.js b/src/base/GEvents.js deleted file mode 100644 index 854c591d7..000000000 --- a/src/base/GEvents.js +++ /dev/null @@ -1,95 +0,0 @@ -const { Collection } = require("discord.js"); -const Color = require("../structures/Color"), { Events } = require("../util/Constants"); -const path = require('path'); -const fs = require('fs'); - -/** - * The GEvents class - */ -class GEvents { - - /** - * Creates new GEvents instance - * @param {DiscordClient} client - * @param {GEventsOptions} options - */ - constructor(GCommandsClient, options = {}) { - if(!GCommandsClient.client) GCommandsClient = { client: GCommandsClient } - if (typeof GCommandsClient.client !== 'object') return console.log(new Color("&d[GCommands EVENTS] &cNo discord.js client provided!",{json:false}).getText()); - if (!Object.keys(options).length) return console.log(new Color("&d[GCommands EVENTS] &cNo default options provided!",{json:false}).getText()); - if(!options.eventDir) return console.log(new Color("&d[GCommands EVENTS] &cNo default options provided! (eventDir)",{json:false}).getText()); - - /** - * GEventsOptions options - * @type {GEventsOptions} - * @property {String} eventDir - */ - - this.eventDir = options.eventDir; - - this.GCommandsClient = GCommandsClient; - this.client = GCommandsClient.client; - this.client.events = new Collection(); - - this.__loadEventFiles(); - } - - /** - * Internal method to loadEventsFiles - * @returns {void} - * @private - */ - async __loadEventFiles() { - await fs.readdirSync(`${__dirname}/../../../../${this.eventDir}`).forEach(async(dir) => { - var file; - var fileName = dir.split(".").reverse()[1] - var fileType = dir.split(".").reverse()[0] - if(fileType == "js" || fileType == "ts") { - try { - file = await require(`../../../../${this.eventDir}${dir}`); - - this.client.events.set(file.name, file); - this.GCommandsClient.emit(Events.LOG, new Color("&d[GEvents] &aLoaded (File): &e➜ &3" + fileName, {json:false}).getText()); - } catch(e) { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GEvents Debug] "+e).getText()); - console.log(new Color("&d[GEvents] &cCan't load " + fileName).getText()); - } - } else { - fs.readdirSync(`${this.eventDir}${dir}`).forEach(async(eventFile) => { - var file2; - var fileName2 = eventFile.split(".").reverse()[1]; - try { - file2 = await require(`../../../../${this.eventDir}${dir}/${eventFile}`); - - this.client.events.set(file2.name, file2); - this.GCommandsClient.emit(Events.LOG, new Color("&d[GEvents] &aLoaded (File): &e➜ &3" + fileName2, {json:false}).getText()); - } catch(e) { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GEvents Debug] "+e).getText()); - console.log(new Color("&d[GEvents] &cCan't load " + fileName2).getText()); - } - }) - } - }) - - await this.__loadEvents() - } - - /** - * Internal method to loadEvents - * @returns {void} - * @private - */ - async __loadEvents() { - this.client.events.forEach(event => { - if(event.name == "ready") return event.run(this.client); - - if (event.once) { - this.client.once(event.name, (...args) => event.run(this.client, ...args)); - } else { - this.client.on(event.name, (...args) => event.run(this.client, ...args)); - } - }) - } -} - -module.exports = GEvents; \ No newline at end of file diff --git a/src/base/actions/channel.js b/src/base/actions/channel.js index a4d844781..f91d4347f 100644 --- a/src/base/actions/channel.js +++ b/src/base/actions/channel.js @@ -1,47 +1,47 @@ module.exports = (client) => { - client.on("channelUpdate", async(oldChannel, newChannel) => { - if(oldChannel.permissionOverwrites != newChannel.permissionOverwrites) { - client.emit("guildChannelPermissionsUpdate", + client.on('channelUpdate', async(oldChannel, newChannel) => { + if(oldChannel.permissionOverwrites !== newChannel.permissionOverwrites) { + client.emit('guildChannelPermissionsUpdate', newChannel, oldChannel.permissionOverwrites, newChannel.permissionOverwrites, ); } - if(oldChannel.type == "text" && oldChannel.topic !== newChannel.topic) { - client.emit("guildChannelTopicUpdate", + if(oldChannel.type == 'text' && oldChannel.topic !== newChannel.topic) { + client.emit('guildChannelTopicUpdate', newChannel, oldChannel.topic, newChannel.topic ) } - if(oldChannel.type == "text" && oldChannel.nsfw !== newChannel.nsfw) { - client.emit("guildChannelNSFWUpdate", + if(oldChannel.type == 'text' && oldChannel.nsfw !== newChannel.nsfw) { + client.emit('guildChannelNSFWUpdate', newChannel, oldChannel.nsfw, newChannel.nsfw ) } - if(oldChannel.type != newChannel.type) { - client.emit("guildChannelTypeUpdate", + if(oldChannel.type !== newChannel.type) { + client.emit('guildChannelTypeUpdate', newChannel, oldChannel.type, newChannel.type ) } - if(oldChannel.type == "voice" && oldChannel.userLimit != newChannel.userLimit) { - client.emit("guildChannelUserLimitUpdate", + if(oldChannel.type == 'voice' && oldChannel.userLimit !== newChannel.userLimit) { + client.emit('guildChannelUserLimitUpdate', newChannel, oldChannel.userLimit, newChannel.userLimit ) } - if(oldChannel.type == "voice" && oldChannel.bitrate != newChannel.bitrate) { - client.emit("guildChannelBitrateUpdate", + if(oldChannel.type == 'voice' && oldChannel.bitrate !== newChannel.bitrate) { + client.emit('guildChannelBitrateUpdate', newChannel, oldChannel.bitrate, newChannel.bitrate diff --git a/src/base/actions/guild.js b/src/base/actions/guild.js index ee294e394..d1348ddff 100644 --- a/src/base/actions/guild.js +++ b/src/base/actions/guild.js @@ -1,7 +1,7 @@ module.exports = (client) => { - client.on("guildUpdate", async(oldGuild, newGuild) => { + client.on('guildUpdate', async(oldGuild, newGuild) => { if(oldGuild.premiumTier < newGuild.premiumTier) { - client.emit("guildBoostLevelUp", + client.emit('guildBoostLevelUp', newGuild, oldGuild.premiumTier, newGuild.premiumTier @@ -9,87 +9,87 @@ module.exports = (client) => { } if(oldGuild.premiumTier > newGuild.premiumTier) { - client.emit("guildBoostLevelDown", + client.emit('guildBoostLevelDown', newGuild, oldGuild.premiumTier, newGuild.premiumTier ) } - if(oldGuild.region != newGuild.region) { - client.emit("guildRegionUpdate", + if(oldGuild.region !== newGuild.region) { + client.emit('guildRegionUpdate', newGuild, oldGuild.region, newGuild.region ) } - if(oldGuild.banner != newGuild.banner) { - client.emit("guildBannerUpdate", + if(oldGuild.banner !== newGuild.banner) { + client.emit('guildBannerUpdate', newGuild, oldGuild.banner, newGuild.banner ) } - if(oldGuild.afkChannel != newGuild.afkChannel) { - client.emit("guildAfkChannelUpdate", + if(oldGuild.afkChannel !== newGuild.afkChannel) { + client.emit('guildAfkChannelUpdate', newGuild, oldGuild.afkChannel, newGuild.afkChannel ) } - if(oldGuild.vanityURLCode != newGuild.vanityURLCode) { - client.emit("guildVanityURLUpdate", + if(oldGuild.vanityURLCode !== newGuild.vanityURLCode) { + client.emit('guildVanityURLUpdate', newGuild, oldGuild.vanityURLCode, newGuild.vanityURLCode ) } - if(oldGuild.features.length != newGuild.features.length) { - client.emit("guildFeaturesUpdate", + if(oldGuild.features.length !== newGuild.features.length) { + client.emit('guildFeaturesUpdate', newGuild, oldGuild.features, newGuild.features ) } - if(oldGuild.nameAcronym != newGuild.nameAcronym) { - client.emit("guildAcronymUpdate", + if(oldGuild.nameAcronym !== newGuild.nameAcronym) { + client.emit('guildAcronymUpdate', newGuild, oldGuild.nameAcronym, newGuild.nameAcronym ) } - if(oldGuild.ownerID != newGuild.ownerID) { - client.emit("guildOwnerUpdate", + if(oldGuild.ownerID !== newGuild.ownerID) { + client.emit('guildOwnerUpdate', newGuild, oldGuild.ownerID, newGuild.ownerID ) } - if(oldGuild.maximumMembers != newGuild.maximumMembers ) { - client.emit("guildMaximumMembersUpdate", + if(oldGuild.maximumMembers !== newGuild.maximumMembers ) { + client.emit('guildMaximumMembersUpdate', newGuild, oldGuild.maximumMembers, newGuild.maximumMembers ) } - if(oldGuild.partnered != newGuild.partnered ) { - client.emit("guildPartnerUpdate", + if(oldGuild.partnered !== newGuild.partnered ) { + client.emit('guildPartnerUpdate', newGuild, oldGuild.partnered, newGuild.partnered ) } - if(oldGuild.verified != newGuild.verified ) { - client.emit("guildVerifyUpdate", + if(oldGuild.verified !== newGuild.verified ) { + client.emit('guildVerifyUpdate', newGuild, oldGuild.verified, newGuild.verified diff --git a/src/base/actions/guildmember.js b/src/base/actions/guildmember.js index f26eba6b0..7c265912c 100644 --- a/src/base/actions/guildmember.js +++ b/src/base/actions/guildmember.js @@ -1,9 +1,9 @@ -const { default: axios } = require("axios"); +const { default: axios } = require('axios'); module.exports = (client) => { - client.on("guildMemberUpdate", async(oldMember, newMember) => { + client.on('guildMemberUpdate', async(oldMember, newMember) => { if(oldMember.premiumSince && newMember.premiumSince) { - client.emit("guildMemberBoost", + client.emit('guildMemberBoost', newMember, oldMember.premiumSince, newMember.premiumSince @@ -11,14 +11,14 @@ module.exports = (client) => { } if(oldMember.premiumSince && !newMember.premiumSince) { - client.emit("guildMemberUnboost", + client.emit('guildMemberUnboost', newMember, oldMember.premiumSince, newMember.premiumSince ) } - if (oldMember.nickname != newMember.nickname) { + if (oldMember.nickname !== newMember.nickname) { client.emit('guildMemberNicknameUpdate', newMember, oldMember.nickname, @@ -30,10 +30,10 @@ module.exports = (client) => { let url = `https://discord.com/api/v9/guilds/${newMember.guild.id}/members/${newMember.user.id}` let config = { - method: "GET", + method: 'GET', headers: { Authorization: `Bot ${client.token}`, - "Content-Type": "application/json" + 'Content-Type': 'application/json' }, url, } diff --git a/src/base/actions/interactions.js b/src/base/actions/interactions.js index 8a160401b..a9bd4a8d2 100644 --- a/src/base/actions/interactions.js +++ b/src/base/actions/interactions.js @@ -1,14 +1,29 @@ -const InteractionEvent = require("../../structures/InteractionEvent"); +const InteractionEvent = require('../../structures/InteractionEvent'); +const { inhibit, interactionRefactor } = require('../../util/util') module.exports = (client) => { - client.ws.on('INTERACTION_CREATE', data => { + client.ws.on('INTERACTION_CREATE', async(data) => { if (!data.message) return; if(data.data.component_type) { const interaction = new InteractionEvent(client, data) + let member = interaction.clicker.member, guild = interaction.guild, channel = interaction.channel + let inhibitReturn = await inhibit(client, interactionRefactor(client, interaction), { + interaction, member, + guild: guild, + channel: channel, + respond: async(result) => { + return interaction.slashRespond(result) + }, + edit: async(result, update = false) => { + return interaction.slashEdit(result, update); + } + }) + if(inhibitReturn == false) return; + client.emit(data.data.component_type == 3 ? `selectMenu` : `clickButton`, interaction) - client.emit("interaction", interaction) + client.emit('interaction', interaction) } }); } \ No newline at end of file diff --git a/src/base/actions/role.js b/src/base/actions/role.js index bb70ca5a6..b2cb1eef7 100644 --- a/src/base/actions/role.js +++ b/src/base/actions/role.js @@ -1,5 +1,5 @@ module.exports = (client) => { - client.on("roleUpdate", async(oldRole, newRole) => { + client.on('roleUpdate', async(oldRole, newRole) => { if (oldRole.rawPosition !== newRole.rawPosition) { client.emit('rolePositionUpdate', newRole, diff --git a/src/base/actions/user.js b/src/base/actions/user.js index 046f95caa..c338fbe4a 100644 --- a/src/base/actions/user.js +++ b/src/base/actions/user.js @@ -1,23 +1,23 @@ module.exports = (client) => { - client.on("userUpdate", async(oldUser, newUser) => { - if(oldUser.displayAvatarURL() != newUser.displayAvatarURL()) { - client.emit("userAvatarUpdate", + client.on('userUpdate', async(oldUser, newUser) => { + if(oldUser.displayAvatarURL() !== newUser.displayAvatarURL()) { + client.emit('userAvatarUpdate', newUser, oldUser.displayAvatarURL(), newUser.displayAvatarURL() ) } - if(oldUser.username != newUser.username) { - client.emit("userUsernameUpdate", + if(oldUser.username !== newUser.username) { + client.emit('userUsernameUpdate', newUser, oldUser.username, newUser.username ) } - if(oldUser.discriminator != newUser.discriminator) { - client.emit("userDiscriminatorUpdate", + if(oldUser.discriminator !== newUser.discriminator) { + client.emit('userDiscriminatorUpdate', newUser, oldUser.discriminator, newUser.discriminator @@ -25,8 +25,8 @@ module.exports = (client) => { } - if(oldUser.flags != newUser.flags) { - client.emit("userFlagsUpdate", + if(oldUser.flags !== newUser.flags) { + client.emit('userFlagsUpdate', newUser, oldUser.flags, newUser.flags diff --git a/src/base/actions/voiceupdate.js b/src/base/actions/voiceupdate.js index bb59b4b13..3eedafad8 100644 --- a/src/base/actions/voiceupdate.js +++ b/src/base/actions/voiceupdate.js @@ -1,5 +1,5 @@ module.exports = (client) => { - client.on("voiceStateUpdate", async(oldState, newState) => { + client.on('voiceStateUpdate', async(oldState, newState) => { const oldMember = oldState.member; const newMember = newState.member; @@ -26,7 +26,7 @@ module.exports = (client) => { } if (!oldState.mute && newState.mute) { - var muteType = newState.selfMute ? 'self-muted' : 'server-muted'; + let muteType = newState.selfMute ? 'self-muted' : 'server-muted'; client.emit('voiceChannelMute', newMember, muteType @@ -34,7 +34,7 @@ module.exports = (client) => { } if (oldState.mute && !newState.mute) { - var muteType = oldState.selfMute ? 'self-muted' : 'server-muted'; + let muteType = oldState.selfMute ? 'self-muted' : 'server-muted'; client.emit('voiceChannelUnmute', newMember, muteType @@ -42,7 +42,7 @@ module.exports = (client) => { } if (!oldState.deaf && newState.deaf) { - var deafType = newState.selfDeaf ? 'self-deafed' : 'server-v'; + let deafType = newState.selfDeaf ? 'self-deafed' : 'server-v'; client.emit('voiceChannelDeaf', newMember, deafType @@ -50,7 +50,7 @@ module.exports = (client) => { } if (oldState.deaf && !newState.deaf) { - var deafType = oldState.selfDeaf ? 'self-deafed' : 'server-v'; + let deafType = oldState.selfDeaf ? 'self-deafed' : 'server-v'; client.emit('voiceChannelUndeaf', newMember, deafType diff --git a/src/commands/argument.js b/src/commands/argument.js new file mode 100644 index 000000000..1615034fd --- /dev/null +++ b/src/commands/argument.js @@ -0,0 +1,108 @@ +const ms = require('ms'); +const StringArgumentType = require('./types/string'); +const IntegerArgumentType = require('./types/integer'); +const BooleanArgumentType = require('./types/boolean'); +const ChannelArgumentType = require('./types/channel'); +const UserArgumentType = require('./types/user'); +const RoleArgumentType = require('./types/role'); + +/** + * The Argument class + */ +class Argument { + /** + * The Argument class + * @param {Client} + * @param {Object} argument + */ + constructor(client, argument) { + /** + * client + * @type {Client} + */ + this.client = client; + + /** + * name + * @type {String} + */ + this.name = argument.name; + + /** + * argument + * @type {Argument} + */ + this.argument = this.determineArgument(client, argument); + + /** + * type + * @type {String} + */ + this.type = this.determineArgument(client, argument).type; + + /** + * prompt + * @type {String} + */ + this.prompt = argument.prompt || `Please define argument ${argument.name}`; + + /** + * choices + * @type {Object} + */ + this.choices = argument.choices; + + return this; + } + + /** + * Method to obtain + * @param {Message|Object} + * @param {String} + */ + async obtain(message, prompt = this.prompt) { + if(message.author.bot) return; + + const wait = 30000; + + message.reply(prompt) + const responses = await message.channel.awaitMessages(msg => msg.author.id === message.author.id, { + max: 1, + time: wait + }); + if(responses.size == 0) return { + valid: true, + timeLimit: true + } + + let valid = await this.argument.validate(this, responses.first()) + if(valid) { + return { + valid: false, + prompt: valid + }; + } + + return { + valid: true, + content: responses.first().content + }; + } + + /** + * Method to determineArgument + * @param {Client} + * @param {Argument} + */ + determineArgument(client, argument) { + if(argument.type == 3) return new StringArgumentType(client, argument); + if(argument.type == 4) return new IntegerArgumentType(client, argument); + if(argument.type == 5) return new BooleanArgumentType(client, argument); + if(argument.type == 6) return new UserArgumentType(client, argument); + if(argument.type == 7) return new ChannelArgumentType(client, argument); + if(argument.type == 8) return new RoleArgumentType(client, argument); + else return new StringArgumentType(client, argument); + } +} + +module.exports = Argument; \ No newline at end of file diff --git a/src/commands/base.js b/src/commands/base.js new file mode 100644 index 000000000..0568c3f04 --- /dev/null +++ b/src/commands/base.js @@ -0,0 +1,137 @@ +const { Snowflake } = require('discord.js'); +const { resolveString } = require('../util/util'); +const Color = require('../structures/Color'); + +/** + * The Command class + */ +class Command { + + /** + * Creates new Command instance + * @param {Client} client + * @param {Object} options + */ + constructor(client, options = {}) { + /** + * Name + * @type {String} + */ + this.name = resolveString(options.name); + + /** + * Description + * @type {String} + */ + this.description = resolveString(options.description); + + /** + * Cooldown + * @type {String} + */ + this.cooldown = resolveString(options.cooldown); + + /** + * expectedArgs + * @type {String | Array} + * @deprecated + */ + this.expectedArgs = options.expectedArgs; + + /** + * args + * @type {String | Array} + */ + this.args = options.args; + + /** + * minArgs + * @type {Number} + */ + this.minArgs = Number(options.minArgs); + + /** + * userRequiredPermissions + * @type {String | Array} + */ + this.userRequiredPermissions = options.userRequiredPermissions; + + /** + * userRequiredRoles + * @type {String | Array} + */ + this.userRequiredRoles = options.userRequiredRoles; + + /** + * clientRequiredPermissions + * @type {String | Array} + */ + this.clientRequiredPermissions = options.clientRequiredPermissions; + + /** + * userOnly + * @type {Snowflake | Array} + */ + this.userOnly = options.userOnly; + + /** + * channelOnly + * @type {Snowflake | Array} + */ + this.channelOnly = options.channelOnly; + + /** + * channelTextOnly + * @type {Boolean} + */ + this.channelTextOnly = Boolean(options.channelTextOnly) || undefined; + + /** + * channelNewsOnly + * @type {Boolean} + */ + this.channelNewsOnly = Boolean(options.channelNewsOnly) || undefined; + + /** + * guildOnly + * @type {Snowflake} + */ + this.guildOnly = options.guildOnly; + + /** + * nsfw + * @type {Boolean} + */ + this.nsfw = Boolean(options.nfsw) || false; + + /** + * slash + * @type {Boolean} + */ + this.slash = Boolean(options.slash) || null; + + /** + * aliases + * @type {Array} + */ + this.aliases = Array(options.aliases); + + /** + * category + * @type {String} + */ + this.category = resolveString(options.category); + } + + /** + * run function + * @param {Object} options + * @param {Array} arrayArgs + * @param {Array | Object} objectArgs + */ + async run({client, interaction, member, message, guild, channel, respond, edit}, arrayArgs, objectArgs) { + return console.log(new Color(`&d[GCommands] &cCommand ${this.name} doesn't provide a run method!`).getText()) + } +} + +module.exports = Command; \ No newline at end of file diff --git a/src/commands/types/base.js b/src/commands/types/base.js new file mode 100644 index 000000000..051d775f5 --- /dev/null +++ b/src/commands/types/base.js @@ -0,0 +1,35 @@ +const Color = require('../../structures/Color'); + +/** + * The ArgumentType class + */ +class ArgumentType { + /** + * The ArgumentType class + * @param {Client} + * @param {String} type + */ + constructor(client, type) { + if(!client) return console.log(new Color('&d[GCommands Args] &cNo discord.js client provided!').getText()); + if(!type) return console.log(new Color('&d[GCommands Args] &cNo argument provided!').getText()); + + /** + * type + * @type {String} + */ + this.type = type; + + return this; + } + + /** + * Method to validate + * @param {Argument} + * @param {Message|Object} + */ + validate() { + return console.log(new Color('&d[GCommands Args] &cArgument doesnt have provided validate() method')) + } +} + +module.exports = ArgumentType; \ No newline at end of file diff --git a/src/commands/types/boolean.js b/src/commands/types/boolean.js new file mode 100644 index 000000000..735559737 --- /dev/null +++ b/src/commands/types/boolean.js @@ -0,0 +1,38 @@ +const ArgumentType = require('./base'); + +/** + * The BooleanArgumentType class + * @extends ArgumentType + */ +class BooleanArgumentType extends ArgumentType { + /** + * The BooleanArgumentType class + * @param {Client} + */ + constructor(client) { + super(client, 'boolean') + + /** + * client + * @type {Client} + */ + this.client = client; + + /** + * answerSet + * @type {Set} + */ + this.answerSet = new Set(['true', 't', 'yes', 'y', 'on', 'enable', 'enabled', 'false', 'f', 'no', 'n', 'off', 'disable', 'disabled']); + } + + async validate(argument, message) { + const b = message.content.toLowerCase(); + const guildLanguage = await message.guild.getLanguage(); + + if(!this.answerSet.has(b)) { + return this.client.languageFile.ARGS_MUST_CONTAIN[guildLanguage].replace('{argument}', argument.name).replace('{type}', 'boolean') + } + } +} + +module.exports = BooleanArgumentType; \ No newline at end of file diff --git a/src/commands/types/channel.js b/src/commands/types/channel.js new file mode 100644 index 000000000..e5b7e216d --- /dev/null +++ b/src/commands/types/channel.js @@ -0,0 +1,33 @@ +const ArgumentType = require('./base'); + +/** + * The ChannelArgumentType class + * @extends ArgumentType + */ +class ChannelArgumentType extends ArgumentType { + /** + * The ChannelArgumentType class + * @param {Client} + */ + constructor(client) { + super(client, 'channel') + + /** + * client + * @type {Client} + */ + this.client = client; + } + + async validate(argument, message) { + const matches = message.content.match(/([0-9]+)/); + const guildLanguage = await message.guild.getLanguage(); + + if(!matches) return this.client.languageFile.ARGS_MUST_CONTAIN[guildLanguage].replace('{argument}', argument.name).replace('{type}', 'channel') + + let channel = this.client.channels.cache.get(matches[1]); + if(!channel) return this.client.languageFile.ARGS_MUST_CONTAIN[guildLanguage].replace('{argument}', argument.name).replace('{type}', 'channel') + } +} + +module.exports = ChannelArgumentType; \ No newline at end of file diff --git a/src/commands/types/integer.js b/src/commands/types/integer.js new file mode 100644 index 000000000..a3f535cce --- /dev/null +++ b/src/commands/types/integer.js @@ -0,0 +1,31 @@ +const ArgumentType = require('./base'); + +/** + * The IntegerArgumentType class + * @extends ArgumentType + */ +class IntegerArgumentType extends ArgumentType { + /** + * The IntegerArgumentType class + * @param {Client} + */ + constructor(client) { + super(client, 'integer') + + /** + * client + * @type {Client} + */ + this.client = client; + } + + async validate(argument, message) { + const guildLanguage = await message.guild.getLanguage(); + + if(!parseInt(message.content)) return this.client.languageFile.ARGS_MUST_CONTAIN[guildLanguage].replace('{argument}', argument.name).replace('{type}', 'integer') + + if(argument.oneOf && !argument.oneOf.includes(message.content.toLowerCase())) return `Please enter one of the following options: ${argument.oneOf.map(opt => `\`${opt}\``).join(', ')}`; + } +} + +module.exports = IntegerArgumentType; \ No newline at end of file diff --git a/src/commands/types/role.js b/src/commands/types/role.js new file mode 100644 index 000000000..89001447d --- /dev/null +++ b/src/commands/types/role.js @@ -0,0 +1,33 @@ +const ArgumentType = require('./base'); + +/** + * The RoleArgumentType class + * @param {Client} + */ +class RoleArgumentType extends ArgumentType { + /** + * The RoleArgumentType class + * @param {Client} + */ + constructor(client) { + super(client, 'role') + + /** + * client + * @type {Client} + */ + this.client = client; + } + + async validate(argument, message) { + const matches = message.content.match(/([0-9]+)/); + const guildLanguage = await message.guild.getLanguage(); + + if(!matches) return this.client.languageFile.ARGS_MUST_CONTAIN[guildLanguage].replace('{argument}', argument.name).replace('{type}', 'role') + + let user = message.guild.roles.cache.get(matches[1]); + if(!user) return this.client.languageFile.ARGS_MUST_CONTAIN[guildLanguage].replace('{argument}', argument.name).replace('{type}', 'role') + } +} + +module.exports = RoleArgumentType; \ No newline at end of file diff --git a/src/commands/types/string.js b/src/commands/types/string.js new file mode 100644 index 000000000..0f46b24f6 --- /dev/null +++ b/src/commands/types/string.js @@ -0,0 +1,22 @@ +const ArgumentType = require('./base'); + +/** + * The StringArgumentType class + * @param {Client} + */ +class StringArgumentType extends ArgumentType { + /** + * The StringArgumentType class + */ + constructor(client) { + super(client, 'string') + } + + async validate(argument, message) { + const guildLanguage = await message.guild.getLanguage(); + + if(argument.choices && !argument.choices.some(ch => ch.value == message.content.toLowerCase())) return this.client.languageFile.ARGS_CHOICES[guildLanguage].replace('{choices}', argument.choices.map(opt => `\`${opt.name}\``).join(', ')) + } +} + +module.exports = StringArgumentType; \ No newline at end of file diff --git a/src/commands/types/user.js b/src/commands/types/user.js new file mode 100644 index 000000000..6372526f2 --- /dev/null +++ b/src/commands/types/user.js @@ -0,0 +1,33 @@ +const ArgumentType = require('./base'); + +/** + * The UserArgumentType class + * @param {Client} + */ +class UserArgumentType extends ArgumentType { + /** + * The UserArgumentType class + * @param {Client} + */ + constructor(client) { + super(client, 'user') + + /** + * client + * @type {Client} + */ + this.client = client; + } + + async validate(argument, message) { + const matches = message.content.match(/([0-9]+)/); + const guildLanguage = await message.guild.getLanguage(); + + if(!matches) return this.client.languageFile.ARGS_MUST_CONTAIN[guildLanguage].replace('{argument}', argument.name).replace('{type}', 'user') + + let user = this.client.users.cache.get(matches[1]); + if(!user) return this.client.languageFile.ARGS_MUST_CONTAIN[guildLanguage].replace('{argument}', argument.name).replace('{type}', 'user') + } +} + +module.exports = UserArgumentType; \ No newline at end of file diff --git a/src/index.js b/src/index.js index c38b04268..8b116a280 100644 --- a/src/index.js +++ b/src/index.js @@ -2,37 +2,37 @@ module.exports = { // Root classes - GCommandsBase: require("./base/GCommandsBase"), - GCommands: require("./base/GCommands.js"), - GEvents: require("./base/GEvents"), - GCommandsDispatcher: require("./base/GCommandsDispatcher"), + GCommandsBase: require('./base/GCommandsBase'), + GCommands: require('./base/GCommands.js'), + GEventLoader: require('@gcommands/events').GEventLoader, + GCommandsDispatcher: require('./base/GCommandsDispatcher'), // Loaders - GEventLoader: require("./managers/GEventLoader"), - GCommandLoader: require("./managers/GCommandLoader"), - GDatabaseLoader: require("./managers/GDatabaseLoader"), + GEventHandling: require('./managers/GEventHandling'), + GCommandLoader: require('./managers/GCommandLoader'), + GDatabaseLoader: require('./managers/GDatabaseLoader'), // Structures - GCommandsGuild: require("./structures/GGuild"), - GCommandsMessage: require("./structures/GMessage"), - GNewsChannel: require("./structures/NewsChannel"), - GTextChannel: require("./structures/TextChannel"), - GDMChannel: require("./structures/DMChannel"), - MessageButton: require("./structures/MessageButton"), - MessageActionRow: require("./structures/MessageActionRow"), - MessageSelectMenu: require("./structures/MessageSelectMenu"), - MessageSelectMenuOption: require("./structures/MessageSelectMenuOption"), - - ButtonCollectorV12: require("./structures/v13/ButtonCollector"), - ButtonCollectorV13: require("./structures/v12/ButtonCollector"), - - SelectMenuCollectorV12: require("./structures/v13/SelectMenuCollector"), - SelectMenuCollectorV13: require("./structures/v12/SelectMenuCollector"), + GMessage: require('./structures/GMessage'), + GNewsChannel: require('./structures/NewsChannel'), + GTextChannel: require('./structures/TextChannel'), + GDMChannel: require('./structures/DMChannel'), + MessageButton: require('./structures/MessageButton'), + MessageActionRow: require('./structures/MessageActionRow'), + MessageSelectMenu: require('./structures/MessageSelectMenu'), + MessageSelectMenuOption: require('./structures/MessageSelectMenuOption'), + Command: require('./commands/base'), + Event: require('@gcommands/events').Event, + GPayload: require('./structures/GPayload'), + ButtonCollectorV12: require('./structures/v13/ButtonCollector'), + ButtonCollectorV13: require('./structures/v12/ButtonCollector'), + SelectMenuCollectorV12: require('./structures/v13/SelectMenuCollector'), + SelectMenuCollectorV13: require('./structures/v12/SelectMenuCollector'), // Other - Color: require("./structures/Color"), - Util: require("./util/util"), - SlashCommand: { + Color: require('./structures/Color'), + Util: require('./util/util'), + ArgumentType: { SUB_COMMAND: 1, SUB_COMMAND_GROUP: 2, STRING: 3, @@ -44,13 +44,13 @@ module.exports = { MENTIONABLE: 9 }, ButtonTypes: { - blurple: "blurple", - gray: "gray", - grey: "gray", - green: "green", - red: "red", - url: "url" + blurple: 'blurple', + gray: 'gray', + grey: 'gray', + green: 'green', + red: 'red', + url: 'url' }, - version: require("../package.json").version + version: require('../package.json').version } \ No newline at end of file diff --git a/src/managers/GCommandLoader.js b/src/managers/GCommandLoader.js index c5929a350..3977bf52e 100644 --- a/src/managers/GCommandLoader.js +++ b/src/managers/GCommandLoader.js @@ -1,13 +1,35 @@ -const cmdUtils = require('../util/cmdUtils'), Color = require("../structures/Color"), { Events } = require("../util/Constants") -const axios = require("axios"); -const fs = require("fs"); -const ms = require("ms") - +const cmdUtils = require('../util/cmdUtils'), Color = require('../structures/Color'), { Events } = require('../util/Constants') +const axios = require('axios'); +const fs = require('fs'); +const ms = require('ms'); +const { isClass } = require('../util/util'); +const Command = require('../commands/base'); + +/** + * The GCommandLoader class + */ class GCommandLoader { + /** + * The GCommandLoader class + * @param {GCommands} GCommandsClient + */ constructor(GCommandsClient) { + /** + * GCommandsClient + * @type {GCommands} + */ this.GCommandsClient = GCommandsClient; + + /** + * client + * @type {Client} + */ this.client = this.GCommandsClient.client; + /** + * cmdDir + * @type {String} + */ this.cmdDir = this.GCommandsClient.cmdDir; this.__loadCommands() @@ -20,33 +42,45 @@ class GCommandLoader { */ async __loadCommands() { await fs.readdirSync(`${__dirname}/../../../../${this.cmdDir}`).forEach(async(dir) => { - var file; - var fileName = dir.split(".").reverse()[1] - var fileType = dir.split(".").reverse()[0] - if(fileType == "js" || fileType == "ts") { + let file; + let fileName = dir.split('.').reverse()[1] + let fileType = dir.split('.').reverse()[0] + if(fileType == 'js' || fileType == 'ts') { try { - file = await require(`../../../../${this.cmdDir}${dir}`); + let finalFile; - if (file && file.aliases && Array.isArray(file.aliases)) file.aliases.forEach(alias => this.client.galiases.set(alias, file.name)); - this.client.gcommands.set(file.name, file); - this.GCommandsClient.emit(Events.LOG, new Color("&d[GCommands] &aLoaded (File): &e➜ &3" + fileName, {json:false}).getText()); + file = await require(`../../../../${this.cmdDir}${dir}`); + if (isClass(file)) { + finalFile = new file(this.client) + if(!(finalFile instanceof Command)) return console.log(new Color(`&d[GCommands] &cComamnd ${fileName} doesnt belong in Commands.`).getText()) + } else finalFile = file; + + if (finalFile && finalFile.aliases && Array.isArray(finalFile.aliases)) finalFile.aliases.forEach(alias => this.client.galiases.set(alias, finalFile.name)); + this.client.gcommands.set(finalFile.name, finalFile); + this.GCommandsClient.emit(Events.LOG, new Color('&d[GCommands] &aLoaded (File): &e➜ &3' + fileName, {json:false}).getText()); } catch(e) { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands Debug] &e("+fileName+") &3"+e).getText()); - this.GCommandsClient.emit(Events.LOG, new Color("&d[GCommands] &cCan't load " + fileName).getText()); + this.GCommandsClient.emit(Events.DEBUG, new Color('&d[GCommands Debug] &e('+fileName+') &3'+e).getText()); + this.GCommandsClient.emit(Events.LOG, new Color('&d[GCommands] &cCan\'t load ' + fileName).getText()); } } else { fs.readdirSync(`${this.cmdDir}${dir}`).forEach(async(cmdFile) => { - var file2; - var fileName2 = cmdFile.split(".").reverse()[1] + let file2; + let fileName2 = cmdFile.split('.').reverse()[1] try { + let finalFile2; + file2 = await require(`../../../../${this.cmdDir}${dir}/${cmdFile}`); - - if (file2.aliases && Array.isArray(file2.aliases)) file2.aliases.forEach(alias => this.client.galiases.set(alias, file2.name)); - this.client.gcommands.set(file2.name, file2); - this.GCommandsClient.emit(Events.LOG, new Color("&d[GCommands] &aLoaded (File): &e➜ &3" + fileName2, {json:false}).getText()); + if (isClass(file2)) { + finalFile2 = new file2(this.client) + if(!(finalFile2 instanceof Command)) return console.log(new Color(`&d[GCommands] &cComamnd ${finalFile2} doesnt belong in Commands.`).getText()) + } else finalFile2 = file2; + + if (finalFile2.aliases && Array.isArray(finalFile2.aliases)) finalFile2.aliases.forEach(alias => this.client.galiases.set(alias, finalFile2.name)); + this.client.gcommands.set(finalFile2.name, finalFile2); + this.GCommandsClient.emit(Events.LOG, new Color('&d[GCommands] &aLoaded (File): &e➜ &3' + fileName2, {json:false}).getText()); } catch(e) { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands Debug] &e("+fileName2+") &3"+e).getText()); - this.GCommandsClient.emit(Events.LOG, new Color("&d[GCommands] &cCan't load " + fileName2).getText()); + this.GCommandsClient.emit(Events.DEBUG, new Color('&d[GCommands Debug] &e('+fileName2+') &3'+e).getText()); + this.GCommandsClient.emit(Events.LOG, new Color('&d[GCommands] &cCan\'t load ' + fileName2).getText()); } }) } @@ -61,33 +95,22 @@ class GCommandLoader { * @private */ async __createCommands() { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands] &3Creating slash commands...").getText()) + this.GCommandsClient.emit(Events.DEBUG, new Color('&d[GCommands] &3Creating slash commands...').getText()) let keys = Array.from(this.client.gcommands.keys()); keys.forEach(async (cmdname) => { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands] &3Creating slash command (&e"+cmdname+"&3)").getText()); - var options = []; - var subCommandGroup = {}; - var subCommand = []; + this.GCommandsClient.emit(Events.DEBUG, new Color('&d[GCommands] &3Creating slash command (&e'+cmdname+'&3)').getText()); + let options = []; const cmd = this.client.gcommands.get(cmdname) - if(cmd.slash == false || cmd.slash == "false") return; + if(String(cmd.slash) == 'false') return; - if(!cmd.name) return console.log(new Color("&d[GCommands] &cParameter name is required! ("+cmdname+")",{json:false}).getText()); - if(!cmd.description) return console.log(new Color("&d[GCommands] &cParameter description is required! ("+cmdname+")",{json:false}).getText()); + if(!cmd.name) return console.log(new Color('&d[GCommands] &cParameter name is required! ('+cmdname+')',{json:false}).getText()); + if(!cmd.description) return console.log(new Color('&d[GCommands] &cParameter description is required! ('+cmdname+')',{json:false}).getText()); - if(cmd.subCommandGroup) { - subCommandGroup = [ - { - name: cmd.subCommandGroup, - description: cmd.subCommandGroup, - type: 2 - } - ] - } - - if (cmd.expectedArgs) { - if(typeof cmd.expectedArgs == "object") { - cmd.expectedArgs.forEach(option => { + if(cmd.expectedArgs) cmd.args = cmd.expectedArgs + if (cmd.args) { + if(typeof cmd.args == 'object') { + cmd.args.forEach(option => { options.push({ name: option.name, description: option.description, @@ -98,154 +121,84 @@ class GCommandLoader { }) }) } else { - var split = cmd.expectedArgs - .substring(1, cmd.expectedArgs.length - 1) + let split = cmd.args + .substring(1, cmd.args.length - 1) .split(/[>\]] [<\[]/) for (let a = 0; a < split.length; ++a) { - var item = split[a]; - var option = item.replace(/ /g, '-').split(":")[0] ? item.replace(/ /g, '-').split(":")[0] : item.replace(/ /g, '-'); - var optionType = item.replace(/ /g, '-').split(":")[1] ? item.replace(/ /g, '-').split(":")[1] : 3; - var optionDescription = item.replace(/ /g, '-').split(":")[2] ? item.replace(/ /g, '-').split(":")[2] : item; + let item = split[a]; + let option = item.replace(/ /g, '-').split(':')[0] ? item.replace(/ /g, '-').split(':')[0] : item.replace(/ /g, '-'); + let optionType = item.replace(/ /g, '-').split(':')[1] ? item.replace(/ /g, '-').split(':')[1] : 3; + let optionDescription = item.replace(/ /g, '-').split(':')[2] ? item.replace(/ /g, '-').split(':')[2] : item; if(optionType == 1 || optionType == 2) optionType = 3 options.push({ name: option, - description: optionDescription, - type: parseInt(optionType), - required: a < cmd.minArgs ? cmd.minArgs : 0, + description: optionDescription || option, + type: parseInt(optionType) || 3, + required: a < cmd.minArgs, }) } } } - - if(cmd.subCommand) { - cmd.subCommand.forEach(sc => { - try { - var opt = [] - var optionsSplit = sc.split(";")[1] - - if(optionsSplit) { - var split = optionsSplit - .substring(1, optionsSplit.length - 1) - .split(/[>\]] [<\[]/) - - for (let a = 0; a < split.length; ++a) { - var item = split[a] - var option = item.replace(/ /g, '-').split(":")[0] ? item.replace(/ /g, '-').split(":")[0] : item.replace(/ /g, '-'); - var optionType = item.replace(/ /g, '-').split(":")[1] ? item.replace(/ /g, '-').split(":")[1] : 3; - var optionDescription = item.replace(/ /g, '-').split(":")[2] ? item.replace(/ /g, '-').split(":")[2] : item; - if(optionType == 1 || optionType == 2) optionType = 3 - - opt.push({ - name: option, - description: optionDescription, - type: parseInt(optionType), - required: a < cmd.minArgs, - }) - } - } - - subCommand.push({ - name: sc.split(";")[0], - description: sc.split(";")[0], - type: 1, - options: opt || [] - }) - } catch(e) { - subCommand.push({ - name: sc.name, - description: sc.description, - type: 1, - options: sc.options || [] - }) - } - }) - } - - if(cmd.subCommandGroup) { - subCommandGroup = [ - { - name: subCommandGroup[0].name, - description: subCommandGroup[0].name, - type: subCommandGroup[0].type, - options: subCommand - } - ] - } - try { - var url = `https://discord.com/api/v8/applications/${this.client.user.id}/commands`; + let url = `https://discord.com/api/v8/applications/${this.client.user.id}/commands`; if(cmd.guildOnly) url = `https://discord.com/api/v8/applications/${this.client.user.id}/guilds/${cmd.guildOnly}/commands`; - var cmdd = { + let cmdd = { name: cmd.name.toLowerCase(), description: cmd.description, options: options || [] } - if(cmd.subCommandGroup && cmd.subCommand) { - cmdd = { - name: cmd.name.toLowerCase(), - description: cmd.description, - options: subCommandGroup || [] - }; - } else { - cmdd = { - name: cmd.name.toLowerCase(), - description: cmd.description, - options: options || [] - }; - } - - var config = { - method: "POST", + let config = { + method: 'POST', headers: { Authorization: `Bot ${this.client.token}`, - "Content-Type": "application/json" + 'Content-Type': 'application/json' }, data: JSON.stringify(cmdd), url, } axios(config).then((response) => { - this.GCommandsClient.emit(Events.LOG, new Color("&d[GCommands] &aLoaded: &e➜ &3" + cmd.name, {json:false}).getText()); + this.GCommandsClient.emit(Events.LOG, new Color('&d[GCommands] &aLoaded: &e➜ &3' + cmd.name, {json:false}).getText()); }) .catch((error) => { - console.log(new Color(`&d[GCommands] ${error.response.status == 429 ? "&aWait &e" + ms(error.response.data["retry_after"] * 1000) : ""} &c${error} &e(${cmd.name})`, {json:false}).getText()); + this.GCommandsClient.emit(Events.LOG, new Color(`&d[GCommands] ${error.response.status == 429 ? '&aWait &e' + ms(error.response.data['retry_after'] * 1000) : ''} &c${error} &e(${cmd.name})`, {json:false}).getText()); if(error.response) { if(error.response.status == 429) { setTimeout(() => { this.__tryAgain(cmd, config) - }, (error.response.data["retry_after"]) * 1000) + }, (error.response.data['retry_after']) * 1000) } else { try { this.GCommandsClient.emit(Events.DEBUG, new Color([ - "&a----------------------", - " &d[GCommands Debug] &3", - "&aCode: &b" + error.response.data.code, - "&aMessage: &b" + error.response.data.message, - " ", - "&b" + error.response.data.errors.guild_id._errors[0].code, - "&b" + rror.response.data.errors.guild_id._errors[0].message, - "&a----------------------" + '&a----------------------', + ' &d[GCommands Debug] &3', + '&aCode: &b' + error.response.data.code, + '&aMessage: &b' + error.response.data.message, + ' ', + '&b' + error.response.data.errors.guild_id._errors[0].code, + '&b' + rror.response.data.errors.guild_id._errors[0].message, + '&a----------------------' ]).getText()) } catch(e) { this.GCommandsClient.emit(Events.DEBUG, new Color([ - "&a----------------------", - " &d[GCommands Debug] &3", - "&aCode: &b" + error.response.data.code, - "&aMessage: &b" + error.response.data.message, - "&a----------------------" + '&a----------------------', + ' &d[GCommands Debug] &3', + '&aCode: &b' + error.response.data.code, + '&aMessage: &b' + error.response.data.message, + '&a----------------------' ]).getText()) } } } }) - }catch(e) { - console.log(e) + } catch(e) { + this.GCommandsClient.emit(Events.DEBUG, e) } }) } @@ -257,16 +210,16 @@ class GCommandLoader { */ async __tryAgain(cmd, config) { axios(config).then((response) => { - this.GCommandsClient.emit(Events.LOG, new Color("&d[GCommands] &aLoaded: &e➜ &3" + cmd.name, {json:false}).getText()); + this.GCommandsClient.emit(Events.LOG, new Color('&d[GCommands] &aLoaded: &e➜ &3' + cmd.name, {json:false}).getText()); }) .catch((error) => { - console.log(new Color(`&d[GCommands] ${error.response.status == 429 ? "&aWait &e" + ms(error.response.data["retry_after"] * 1000) : ""} &c${error} &e(${cmd.name})`, {json:false}).getText()); + this.GCommandsClient.emit(Events.LOG, new Color(`&d[GCommands] ${error.response.status == 429 ? '&aWait &e' + ms(error.response.data['retry_after'] * 1000) : ''} &c${error} &e(${cmd.name})`, {json:false}).getText()); if(error.response) { if(error.response.status == 429) { setTimeout(() => { this.__tryAgain(cmd, config) - }, (error.response.data["retry_after"]) * 1000) + }, (error.response.data['retry_after']) * 1000) } } }) @@ -279,20 +232,20 @@ class GCommandLoader { */ async __deleteAllGlobalCmds() { try { - var allcmds = await cmdUtils.__getAllCommands(this.client); - if(!this.client.slash) { + let allcmds = await cmdUtils.__getAllCommands(this.client); + if(String(this.client.slash) == 'false') { allcmds.forEach(cmd => { cmdUtils.__deleteCmd(this.client, cmd.id) }) } - var nowCMDS = []; + let nowCMDS = []; - var keys = Array.from(this.client.gcommands.keys()); + let keys = Array.from(this.client.gcommands.keys()); keys.forEach(cmdname => { nowCMDS.push(cmdname) - if(this.client.gcommands.get(cmdname).slash == false || this.client.gcommands.get(cmdname).slash == "false") { + if(this.client.gcommands.get(cmdname).slash == false || this.client.gcommands.get(cmdname).slash == 'false') { allcmds.forEach(cmd => { if(cmd.name == cmdname) { cmdUtils.__deleteCmd(this.client, cmd.id) @@ -302,14 +255,14 @@ class GCommandLoader { }) allcmds.forEach(cmd => { - var f = nowCMDS.some(v => cmd.name.toLowerCase().includes(v.toLowerCase())) + let f = nowCMDS.some(v => cmd.name.toLowerCase().includes(v.toLowerCase())) if(!f) { cmdUtils.__deleteCmd(this.client, cmd.id) } }) } catch(e) { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands Debug] &3Can't remove global commands!").getText()) + return; } this.__deleteAllGuildCmds() @@ -323,7 +276,7 @@ class GCommandLoader { async __deleteAllGuildCmds() { try { this.client.guilds.cache.forEach(async(guild) => { - var allcmds = await cmdUtils.__getAllCommands(this.client, guild.id); + let allcmds = await cmdUtils.__getAllCommands(this.client, guild.id); if(!allcmds) return; if(!this.client.slash) { @@ -332,12 +285,13 @@ class GCommandLoader { }) } - var nowCMDS = []; - var keys = Array.from(this.client.gcommands.keys()); + let nowCMDS = []; + let keys = Array.from(this.client.gcommands.keys()); keys.forEach(cmdname => { nowCMDS.push(cmdname) + let command = this.client.gcommands.get(cmdname); - if(this.client.gcommands.get(cmdname).slash == false || this.client.gcommands.get(cmdname).slash == "false") { + if(command.slash == false || command.slash == 'false') { allcmds.forEach(cmd => { if(cmd.name == cmdname) { cmdUtils.__deleteCmd(this.client, cmd.id, guild.id) @@ -347,7 +301,7 @@ class GCommandLoader { }) allcmds.forEach(cmd => { - var f = nowCMDS.some(v => cmd.name.toLowerCase().includes(v.toLowerCase())) + let f = nowCMDS.some(v => cmd.name.toLowerCase().includes(v.toLowerCase())) if(!f) { cmdUtils.__deleteCmd(this.client, cmd.id, guild.id) @@ -355,14 +309,12 @@ class GCommandLoader { }) }) - console.log(new Color("&d[GCommands TIP] &3Are guild commands not deleted when you delete them? Use this site for remove &ehttps://gcommands-slash-gui.netlify.app/").getText()) - if((this.client.slash) || (this.client.slash == "both")) { + console.log(new Color('&d[GCommands TIP] &3Are guild commands not deleted when you delete them? Use this site for remove &ehttps://gcommands-slash-gui.netlify.app/').getText()) + if((this.client.slash) || (this.client.slash == 'both')) { this.__createCommands(); } } catch(e) { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands Debug] &3Can't remove guild commands!").getText()) - - if((this.client.slash) || (this.client.slash == "both")) { + if((this.client.slash) || (this.client.slash == 'both')) { this.__createCommands(); } } diff --git a/src/managers/GDatabaseLoader.js b/src/managers/GDatabaseLoader.js index cb246d546..fffb602af 100644 --- a/src/managers/GDatabaseLoader.js +++ b/src/managers/GDatabaseLoader.js @@ -1,10 +1,25 @@ -const Color = require("../structures/Color") - +/** + * The GDatabaseLoader class + */ class GDatabaseLoader { + /** + * The GDatabaseLoader class + * @param {GCommands} GCommandsClient + */ constructor(GCommandsClient) { + + /** + * GCommandsClient + * @type {GCommands} + */ this.GCommandsClient = GCommandsClient; - this.client = this.GCommandsClient.client; + /** + * client + * @type {Client} + */ + this.client = this.GCommandsClient.client; + this.__loadDB() } @@ -13,32 +28,13 @@ class GDatabaseLoader { * @returns {boolean} * @private */ - async __loadDB() { + __loadDB() { let dbType = this.GCommandsClient.database; if(!dbType) this.client.database = undefined; else { const Keyv = require('keyv'); this.client.database = new Keyv(dbType) } - - this.__guildConfig() - } - - async __guildConfig() { - this.client.guilds.cache.forEach(async (guild) => { - let prefix = await this.client.dispatcher.getGuildPrefix(guild.id, false) - guild.prefix = prefix; - }) - - this.client.guilds.cache.forEach(async (guild) => { - let language = await this.client.dispatcher.getGuildLanguage(guild.id, false) - guild.language = language; - }) - - this.client.on("guildCreate", (guild) => { - guild.prefix = this.client.dispatcher.getGuildPrefix(guild.id, false) - guild.language = this.client.dispatcher.getGuildLanguage(guild.id, false) - }) } } diff --git a/src/managers/GEventHandling.js b/src/managers/GEventHandling.js new file mode 100644 index 000000000..33bfed736 --- /dev/null +++ b/src/managers/GEventHandling.js @@ -0,0 +1,429 @@ +const {Collection} = require('discord.js'); +const { readdirSync } = require('fs'); +const Argument = require('../commands/argument'); +const Color = require('../structures/Color'), { Events } = require('../util/Constants'); +const GInteraction = require('../structures/GInteraction'); +const ifDjsV13 = require('../util/updater').checkDjsVersion('13'), { inhibit, interactionRefactor } = require('../util/util') + +/** + * The GEventHandling class +*/ +class GEventHandling { + + /** + * Creates new GEventHandling instance + * @param {GCommandsClient} GCommandsClient + */ + constructor(GCommandsClient) { + /** + * GCommandsClient + * @type {GCommands} + */ + this.GCommandsClient = GCommandsClient; + + /** + * client + * @type {Client} + */ + this.client = GCommandsClient.client; + + this.messageEvent() + this.slashEvent() + this.loadMoreEvents() + } + + /** + * Internal method to messageEvent + * @returns {void} + * @private + */ + async messageEvent() { + if((this.client.slash == false) || (this.client.slash == 'both')) { + this.client.on('message', async(message) => { + messageEventUse(message) + }) + + this.client.on('messageUpdate', async(oldMessage, newMessage) => { + if(oldMessage.content == newMessage.content || oldMessage.embeds == newMessage.embeds) return; + messageEventUse(newMessage) + }) + } + + let messageEventUse = async(message) => { + if (!message || !message.author || message.author.bot || !message.guild) return; + + let mentionRegex = new RegExp(`^<@!?(${this.client.user.id})> `) + + let prefix = message.content.match(mentionRegex) ? message.content.match(mentionRegex) : (await message.guild.getCommandPrefix()).filter(p => message.content.startsWith(p)) + if(prefix.length === 0) return; + + + if (this.GCommandsClient.caseSensitivePrefixes && !message.content.toLowerCase().startsWith(prefix[0].toLowerCase())) return; + else if (!message.content.startsWith(prefix[0])) return; + + const [cmd, ...args] = message.content.slice(prefix.length).trim().split(/ +/g); + + if (cmd.length === 0) return; + + try { + let commandos = this.client.gcommands.get(this.GCommandsClient.caseSensitiveCommands ? cmd.toLowerCase() : cmd); + if(!commandos) commandos = this.client.gcommands.get(this.client.galiases.get(this.GCommandsClient.caseSensitiveCommands ? cmd.toLowerCase() : cmd)); + if(!commandos || String(commandos.slash) == 'true') return; + + let member = message.member, guild = message.guild, channel = message.channel + let botMessageInhibit; + let inhibitReturn = await inhibit(this.client, interactionRefactor(this.client, commandos), { + message, member, guild, channel, + /** + * Respond + * @type {Interface} + * @param {RespondOptions} result + * @returns {Object} + */ + respond: async(options = undefined) => { + if(this.client.autoTyping) channel.startTyping(this.client.autoTyping); + + let msg = await message.send(options); + botMessageInhibit = msg; + + if(this.client.autoTyping) channel.stopTyping(true); + return msg; + }, + edit: async(options = undefined) => { + if(!botMessageInhibit) return console.log(new Color('&d[GCommands Errors] &cFirst you need to send a respond.')) + return await botMessageInhibit.edit(options); + } + }, args, args) + if(inhibitReturn == false) return; + + let guildLanguage = await this.client.dispatcher.getGuildLanguage(message.guild.id); + let cooldown = await this.client.dispatcher.getCooldown(message.guild.id, message.author.id, commandos) + if(cooldown.cooldown) return message.inlineReply(this.client.languageFile.COOLDOWN[guildLanguage].replace(/{COOLDOWN}/g, cooldown.wait).replace(/{CMDNAME}/g, commandos.name)) + + if(commandos.nsfw) { + if(!message.channel.nsfw) { + return ifDjsV13 ? message.inlineReply(this.client.languageFile.NSFW[guildLanguage]) : message.reply(this.client.languageFile.NSFW[guildLanguage]); + } + } + + if(commandos.guildOnly) { + if(message.guild.id !== commandos.guildOnly) return; + } + + if(commandos.userOnly) { + if(typeof commandos.userOnly == 'object') { + let users = commandos.userOnly.some(v => message.author.id == v) + if(!users) return; + } else { + if(message.author.id !== commandos.userOnly) return; + } + } + + if(commandos.channelOnly) { + if(typeof commandos.channelOnly == 'object') { + let channels = commandos.channelOnly.some(v => message.channel.id == v) + if(!channels) return; + } else { + if(message.channel.id !== commandos.channelOnly) return; + } + } + + if(commandos.channelTextOnly && message.channel.type != 'text') return message.send(this.client.languageFile.CHANNEL_TEXT_ONLY[guildLanguage]) + if(commandos.channelNewsOnly && message.channel.type != 'news') return message.send(this.client.languageFile.CHANNEL_NEWS_ONLY[guildLanguage]) + + if(commandos.clientRequiredPermissions) { + if(!Array.isArray(commandos.clientRequiredPermissions)) commandos.clientRequiredPermissions = [commandos.clientRequiredPermissions]; + + if(message.channel.permissionsFor(message.guild.me).missing(commandos.clientRequiredPermissions).length > 0) { + let permsNeed = this.client.languageFile.MISSING_CLIENT_PERMISSIONS[guildLanguage].replace('{PERMISSION}',commandos.clientRequiredPermissions.map(v => v.split(' ').map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(' ')).join(', ')); + return ifDjsV13 ? message.inlineReply(permsNeed) : message.reply(permsNeed); + } + } + + if(commandos.userRequiredPermissions) { + if(!Array.isArray(commandos.userRequiredPermissions)) commandos.userRequiredPermissions = [commandos.userRequiredPermissions]; + + if(!member.permissions.has(commandos.userRequiredPermissions)) { + let permsNeed = this.client.languageFile.MISSING_PERMISSIONS[guildLanguage].replace('{PERMISSION}',commandos.userRequiredPermissions.map(v => v.split(' ').map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(' ')).join(', ')); + return ifDjsV13 ? message.inlineReply(permsNeed) : message.reply(permsNeed); + } + } + + if(commandos.userRequiredRoles) { + if(!Array.isArray(commandos.userRequiredRoles)) commandos.userRequiredRoles = [commandos.userRequiredRoles]; + + let roles = commandos.userRequiredRoles.some(v => member._roles.includes(v)) + if(!roles) { + let permsNeed = this.client.languageFile.MISSING_ROLES[guildLanguage].replace('{ROLES}', `\`${commandos.userRequiredRoles.map(r => message.guild.roles.cache.get(r).name).join(', ')}\``); + return ifDjsV13 ? message.inlineReply(permsNeed) : message.reply(permsNeed); + } + } + + if(commandos.userRequiredRole) { + if(!Array.isArray(commandos.userRequiredRole)) commandos.userRequiredRole = [commandos.userRequiredRole]; + + let roles = commandos.userRequiredRole.some(v => member._roles.includes(v)) + if(!roles) { + let permsNeed = this.client.languageFile.MISSING_ROLES[guildLanguage].replace('{ROLES}', `\`${commandos.userRequiredRoles.map(r => message.guild.roles.cache.get(r).name).join(', ')}\``); + return ifDjsV13 ? message.inlineReply(permsNeed) : message.reply(permsNeed); + } + } + + for(let i in commandos.args) { + let arg = new Argument(this.client, commandos.args[i]); + + if(args[i]) { + let argInvalid = await arg.argument.validate(arg, {content: args[i], guild: message.guild}) + if(argInvalid) { + let argInput = await arg.obtain(message, argInvalid) + if(!argInput.valid) argInput = await arg.obtain(message, argInput.prompt); + + if(argInput.timeLimit) return message.reply(this.client.languageFile.ARGS_TIME_LIMIT[guildLanguage]); + args[i] = argInput.content; + } + + continue; + } + + let argInput = await arg.obtain(message) + if(!argInput.valid) argInput = await arg.obtain(message, argInput.prompt); + + if(argInput.timeLimit) return message.reply(this.client.languageFile.ARGS_TIME_LIMIT[guildLanguage]); + args[i] = argInput.content; + } + + this.GCommandsClient.emit(Events.DEBUG, new Color('&d[GCommands Debug] &3User &a' + message.author.id + '&3 used &a' + cmd).getText()) + + const client = this.client, bot = this.client + let botMessage; + commandos.run({ + client, bot, message, member, guild, channel, + /** + * Respond + * @type {Interface} + * @param {RespondOptions} result + * @returns {Object} + */ + respond: async(options = undefined) => { + if(this.client.autoTyping) channel.startTyping(this.client.autoTyping); + + let msg = await message.send(options); + botMessage = msg; + + if(this.client.autoTyping) channel.stopTyping(true); + return msg; + }, + edit: async(options = undefined) => { + if(!botMessage) return console.log(new Color('&d[GCommands Errors] &cFirst you need to send a respond.')) + return await botMessage.edit(options); + } + }, args, args) + } catch(e) { + this.GCommandsClient.emit(Events.DEBUG, e); + + if(!this.GCommandsClient.unkownCommandMessage) return; + if(this.client.languageFile.UNKNOWN_COMMAND[this.client.language]) { + message.channel.send(this.client.languageFile.UNKNOWN_COMMAND[guildLanguage].replace('{COMMAND}',cmd)); + } + } + } + } + + /** + * Internal method to slashEvent + * @returns {void} + * @private + */ + async slashEvent() { + if((this.client.slash) || (this.client.slash == 'both')) { + this.client.ws.on('INTERACTION_CREATE', async (int) => { + let interaction = new GInteraction(this.client, int) + if(interaction.type !== 2) return; + + try { + let commandos = this.client.gcommands.get(this.GCommandsClient.caseSensitiveCommands ? interaction.interaction.name.toLowerCase() : interaction.interaction.name); + if(!commandos || String(commandos.slash) == 'false') return; + + let inhibitReturn = await inhibit(this.client, interactionRefactor(this.client, commandos), { + interaction, + member: interaction.member, + author: interaction.user, + guild: interaction.guild, + channel: interaction.channel, + respond: async(result) => { + return interaction.reply.send(result); + }, + edit: async(result) => { + return interaction.reply.edit(result); + } + }, await this.getSlashArgs(interaction.interaction.options || []), await this.getSlashArgsObject(interaction.interaction.options || [])) + if(inhibitReturn == false) return; + + let guildLanguage = await this.client.dispatcher.getGuildLanguage(interaction.guild.id); + let cooldown = await this.client.dispatcher.getCooldown(interaction.guild.id, interaction.author.id, commandos) + if(cooldown.cooldown) return interaction.reply.send(this.client.languageFile.COOLDOWN[guildLanguage].replace(/{COOLDOWN}/g, cooldown.wait).replace(/{CMDNAME}/g, commandos.name)) + + if(commandos.nsfw && !interaction.channel.nsfw) return interaction.reply.send(this.client.languageFile.NSFW[guildLanguage]) + + if(commandos.userOnly) { + if(typeof commandos.userOnly == 'object') { + let users = commandos.userOnly.some(v => interaction.user.id == v) + if(!users) return; + } else { + if(interaction.author.id !== commandos.userOnly) return; + } + } + + if(commandos.channelOnly) { + if(typeof commandos.channelOnly == 'object') { + let channels = commandos.channelOnly.some(v => interaction.channel.id == v); + if(!channels) return; + } else { + if(interaction.channel.id !== commandos.channelOnly) return; + } + } + + if(commandos.channelTextOnly && interaction.channel.type != 'text') return interaction.reply.send({content: this.client.languageFile.CHANNEL_TEXT_ONLY[guildLanguage], ephemeral: true}) + if(commandos.channelNewsOnly && interaction.channel.type != 'news') return interaction.reply.send({content: this.client.languageFile.CHANNEL_NEWS_ONLY[guildLanguage], ephemeral: true}) + + if(commandos.clientRequiredPermissions) { + if(!Array.isArray(commandos.clientRequiredPermissions)) commandos.clientRequiredPermissions = [commandos.clientRequiredPermissions]; + + if(interaction.guild.channels.cache.get(interaction.channel.id).permissionsFor(interaction.guild.me).missing(commandos.clientRequiredPermissions).length > 0) return interaction.reply.send({content: this.client.languageFile.MISSING_CLIENT_PERMISSIONS[guildLanguage].replace('{PERMISSION}',commandos.clientRequiredPermissions.map(v => v.split(' ').map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(' ')).join(', ')), ephemeral: true}) + } + + if(commandos.userRequiredPermissions) { + if(!Array.isArray(commandos.userRequiredPermissions)) commandos.userRequiredPermissions = [commandos.userRequiredPermissions]; + + if(!interaction.member.permissions.has(commandos.userRequiredPermissions)) return interaction.reply.send({content:this.client.languageFile.MISSING_PERMISSIONS[guildLanguage].replace('{PERMISSION}',commandos.userRequiredPermissions.map(v => v.split(' ').map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(' ')).join(', ')), ephemeral: true}) + } + + if((commandos.userRequiredRoles) || (commandos.userRequiredRole)) { + if(commandos.userRequiredRole) commandos.userRequiredRoles = commandos.userRequiredRole; + if(!Array.isArray(commandos.userRequiredRoles)) commandos.userRequiredRoles = [commandos.userRequiredRoles]; + + let roles = commandos.userRequiredRoles.some(v => interaction.member.roles.includes(v)) + if(!roles) return interaction.reply.send({content: this.client.languageFile.MISSING_ROLES[guildLanguage].replace('{ROLES}', `\`${commandos.userRequiredRoles.map(r => interaction.guild.roles.cache.get(r).name).join(', ')}\``), ephemeral: true}) + } + + try { + /** + * Return system for slash + * @name ReturnSystem + * @param {DiscordClient} client + * @param {Object} interaction + * @example + * return { + * content: 'hi', + * ephemeral: true, + * allowedMentions: { parse: [], repliedUser: true } + * } + */ + + const client = this.client, bot = this.client + commandos.run({ + client, bot, interaction, + member: interaction.member, + author: interaction.user, + guild: interaction.guild, + channel: interaction.channel, + /** + * Respond + * @type {Interface} + * @param {RespondOptions} result + * @returns {Object} + */ + respond: async(result) => { + return interaction.reply.send(result); + }, + edit: async(result) => { + return interaction.reply.edit(result); + } + }, await this.getSlashArgs(interaction.interaction.options || []), await this.getSlashArgsObject(interaction.interaction.options || [])) + } catch(e) { + this.GCommandsClient.emit(Events.DEBUG, new Color('&d[GCommands Debug] &3' + e).getText()) + } + + this.GCommandsClient.emit(Events.DEBUG, new Color('&d[GCommands Debug] &3User &a' + interaction.member.user.id + '&3 used &a' + interaction.interaction.name).getText()) + }catch(e) { + this.GCommandsClient.emit(Events.DEBUG, e); + + if(!this.unkownCommandMessage) return; + if(this.client.languageFile.UNKNOWN_COMMAND[guildLanguage]) { + this.client.api.interactions(interaction.id, interaction.token).callback.post({ + data: { + type: 4, + data: { + content: this.client.languageFile.UNKNOWN_COMMAND[guildLanguage].replace('{COMMAND}',interaction.data.name) + } + } + }); + } + } + }) + } + } + + /** + * Internal method to loadMoreEvents + * @returns {void} + * @private + */ + async loadMoreEvents() { + await readdirSync(`${__dirname}/../base/actions/`).forEach(file => { + require(`../base/actions/${file}`)(this.client) + }) + } + + /** + * Internal method to getSlashArgs + * @returns {Array} + * @private + */ + getSlashArgs(options) { + let args = []; + + let check = (option) => { + if (!option) return; + if (option.value) args.push(option.value); + else args.push(option.name); + + if (option.options) { + for (let o = 0; o < option.options.length; o++) { + check(option.options[o]); + } + } + } + + if (Array.isArray(options)) { + for (let o = 0; o < options.length; o++) { + check(options[o]); + } + } else { + check(options); + } + + return args; + } + + /** + * Internal method to getSlashArgsObject + * @returns {object} + * @private + */ + getSlashArgsObject(options) { + let args = {}; + + for (let o of options) { + if (o.type == 1) args[o.name] = this.getSlashArgsObject(o.options || []); + else if (o.type == 2) args[o.name] = this.getSlashArgsObject(o.options || []); + else { + args[o.name] = o.value; + } + } + + return args; + } +} + +module.exports = GEventHandling; \ No newline at end of file diff --git a/src/managers/GEventLoader.js b/src/managers/GEventLoader.js index ddffc9c31..4fe3011ba 100644 --- a/src/managers/GEventLoader.js +++ b/src/managers/GEventLoader.js @@ -1,720 +1,48 @@ -const { default: axios } = require("axios"); -const {Collection,MessageEmbed, Message} = require("discord.js"); -const Color = require("../structures/Color"), { Events } = require("../util/Constants") -const GMessage = require("../structures/GMessage"); -const ifDjsV13 = require("../util/updater").checkDjsVersion("13") +// ONLY FOR DOCS +const GEvents = require('@gcommands/events') /** - * The GCommandsEventLoader class -*/ -class GEventLoader { - - /** - * Creates new GCommandsEventLoader instance - * @param {GCommandsClient} GCommandsClient - */ - constructor(GCommandsClient) { - /** - * GCommandsEventLoader options - * @property {Object} GCommandsClient - */ - this.GCommandsClient = GCommandsClient; - - this.client = GCommandsClient.client; - - this.messageEvent() - this.slashEvent() - this.loadMoreEvents() - } - - /** - * Internal method to messageEvent - * @returns {void} - * @private - */ - async messageEvent() { - if(this.client == undefined) return; - if((this.client.slash == false) || (this.client.slash == "both")) { - this.client.on('message', async(message) => { - messageEventUse(message) - }) - - this.client.on('messageUpdate', async(oldMessage, newMessage) => { - if(oldMessage.content == newMessage.content || oldMessage.embeds == newMessage.embeds) return; - messageEventUse(newMessage) - }) - } - - let messageEventUse = async(message) => { - if(!message) return; - if (!message.author || message.author.bot) return; - if (!message.guild) return; - - let mentionRegex = new RegExp(`^<@!?(${this.client.user.id})> `) - - let clientDefaultPrefix; - if(Array.isArray(this.client.prefix)) { - this.client.prefix.some(pf => { - if(message.content.startsWith(pf)) { - clientDefaultPrefix = pf; - } - }) - } else clientDefaultPrefix = this.client.prefix - - let prefix = message.content.match(mentionRegex) ? message.content.match(mentionRegex)[0] : clientDefaultPrefix - - if(this.client.database) { - let guildDefaultPrefix; - if(Array.isArray(message.guild.prefix)) { - message.guild.prefix.some(pf => { - if(message.content.startsWith(pf)) { - guildDefaultPrefix = pf; - } - }) - } else guildDefaultPrefix = message.guild.prefix - - let guildSettings = guildDefaultPrefix || clientDefaultPrefix; - prefix = message.content.match(mentionRegex) ? message.content.match(mentionRegex)[0] : guildSettings - } - - if (!message.content.startsWith(prefix)) return; - - const args = message.content.slice(prefix.length).trim().split(/ +/g); - const cmd = args.shift().toLowerCase(); - - if (cmd.length === 0) return; - - try { - let commandos = this.client.gcommands.get(cmd); - if(!commandos) commandos = this.client.gcommands.get(this.client.galiases.get(cmd)); - if(!commandos) return; - if(commandos.slash == true || commandos.slash == "true") return; - - let member = message.member, guild = message.guild, channel = message.channel - let inhibit = await this.inhibit(commandos, { - message, member, guild, channel, - /** - * Respond - * @type {Interface} - * @param {RespondOptions} result - * @returns {Object} - */ - respond: async(options = undefined) => { - if(this.client.autoTyping) channel.startTyping(this.client.autoTyping); - - if(ifDjsV13) options.client = this.client, options.channel = message.channel - if(typeof options == "object" && options.content) { - msg = await ifDjsV13 ? GMessage.buttons(options.content, options) : message.buttons(options.content, options) - } else if(typeof options == "object" && !options.content) { - if(ifDjsV13) options.client = this.client, options.channel = message.channel - if(options.inlineReply) msg = await ifDjsV13 ? GMessage.inlineReply(options) : message.inlineReply(options) - else msg = await message.channel.send(options) - } else { - if(options.inlineReply) msg = await ifDjsV13 ? GMessage.inlineReply({content:options}) : message.inlineReply({content:options}); - else msg = await message.channel.send(options) - } - - msg = msg.toJSON() - msg.client = this.client; - msg.createButtonCollector = function createButtonCollector(filter, options) {return client.dispatcher.createButtonCollector(msg, filter, options)} - msg.awaitButtons = function awaitButtons(filter, options) {return client.dispatcher.awaitButtons(msg, filter, options)} - msg.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return client.dispatcher.createSelectMenuCollector(msg, filter, options)}; - msg.awaitSelectMenus = function awaitSelectMenus(filter, options) {return client.dispatcher.awaitSelectMenus(msg, filter, options)}; - - if(this.client.autoTyping) channel.stopTyping(true); - return msg; - }, - edit: async(options = undefined) => { - if(ifDjsV13) options.client = this.client, options.channel = message.channel - if(typeof options == "object" && options.content) { - msg = await ifDjsV13 ? GMessage.buttonsEdit(msg.id, options.content, options) : message.buttonsEdit(msg.id, options.content, options) - } else { - msg = ifDjsV13 ? GMessage.buttonsEdit(msg.id, options, []) : message.buttonsEdit(msg.id, options, []) - } - - msg = await msg; - msg = msg.toJSON() - msg.client = this.client; - msg.createButtonCollector = function createButtonCollector(filter, options) {return client.dispatcher.createButtonCollector(msg, filter, options)} - msg.awaitButtons = function awaitButtons(filter, options) {return client.dispatcher.awaitButtons(msg, filter, options)} - msg.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return client.dispatcher.createSelectMenuCollector(msg, filter, options)}; - msg.awaitSelectMenus = function awaitSelectMenus(filter, options) {return client.dispatcher.awaitSelectMenus(msg, filter, options)}; - - return msg; - } - }) - if(inhibit == false) return; - - let guildLanguage = await this.client.dispatcher.getGuildLanguage(message.guild.id); - let cooldown = await this.client.dispatcher.getCooldown(message.guild.id, message.author.id, commandos) - if(cooldown.cooldown) return message.inlineReply(this.client.languageFile.COOLDOWN[guildLanguage].replace(/{COOLDOWN}/g, cooldown.wait).replace(/{CMDNAME}/g, commandos.name)) - - if(commandos.nsfw) { - if(!message.channel.nsfw) { - return message.inlineReply(this.client.languageFile.NSFW[guildLanguage]) - } - } - - if(commandos.guildOnly) { - if(message.guild.id != commandos.guildOnly) { - return; - } - } - - if(commandos.userOnly) { - if(typeof commandos.userOnly == "object") { - let users = commandos.userOnly.some(v => message.author.id == v) - if(!users) { - return - } - } else { - if(message.author.id != commandos.userOnly) { - return; - } - } - } - - if(commandos.channelOnly) { - if(typeof commandos.channelOnly == "object") { - let channels = commandos.channelOnly.some(v => message.channel.id == v) - if(!channels) { - return - } - } else { - if(message.channel.id != commandos.channelOnly) { - return; - } - } - } - - if(commandos.clientRequiredPermissions) { - if(!Array.isArray(commandos.clientRequiredPermissions)) commandos.clientRequiredPermissions = [commandos.clientRequiredPermissions] - if(message.channel.permissionsFor(message.guild.me).missing(commandos.clientRequiredPermissions).length > 0) { - message.channel.send(this.client.languageFile.MISSING_CLIENT_PERMISSIONS[guildLanguage].replace("{PERMISSION}",commandos.clientRequiredPermissions.map(v => v.split(" ").map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(" ")).join(", "))) - return; - } - } - - if(commandos.userRequiredPermissions) { - if(!Array.isArray(commandos.userRequiredPermissions)) commandos.userRequiredPermissions = [commandos.userRequiredPermissions] - if(this.client.discordjsversion.includes("12.")) { - if(!message.member.hasPermission(commandos.userRequiredPermissions)) { - message.channel.send(this.client.languageFile.MISSING_PERMISSIONS[guildLanguage].replace("{PERMISSION}",commandos.userRequiredPermissions.map(v => v.split(" ").map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(" ")).join(", "))) - return; - } - } else { - if(!message.member.permissions.has(commandos.userRequiredPermissions)) { - message.channel.send(this.client.languageFile.MISSING_PERMISSIONS[guildLanguage].replace("{PERMISSION}",commandos.userRequiredPermissions.map(v => v.split(" ").map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(" ")).join(", "))) - return; - } - } - } - - if(commandos.userRequiredRoles) { - if(!Array.isArray(commandos.userRequiredRoles)) commandos.userRequiredRoles = [commandos.userRequiredRoles] - - let roles = commandos.userRequiredRoles.some(v => message.member._roles.includes(v)) - if(!roles) { - message.channel.send(this.client.languageFile.MISSING_ROLES[guildLanguage].replace("{ROLES}", `\`${commandos.userRequiredRoles.map(r => message.guild.roles.cache.get(r).name).join(", ")}\``)) - return; - } - } - - if(commandos.userRequiredRole) { - if(!Array.isArray(commandos.userRequiredRole)) commandos.userRequiredRole = [commandos.userRequiredRole] - - let roles = commandos.userRequiredRole.some(v => message.member._roles.includes(v)) - if(!roles) { - message.channel.send(this.client.languageFile.MISSING_ROLES[guildLanguage].replace("{ROLES}", `\`${commandos.userRequiredRoles.map(r => message.guild.roles.cache.get(r).name).join(", ")}\``)) - return; - } - } - - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands Debug] &3User &a" + message.author.id + "&3 used &a" + cmd).getText()) - - const client = this.client, bot = this.client - var msg = ""; - commandos.run({ - client, bot, message, member, guild, channel, - /** - * Respond - * @type {Interface} - * @param {RespondOptions} result - * @returns {Object} - */ - respond: async(options = undefined) => { - if(this.client.autoTyping) channel.startTyping(this.client.autoTyping); - - if(ifDjsV13) options.client = this.client, options.channel = message.channel - if(typeof options == "object" && options.content) { - msg = await ifDjsV13 ? GMessage.buttons(options.content, options) : message.buttons(options.content, options) - } else if(typeof options == "object" && !options.content) { - if(ifDjsV13) options.client = this.client, options.channel = message.channel - if(options.inlineReply) msg = await ifDjsV13 ? GMessage.inlineReply(options) : message.inlineReply(options) - else msg = await message.channel.send(options) - } else { - if(options.inlineReply) msg = await ifDjsV13 ? GMessage.inlineReply({content:options}) : message.inlineReply({content:options}); - else msg = await message.channel.send(options) - } - - msg = await msg; - msg = msg.toJSON() - msg.client = this.client; - msg.createButtonCollector = function createButtonCollector(filter, options) {return client.dispatcher.createButtonCollector(msg, filter, options)} - msg.awaitButtons = function awaitButtons(filter, options) {return client.dispatcher.awaitButtons(msg, filter, options)} - msg.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return client.dispatcher.createSelectMenuCollector(msg, filter, options)}; - msg.awaitSelectMenus = function awaitSelectMenus(filter, options) {return client.dispatcher.awaitSelectMenus(msg, filter, options)}; - - if(this.client.autoTyping) channel.stopTyping(true); - return msg; - }, - edit: async(options = undefined) => { - if(ifDjsV13) options.client = this.client, options.channel = message.channel - if(typeof options == "object" && options.content) { - msg = await ifDjsV13 ? GMessage.buttonsEdit(msg.id, options.content, options) : message.buttonsEdit(msg.id, options.content, options) - } else { - msg = ifDjsV13 ? GMessage.buttonsEdit(msg.id, options, []) : message.buttonsEdit(msg.id, options, []) - } - - msg = await msg; - msg = msg.toJSON() - msg.client = this.client; - msg.createButtonCollector = function createButtonCollector(filter, options) {return client.dispatcher.createButtonCollector(msg, filter, options)} - msg.awaitButtons = function awaitButtons(filter, options) {return client.dispatcher.awaitButtons(msg, filter, options)} - msg.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return client.dispatcher.createSelectMenuCollector(msg, filter, options)}; - msg.awaitSelectMenus = function awaitSelectMenus(filter, options) {return client.dispatcher.awaitSelectMenus(msg, filter, options)}; - - return msg; - } - }, args, args) - } catch(e) { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands Debug] &3" + e).getText()) - if(!this.GCommandsClient.unkownCommandMessage) return; - if(this.client.languageFile.UNKNOWN_COMMAND[this.client.language]) { - message.channel.send(this.client.languageFile.UNKNOWN_COMMAND[guildLanguage].replace("{COMMAND}",cmd)); - } - } - } - } + * The GEventLoader class + */ +class GEventLoader extends null {} - /** - * Internal method to slashEvent - * @returns {void} - * @private - */ - async slashEvent() { - if(this.client == undefined) return; - if((this.client.slash) || (this.client.slash == "both")) { - this.client.ws.on('INTERACTION_CREATE', async (interaction) => { - if(interaction.type != 2) return; - - if(this.client == undefined) return; - try { - let commandos = this.client.gcommands.get(interaction.data.name); - if(!commandos) return; - if(commandos.slash == false || commandos.slash == "false") return; - if (!this.client.cooldowns.has(commandos.name)) { - this.client.cooldowns.set(commandos.name, new Collection()); - } - - let guild = await this.client.guilds.cache.get(interaction.guild_id) - let member = guild.members.cache.get(interaction.member.user.id); - - let inhibit = await this.inhibit(commandos, { - interaction, member, - guild: guild, - channel: guild.channels.cache.get(interaction.channel_id), - respond: async(result) => { - return this.slashRespond(guild.channels.cache.get(interaction.channel_id), interaction, result); - }, - edit: async(result) => { - return this.slashEdit(interaction, result); - } - }) - if(inhibit == false) return; - - let guildLanguage = await this.client.dispatcher.getGuildLanguage(member.guild.id); - let cooldown = await this.client.dispatcher.getCooldown(member.guild.id, member.user.id, commandos) - if(cooldown.cooldown) { - return this.client.api.interactions(interaction.id, interaction.token).callback.post({ - data: { - type: 4, - data: { - flags: 64, - content: this.client.languageFile.COOLDOWN[guildLanguage].replace(/{COOLDOWN}/g, cooldown.wait).replace(/{CMDNAME}/g, commandos.name) - } - } - }); - } - - if(commandos.nsfw) { - if(!member.guild.channels.cache.get(interaction.channel_id).nsfw) { - return this.client.api.interactions(interaction.id, interaction.token).callback.post({ - data: { - type: 4, - data: { - flags: 64, - content: this.client.languageFile.NSFW[guildLanguage] - } - } - }); - } - } - - if(commandos.userOnly) { - if(typeof commandos.userOnly == "object") { - let users = commandos.userOnly.some(v => interaction.member.user.id == v) - if(!users) { - return; - } - } else { - if(interaction.member.user.id != commandos.userOnly) { - return; - } - } - } - - if(commandos.channelOnly) { - if(typeof commandos.channelOnly == "object") { - let users = commandos.channelOnly.some(v => interaction.channel_id == v) - if(!users) { - return; - } - } else { - if(interaction.channel_id != commandos.channelOnly) { - return; - } - } - } - - if(commandos.clientRequiredPermissions) { - if(!Array.isArray(commandos.clientRequiredPermissions)) commandos.clientRequiredPermissions = [commandos.clientRequiredPermissions] - if(member.guild.channels.cache.get(interaction.channel_id).permissionsFor(member.guild.me).missing(commandos.clientRequiredPermissions).length > 0) { - this.client.api.interactions(interaction.id, interaction.token).callback.post({ - data: { - type: 4, - data: { - flags: 64, - content: this.client.languageFile.MISSING_CLIENT_PERMISSIONS[guildLanguage].replace("{PERMISSION}",commandos.clientRequiredPermissions.map(v => v.split(" ").map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(" ")).join(", ")) - } - } - }); - return; - } - } - - if(commandos.userRequiredPermissions) { - if(!Array.isArray(commandos.userRequiredPermissions)) commandos.userRequiredPermissions = [commandos.userRequiredPermissions] - if(this.client.discordjsversion.includes("12.")) { - if(!this.client.guilds.cache.get(interaction.guild_id).members.cache.get(interaction.member.user.id).hasPermission(commandos.userRequiredPermissions)) { - this.client.api.interactions(interaction.id, interaction.token).callback.post({ - data: { - type: 4, - data: { - flags: 64, - content: this.client.languageFile.MISSING_PERMISSIONS[guildLanguage].replace("{PERMISSION}",commandos.userRequiredPermissions.map(v => v.split(" ").map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(" ")).join(", ")) - } - } - }); - return; - } - } else { - if(!this.client.guilds.cache.get(interaction.guild_id).members.cache.get(interaction.member.user.id).permissions.has(commandos.userRequiredPermissions)) { - this.client.api.interactions(interaction.id, interaction.token).callback.post({ - data: { - type: 4, - data: { - flags: 64, - content: this.client.languageFile.MISSING_PERMISSIONS[guildLanguage].replace("{PERMISSION}",commandos.userRequiredPermissions.map(v => v.split(" ").map(vv => vv[0].toUpperCase() + vv.slice(1).toLowerCase()).join(" ")).join(", ")) - } - } - }); - return; - } - } - } - - if(commandos.userRequiredRoles) { - if(!Array.isArray(commandos.userRequiredRoles)) commandos.userRequiredRoles = [commandos.userRequiredRoles] - - let roles = commandos.userRequiredRoles.some(v => interaction.member.roles.includes(v)) - if(!roles) { - this.client.api.interactions(interaction.id, interaction.token).callback.post({ - data: { - type: 4, - data: { - flags: 64, - content: this.client.languageFile.MISSING_ROLES[guildLanguage].replace("{ROLES}", `\`${commandos.userRequiredRoles.map(r => member.guild.roles.cache.get(r).name).join(", ")}\``), - } - } - }); - return; - } - } - - if(commandos.userRequiredRole) { - if(!Array.isArray(commandos.userRequiredRole)) commandos.userRequiredRole = [commandos.userRequiredRole] - - let roles = commandos.userRequiredRole.some(v => interaction.member.roles.includes(v)) - if(!roles) { - this.client.api.interactions(interaction.id, interaction.token).callback.post({ - data: { - type: 4, - data: { - flags: 64, - content: this.client.languageFile.MISSING_ROLES[guildLanguage].replace("{ROLES}", `\`${commandos.userRequiredRoles.map(r => member.guild.roles.cache.get(r).name).join(", ")}\``), - } - } - }); - return; - } - } - - try { - - /** - * Return system for slash - * @name ReturnSystem - * @param {DiscordClient} client - * @param {Object} interaction - * @example - * return { - * content: "hi", - * ephemeral: true, - * allowedMentions: { parse: [], repliedUser: true } - * } - */ - - const client = this.client, bot = this.client, channel = member.guild.channels.cache.get(interaction.channel_id) - commandos.run({ - client, bot, interaction, member, channel, - guild: member.guild, - /** - * Respond - * @type {Interface} - * @param {RespondOptions} result - * @returns {Object} - */ - respond: async(result) => { - return this.slashRespond(channel, interaction, result); - }, - edit: async(result) => { - return this.slashEdit(interaction, result); - } - }, await this.getSlashArgs(interaction.data.options || []), await this.getSlashArgs2(interaction.data.options || [])) - } catch(e) { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands Debug] &3" + e).getText()) - } - - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands Debug] &3User &a" + interaction.member.user.id + "&3 used &a" + interaction.data.name).getText()) - }catch(e) { - this.GCommandsClient.emit(Events.DEBUG, new Color("&d[GCommands Debug] &3" + e).getText()) - if(!this.unkownCommandMessage) return; - if(this.client.languageFile.UNKNOWN_COMMAND[guildLanguage]) { - this.client.api.interactions(interaction.id, interaction.token).callback.post({ - data: { - type: 4, - data: { - content: this.client.languageFile.UNKNOWN_COMMAND[guildLanguage].replace("{COMMAND}",interaction.data.name) - } - } - }); - } - } - }) - } - } - - /** - * Internal method to loadMoreEvents - * @returns {void} - * @private - */ - async loadMoreEvents() { - require("../base/actions/channel")(this.client) - require("../base/actions/guild")(this.client) - require("../base/actions/guildmember")(this.client) - require("../base/actions/role")(this.client) - require("../base/actions/user")(this.client) - require("../base/actions/voiceupdate")(this.client) - require("../base/actions/interactions")(this.client) - } - - async slashRespond(channel, interaction, result) { - if(!result.ephemeral && this.client.autoTyping) channel.startTyping(this.client.autoTyping); - - var data = {} - - if(typeof result != "object") data.content = result; - if(typeof result == "object" && !result.content) data.embeds = [result]; - if(typeof result == "object" && typeof result.content != "object") data.content = result.content; - if(typeof result == "object" && typeof result.content == "object") data.embeds = [result.content]; - if(typeof result == "object" && result.allowedMentions) { data.allowedMentions = result.allowedMentions } else data.allowedMentions = { parse: [], repliedUser: true } - if(typeof result == "object" && result.ephemeral) { data.flags = 64 } - if(typeof result == "object" && result.components) { - if(!Array.isArray(result.components)) result.components = [result.components]; - data.components = result.components; - } - if(typeof result == "object" && result.embeds) { - if(!Array.isArray(result.embeds)) result.embeds = [result.embeds] - data.embeds = result.embeds; - } - if(result.embeds && !result.content) result.content = "\u200B" - - let finalFiles = []; - if(typeof result == "object" && result.attachments) { - if(!Array.isArray(result.attachments)) result.attachments = [result.attachments] - result.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) - } - - let apiMessage = (await this.client.api.interactions(interaction.id, interaction.token).callback.post({ - data: { - type: result.thinking ? 5 : 4, - data - }, - files: finalFiles - })) - - let apiMessageMsg = {}; - try { - apiMessageMsg = (await axios.get(`https://discord.com/api/v8/webhooks/${this.client.user.id}/${interaction.token}/messages/@original`)).data; - } catch(e) { - apiMessage = { - id: undefined - } - } - - if(typeof apiMessage != "object") apiMessage = apiMessage.toJSON(); - if(apiMessage) { - apiMessage = apiMessageMsg; - apiMessage.client = this.client ? this.client : client; - apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; - apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; - apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; - apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; - apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessageMsg.id].delete()}; - } - - if(!result.ephemeral && this.client.autoTyping) channel.stopTyping(true) - - if(ifDjsV13) return apiMessage.id ? new Message(this.client, apiMessage, channel) : apiMessage; - else return apiMessage.id ? new GMessage(this.client, apiMessage, channel) : apiMessage; - } - - async slashEdit(interaction, result) { - if (typeof result == "object") { - if(result.components) { - if(!Array.isArray(result.components)) result.components = [result.components]; - - result.components = result.components; - } else result.components = []; - - if(typeof result.content == "object") { - result.embeds = [result.content] - result.content = "\u200B" - } - if(typeof result == "object" && result.embeds) { - if(!Array.isArray(result.embeds)) result.embeds = [result.embeds]; - result.embeds = result.embeds; - } else result.embeds = [] - if(result.embeds && !result.content) result.content = "\u200B" - - let finalFiles = []; - if(typeof result == "object" && result.attachments) { - if(!Array.isArray(result.attachments)) result.attachments = [result.attachments] - result.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) - } - - let apiMessage = (await this.client.api.webhooks(this.client.user.id, interaction.token).messages[result.messageId ? result.messageId : "@original"].patch({ - data: { - content: result.content, - components: result.components, - embeds: result.embeds || [] - }, - files: finalFiles - })) +/** + * GCommandsClient + * @type {GCommands} +*/ +GEventLoader.GCommandsClient = GEvents.GEvents.GCommandsClient; - if(apiMessage) { - apiMessage.client = this.client; - apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; - apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; - apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; - apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; - apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessage.id].delete()}; - } +/** + * eventDir + * @type {String} + */ +GEventLoader.eventDir = GEvents.GEvents.eventDir; - if(ifDjsV13) return apiMessage.id ? new Message(this.client, apiMessage, channel) : apiMessage; - else return apiMessage.id ? new GMessage(this.client, apiMessage, channel) : apiMessage; - } +/** + * client + * @type {Client} + */ +GEventLoader.client = GEvents.GEvents.client; - return this.client.api.webhooks(this.client.user.id, interaction.token).messages["@original"].patch({ data: { content: result }}) - } +/** + * gevents + * @type {Collection} + */ +GEventLoader.client.gevents = GEvents.GEvents.gevents; - /** - * Internal method to getSlashArgs - * @returns {object} - */ - getSlashArgs(options) { - var args = []; - - let check = (option) => { - if (!option) return; - if (option.value) args.push(option.value); - else args.push(option.name); - - if (option.options) { - for (let o = 0; o < option.options.length; o++) { - check(option.options[o]); - } - } - } - - if (Array.isArray(options)) { - for (let o = 0; o < options.length; o++) { - check(options[o]); - } - } else { - check(options); - } - - return args; - } - getSlashArgs2(options) { - var args = {}; - for (let o of options) { - if (o.type == 1) args[o.name] = this.getSlashArgs2(o.options || []); - else if (o.type == 2) args[o.name] = this.getSlashArgs2(o.options || []); - else { - args[o.name] = o.value; - } - } - return args; - } +/** + * Internal method to loadEventsFiles + * @returns {void} + * @private +*/ +GEventLoader.__loadEventFiles = () => GEvents.GEvents.__loadEventFiles; - /** - * Internal method to inhivit - * @returns {object} - */ - async inhibit(cmd, data) { - for(const inhibitor of this.client.inhibitors) { - let inhibit = inhibitor(cmd, data); - return inhibit; - } - return null; - } -} +/** + * Internal method to loadEvents + * @returns {void} + * @private +*/ +GEventLoader.__loadEvents = () => GEvents.GEvents.__loadEvents; module.exports = GEventLoader; \ No newline at end of file diff --git a/src/structures/Color.js b/src/structures/Color.js index 56e7bfe76..306062852 100644 --- a/src/structures/Color.js +++ b/src/structures/Color.js @@ -1,3 +1,5 @@ +const { resolveString } = require('../util/util'); + /** * The Color class */ @@ -8,43 +10,34 @@ class Color { * @param {string} text * @param {ColorOptions} options */ - constructor(text = "", options = {}) { + constructor(text = '', options = {}) { /** - * Color options - * @param {string} text - * @param {ColorOptions} json - * @type {ColorOptions} + * text + * @type {String} */ - this.text = text; + this.text = resolveString(text); + /** + * json + * @type {Object} + */ this.json = options.json; - if(typeof this.text == "object") { - this.text2 = ""; - var i; - for (i = 0; i < this.text.length; i++) { - this.text2 += text[i] + "\n" - } - } - - if(this.text2) { - this.text = this.text2 - } this.text = this.text // COLORS - .replace(/&c/g, "\x1b[31m") - .replace(/&a/g, "\x1b[32m") - .replace(/&e/g, "\x1b[33m") - .replace(/&b/g, "\x1b[34m") - .replace(/&d/g, "\x1b[35m") - .replace(/&3/g, "\x1b[36m") - .replace(/&f/g, "\x1b[37m") + .replace(/&c/g, '\x1b[31m') + .replace(/&a/g, '\x1b[32m') + .replace(/&e/g, '\x1b[33m') + .replace(/&b/g, '\x1b[34m') + .replace(/&d/g, '\x1b[35m') + .replace(/&3/g, '\x1b[36m') + .replace(/&f/g, '\x1b[37m') // OTHER - .replace(/&r/g, "\x1b[0m") - .replace(/&n/g, "\x1b[4m") - .replace(/&p/g, "\x1b[7m") + .replace(/&r/g, '\x1b[0m') + .replace(/&n/g, '\x1b[4m') + .replace(/&p/g, '\x1b[7m') } @@ -55,9 +48,9 @@ class Color { */ getText() { if(this.json) { - return {text:this.text + "\x1b[0m"} + return {text:this.text + '\x1b[0m'} } - return this.text + "\x1b[0m"; + return this.text + '\x1b[0m'; } /** @@ -66,7 +59,7 @@ class Color { * @returns {string} */ getRGB() { - var get = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(this.text); + let get = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(this.text); if(this.json) { return { @@ -78,6 +71,6 @@ class Color { return `r: ${parseInt(get[1], 16)}, g: ${parseInt(get[2], 16)}, b: ${parseInt(get[3], 16)}` } -}; +} module.exports = Color; \ No newline at end of file diff --git a/src/structures/DMChannel.js b/src/structures/DMChannel.js index db156dcae..dec1b19c4 100644 --- a/src/structures/DMChannel.js +++ b/src/structures/DMChannel.js @@ -1,89 +1,64 @@ -const { Structures, MessageEmbed, DMChannel } = require("discord.js"); +const { DMChannel } = require('discord.js'); const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector') -const updater = require("../util/updater") +const GPayload = require('./GPayload'); +const ifDjsV13 = (require('../util/updater')).checkDjsVersion('13'); -if(!updater.checkDjsVersion("13")) { - module.exports = Structures.extend("DMChannel", DMChannel => { - class GDMChannel extends DMChannel { - constructor(...args) { - super(...args) - } +module.exports = Object.defineProperties(DMChannel.prototype, { + send: { + value: async function(result) { + let GPayloadResult = await GPayload.create(this, result) + .resolveData() + .resolveFiles(); - async send(result) { - var data = {} - - if(typeof result != "object") data.content = result; - if(typeof result == "object" && !result.content) data.embeds = [result]; - if(typeof result == "object" && typeof result.content != "object") data.content = result.content; - if(typeof result == "object" && typeof result.content == "object") data.embeds = [result.content]; - if(typeof result == "object" && result.allowedMentions) { data.allowedMentions = result.allowedMentions } else data.allowedMentions = { parse: [], repliedUser: true } - if(typeof result == "object" && result.ephemeral) { data.flags = 64 } - if(typeof result == "object" && result.components) { - if(!Array.isArray(result.components)) result.components = [result.components]; - data.components = result.components; - } - if(typeof result == "object" && result.embeds) { - if(!Array.isArray(result.embeds)) result.embeds = [result.embeds] - data.embeds = result.embeds; - } - if(result.embeds && !result.content) result.content = "\u200B" - - let finalFiles = []; - if(typeof result == "object" && result.attachments) { - if(!Array.isArray(result.attachments)) result.attachments = [result.attachments] - result.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) - } + return this.client.api.channels[this.id].messages.post({ + data: GPayloadResult.data, + files: GPayloadResult.files + }) + .then(d => this.client.actions.MessageCreate.handle(d).message); + } + }, - return this.client.api.channels[this.id].messages.post({ - data, - files: finalFiles - }) - .then(d => this.client.actions.MessageCreate.handle(d).message); - } + createButtonCollector: { + value: async function (msg, filter, options = {}) { + if(ifDjsV13) return new ButtonCollectorV13(msg, filter, options); + else return new ButtonCollectorV12(msg, filter, options); + } + }, - createButtonCollector(msg, filter, options = {}) { - if(updater.checkDjsVersion("13")) return new ButtonCollectorV13(msg, filter, options); - else return new ButtonCollectorV12(msg, filter, options); - } - - awaitButtons(msg, filter, options = {}) { - return new Promise((resolve, reject) => { - const collector = this.createButtonCollector(msg, filter, options); - collector.once('end', (buttons, reason) => { - if (options.errors && options.errors.includes(reason)) { - reject(buttons); - } else { - resolve(buttons); - } - }); - }) - } + awaitButtons: { + value: async function(msg, filter, options = {}) { + return new Promise((resolve, reject) => { + const collector = this.createButtonCollector(msg, filter, options); + collector.once('end', (buttons, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(buttons); + } else { + resolve(buttons); + } + }); + }) + } + }, - createSelectMenuCollector(msg, filter, options = {}) { - if(updater.checkDjsVersion("13")) return new SelectMenuCollectorV13(msg, filter, options); - else return new SelectMenuCollectorV12(msg, filter, options); - } - - awaitSelectMenus(msg, filter, options = {}) { - return new Promise((resolve, reject) => { - const collector = this.createSelectMenuCollector(msg, filter, options); - collector.once('end', (buttons, reason) => { - if (options.errors && options.errors.includes(reason)) { - reject(buttons); - } else { - resolve(buttons); - } - }); - }) - } + createSelectMenuCollector: { + value: async function(msg, filter, options = {}) { + if(ifDjsV13) return new SelectMenuCollectorV13(msg, filter, options); + else return new SelectMenuCollectorV12(msg, filter, options); } + }, - return GDMChannel; - }) -} else module.exports = DMChannel; \ No newline at end of file + awaitSelectMenus: { + value: async function(msg, filter, options = {}) { + return new Promise((resolve, reject) => { + const collector = this.createSelectMenuCollector(msg, filter, options); + collector.once('end', (buttons, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(buttons); + } else { + resolve(buttons); + } + }); + }) + } + } +}); \ No newline at end of file diff --git a/src/structures/Event.js b/src/structures/Event.js new file mode 100644 index 000000000..f1b54ced8 --- /dev/null +++ b/src/structures/Event.js @@ -0,0 +1,34 @@ +// ONLY FOR DOCS +const GEvents = require('@gcommands/events') + +/** + * The Event class + */ +class Event extends null {} + +/** + * Name + * @type {String} + */ +Event.name = GEvents.Event.name; + +/** + * once + * @type {Boolean} + */ +Event.once = GEvents.Event.once; + +/** + * ws + * @type {Boolean} + */ +Event.ws = GEvents.Event.ws; + +/** + * run function + * @param {Client} + * @param {String} + */ +Event.run = () => GEvents.Event.run; + +module.exports = Event; \ No newline at end of file diff --git a/src/structures/GGuild.js b/src/structures/GGuild.js index 5205c4936..d856f94e3 100644 --- a/src/structures/GGuild.js +++ b/src/structures/GGuild.js @@ -1,53 +1,25 @@ -const { Structures, Guild } = require("discord.js") -const updater = require("../util/updater") +const { Guild } = require('discord.js'); -if(!updater.checkDjsVersion("13")) { - module.exports = Structures.extend('Guild', Guild => { - /** - * The GuildStructure structure - * @extends Guild - */ - - class GCommandsGuild extends Guild { - constructor(...args) { - super(...args) - } - - /** - * Method to getCommandPrefix - * @returns {Promise} - */ - async getCommandPrefix(cache = true) { - return this.client.dispatcher.getGuildPrefix(this.id, cache); - } - - /** - * Method to setCommandPrefix - * @returns {void} - */ - async setCommandPrefix(prefix) { - this.client.dispatcher.setGuildPrefix(this.id, prefix); - this.client.emit('commandPrefixChange', this, this.prefix); - } - - /** - * Method to getLanguage - * @returns {Promise} - */ - async getLanguage(cache = true) { - return this.client.dispatcher.getGuildLanguage(this.id, cache); - } - - /** - * Method to setLanguage - * @returns {void} - */ - async setLanguage(lang) { - this.client.dispatcher.setGuildLanguage(this.id, lang); - this.client.emit('guildLanguageChange', this, this.language); - } +module.exports = Object.defineProperties(Guild.prototype, { + getCommandPrefix: { + value: async function(cache = true) { + return this.client.dispatcher.getGuildPrefix(this.id, cache) + } + }, + setCommandPrefix: { + value: async function(prefix) { + this.client.dispatcher.setGuildPrefix(this.id, prefix); + this.client.emit('commandPrefixChange', this, prefix); } + }, - return GCommandsGuild; - }) -} else module.exports = Guild; \ No newline at end of file + getLanguage: { + value: async function(cache = true) { return this.client.dispatcher.getGuildLanguage(this.id, cache) } + }, + setLanguage: { + value: async function(lang) { + this.client.dispatcher.setGuildLanguage(this.id, lang); + this.client.emit('guildLanguageChange', this, lang); + } + } +}); \ No newline at end of file diff --git a/src/structures/GInteraction.js b/src/structures/GInteraction.js new file mode 100644 index 000000000..5424e4bf3 --- /dev/null +++ b/src/structures/GInteraction.js @@ -0,0 +1,303 @@ +const { Message } = require('discord.js'); +const Color = require('../structures/Color'); +const GPayload = require('./GPayload'); +const axios = require('axios'); + +/** + * The GInteraction class + */ +class GInteraction { + + /** + * Creates new GInteraction instance + * @param {Client} client + * @param {Object} data + */ + constructor(client, data) { + + /** + * client + * @type {Client} + */ + this.client = client; + + /** + * type + * @type {number} + */ + this.type = data.type; + + /** + * token + * @type {string} + */ + this.token = data.token; + + /** + * discordId + * @type {number} + */ + this.discordId = data.id; + + /** + * version + * @type {number} + */ + this.version = data.version; + + /** + * applicationId + * @type {number} + */ + this.applicationId = data.application_id; + + /** + * guild + * @type {Guild} + */ + this.guild = data.guild_id ? this.client.guilds.cache.get(data.guild_id) : null; + + /** + * channel + * @type {TextChannel | NewsChannel | DMChannel} + */ + this.channel = data.guild_id ? this.guild.channels.cache.get(data.channel_id) : client.channels.cache.get(data.channel_id) + + /** + * createdTimestamp + * @type {Number} + */ + this.createdTimestamp = Date.now(); + + /** + * author + * @type {User} + */ + this.author = this.client.users.cache.get(data.guild_id ? data.member.user.id : data.user.id); + + /** + * member + * @type {GuildMember | null} + */ + this.member = data.guild_id ? this.guild.members.cache.get(data.member.user.id) : null; + + /** + * interaction + * @type {Object} + */ + this.interaction = { + name: data.data.name, + options: data.data.options, + id: data.data.id + } + + /** + * replied + * @type {boolean} + */ + this.replied = false; + + /** + * deferred + * @type {boolean} + */ + this.deferred = false; + + return this; + } + + /** + * Method to defer + * @param {Boolean} ephemeral + */ + async defer(ephemeral) { + if (this.deferred || this.replied) return console.log(new Color('&d[GCommands] &cThis button already has a reply').getText()); + await this.client.api.interactions(this.discordId, this.token).callback.post({ + data: { + type: 6, + data: { + flags: ephemeral ? 1 << 6 : null, + }, + }, + }); + this.deferred = true; + } + + /** + * Method to think + * @param {Boolean} ephemeral + */ + async think(ephemeral) { + if (this.deferred || this.replied) return console.log(new Color('&d[GCommands] &cThis button already has a reply').getText()); + await this.client.api.interactions(this.discordId, this.token).callback.post({ + data: { + type: 5, + data: { + flags: ephemeral ? 1 << 6 : null, + }, + }, + }); + this.deferred = true; + } + + /** + * Method to edit + * @param {Object} options + */ + async edit(result) { + if(result.autoDefer == true) { + await this.client.api.interactions(this.discordId, this.token).callback.post({ + data: { + type: 6, + }, + }); + } + + this.slashEdit(result) + } + + /** + * Method to update + * @param {Object} options + */ + async update(result) { + if(result.autoDefer == true) { + await this.client.api.interactions(this.discordId, this.token).callback.post({ + data: { + type: 6, + }, + }); + } + + this.slashEdit(result, true) + } + + /** + * Method to reply + * @type {get} + */ + get reply() { + /** + * Method to replySend + * @param {Object} options + */ + let _send = async(result) => { + this.replied = true; + return this.slashRespond(result) + } + + /** + * Method to replyEdit + * @param {Object} options + */ + let _edit = async(result) => { + if(!this.replied) return console.log(new Color('&d[GCommands] &cThis button has no reply.').getText()) + return this.slashEdit(result) + } + + /** + * Method to replyUpdate + * @param {Object} options + */ + let _update = async(result) => { + if(!this.replied) return console.log(new Color('&d[GCommands] &cThis button has no reply.').getText()) + return this.slashEdit(result, true) + } + + /** + * Method to replyFetch + * @param {Object} options + */ + let _fetch = async() => { + if(!this.replied) return console.log(new Color('&d[GCommands] &cThis button has no reply.').getText()) + let apiMessage = (await this.client.api.webhooks(this.client.user.id, this.token).messages['@original'].get()); + + if(apiMessage) { + apiMessage.client = this.client; + apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; + apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; + apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; + apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; + apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessage.id].delete()}; + } + + return new Message(this.client, data.message, this.channel) + } + + return { + send: _send, + edit: _edit, + update: _update, + fetch: _fetch + } + } + + async slashRespond(result) { + let GPayloadResult = await GPayload.create(this.channel, result) + .resolveData() + .resolveFiles(); + + let apiMessage = (await this.client.api.interactions(this.discordId, this.token).callback.post({ + data: { + type: result.thinking ? 5 : 4, + data: GPayloadResult.data + }, + files: GPayloadResult.files + })) + + let apiMessageMsg = {}; + try { + apiMessageMsg = (await axios.get(`https://discord.com/api/v8/webhooks/${this.client.user.id}/${this.token}/messages/@original`)).data; + } catch(e) { + apiMessageMsg = { + id: undefined + } + } + + if(typeof apiMessage !== 'object') apiMessage = apiMessage.toJSON(); + if(apiMessage) { + apiMessage = apiMessageMsg; + apiMessage.client = this.client ? this.client : client; + apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; + apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; + apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; + apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; + apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessageMsg.id].delete()}; + } + + return apiMessage.id ? new Message(this.client, apiMessage, this.channel) : apiMessage; + } + + async slashEdit(result, update) { + let GPayloadResult = await GPayload.create(this.channel, result) + .resolveData(); + + let apiMessage = {} + if(update) { + apiMessage = this.client.api.interactions(this.discordId, this.token).callback.post({ + data: { + type: 7, + data: GPayloadResult.data + }, + }) + } else { + apiMessage = (await this.client.api.webhooks(this.client.user.id, this.token).messages[result.messageId ? result.messageId : '@original'].patch({ + data: GPayloadResult.data + })) + } + + if(typeof apiMessage !== 'object') apiMessage = apiMessage.toJSON(); + if(apiMessage) { + apiMessage.client = this.client ? this.client : client; + apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; + apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; + apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; + apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; + apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessageMsg.id].delete()}; + } + + return apiMessage.id ? new Message(this.client, apiMessage, this.channel) : apiMessage; + } +} + +module.exports = GInteraction; \ No newline at end of file diff --git a/src/structures/GMessage.js b/src/structures/GMessage.js index 2c5261b22..af3926e70 100644 --- a/src/structures/GMessage.js +++ b/src/structures/GMessage.js @@ -1,434 +1,123 @@ -const { APIMessage, Structures, Message } = require('discord.js'); -const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector') -const updater = require("../util/updater") +const { Message } = require('discord.js'); +const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector'); +const GPayload = require('./GPayload'); +const ifDjsV13 = (require('../util/updater')).checkDjsVersion('13'); -if(!updater.checkDjsVersion("13")) { - module.exports = Structures.extend("Message", Message => { - /** - * The MessageStructure structure - * @extends Message - */ - class GCommandsMessage extends Message { - constructor(...args) { - super(...args) - } - - /** - * Method to make buttons - * @param {String} content - * @param {Object} options - * @returns {Promise} - */ - async buttons(content, options) { - var embed = null; - if(typeof content == "object") { - embed = content; - content = "\u200B" - } - - - if(!options.allowedMentions) { - options.allowedMentions = { parse: [] }; - } - - if(options.components) { - if(!Array.isArray(options.components)) options.components = [options.components]; - options.components = options.components; - } - if(options.embeds) { - if(!Array.isArray(options.embeds)) options.embeds = [options.embeds]; - options.embeds = options.embeds; - } - - let finalFiles = []; - if(options.attachments) { - if(!Array.isArray(options.attachments)) options.attachments = [options.attachments] - options.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) - } - - if(options.inlineReply) { - options.message_reference = { - message_id: this.channel.lastMessageID - } - } +module.exports = Object.defineProperties(Message.prototype, { + /** + * Method to edit message + * @param {Object} options + */ + edit: { + value: async function(result) { + let GPayloadResult = await GPayload.create(this.channel, result) + .resolveData(); - return this.client.api.channels[this.channel.id].messages.post({ - data: { - allowed_mentions: options.allowedMentions, - content: content, - components: options.components, - options, - embed: embed || null - }, - files: finalFiles - }) - .then(d => this.client.actions.MessageCreate.handle(d).message); - } - - /** - * Method to buttonsEdit - * @param {String} content - * @param {Object} options - * @returns {Promise} - */ - async buttonsEdit(msgID, content, options) { - var embed = null; - if(typeof content == "object") { - embed = content; - content = "\u200B" - } - - if(!options.allowedMentions) { - options.allowedMentions = { parse: [] }; - } - - if(options.components) { - if(!Array.isArray(options.components)) options.components = [options.components]; - options.components = options.components; - } - if(options.embeds) { - if(!Array.isArray(options.embeds)) options.embeds = [options.embeds]; - options.embeds = options.embeds; - } - - let finalFiles = []; - if(options.attachments) { - if(!Array.isArray(options.attachments)) options.attachments = [options.attachments] - options.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) - } - - return this.client.api.channels[this.channel.id].messages[msgID].patch({ + if(result.edited == false) { + return this.client.api.channels(this.channel.id).messages[this.id].patch({ data: { - allowed_mentions: options.allowedMentions, - content: content, - components: options.components, - options, - embed: embed || null + type: 7, + data: GPayloadResult.data }, - files: finalFiles - }) - .then(d => this.client.actions.MessageCreate.handle(d).message); - } - - /** - * Method to edit message - * @param {Object} options - */ - async edit(result) { - if (typeof result == "object") { - var finalData = []; - - if(result.components && !Array.isArray(result.components)) result.components = [result.components]; - if(result.embeds && !Array.isArray(result.embeds)) result.embeds = [result.embeds] - - if(typeof result == "object" && !result.content) result.embeds = [result] - if(typeof result.content == "object") { - result.embeds = [result.content] - result.content = "\u200B" - } - - if(result.edited == false) { - return this.client.api.channels(this.channel.id).messages[this.id].patch({ - data: { - type: 7, - data: { - content: result.content, - components: result.components, - embeds: result.embeds - }, - }, - }).then(d => this.client.actions.MessageCreate.handle(d).message); - } - - let finalFiles = []; - if(typeof result == "object" && result.attachments) { - if(!Array.isArray(result.attachments)) result.attachments = [result.attachments] - result.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) - } - - return this.client.api.channels(this.channel.id).messages[result.messageId ? result.messageId : this.id].patch({ - data: { - content: result.content, - components: result.components || [], - embeds: result.embeds || [] - }, - files: finalFiles - }) - } else { - return this.client.api.channels(this.channel.id).messages[this.id].patch({ data: { - content: result, - }}) - } - } - - /** - * Method to update message - * @param {Object} options - */ - async update(result) { - if (typeof result == "object") { - var finalData = []; - - if(result.components && !Array.isArray(result.components)) result.components = [result.components]; - if(result.embeds && !Array.isArray(result.embeds)) result.embeds = [result.embeds] - - if(typeof result == "object" && !result.content) result.embeds = [result] - if(typeof result.content == "object") { - result.embeds = [result.content] - result.content = "\u200B" - } - - return this.client.api.channels(this.channel.id).messages[this.id].patch({ - data: { - type: 7, - data: { - content: result.content, - components: result.components, - embeds: result.embeds - }, - }, - }).then(d => this.client.actions.MessageCreate.handle(d).message); - } else { - return this.client.api.channels(this.channel.id).messages[this.id].patch({ - data: { - type: 7, - data: { - content: result, - } - } - }) - } - } - - /** - * Method to inlineReply - * @param {String} content - * @param {Object} options - * @returns {Promise} - */ - async inlineReply(content, options) { - const mentionRepliedUser = typeof ((options || content || {}).allowedMentions || {}).repliedUser === "undefined" ? true : ((options || content).allowedMentions).repliedUser; - delete ((options || content || {}).allowedMentions || {}).repliedUser; - - const apiMessage = content instanceof APIMessage ? content.resolveData() : APIMessage.create(this.channel, content, options).resolveData(); - Object.assign(apiMessage.data, { message_reference: { message_id: this.id } }); - - if (!apiMessage.data.allowed_mentions || Object.keys(apiMessage.data.allowed_mentions).length === 0) - apiMessage.data.allowed_mentions = { parse: ["users", "roles", "everyone"] }; - if (typeof apiMessage.data.allowed_mentions.replied_user === "undefined") - Object.assign(apiMessage.data.allowed_mentions, { replied_user: mentionRepliedUser }); - - if (Array.isArray(apiMessage.data.content)) { - return Promise.all(apiMessage.split().map(x => { - x.data.allowed_mentions = apiMessage.data.allowed_mentions; - return x; - }).map(this.inlineReply.bind(this))); - } - - const { data, files } = await apiMessage.resolveFiles(); - return this.client.api.channels[this.channel.id].messages - .post({ data, files }) - .then(d => this.client.actions.MessageCreate.handle(d).message); + }).then(d => this.client.actions.MessageCreate.handle(d).message); } - - createButtonCollector(filter, options = {}) { - if(updater.checkDjsVersion("13")) return new ButtonCollectorV13(this, filter, options); - else return new ButtonCollectorV12(this, filter, options); - } - - awaitButtons(filter, options = {}) { - return new Promise((resolve, reject) => { - const collector = this.createButtonCollector(this, filter, options); - collector.once('end', (buttons, reason) => { - if (options.errors && options.errors.includes(reason)) { - reject(buttons); - } else { - resolve(buttons); - } - }); - }) - } - - createSelectMenuCollector(filter, options = {}) { - if(updater.checkDjsVersion("13")) return new SelectMenuCollectorV13(this, filter, options); - else return new SelectMenuCollectorV12(this, filter, options); - } - - awaitSelectMenus(filter, options = {}) { - return new Promise((resolve, reject) => { - const collector = this.createSelectMenuCollector(this, filter, options); - collector.once('end', (buttons, reason) => { - if (options.errors && options.errors.includes(reason)) { - reject(buttons); - } else { - resolve(buttons); - } - }); - }) - } - } - - return GCommandsMessage; - }) -} else module.exports = { - /** - * Method to make buttons - * @param {String} content - * @param {Object} options - * @returns {Promise} - */ - buttons: async function(content, options) { - this.client = options.client, this.channel = options.channel - var embed = null; - if(typeof content == "object") { - embed = content; - content = "\u200B" - } + let apiMessage = (await this.client.api.channels(this.channel.id).messages[result.messageId ? result.messageId : this.id].patch({ + data: GPayloadResult.data + })) + + if(typeof apiMessage !== 'object') apiMessage = apiMessage.toJSON(); + if(apiMessage) { + apiMessage.client = this.client ? this.client : client; + apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; + apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; + apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; + apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; + apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessageMsg.id].delete()}; + } - if(!options.allowedMentions) { - options.allowedMentions = { parse: [] }; - } - - if(options.components) { - if(!Array.isArray(options.components)) options.components = [options.components]; - options.components = options.components; - } - if(options.embeds) { - if(!Array.isArray(options.embeds)) options.embeds = [options.embeds]; - options.embeds = options.embeds; - } - - let finalFiles = []; - if(options.attachments) { - if(!Array.isArray(options.attachments)) options.attachments = [options.attachments] - options.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) + return apiMessage.id ? new Message(this.client, apiMessage, this.channel) : apiMessage; } + }, - if(options.inlineReply) { - options.message_reference = { - message_id: this.channel.lastMessageID - } + /** + * Method to update message + * @param {Object} options + */ + update: { + value: async function(result) { + let GPayloadResult = await GPayload.create(this.channel, result) + .resolveData(); + + return this.client.api.channels(this.channel.id).messages[this.id].patch({ + data: { + type: 7, + data: GPayloadResult.data + }, + }).then(d => this.client.actions.MessageCreate.handle(d).message); } - - return this.client.api.channels[this.channel.id].messages.post({ - data: { - allowed_mentions: options.allowedMentions, - content: content, - components: options.components, - options, - embed: embed || null - }, - files: finalFiles - }) - .then(d => this.client.actions.MessageCreate.handle(d).message); }, /** - * Method to buttonsEdit - * @param {String} content - * @param {Object} options + * Method to send + * @param {Object} result * @returns {Promise} */ - buttonsEdit: async function(msgID, content, options) { - this.client = options.client, this.channel = options.channel - var embed = null; - if(typeof content == "object") { - embed = content; - content = "\u200B" - } - - if(!options.allowedMentions) { - options.allowedMentions = { parse: [] }; + send: { + value: async function(result) { + let GPayloadResult = await GPayload.create(this.channel, result) + .resolveData() + .resolveFiles(); + + return this.client.api.channels[this.channel.id].messages.post({ + data: GPayloadResult.data, + files: GPayloadResult.files + }) + .then(d => this.client.actions.MessageCreate.handle(d).message); } + }, - if(options.components) { - if(!Array.isArray(options.components)) options.components = [options.components]; - options.components = options.components; - } - if(options.embeds) { - if(!Array.isArray(options.embeds)) options.embeds = [options.embeds]; - options.embeds = options.embeds; + createButtonCollector: { + value: function(filter, options = {}) { + if(ifDjsV13) return new ButtonCollectorV13(this, filter, options); + else return new ButtonCollectorV12(this, filter, options); } + }, - let finalFiles = []; - if(options.attachments) { - if(!Array.isArray(options.attachments)) options.attachments = [options.attachments] - options.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) + awaitButtons: { + value: async function(filter, options = {}) { + return new Promise((resolve, reject) => { + const collector = this.client.dispatcher.createButtonCollector(this, filter, options); + collector.once('end', (buttons, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(buttons); + } else { + resolve(buttons); + } + }); }) } - - return this.client.api.channels[this.channel.id].messages[msgID].patch({ - data: { - allowed_mentions: options.allowedMentions, - content: content, - components: options.components, - options, - embed: embed || null - }, - files: finalFiles - }) - .then(d => this.client.actions.MessageCreate.handle(d).message); }, - /** - * Method to inlineReply - * @param {String} content - * @param {Object} options - * @returns {Promise} - */ - inlineReply: async function(content, options) { - this.client = options.client, this.channel = options.channel - const mentionRepliedUser = typeof ((options || content || {}).allowedMentions || {}).repliedUser === "undefined" ? true : ((options || content).allowedMentions).repliedUser; - delete ((options || content || {}).allowedMentions || {}).repliedUser; - - const apiMessage = content instanceof APIMessage ? content.resolveData() : APIMessage.create(this.channel, content, options).resolveData(); - Object.assign(apiMessage.data, { message_reference: { message_id: this.id } }); - - if (!apiMessage.data.allowed_mentions || Object.keys(apiMessage.data.allowed_mentions).length === 0) - apiMessage.data.allowed_mentions = { parse: ["users", "roles", "everyone"] }; - if (typeof apiMessage.data.allowed_mentions.replied_user === "undefined") - Object.assign(apiMessage.data.allowed_mentions, { replied_user: mentionRepliedUser }); - - if (Array.isArray(apiMessage.data.content)) { - return Promise.all(apiMessage.split().map(x => { - x.data.allowed_mentions = apiMessage.data.allowed_mentions; - return x; - }).map(this.inlineReply.bind(this))); + createSelectMenuCollector: { + value: function(filter, options = {}) { + if(ifDjsV13) return new SelectMenuCollectorV13(this, filter, options); + else return new SelectMenuCollectorV12(this, filter, options); } + }, - const { data, files } = await apiMessage.resolveFiles(); - return this.client.api.channels[this.channel.id].messages - .post({ data, files }) - .then(d => this.client.actions.MessageCreate.handle(d).message); + awaitSelectMenus: { + value: async function(filter, options = {}) { + return new Promise((resolve, reject) => { + const collector = this.client.dispatcher.createSelectMenuCollector(this, filter, options); + collector.once('end', (buttons, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(buttons); + } else { + resolve(buttons); + } + }); + }) + } } -}; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/structures/GPayload.js b/src/structures/GPayload.js new file mode 100644 index 000000000..ca53d4636 --- /dev/null +++ b/src/structures/GPayload.js @@ -0,0 +1,146 @@ +const { MessageEmbed, MessageAttachment, DataResolver, Util } = require('discord.js'); +const { browser } = require('discord.js').Constants; + +/** + * The GPayload class + */ +class GPayload { + + /** + * Creates new GPayload instance + * @param {TextChannel | NewsChannel | DMChannel} channel + * @param {options} data + */ + constructor(channel, options) { + /** + * Channel + * @type {TextChannel | NewsChannel | DMChannel} + */ + + this.channel = channel; + + /** + * Options + * @type {string|GPayloadOptions} + */ + this.options = options; + + /** + * Data sendable to the API + * @type {GPayloadOptions} + */ + this.data = null; + + /** + * Files sendable to the API + * @type {?MessageFile[]} + */ + this.files = null; + } + + /** + * Resolves data. + * @returns {GPayload} + */ + resolveData() { + if(this.data) return this; + else this.data = {}; + + let type = typeof this.options; + if(type !== 'object' || this.options instanceof MessageEmbed || this.options instanceof MessageAttachment) this.options = { content: this.options } + + this.options.inlineReply = this.options.inlineReply == undefined ? true : this.options.inlineReply; + + if(this.options.content && typeof this.options.content == 'object') { + this.options.embeds = this.options.content instanceof MessageEmbed ? this.options.content : []; + this.options.attachments = this.options.content instanceof MessageAttachment ? this.options.content : []; + } else this.data.content = this.options.content || null + + this.data.allowed_mentions = this.options.allowedMentions ? this.options.allowedMentions : { parse: [], repliedUser: true } + this.data.flags = this.options.ephemeral ? 64 : null + + if(this.options.components) this.data.components =!Array.isArray(this.options.components) ? [this.options.components] : this.options.components + if(this.options.embeds) this.data.embeds = !Array.isArray(this.options.embeds) ? [this.options.embeds] : this.options.embeds + if(this.options.attachments) this.options.attachments = !Array.isArray(this.options.attachments) ? [this.options.attachments] : this.options.attachments + if(this.options.files) this.options.files = !Array.isArray(this.options.files) ? [this.options.files] : this.options.files + + if(this.options.inlineReply && this.channel.lastMessageID) this.data.message_reference = { message_id: this.channel.lastMessageID } + + return this; + } + + /** + * Resolves files. + * @returns {GPayload} + */ + async resolveFiles() { + if (this.files) return this; + + const finalFiles = []; + if(this.options.files) this.options.attachments = this.options.files; + if (this.options.attachments) { + finalFiles.push(...this.options.attachments); + } + + if(this.data.embeds) { + for (const embed of this.data.embeds) { + if (embed.files) { + finalFiles.push(...embed.files); + } + } + } + + this.files = await Promise.all(finalFiles.map(f => this.constructor.resolveFile(f))); + return this; + } + + /** + * Resolves a single file into an object sendable to the API. + * from djs + * @param {BufferResolvable|Stream|FileOptions|MessageAttachment} fileLike Something that could be resolved to a file + * @returns {Object} + */ + static async resolveFile(fileLike) { + let attachment; + let name; + + const findName = thing => { + if (typeof thing === 'string') { + return Util.basename(thing); + } + + if (thing.path) { + return Util.basename(thing.path); + } + + return 'file.jpg'; + }; + + const ownAttachment = + typeof fileLike === 'string' || + fileLike instanceof (browser ? ArrayBuffer : Buffer) || + typeof fileLike.pipe === 'function'; + if (ownAttachment) { + attachment = fileLike; + name = findName(attachment); + } else { + attachment = fileLike.attachment; + name = fileLike.name || findName(attachment); + } + + const resource = await DataResolver.resolveFile(attachment); + return { attachment, name, file: resource }; + } + + /** + * Creates a `GPayload` from user-level arguments. + * @param {TextChannel | NewsChannel | DMChannel} channel + * @param {string|GPayloadOptions} options + * @returns {GPayload} + */ + static create(channel, options) { + return new this(channel, options); + } +} + +module.exports = GPayload; \ No newline at end of file diff --git a/src/structures/InteractionEvent.js b/src/structures/InteractionEvent.js index c9b23b50e..c7633a814 100644 --- a/src/structures/InteractionEvent.js +++ b/src/structures/InteractionEvent.js @@ -1,14 +1,11 @@ -/* From discord-buttons edited */ -const { default: axios } = require("axios"); -const {Client, MessageEmbed, Guild, NewsChannel, GuildMember, User, Message} = require("discord.js") -const Color = require("../structures/Color"); -const GMessage = require("./GMessage"); -const ifDjsV13 = require("../util/updater").checkDjsVersion("13") +const { interactionRefactor } = require('../util/util'); +const { Message } = require('discord.js') +const GInteraction = require('./GInteraction'); /** * The InteractionEvent class */ -class InteractionEvent { +class InteractionEvent extends GInteraction { /** * Creates new InteractionEvent instance @@ -16,16 +13,25 @@ class InteractionEvent { * @param {Object} data */ constructor(client, data) { - this.client = client; + super(client, data) + this.functions = interactionRefactor(client, data); + + /** + * componentType + * @type {Number} + */ + this.componentType = data.data.component_type /** * selectMenuId + * @type {String} * @deprecated */ this.selectMenuId = data.data.values ? data.data.custom_id : undefined; /** - * selectMenuId + * valueId + * @type {Array} * @deprecated */ this.valueId = data.data.values ? data.data.values : undefined; @@ -42,49 +48,13 @@ class InteractionEvent { */ this.values = data.data.values ? data.data.values : undefined - /** - * version - * @type {Number} - */ - this.version = data.version; - - /** - * token - * @type {String} - */ - this.token = data.token; - - /** - * discordID - * @type {Number} - */ - this.discordID = data.id; - - /** - * applicationID - * @type {Number} - */ - this.applicationID = data.application_id; - - /** - * guild - * @type {Guild} - */ - this.guild = data.guild_id ? client.guilds.cache.get(data.guild_id) : undefined; - - /** - * channel - * @type {TextChannel | NewsChannel | DMChannel} - */ - this.channel = client.channels.cache.get(data.channel_id); - /** * clicker * @type {GuildMember | User | Number} */ this.clicker = { - member: this.guild ? this.guild.members.cache.get(data.member.user.id) : undefined, - user: this.client.users.cache.get(data.guild_id ? data.member.user.id : data.user.id), + member: this.member, + user: this.author, id: data.guild_id ? data.member.user.id : data.member.user.id, }; @@ -92,302 +62,7 @@ class InteractionEvent { * message * @type {GMessage} */ - this.message = ifDjsV13 ? new Message(this.client, data.message, this.channel) : new GMessage(this.client, data.message, this.channel); - - /** - * replied - * @type {boolean} - */ - this.replied = false; - - /** - * deferred - * @type {boolean} - */ - this.deferred = false; - } - - /** - * Method to defer - * @param {Boolean} ephemeral - */ - async defer(ephemeral) { - if (this.deferred || this.replied) return console.log(new Color('&d[GCommands] &cThis button already has a reply').getText()); - await this.client.api.interactions(this.discordID, this.token).callback.post({ - data: { - type: 6, - data: { - flags: ephemeral ? 1 << 6 : null, - }, - }, - }); - this.deferred = true; - } - - /** - * Method to think - * @param {Boolean} ephemeral - */ - async think(ephemeral) { - if (this.deferred || this.replied) return console.log(new Color('&d[GCommands] &cThis button already has a reply').getText()); - await this.client.api.interactions(this.discordID, this.token).callback.post({ - data: { - type: 5, - data: { - flags: ephemeral ? 1 << 6 : null, - }, - }, - }); - this.deferred = true; - } - - /** - * Method to edit - * @param {Object} options - */ - async edit(result) { - if(result.autoDefer == true) { - await this.client.api.interactions(this.discordID, this.token).callback.post({ - data: { - type: 6, - }, - }); - } - - this.slashEdit(result) - } - - /** - * Method to update - * @param {Object} options - */ - async update(result) { - if(result.autoDefer == true) { - await this.client.api.interactions(this.discordID, this.token).callback.post({ - data: { - type: 6, - }, - }); - } - - this.slashEdit(result, true) - } - - /** - * Method to reply - */ - get reply() { - /** - * Method to replySend - * @param {Object} options - */ - let _send = async(result) => { - this.replied = true; - this.slashRespond(result) - } - - /** - * Method to replyEdit - * @param {Object} options - */ - let _edit = async(result) => { - if(!this.replied) return console.log(new Color("&d[GCommands] &cThis button has no reply.").getText()) - this.slashEdit(result) - } - - /** - * Method to replyUpdate - * @param {Object} options - */ - let _update = async(result) => { - if(!this.replied) return console.log(new Color("&d[GCommands] &cThis button has no reply.").getText()) - this.slashEdit(result, true) - } - - /** - * Method to replyFetch - * @param {Object} options - */ - let _fetch = async() => { - if(!this.replied) return console.log(new Color("&d[GCommands] &cThis button has no reply.").getText()) - let apiMessage = (await this.client.api.webhooks(this.client.user.id, this.token).messages["@original"].get()); - - if(apiMessage) { - apiMessage.client = this.client; - apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; - apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; - apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; - apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; - apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessage.id].delete()}; - } - - return ifDjsV13 ? new Message(this.client, data.message, this.channel) : new GMessage(this.client, data.message, this.channel); - } - - return { - send: _send, - edit: _edit, - update: _update, - fetch: _fetch - } - } - - async slashRespond(result) { - var data = {} - - if(typeof result != "object") data.content = result; - if(typeof result == "object" && !result.content) data.embeds = [result]; - if(typeof result == "object" && typeof result.content != "object") data.content = result.content; - if(typeof result == "object" && typeof result.content == "object") data.embeds = [result.content]; - if(typeof result == "object" && result.allowedMentions) { data.allowedMentions = result.allowedMentions } else data.allowedMentions = { parse: [], repliedUser: true } - if(typeof result == "object" && result.ephemeral) { data.flags = 64 } - if(typeof result == "object" && result.components) { - if(!Array.isArray(result.components)) result.components = [result.components]; - data.components = result.components; - } - if(typeof result == "object" && result.embeds) { - if(!Array.isArray(result.embeds)) result.embeds = [result.embeds] - data.embeds = result.embeds; - } - - let finalFiles = []; - if(typeof result == "object" && result.attachments) { - if(!Array.isArray(result.attachments)) result.attachments = [result.attachments] - result.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) - } - - let apiMessage = (await this.client.api.interactions(this.discordID, this.token).callback.post({ - data: { - type: result.thinking ? 5 : 4, - data - }, - files: finalFiles - })) - - let apiMessageMsg = {}; - try { - apiMessageMsg = (await axios.get(`https://discord.com/api/v8/webhooks/${this.client.user.id}/${interaction.token}/messages/@original`)).data; - } catch(e) { - apiMessage = { - id: undefined - } - } - - if(typeof apiMessage != "object") apiMessage = apiMessage.toJSON(); - if(apiMessage) { - apiMessage = apiMessageMsg; - apiMessage.client = this.client ? this.client : client; - apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; - apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; - apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; - apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; - apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessageMsg.id].delete()}; - } - - return apiMessage - } - - async slashEdit(result, update) { - if (typeof result == "object") { - if(result.components) { - if(!Array.isArray(result.components)) result.components = [result.components]; - - result.components = result.components; - } else result.components = []; - - if(typeof result.content == "object") { - result.embeds = [result.content] - result.content = "\u200B" - } - if(typeof result == "object" && result.embeds) { - if(!Array.isArray(result.embeds)) result.embeds = [result.embeds]; - result.embeds = result.embeds; - } else result.embeds = [] - if(result.embeds && !result.content) result.content = "\u200B" - - let apiMessage = {}; - if(update) { - apiMessage = (await this.client.api.interactions(this.discordID, this.token).callback.post({ - data: { - type: 7, - data: { - content: result.content, - components: result.components, - embeds: result.embeds || [] - } - } - })) - } else { - apiMessage = (await this.client.api.webhooks(this.client.user.id, this.token).messages[result.messageId ? result.messageId : "@original"].patch({ - data: { - content: result.content, - components: result.components, - embeds: result.embeds || [] - } - })) - } - - if(typeof apiMessage != "object") apiMessage = apiMessage.toJSON(); - if(apiMessage) { - apiMessage.client = this.client; - apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; - apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; - apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; - apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; - apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessage.id].delete()}; - } - return apiMessage; - } - - let apiMessage; - if(update) { - apiMessage = (await this.client.api.interactions(this.discordID, this.token).callback.post({ - data: { - data: { - content: result, - }, - type: 7 - }, - })) - } else { - apiMessage = (await this.client.api.webhooks(this.client.user.id, this.token).messages[result.messageId ? result.messageId : "@original"].patch({ - data: { - content: result, - } - })) - } - - if(typeof apiMessage != "object") apiMessage = apiMessage.toJSON(); - if(apiMessage) { - apiMessage.client = this.client; - apiMessage.createButtonCollector = function createButtonCollector(filter, options) {return this.client.dispatcher.createButtonCollector(apiMessage, filter, options)}; - apiMessage.awaitButtons = function awaitButtons(filter, options) {return this.client.dispatcher.awaitButtons(apiMessage, filter, options)}; - apiMessage.createSelectMenuCollector = function createSelectMenuCollector(filter, options) {return this.client.dispatcher.createSelectMenuCollector(apiMessage, filter, options)}; - apiMessage.awaitSelectMenus = function awaitSelectMenus(filter, options) {return this.client.dispatcher.awaitSelectMenus(apiMessage, filter, options)}; - apiMessage.delete = function deleteMsg() {return this.client.api.webhooks(this.client.user.id, interaction.token).messages[apiMessage.id].delete()}; - } - - return apiMessage; - } - - /** - * Method to isSelectMenu - */ - async isSelectMenu() { - return data.data.values ? true : false; - } - - /** - * Method to isButton - */ - async isButton() { - return data.data.values ? false : true; + this.message = new Message(this.client, data.message, this.channel) } } diff --git a/src/structures/MessageActionRow.js b/src/structures/MessageActionRow.js index 8fc45d949..f44ff6b5f 100644 --- a/src/structures/MessageActionRow.js +++ b/src/structures/MessageActionRow.js @@ -1,4 +1,4 @@ -const Color = require("../structures/Color"); +const Color = require('../structures/Color'); /** * The MessageActionRow class @@ -7,15 +7,33 @@ class MessageActionRow { /** * Creates new MessageActionRow instance - * @param {Object} data + * @param {Array} data */ constructor(data = {}) { + + /** + * type + * @type {Number} + */ + this.type = 1; + + /** + * components + * @type {Array} + */ + this.components = []; + this.setup(data); } + /** + * Setup + * @param {Array} data + * @returns {MessageActionRow} + * @private + */ setup(data) { - this.type = 1; - this.components = []; + this.components = 'components' in data ? Array(data.components) : []; return this.toJSON(); } @@ -25,7 +43,7 @@ class MessageActionRow { * @param {MessageButton | MessageSelectMenu} cmponent */ addComponent(component) { - if(typeof component != "object") return console.log(new Color("&d[GCommands] &cNeed provide MessageButton!").getText()) + if(typeof component !== 'object') return console.log(new Color('&d[GCommands] &cNeed provide MessageButton!').getText()) this.components.push(component) return this; } @@ -35,7 +53,7 @@ class MessageActionRow { * @param {MessageButton[] | MessageSelectMenu[]} components */ addComponents(components) { - if(typeof components != "object") return console.log(new Color("&d[GCommands] &cNeed provide MessageButton!").getText()) + if(typeof components !== 'object') return console.log(new Color('&d[GCommands] &cNeed provide MessageButton!').getText()) this.components.push(...components.flat(Infinity).map((c) => c)); return this; } @@ -47,7 +65,7 @@ class MessageActionRow { * @param {MessageButton[] | MessageSelectMenu[]} components */ removeComponents(index, deleteCount, ...components) { - if(typeof components != "object") return console.log(new Color("&d[GCommands] &cNeed provide MessageSelectOption!").getText()) + if(typeof components !== 'object') return console.log(new Color('&d[GCommands] &cNeed provide MessageSelectOption!').getText()) this.components.splice(index, deleteCount, ...components.flat(Infinity).map((c) => c)); return this; } diff --git a/src/structures/MessageButton.js b/src/structures/MessageButton.js index cbe368397..1fb3884fa 100644 --- a/src/structures/MessageButton.js +++ b/src/structures/MessageButton.js @@ -1,6 +1,6 @@ /* From discord-buttons edited */ -const { resolveString } = require("../util/util"); -const Color = require("../structures/Color") +const { resolveString, parseEmoji } = require('../util/util'); +const Color = require('./Color') const styles = { 'blurple': 1, 'gray': 2, @@ -28,17 +28,50 @@ class MessageButton { this.setup(data); } + /** + * Setup + * @param {Object} data + * @returns {MessageButton} + * @private + */ setup(data) { + + /** + * style + * @type {String} + */ this.style = 'style' in data ? this.resolveStyle(resolveString(data.style)) : null; + + /** + * label + * @type {String} + */ this.label = 'label' in data ? resolveString(data.label) : null; + + /** + * disabled + * @type {Boolean} + */ this.disabled = 'disabled' in data ? Boolean(data.disabled) : false; if (this.style === 5) { + /** + * url + * @type {String} + */ this.url = 'url' in data ? resolveString(data.url) : null; } else { + /** + * customId + * @type {String} + */ this.custom_id = 'id' in data ? resolveString(data.id): null; } + /** + * type + * @type {Number} + */ this.type = 2; return this.toJSON(); @@ -67,7 +100,7 @@ class MessageButton { * @param {String} emoji */ setEmoji(emoji) { - this.emoji = this.parseEmoji(`${emoji}`); + this.emoji = parseEmoji(`${emoji}`); return this; } @@ -115,24 +148,22 @@ class MessageButton { } resolveStyle(style) { - - if (!style || style === undefined || style === null) return console.log(new Color("&d[GCommands] &cAn invalid button styles was provided").getText()) + if (!style || style === undefined || style === null) return console.log(new Color('&d[GCommands] &cAn invalid button styles was provided').getText()) - if (!styles[style] || styles[style] === undefined || styles[style] === null) return console.log(new Color("&d[GCommands] &cAn invalid button styles was provided").getText()) + if (!styles[style] || styles[style] === undefined || styles[style] === null) return console.log(new Color('&d[GCommands] &cAn invalid button styles was provided').getText()) return styles[style] || style; } resolveButton(data) { + if (!data.style) return console.log(new Color('&d[GCommands] &cPlease provide button style').getText()) - if (!data.style) return console.log(new Color("&d[GCommands] &cPlease provide button style").getText()) - - if (!data.label) return console.log(new Color("&d[GCommands] &cPlease provide button label").getText()) + if (!data.label) return console.log(new Color('&d[GCommands] &cPlease provide button label').getText()) if (data.style === 5) { - if (!data.url) return console.log(new Color("&d[GCommands] &cYou provided url style, you must provide an URL").getText()) + if (!data.url) return console.log(new Color('&d[GCommands] &cYou provided url style, you must provide an URL').getText()) } else { - if (!data.custom_id) return console.log(new Color("&d[GCommands] &cPlease provide button id").getText()) + if (!data.custom_id) return console.log(new Color('&d[GCommands] &cPlease provide button id').getText()) } return { @@ -144,14 +175,6 @@ class MessageButton { type: 2 } } - - parseEmoji(text) { - if (text.includes('%')) text = decodeURIComponent(text); - if (!text.includes(':')) return { animated: false, name: text, id: null }; - const m = text.match(/?/); - if (!m) return null; - return { animated: Boolean(m[1]), name: m[2], id: m[3] || null }; - } } module.exports = MessageButton; \ No newline at end of file diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index baab35951..24eed37d6 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -1,5 +1,5 @@ -const { resolveString } = require("../util/util"); -const Color = require("../structures/Color") +const { resolveString } = require('../util/util'); +const Color = require('./Color') /** * The MessageSelectMenu class @@ -10,14 +10,61 @@ class MessageSelectMenu { * Creates new MessageSelectMenu instance * @param {Object} data */ - constructor(data = {}) { + constructor(data = {}) { + + /** + * type + * @type {Number} + */ + this.type = 3; + + /** + * options + * @type {Array} + */ this.options = []; this.setup(data); } - setup(data) { - this.type = 3; + /** + * Setup + * @param {Object} data + * @returns {MessageSelectMenu} + * @private + */ + setup(data) { + /** + * placeholder + * @type {String} + */ + this.placeholder = 'placeholder' in data ? resolveString(data.placeholder) : null; + + /** + * maxValues + * @type {number} + */ + this.max_values = 'max_values' in data ? Number(data.max_values) : 1; + + /** + * minValues + * @type {number} + */ + this.min_values = 'min_values' in data ? Number(data.min_values) : 1; + + /** + * id + * @type {String} + */ + this.id = 'id' in data ? resolveString(data.id) : null; + + this.options = 'options' in data ? Array(data.options) : []; + + /** + * disabled + * @type {Boolean} + */ + this.disabled = 'disabled' in data ? Boolean(data.disabled) : false; return this.toJSON(); } @@ -58,22 +105,31 @@ class MessageSelectMenu { return this; } + /** + * Method to setDisabled + * @param {String} boolean + */ + setDisabled(boolean = true) { + this.disabled = Boolean(boolean); + return this; + } + /** * Method to addOption - * @param {MessageSelectOption} MessageSelectOption + * @param {MessageSelectMenuOption} MessageSelectMenuOption */ addOption(option) { - if(typeof option != "object") return console.log(new Color("&d[GCommands] &cNeed provide MessageSelectOption!").getText()) + if(typeof option !== 'object') return console.log(new Color('&d[GCommands] &cNeed provide MessageSelectOption!').getText()) this.options.push(option) return this; } /** * Method to addOptions - * @param {MessageSelectOption[]} MessageSelectOptions + * @param {MessageSelectMenuOption[]} MessageSelectMenuOption */ addOptions(...options) { - if(typeof options != "object") return console.log(new Color("&d[GCommands] &cNeed provide MessageSelectOption!").getText()) + if(typeof options !== 'object') return console.log(new Color('&d[GCommands] &cNeed provide MessageSelectOption!').getText()) this.options.push(...options.flat(Infinity).map((o) => o)); return this; } @@ -82,10 +138,10 @@ class MessageSelectMenu { * Method to removeOptions * @param {Number} index * @param {Number} deleteCount - * @param {MessageSelectOption[]} MessageSelectOptions + * @param {MessageSelectMenuOption[]} MessageSelectMenuOption[] */ removeOptions(index, deleteCount, ...options) { - if(typeof options != "object") return console.log(new Color("&d[GCommands] &cNeed provide MessageSelectOption!").getText()) + if(typeof options !== 'object') return console.log(new Color('&d[GCommands] &cNeed provide MessageSelectOption!').getText()) this.components.splice(index, deleteCount, ...options.flat(Infinity).map((o) => o)); return this; } @@ -99,8 +155,9 @@ class MessageSelectMenu { type: 3, min_values: this.min_values, max_values: this.max_values || this.options.length, - placeholder: this.placeholder || "", + placeholder: this.placeholder || '', custom_id: this.custom_id, + disabled: this.disabled, options: this.options } } diff --git a/src/structures/MessageSelectMenuOption.js b/src/structures/MessageSelectMenuOption.js index f1a61e927..b7e899e15 100644 --- a/src/structures/MessageSelectMenuOption.js +++ b/src/structures/MessageSelectMenuOption.js @@ -1,4 +1,4 @@ -const { resolveString } = require("../util/util"); +const { resolveString, parseEmoji } = require('../util/util'); /** * The MessageSelectMenuOption class @@ -13,9 +13,43 @@ class MessageSelectMenuOption { this.setup(data); } + /** + * Setup + * @param {Object} data + * @returns {MessageButton} + * @private + */ setup(data) { + /** + * label + * @type {String} + */ this.label = 'label' in data ? resolveString(data.label) : null; + /** + * value + * @type {String} + */ + this.value = 'value' in data ? resolveString(data.value) : null; + + /** + * description + * @type {String} + */ + this.description = 'description' in data ? resolveString(data.description) : null; + + /** + * emoji + * @type {String} + */ + this.emoji = 'emoji' in data ? parseEmoji(data.emoji) : null; + + /** + * default + * @type {Boolean} + */ + this.default = 'default' in data ? Boolean(data.default) : false; + return this.toJSON(); } @@ -51,7 +85,7 @@ class MessageSelectMenuOption { * @param {String} emoji */ setEmoji(emoji) { - this.emoji = this.parseEmoji(`${emoji}`); + this.emoji = parseEmoji(`${emoji}`); return this; } @@ -77,14 +111,6 @@ class MessageSelectMenuOption { default: this.default } } - - parseEmoji(text) { - if (text.includes('%')) text = decodeURIComponent(text); - if (!text.includes(':')) return { animated: false, name: text, id: null }; - const m = text.match(/?/); - if (!m) return null; - return { animated: Boolean(m[1]), name: m[2], id: m[3] || null }; - } } module.exports = MessageSelectMenuOption; \ No newline at end of file diff --git a/src/structures/NewsChannel.js b/src/structures/NewsChannel.js index 13a2bad67..5b7a297b4 100644 --- a/src/structures/NewsChannel.js +++ b/src/structures/NewsChannel.js @@ -1,89 +1,64 @@ -const { Structures, MessageEmbed, NewsChannel } = require("discord.js"); +const { NewsChannel } = require('discord.js'); const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector') -const updater = require("../util/updater") +const GPayload = require('./GPayload'); +const ifDjsV13 = (require('../util/updater')).checkDjsVersion('13'); -if(!updater.checkDjsVersion("13")) { - module.exports = Structures.extend("NewsChannel", NewsChannel => { - class GNewsChannel extends NewsChannel { - constructor(...args) { - super(...args) - } +module.exports = Object.defineProperties(NewsChannel.prototype, { + send: { + value: async function(result) { + let GPayloadResult = await GPayload.create(this, result) + .resolveData() + .resolveFiles(); - async send(result) { - var data = {} - - if(typeof result != "object") data.content = result; - if(typeof result == "object" && !result.content) data.embeds = [result]; - if(typeof result == "object" && typeof result.content != "object") data.content = result.content; - if(typeof result == "object" && typeof result.content == "object") data.embeds = [result.content]; - if(typeof result == "object" && result.allowedMentions) { data.allowedMentions = result.allowedMentions } else data.allowedMentions = { parse: [], repliedUser: true } - if(typeof result == "object" && result.ephemeral) { data.flags = 64 } - if(typeof result == "object" && result.components) { - if(!Array.isArray(result.components)) result.components = [result.components]; - data.components = result.components; - } - if(typeof result == "object" && result.embeds) { - if(!Array.isArray(result.embeds)) result.embeds = [result.embeds] - data.embeds = result.embeds; - } - if(result.embeds && !result.content) result.content = "\u200B" - - let finalFiles = []; - if(typeof result == "object" && result.attachments) { - if(!Array.isArray(result.attachments)) result.attachments = [result.attachments] - result.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) - } + return this.client.api.channels[this.id].messages.post({ + data: GPayloadResult.data, + files: GPayloadResult.files + }) + .then(d => this.client.actions.MessageCreate.handle(d).message); + } + }, - return this.client.api.channels[this.id].messages.post({ - data, - files: finalFiles - }) - .then(d => this.client.actions.MessageCreate.handle(d).message); - } + createButtonCollector: { + value: async function (msg, filter, options = {}) { + if(ifDjsV13) return new ButtonCollectorV13(msg, filter, options); + else return new ButtonCollectorV12(msg, filter, options); + } + }, - createButtonCollector(msg, filter, options = {}) { - if(updater.checkDjsVersion("13")) return new ButtonCollectorV13(msg, filter, options); - else return new ButtonCollectorV12(msg, filter, options); - } - - awaitButtons(msg, filter, options = {}) { - return new Promise((resolve, reject) => { - const collector = this.createButtonCollector(msg, filter, options); - collector.once('end', (buttons, reason) => { - if (options.errors && options.errors.includes(reason)) { - reject(buttons); - } else { - resolve(buttons); - } - }); - }) - } + awaitButtons: { + value: async function(msg, filter, options = {}) { + return new Promise((resolve, reject) => { + const collector = this.createButtonCollector(msg, filter, options); + collector.once('end', (buttons, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(buttons); + } else { + resolve(buttons); + } + }); + }) + } + }, - createSelectMenuCollector(msg, filter, options = {}) { - if(updater.checkDjsVersion("13")) return new SelectMenuCollectorV13(msg, filter, options); - else return new SelectMenuCollectorV12(msg, filter, options); - } - - awaitSelectMenus(msg, filter, options = {}) { - return new Promise((resolve, reject) => { - const collector = this.createSelectMenuCollector(msg, filter, options); - collector.once('end', (buttons, reason) => { - if (options.errors && options.errors.includes(reason)) { - reject(buttons); - } else { - resolve(buttons); - } - }); - }) - } + createSelectMenuCollector: { + value: async function(msg, filter, options = {}) { + if(ifDjsV13) return new SelectMenuCollectorV13(msg, filter, options); + else return new SelectMenuCollectorV12(msg, filter, options); } + }, - return GNewsChannel; - }) -} else module.exports = NewsChannel; \ No newline at end of file + awaitSelectMenus: { + value: async function(msg, filter, options = {}) { + return new Promise((resolve, reject) => { + const collector = this.createSelectMenuCollector(msg, filter, options); + collector.once('end', (buttons, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(buttons); + } else { + resolve(buttons); + } + }); + }) + } + } +}); \ No newline at end of file diff --git a/src/structures/TextChannel.js b/src/structures/TextChannel.js index a313ffc1b..d546e439e 100644 --- a/src/structures/TextChannel.js +++ b/src/structures/TextChannel.js @@ -1,89 +1,64 @@ -const { Structures, MessageEmbed, TextChannel } = require("discord.js"); +const { TextChannel } = require('discord.js'); const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector') -const updater = require("../util/updater"); +const GPayload = require('./GPayload'); +const ifDjsV13 = (require('../util/updater')).checkDjsVersion('13'); -if(!updater.checkDjsVersion("13")) { - module.exports = Structures.extend("TextChannel", TextChannel => { - class GTextChannel extends TextChannel { - constructor(...args) { - super(...args) - } +module.exports = Object.defineProperties(TextChannel.prototype, { + send: { + value: async function(result) { + let GPayloadResult = await GPayload.create(this, result) + .resolveData() + .resolveFiles(); - async send(result) { - var data = {} - - if(typeof result != "object") data.content = result; - if(typeof result == "object" && !result.content) data.embeds = [result]; - if(typeof result == "object" && typeof result.content != "object") data.content = result.content; - if(typeof result == "object" && typeof result.content == "object") data.embeds = [result.content]; - if(typeof result == "object" && result.allowedMentions) { data.allowedMentions = result.allowedMentions } else data.allowedMentions = { parse: [], repliedUser: true } - if(typeof result == "object" && result.ephemeral) { data.flags = 64 } - if(typeof result == "object" && result.components) { - if(!Array.isArray(result.components)) result.components = [result.components]; - data.components = result.components; - } - if(typeof result == "object" && result.embeds) { - if(!Array.isArray(result.embeds)) result.embeds = [result.embeds] - data.embeds = result.embeds; - } - if(result.embeds && !result.content) result.content = "\u200B" - - let finalFiles = []; - if(typeof result == "object" && result.attachments) { - if(!Array.isArray(result.attachments)) result.attachments = [result.attachments] - result.attachments.forEach(file => { - finalFiles.push({ - attachment: file.attachment, - name: file.name, - file: file.attachment - }) - }) - } + return this.client.api.channels[this.id].messages.post({ + data: GPayloadResult.data, + files: GPayloadResult.files + }) + .then(d => this.client.actions.MessageCreate.handle(d).message); + } + }, - return this.client.api.channels[this.id].messages.post({ - data, - files: finalFiles - }) - .then(d => this.client.actions.MessageCreate.handle(d).message); - } + createButtonCollector: { + value: async function (msg, filter, options = {}) { + if(ifDjsV13) return new ButtonCollectorV13(msg, filter, options); + else return new ButtonCollectorV12(msg, filter, options); + } + }, - createButtonCollector(msg, filter, options = {}) { - if(updater.checkDjsVersion("13")) return new ButtonCollectorV13(msg, filter, options); - else return new ButtonCollectorV12(msg, filter, options); - } - - awaitButtons(msg, filter, options = {}) { - return new Promise((resolve, reject) => { - const collector = this.createButtonCollector(msg, filter, options); - collector.once('end', (buttons, reason) => { - if (options.errors && options.errors.includes(reason)) { - reject(buttons); - } else { - resolve(buttons); - } - }); - }) - } + awaitButtons: { + value: async function(msg, filter, options = {}) { + return new Promise((resolve, reject) => { + const collector = this.createButtonCollector(msg, filter, options); + collector.once('end', (buttons, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(buttons); + } else { + resolve(buttons); + } + }); + }) + } + }, - createSelectMenuCollector(msg, filter, options = {}) { - if(updater.checkDjsVersion("13")) return new SelectMenuCollectorV13(msg, filter, options); - else return new SelectMenuCollectorV12(msg, filter, options); - } - - awaitSelectMenus(msg, filter, options = {}) { - return new Promise((resolve, reject) => { - const collector = this.createSelectMenuCollector(msg, filter, options); - collector.once('end', (buttons, reason) => { - if (options.errors && options.errors.includes(reason)) { - reject(buttons); - } else { - resolve(buttons); - } - }); - }) - } + createSelectMenuCollector: { + value: async function(msg, filter, options = {}) { + if(ifDjsV13) return new SelectMenuCollectorV13(msg, filter, options); + else return new SelectMenuCollectorV12(msg, filter, options); } + }, - return GTextChannel; - }) -} else module.exports = TextChannel; \ No newline at end of file + awaitSelectMenus: { + value: async function(msg, filter, options = {}) { + return new Promise((resolve, reject) => { + const collector = this.createSelectMenuCollector(msg, filter, options); + collector.once('end', (buttons, reason) => { + if (options.errors && options.errors.includes(reason)) { + reject(buttons); + } else { + resolve(buttons); + } + }); + }) + } + } +}); \ No newline at end of file diff --git a/src/structures/v12/ButtonCollector.js b/src/structures/v12/ButtonCollector.js index c02ff60e8..4f129a17c 100644 --- a/src/structures/v12/ButtonCollector.js +++ b/src/structures/v12/ButtonCollector.js @@ -1,14 +1,32 @@ -const { Collector } = require("discord.js"); +const { Collector } = require('discord.js'); const Collection = require('discord.js').Collection; const { Events } = require('discord.js').Constants; +/** + * Collects buttons on a message. + * Will automatically stop if the channel (`'channelDelete'`), guild (`'guildDelete'`) or (`'messageDelete'`) are deleted. + * @extends {Collector} + */ class ButtonCollector extends Collector { + /** + * @param {Message} message + * @param {CollectorOptions} options The options to be applied to this collector + * @emits ButtonCollector#clickButton + */ constructor(message, filter, options = {}) { super(message.client, filter, options); this.message = message; + /** + * users + * @type {Collection} + */ this.users = new Collection(); + /** + * total + * @type {Number} + */ this.total = 0; this.empty = this.empty.bind(this); @@ -36,16 +54,28 @@ class ButtonCollector extends Collector { }); } + /** + * Handles a button for possible collection. + * @param {GInteraction} button + * @returns {GInteraction} + * @private + */ collect(button) { if(this.message.unstable) return ButtonCollector.key(button) if (button.message.id !== this.message.id) return null; return ButtonCollector.key(button); } + /** + * @returns {null} + */ dispose() { return null; } + /** + * Empties this button collector. + */ empty() { this.total = 0; this.collected.clear(); @@ -53,6 +83,11 @@ class ButtonCollector extends Collector { this.checkEnd(); } + /** + * The reason this collector has ended with, or null if it hasn't ended yet + * @type {?string} + * @readonly + */ endReason() { if (this.options.max && this.total >= this.options.max) return 'limit'; if (this.options.maxEmojis && this.collected.size >= this.options.maxEmojis) return 'emojiLimit'; @@ -60,24 +95,47 @@ class ButtonCollector extends Collector { return null; } + /** + * Handles checking if the message has been deleted, and if so, stops the collector with the reason 'messageDelete'. + * @private + * @param {Guild} message The message that was deleted + * @returns {void} + */ _handleMessageDeletion(message) { if (message.id === this.message.id) { this.stop('messageDelete'); } } + /** + * Handles checking if the channel has been deleted, and if so, stops the collector with the reason 'channelDelete'. + * @private + * @param {Guild} channel The channel that was deleted + * @returns {void} + */ _handleChannelDeletion(channel) { if (channel.id === this.message.channel.id) { this.stop('channelDelete'); } } + /** + * Handles checking if the guild has been deleted, and if so, stops the collector with the reason 'guildDelete'. + * @private + * @param {Guild} guild The guild that was deleted + * @returns {void} + */ _handleGuildDeletion(guild) { if (this.message.guild && guild.id === this.message.guild.id) { this.stop('guildDelete'); } } + /** + * Gets the collector key for a button. + * @param {MessageButton} button The button to get the key for + * @returns {Snowflake|string} + */ static key(button) { return button.id; } diff --git a/src/structures/v12/SelectMenuCollector.js b/src/structures/v12/SelectMenuCollector.js index af06028c0..45ac3be9d 100644 --- a/src/structures/v12/SelectMenuCollector.js +++ b/src/structures/v12/SelectMenuCollector.js @@ -1,14 +1,32 @@ -const { Collector } = require("discord.js"); +const { Collector } = require('discord.js'); const Collection = require('discord.js').Collection; const { Events } = require('discord.js').Constants; -class SelectMenuCollector extends Collector { +/** + * Collects menus on a message. + * Will automatically stop if the channel (`'channelDelete'`), guild (`'guildDelete'`) or (`'messageDelete'`) are deleted. + * @extends {Collector} + */ + class SelectMenuCollector extends Collector { + /** + * @param {Message} message + * @param {CollectorOptions} options The options to be applied to this collector + * @emits SelectMenuCollector#selectMenu + */ constructor(message, filter, options = {}) { super(message.client, filter, options); this.message = message; + /** + * users + * @type {Collection} + */ this.users = new Collection(); + /** + * total + * @type {Number} + */ this.total = 0; this.empty = this.empty.bind(this); @@ -36,16 +54,28 @@ class SelectMenuCollector extends Collector { }); } + /** + * Handles a menu for possible collection. + * @param {GInteraction} menu + * @returns {GInteraction} + * @private + */ collect(menu) { if(this.message.unstable) return SelectMenuCollector.key(menu) if (menu.message.id !== this.message.id) return null; return SelectMenuCollector.key(menu); } + /** + * @returns {null} + */ dispose() { return null; } + /** + * Empties this menu collector. + */ empty() { this.total = 0; this.collected.clear(); @@ -53,6 +83,11 @@ class SelectMenuCollector extends Collector { this.checkEnd(); } + /** + * The reason this collector has ended with, or null if it hasn't ended yet + * @type {?string} + * @readonly + */ endReason() { if (this.options.max && this.total >= this.options.max) return 'limit'; if (this.options.maxEmojis && this.collected.size >= this.options.maxEmojis) return 'emojiLimit'; @@ -60,26 +95,49 @@ class SelectMenuCollector extends Collector { return null; } - _handleMessageDeletion(message) { + /** + * Handles checking if the message has been deleted, and if so, stops the collector with the reason 'messageDelete'. + * @private + * @param {Guild} message The message that was deleted + * @returns {void} + */ + _handleMessageDeletion(message) { if (message.id === this.message.id) { this.stop('messageDelete'); } } + /** + * Handles checking if the channel has been deleted, and if so, stops the collector with the reason 'channelDelete'. + * @private + * @param {Guild} channel The channel that was deleted + * @returns {void} + */ _handleChannelDeletion(channel) { if (channel.id === this.message.channel.id) { this.stop('channelDelete'); } } + /** + * Handles checking if the guild has been deleted, and if so, stops the collector with the reason 'guildDelete'. + * @private + * @param {Guild} guild The guild that was deleted + * @returns {void} + */ _handleGuildDeletion(guild) { if (this.message.guild && guild.id === this.message.guild.id) { this.stop('guildDelete'); } } + /** + * Gets the collector key for a menu. + * @param {MessageSelectMenu} menu The menu to get the key for + * @returns {Snowflake|string} + */ static key(menu) { - return menu.selectMenuId; + return menu.id; } } diff --git a/src/structures/v13/ButtonCollector.js b/src/structures/v13/ButtonCollector.js index 27a5a8af6..f105a68ca 100644 --- a/src/structures/v13/ButtonCollector.js +++ b/src/structures/v13/ButtonCollector.js @@ -1,4 +1,4 @@ -const { Collector } = require("discord.js"); +const { Collector } = require('discord.js'); const Collection = require('discord.js').Collection; const { Events } = require('discord.js').Constants; diff --git a/src/structures/v13/SelectMenuCollector.js b/src/structures/v13/SelectMenuCollector.js index 9ea1decce..043258d19 100644 --- a/src/structures/v13/SelectMenuCollector.js +++ b/src/structures/v13/SelectMenuCollector.js @@ -1,4 +1,4 @@ -const { Collector } = require("discord.js"); +const { Collector } = require('discord.js'); const Collection = require('discord.js').Collection; const { Events } = require('discord.js').Constants; @@ -79,7 +79,7 @@ class SelectMenuCollector extends Collector { } static key(menu) { - return menu.selectMenuId; + return menu.id; } } diff --git a/src/util/Constants.js b/src/util/Constants.js index eb7ae4aec..5a7f2c504 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -11,6 +11,6 @@ */ exports.Events = { - DEBUG: "debug", - LOG: "log" + DEBUG: 'debug', + LOG: 'log' } \ No newline at end of file diff --git a/src/util/cmdUtils.js b/src/util/cmdUtils.js index fa55316d1..ffdf414a4 100644 --- a/src/util/cmdUtils.js +++ b/src/util/cmdUtils.js @@ -1,8 +1,9 @@ -const {Collection,MessageEmbed,APIMessage} = require("discord.js") - module.exports = { /** * Internal method to deleteCmd + * @param {Client} client + * @param {Number} commandId + * @param {?Number} guildId * @private */ __deleteCmd: async function(client, commandId, guildId = undefined) { @@ -18,7 +19,9 @@ module.exports = { /** * Internal method to getAllCmds - * @returns {object} + * @param {Client} client + * @param {Number} guildId + * @private */ __getAllCommands: async function(client, guildId = undefined) { try { diff --git a/src/util/message.json b/src/util/message.json index 25f1d0b60..1d90aeb70 100644 --- a/src/util/message.json +++ b/src/util/message.json @@ -54,6 +54,28 @@ "turkish": "{COMMAND} adındaki komutu bulamadım!", "polish": "Nie mogę znaleźć komendy {COMMAND}!" }, + "CHANNEL_TEXT_ONLY": { + "english": "You can only run this command in text channels.", + "spanish": "Sólo puede ejecutar este comando en los canales de texto.", + "portuguese": "Só se pode executar este comando em canais de texto.", + "russian": "Эту команду можно выполнить только в текстовых каналах.", + "german": "Sie können diesen Befehl nur in Textkanälen ausführen.", + "czech": "Tento příkaz lze spustit pouze v textových kanálech.", + "slovak": "Tento príkaz môžete spustiť len v textových kanáloch.", + "turkish": "Bu komutu yalnızca metin kanallarında çalıştırabilirsiniz.", + "polish": "Możesz uruchomić to polecenie tylko w kanałach tekstowych." + }, + "CHANNEL_NEWS_ONLY": { + "english": "You can only run this command in news channels.", + "spanish": "Sólo puede ejecutar este comando en los canales de noticias.", + "portuguese": "Só se pode executar este comando em canais de notícias.", + "russian": "Эту команду можно выполнять только в новостных каналах.", + "german": "Sie können diesen Befehl nur in Nachrichtenkanälen ausführen.", + "czech": "Tento příkaz lze spustit pouze ve zpravodajských kanálech.", + "slovak": "Tento príkaz môžete spustiť len v spravodajských kanáloch.", + "turkish": "Bu komutu sadece haber kanallarında çalıştırabilirsiniz.", + "polish": "To polecenie można uruchomić tylko w kanałach informacyjnych." + }, "NSFW": { "english": "You can only use this command in nsfw channel.", "spanish": "Sólo puede utilizar este comando en el canal nsfw.", @@ -64,5 +86,38 @@ "slovak": "Príkaz môžete využiť iba v nsfw room.", "turkish": "Bu komutu sadece nsfw kanalında kullanabilirsiniz.", "polish": "Tej komendy możesz użyć tylko na kanale NSFW." + }, + "ARGS_TIME_LIMIT": { + "english": "You have no time left to answer. You must run the command again.", + "spanish": "No te queda tiempo para responder. Debe ejecutar el comando de nuevo.", + "portuguese": "Não lhe resta tempo para responder. Deve executar o comando de novo.", + "russian": "У вас не осталось времени на ответ. Вы должны выполнить команду еще раз.", + "german": "Sie haben keine Zeit mehr zu antworten. Sie müssen den Befehl erneut ausführen.", + "czech": "Na odpověď vám nezbývá čas. Příkaz musíte spustit znovu.", + "slovak": "Na odpoveď vám už nezostáva čas. Príkaz musíte spustiť znova.", + "turkish": "Cevaplamak için zamanınız kalmadı. Komutu tekrar çalıştırmanız gerekir.", + "polish": "Nie masz już czasu na odpowiedź. Musisz ponownie uruchomić polecenie." + }, + "ARGS_MUST_CONTAIN": { + "english": "The argument `{argument}` must contain a {type}.", + "spanish": "El argumento `{argument}` debe contener un {type}.", + "portuguese": "O argumento ``{argument}` deve conter um {type}.", + "russian": "Аргумент `{argument}` должен содержать {type}.", + "german": "Das Argument `{argument}` muss einen {type} enthalten.", + "czech": "Argument `{argument}` musí obsahovat {type}.", + "slovak": "Argument `{argument}` musí obsahovať {type}.", + "turkish": "`{argument}` bağımsız değişkeni bir {type} içermelidir.", + "polish": "Argument `{argument}` musi zawierać {type}." + }, + "ARGS_CHOICES": { + "english": "Please enter one of the following options: {choices}.", + "spanish": "Por favor, introduzca una de las siguientes opciones: {choices}.", + "portuguese": "Por favor, introduza uma das seguintes opções: {choices}.", + "russian": "Пожалуйста, введите один из следующих вариантов: {choices}.", + "german": "Bitte geben Sie eine der folgenden Optionen ein: {choices}.", + "czech": "Zadejte jednu z následujících možností: {choices}.", + "slovak": "Zadajte jednu z nasledujúcich možností: {choices}.", + "turkish": "Lütfen aşağıdaki seçeneklerden birini girin: {choices}.", + "polish": "Proszę wprowadzić jedną z poniższych opcji: {choices}." } } \ No newline at end of file diff --git a/src/util/updater.js b/src/util/updater.js index 505a79ea8..73b050df2 100644 --- a/src/util/updater.js +++ b/src/util/updater.js @@ -1,5 +1,5 @@ -const axios = require("axios") -const { writeFileSync, readFileSync } = require("fs") +const axios = require('axios') +const { writeFileSync, readFileSync } = require('fs') const { version } = require('discord.js'); module.exports = { @@ -9,21 +9,26 @@ module.exports = { * @private */ __updater: async function() { - var { Color } = require("../index") - var version = require("../index").version + let { Color } = require('../index') + let version = require('../index').version - var GCommandsUpdater = await axios.get("https://registry.npmjs.org/gcommands") - var stableVersion = GCommandsUpdater.data["dist-tags"].latest + let GCommandsUpdater = await axios.get('https://registry.npmjs.org/gcommands') + let stableVersion = GCommandsUpdater.data['dist-tags'].latest - if(stableVersion != version && !version.includes("dev")) { - console.log(new Color("&d[GCommands Updater] &cPlease update GCommands &ehttps://npmjs.org/package/gcommands").getText()) - } else if(version.includes("dev")) { - console.log(new Color("&d[GCommands Updater] &cYou have &eDEV &cversion of GCommands &ehttps://gcommands.js.org&c and select dev version.").getText()) + if(stableVersion !== version && !version.includes('dev')) { + console.log(new Color('&d[GCommands Updater] &cPlease update GCommands &ehttps://npmjs.org/package/gcommands').getText()) + } else if(version.includes('dev')) { + console.log(new Color('&d[GCommands Updater] &cYou have &eDEV &cversion of GCommands &ehttps://gcommands.js.org&c and select dev version.').getText()) } }, + /** + * Internal method to checkDjsVersion + * @returns {Boolean} + * @private + */ checkDjsVersion: function(needVer) { - var ver = parseInt(version.split("")[0] + version.split("")[1]); + let ver = parseInt(version.split('')[0] + version.split('')[1]); if(ver == parseInt(needVer)) { return true; } else { diff --git a/src/util/util.js b/src/util/util.js index dae79ed89..ba19d7b77 100644 --- a/src/util/util.js +++ b/src/util/util.js @@ -1,12 +1,91 @@ module.exports = { + /** + * Internal method to resolveString + * @param {String | Array} data + * @returns {String} + */ resolveString(data) { if (typeof data === 'string') return data; if (Array.isArray(data)) return data.join('\n'); return String(data); }, + /** + * Internal method to msToSeconds + * @param {Number} ms + * @returns {number} + */ msToSeconds(ms) { let seconds = ms / 1000; return seconds; - } + }, + + /** + * Internal method to parseEmoji + * @param {String} text + * @returns {Object} + */ + parseEmoji(text) { + if (text.includes('%')) text = decodeURIComponent(text); + if (!text.includes(':')) return { animated: false, name: text, id: null }; + const match = text.match(/?/); + return match && { animated: Boolean(match[1]), name: match[2], id: match[3] || null }; + }, + + /** + * Internal method to interactionRefactor + * @param {Client} djsclient + * @param {GInteraction} interaction + * @returns {Object} + */ + interactionRefactor(client, interaction) { + let is = { + button: false, + menu: false, + command: false, + } + + if(interaction.name && client.gcommands.get(interaction.name)) { + is.command = true; + } + + if(interaction.componentType == 2) { + is.button = true; + } + + if(interaction.componentType == 3) { + is.menu = true; + } + + interaction.isCommand = async() => is.command; + interaction.isButton = async() => is.button; + interaction.isSelectMenu = async() => is.menu; + return interaction; + }, + + /** + * Internal method to inhivit + * @param {Client} client + * @param {GInteraction} interaction + * @param {Function} data + * @returns {object} + */ + inhibit(client, interaction, data) { + for(const inhibitor of client.inhibitors) { + let inhibit = inhibitor(interaction, data); + return inhibit; + } + return null; + }, + + /** + * Internal method to isClass + * @param {File} input + * @returns {boolean} + */ + isClass(input) { + return typeof input === 'function' && + typeof input.prototype === 'object' && + input.toString().substring(0, 5) === 'class'; + } } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index fda50cc42..000000000 --- a/tsconfig.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } -} \ No newline at end of file diff --git a/typings/index.d.ts b/typings/index.d.ts index b4e53bc9c..17c7f3546 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1,4 +1,4 @@ -import discord, { Channel, Client, Collector, Collection, Guild, GuildChannel, GuildMember, Message, MessageAttachment, MessageCollectorOptions, CollectorOptions, MessageEmbed, Snowflake, User } from 'discord.js'; +import discord, { Channel, Client, Collector, Collection, Guild, GuildChannel, GuildMember, Message, MessageAttachment, MessageCollectorOptions, CollectorOptions, MessageEmbed, Snowflake, User, NewsChannel, TextChannel, DMChannel, MembershipStates } from 'discord.js'; import InteractionEvent = require('../src/structures/InteractionEvent'); import { EventEmitter } from 'events'; type GuildLanguageTypes = 'english' | 'spanish' | 'portuguese' | 'russian' | 'german' | 'czech' | 'slovak' | 'turkish' | 'polish'; @@ -104,34 +104,21 @@ declare module 'discord.js' { } declare module 'gcommands' { - export class GCommandsGuild extends Guild { - public prefix: string; - public language: string; - - public getCommandPrefix(cache?: boolean): string; - public getLanguage(cache?: boolean): string; - - public setCommandPrefix(prefix: string): void; - public setLanguage(language: GuildLanguageTypes): void; - } - - export class InteractionEvent { - constructor(client: Client, data: object) - public selectMenuId: string; - public valueId: string; - public values: array; - public id: string; - public version: number; + export class GInteraction { + public type: number; public token: number; - public discordID: number; - public applicationID: number; + public discordId: number; + public version: number; + public applicationId: number; public guild: Guild; - public channel: Channel; - public clicker: { - member: GuildMember; - user: User; + public channel: TextChannel | NewsChannel | DMChannel; + public author: User; + public member: GuildMember; + public interaction: { + name: string; + id: number; + options: Array; } - public message: Message; public replied: boolean; public deffered: boolean; @@ -145,6 +132,19 @@ declare module 'gcommands' { } } + export class InteractionEvent extends GInteraction { + constructor(client: Client, data: object) + + public values: array; + public id: string; + public clicker: { + member: GuildMember; + user: User; + } + + public message: Message; + } + export class Color { constructor(text: string, options: object) public text: string; @@ -203,13 +203,13 @@ declare module 'gcommands' { public type: number; public disabled: boolean; - public setStyle(style: MessageButtonStyle): this; - public setLabel(label: string): this; - public setEmoji(emoji: string): this; - public setURL(url: string): this; - public setDisabled(disabled: boolean): this; - public setID(id: number): this; - public toJSON(): this; + public setStyle(style: MessageButtonStyle): MessageButton; + public setLabel(label: string): MessageButton; + public setEmoji(emoji: string): MessageButton; + public setURL(url: string): MessageButton; + public setDisabled(disabled: boolean): MessageButton; + public setID(id: number): MessageButton; + public toJSON(): MessageButton; } export class MessageSelectMenu { @@ -220,14 +220,15 @@ declare module 'gcommands' { public custom_id: string; public options: object; - public setPlaceholder(placeholder: string): this; - public setMaxValues(max: number): this; - public setMinValues(min: number): this; - public setID(id: number): this; + public setPlaceholder(placeholder: string): tMessageSelectMenuhis; + public setMaxValues(max: number): MessageSelectMenu; + public setMinValues(min: number): MessageSelectMenu; + public setID(id: number): MessageSelectMenu; + public setDisabled(disabled: boolean): MessageSelectMenu; public addOption(option: MessageSelectMenuOption) public addOptions(...options: MessageSelectMenuOption[]) public removeOptions(index: number, deleteCount: number, ...options: MessageSelectMenuOption[]) - public toJSON(): this; + public toJSON(): MessageSelectMenu; } export class MessageSelectMenuOption { @@ -239,12 +240,12 @@ declare module 'gcommands' { public emoji: object; public default: boolean; - public setLabel(label: string): this; - public setValue(value: string): this; - public setDescription(value: string): this; - public setEmoji(emoji: string): this; - public setDefault(disabled: boolean): this; - public toJSON(): this; + public setLabel(label: string): MessageSelectMenuOption; + public setValue(value: string): MessageSelectMenuOption; + public setDescription(value: string): MessageSelectMenuOption; + public setEmoji(emoji: string): MessageSelectMenuOption; + public setDefault(disabled: boolean): MessageSelectMenuOption; + public toJSON(): MessageSelectMenuOption; } export class GCommandsDispatcher { @@ -281,6 +282,53 @@ declare module 'gcommands' { ): this; } + export class Command { + constructor(client: Client, options: CommandOptions) + + public name: string; + public description: string; + public cooldown: string; + public expectedArgs: String | Array; + public args: Array; + public minArgs: number; + public clientRequiredPermissions: String | Array; + public userRequiredPermissions: String | Array; + public userRequiredRoles: String | Array; + public userOnly: Snowflake | Array; + public channelOnly: Snowflake | Array; + public channelTextOnly: Boolean; + public channelNewsOnly: Boolean; + public guildOnly: Snowflake | String; + public nsfw: boolean; + public aliases: Array; + public category: string; + + public run(options: CommandRunOptions, args: Array, args2: Object | Array): void; + } + + export class Event { + constructor(client: Client, options: EventOptions) + + public name: string; + public once: boolean; + public ws: boolean; + + public run(client: Client, ...args): void; + } + + export class GPayload { + constructor(channel: TextChannel | NewsChannel | DMChannel, options: String | GPayloadOptions) + + public channel: TextChannel | NewsChannel | DMChannel; + public options: GPayloadOptions; + public data: GPayloadOptions; + public files: GPayloadFiles; + + public create(channel: TextChannel | NewsChannel | DMChannel, options: String | GPayloadOptions): GPayload; + public resolveData(): GPayload; + public resolveFiles(): GPayload; + } + interface GEvents { debug: [string]; log: [string]; @@ -292,14 +340,28 @@ declare module 'gcommands' { language: GuildLanguageTypes; unkownCommandMessage?: boolean; slash: { - slash: string | boolean; - prefix: string; - } + slash: string | boolean; + prefix?: string; + }, + caseSensitiveCommands?: boolean; + caseSensitivePrefixes?: boolean; defaultCooldown?: string; database?: string; } - interface RespondOptions { + interface CommandRunOptions { + client: Client; + interaction: Object; + member: GuildMember; + message: Message; + guild: Guild; + channel: TextChannel | NewsChannel; + + respond(options: string | GPayloadOptions): void; + edit(options: string | GPayloadOptions): void; + } + + interface GPayloadOptions { content: [string | MessageEmbed]; embeds?: [MessageEmbed]; components?: [MessageActionRow]; @@ -308,6 +370,11 @@ declare module 'gcommands' { allowedMentions?: [object]; } + interface GPayloadFiles { + files?: [MessageAttachment | MessageAttachment[]]; + attachments?: [MessageAttachment | MessageAttachment[]]; + } + interface MessageEditAndUpdateOptions { content: string, embeds?: MessageEmbed, @@ -316,4 +383,25 @@ declare module 'gcommands' { attachments?: MessageAttachment | MessageAttachment[], allowedMentions?: object } + + interface CommandOptions { + name: string; + description: string; + cooldown?: string; + expectedArgs?: string; + minArgs?: number; + userRequiredPermissions?: Array | String; + userRequiredRoles?: Array | String; + clientRequiredPermissions?: Array | String; + userOnly?: Array | Snowflake; + channelOnly?: Array | Snowflake; + guildOnly?: Array | Snowflake; + nsfw?: boolean; + aliases?: Array; + } + + interface EventOptions { + name: string; + once: boolean; + } } \ No newline at end of file