diff --git a/package.json b/package.json index 83961f95e..07be3d2a3 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "name": "gcommands", - "version": "4.2.7", + "version": "4.2.8", "description": "Open-source slash/normal command handler", "main": "src/index.js", "types": "./typings/index.d.ts", "dependencies": { "axios": "^0.21.1", - "discord.js": "^12.5.3" + "discord.js": "^12.5.3", + "ms": "^2.1.3" }, "scripts": { "test": "node test.js" diff --git a/src/base/GCommandsDispatcher.js b/src/base/GCommandsDispatcher.js index 76c5b90d6..78361232e 100644 --- a/src/base/GCommandsDispatcher.js +++ b/src/base/GCommandsDispatcher.js @@ -1,7 +1,7 @@ const { Collector, Collection } = require('discord.js'); -const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), Color = require("../structures/Color") +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 ms = require("ms"); /** * The GCommansDispatcher class @@ -22,7 +22,7 @@ class GCommandsDispatcher { * Internal method to setGuildPrefix * @returns {boolean} */ - async setGuildPrefix(prefix, guildId) { + async setGuildPrefix(guildId, prefix) { if(!this.client.database) return false; let guildData = await this.client.database.get(`guild_${guildId}`) || {} @@ -207,6 +207,36 @@ class GCommandsDispatcher { }); }) } + + /** + * Internal method to createSelectMenuCollector + * @param {Function} filter + * @param {Object} options + * @returns {Collector} + */ + createSelectMenuCollector(msg, filter, options = {}) { + if(updater.checkDjsVersion("13")) return new SelectMenuCollectorV13(msg, filter, options); + else return new SelectMenuCollectorV12(msg, filter, options); + } + + /** + * Internal method to createButtonCollector + * @param {Function} filter + * @param {Object} options + * @returns {Collector} + */ + 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); + } + }); + }) + } } module.exports = GCommandsDispatcher; \ No newline at end of file diff --git a/src/base/actions/interactions.js b/src/base/actions/interactions.js index b903f6b2d..8a160401b 100644 --- a/src/base/actions/interactions.js +++ b/src/base/actions/interactions.js @@ -1,14 +1,14 @@ -const ButtonEvent = require("../../structures/ButtonEvent") - +const InteractionEvent = require("../../structures/InteractionEvent"); + module.exports = (client) => { client.ws.on('INTERACTION_CREATE', data => { - if(data.type != 3) return; if (!data.message) return; - if (data.data.component_type === 2) { - const button = new ButtonEvent(client, data); + if(data.data.component_type) { + const interaction = new InteractionEvent(client, data) - client.emit('clickButton', button); + client.emit(data.data.component_type == 3 ? `selectMenu` : `clickButton`, interaction) + client.emit("interaction", interaction) } }); } \ No newline at end of file diff --git a/src/index.js b/src/index.js index 6d17f49a8..ab06cd8d4 100644 --- a/src/index.js +++ b/src/index.js @@ -20,10 +20,15 @@ module.exports = { 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"), + // Other Color: require("./structures/Color"), Util: require("./util/util"), diff --git a/src/managers/GEventLoader.js b/src/managers/GEventLoader.js index 53b6f92a3..bbe43ca45 100644 --- a/src/managers/GEventLoader.js +++ b/src/managers/GEventLoader.js @@ -569,6 +569,8 @@ class GEventLoader { 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()}; } @@ -617,6 +619,8 @@ class GEventLoader { 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()}; } diff --git a/src/structures/DMChannel.js b/src/structures/DMChannel.js index 9f993988b..c3a0016d3 100644 --- a/src/structures/DMChannel.js +++ b/src/structures/DMChannel.js @@ -1,5 +1,5 @@ const { Structures, MessageEmbed } = require("discord.js"); -const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), { createAPIMessage } = require("../util/util") +const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector'), { createAPIMessage } = require("../util/util") const updater = require("../util/updater") module.exports = Structures.extend("DMChannel", DMChannel => { @@ -18,6 +18,7 @@ module.exports = Structures.extend("DMChannel", DMChannel => { } if(typeof result != "object") data.content = result; + if(typeof result == "object" && !result.content) data.embeds = [result.content]; 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 } @@ -67,6 +68,24 @@ module.exports = Structures.extend("DMChannel", DMChannel => { }); }) } + + 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); + } + }); + }) + } } return GDMChannel; diff --git a/src/structures/ButtonEvent.js b/src/structures/InteractionEvent.js similarity index 86% rename from src/structures/ButtonEvent.js rename to src/structures/InteractionEvent.js index dee26e1e6..185d8049d 100644 --- a/src/structures/ButtonEvent.js +++ b/src/structures/InteractionEvent.js @@ -4,19 +4,22 @@ const {Client, MessageEmbed} = require("discord.js") const Color = require("../structures/Color"), { createAPIMessage } = require("../util/util"); /** - * The ButtonEvent class + * The InteractionEvent class */ -class ButtonEvent { +class InteractionEvent { /** - * Creates new ButtonEvent instance + * Creates new InteractionEvent instance * @param {Client} client * @param {Object} data */ constructor(client, data) { this.client = client; - this.id = data.data.custom_id; + if(data.data.values) { + this.selectMenuId = data.data.custom_id; + this.valueId = data.data.values; + } else this.id = data.data.custom_id; this.version = data.version; @@ -49,7 +52,7 @@ class ButtonEvent { * Method to defer * @param {Boolean} ephemeral */ - async defer(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: { @@ -179,6 +182,8 @@ class ButtonEvent { 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()}; } @@ -226,6 +231,8 @@ class ButtonEvent { 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()}; } @@ -234,6 +241,20 @@ class ButtonEvent { return this.client.api.webhooks(this.client.user.id, this.token).messages["@original"].patch({ data: { content: result }}) } + + /** + * Method to isSelectMenu + */ + async isSelectMenu() { + return data.data.values ? true : false; + } + + /** + * Method to isButton + */ + async isButton() { + return data.data.values ? false : true; + } } -module.exports = ButtonEvent; \ No newline at end of file +module.exports = InteractionEvent; \ No newline at end of file diff --git a/src/structures/MessageActionRow.js b/src/structures/MessageActionRow.js index c4221be14..1cd06ffc7 100644 --- a/src/structures/MessageActionRow.js +++ b/src/structures/MessageActionRow.js @@ -22,7 +22,7 @@ class MessageActionRow { /** * Method to addComponent - * @param {MessageButton} button + * @param {MessageButton} MessageButton */ addComponent(component) { if(typeof component != "object") return console.log(new Color("&d[GCommands] &cNeed provide MessageButton!").getText()) @@ -30,6 +30,26 @@ class MessageActionRow { return this; } + /** + * Method to addComponents + * @param {MessageButton} MessageButton + */ + addComponents(components) { + 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; + } + + /** + * Method to removeOptions + * @param {Object} MessageButton + */ + removeComponents(index, deleteCount, ...options) { + if(typeof options != "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; + } + /** * Method to toJSON * @return {Object} diff --git a/src/structures/MessageButton.js b/src/structures/MessageButton.js index 6871cc992..06b75538e 100644 --- a/src/structures/MessageButton.js +++ b/src/structures/MessageButton.js @@ -55,7 +55,7 @@ class MessageButton { /** * Method to setLabel - * @param {String} style + * @param {String} label */ setLabel(label) { this.label = resolveString(label); diff --git a/src/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js new file mode 100644 index 000000000..f6935cf0b --- /dev/null +++ b/src/structures/MessageSelectMenu.js @@ -0,0 +1,107 @@ +const { resolveString } = require("../util/util"); +const Color = require("../structures/Color") + +/** + * The MessageSelectMenu class + */ +class MessageSelectMenu { + + /** + * Creates new MessageSelectMenu instance + * @param {Object} data + */ + constructor(data = {}) { + this.options = []; + + this.setup(data); + } + + setup(data) { + this.type = 3; + + return this.toJSON(); + } + + /** + * Method to setDisabled + * @param {String} boolean + */ + setPlaceholder(string) { + this.placeholder = resolveString(string); + return this; + } + + /** + * Method to setMaxValues + * @param {Number} int + */ + setMaxValues(int = 1) { + this.max_values = Number(int) + return this; + } + + /** + * Method to setMinValues + * @param {Number} int + */ + setMinValues(int = 1) { + this.min_values = Number(int) + return this; + } + + /** + * Method to setID + * @param {String} id + */ + setID(id) { + this.custom_id = this.style === 5 ? null : resolveString(id); + return this; + } + + /** + * Method to addOption + * @param {Object} MessageSelectOption + */ + addOption(option) { + 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 {Object} MessageSelectOptions + */ + addOptions(...options) { + 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; + } + + /** + * Method to removeOptions + * @param {Object} MessageSelectOptions + */ + removeOptions(index, deleteCount, ...options) { + 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; + } + + /** + * Method to toJSON + * @return {Object} + */ + toJSON() { + return { + type: 3, + min_values: this.min_values, + max_values: this.max_values || this.options.length, + placeholder: this.placeholder || "", + custom_id: this.custom_id, + options: this.options + } + } +} + +module.exports = MessageSelectMenu; \ No newline at end of file diff --git a/src/structures/MessageSelectMenuOption.js b/src/structures/MessageSelectMenuOption.js new file mode 100644 index 000000000..f1a61e927 --- /dev/null +++ b/src/structures/MessageSelectMenuOption.js @@ -0,0 +1,90 @@ +const { resolveString } = require("../util/util"); + +/** + * The MessageSelectMenuOption class + */ +class MessageSelectMenuOption { + + /** + * Creates new MessageSelectMenuOption instance + * @param {Object} data + */ + constructor(data = {}) { + this.setup(data); + } + + setup(data) { + this.label = 'label' in data ? resolveString(data.label) : null; + + return this.toJSON(); + } + + /** + * Method to setLabel + * @param {String} label + */ + setLabel(label) { + this.label = resolveString(label); + return this; + } + + /** + * Method to setValue + * @param {String} value + */ + setValue(value) { + this.value = resolveString(value); + return this; + } + + /** + * Method to setValue + * @param {String} desc + */ + setDescription(desc) { + this.description = resolveString(desc); + return this; + } + + /** + * Method to setEmoji + * @param {String} emoji + */ + setEmoji(emoji) { + this.emoji = this.parseEmoji(`${emoji}`); + return this; + } + + /** + * Method to setDefault + * @param {Boolean} default + */ + setDefault(def = true) { + this.default = Boolean(def) + return this; + } + + /** + * Method to toJSON + * @return {Object} + */ + toJSON() { + return { + label: this.label, + value: this.value, + description: this.description, + emoji: this.emoji, + 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 63b8ddf21..2812b8f51 100644 --- a/src/structures/NewsChannel.js +++ b/src/structures/NewsChannel.js @@ -1,5 +1,5 @@ const { Structures, MessageEmbed } = require("discord.js"); -const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), { createAPIMessage } = require("../util/util") +const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector'), { createAPIMessage } = require("../util/util") const updater = require("../util/updater") module.exports = Structures.extend("NewsChannel", NewsChannel => { @@ -18,6 +18,7 @@ module.exports = Structures.extend("NewsChannel", NewsChannel => { } if(typeof result != "object") data.content = result; + if(typeof result == "object" && !result.content) data.embeds = [result.content]; 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 } @@ -67,6 +68,24 @@ module.exports = Structures.extend("NewsChannel", NewsChannel => { }); }) } + + 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); + } + }); + }) + } } return GNewsChannel; diff --git a/src/structures/TextChannel.js b/src/structures/TextChannel.js index 1b87de108..41407b2b3 100644 --- a/src/structures/TextChannel.js +++ b/src/structures/TextChannel.js @@ -1,6 +1,6 @@ const { Structures, MessageEmbed } = require("discord.js"); -const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), { createAPIMessage } = require("../util/util") -const updater = require("../util/updater") +const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector'), { createAPIMessage } = require("../util/util") +const updater = require("../util/updater"); module.exports = Structures.extend("TextChannel", TextChannel => { class GTextChannel extends TextChannel { @@ -16,6 +16,7 @@ module.exports = Structures.extend("TextChannel", TextChannel => { } if(typeof result != "object") data.content = result; + if(typeof result == "object" && !result.content) data.embeds = [result.content]; 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 } @@ -65,6 +66,24 @@ module.exports = Structures.extend("TextChannel", TextChannel => { }); }) } + + 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); + } + }); + }) + } } return GTextChannel; diff --git a/src/structures/guild.js b/src/structures/guild.js index 17e3c4dfd..5435e1e8f 100644 --- a/src/structures/guild.js +++ b/src/structures/guild.js @@ -24,7 +24,7 @@ module.exports = Structures.extend('Guild', Guild => { * @returns {Boolean} */ async setCommandPrefix(prefix) { - this.client.dispatcher.setGuildPrefix(prefix, this.id); + this.client.dispatcher.setGuildPrefix(this.id, prefix); this.client.emit('commandPrefixChange', this, this.prefix); } @@ -41,7 +41,7 @@ module.exports = Structures.extend('Guild', Guild => { * @returns {Boolean} */ async setLanguage(lang) { - this.client.dispatcher.setGuildLanguage(lang, this.id); + this.client.dispatcher.setGuildLanguage(this.id, lang); this.client.emit('guildLanguageChange', this, this.language); } } diff --git a/src/structures/message.js b/src/structures/message.js index 8cce03c8a..416477063 100644 --- a/src/structures/message.js +++ b/src/structures/message.js @@ -1,5 +1,5 @@ const { APIMessage, Structures } = require('discord.js'); -const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), { createAPIMessage } = require("../util/util") +const ButtonCollectorV12 = require('../structures/v12/ButtonCollector'), ButtonCollectorV13 = require('../structures/v13/ButtonCollector'), SelectMenuCollectorV12 = require('../structures/v12/SelectMenuCollector'), SelectMenuCollectorV13 = require('../structures/v13/SelectMenuCollector'), { createAPIMessage } = require("../util/util") const updater = require("../util/updater") module.exports = Structures.extend("Message", Message => { @@ -274,6 +274,24 @@ module.exports = Structures.extend("Message", Message => { }); }) } + + 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; diff --git a/src/structures/v12/SelectMenuCollector.js b/src/structures/v12/SelectMenuCollector.js new file mode 100644 index 000000000..af06028c0 --- /dev/null +++ b/src/structures/v12/SelectMenuCollector.js @@ -0,0 +1,86 @@ +const { Collector } = require("discord.js"); +const Collection = require('discord.js').Collection; +const { Events } = require('discord.js').Constants; + +class SelectMenuCollector extends Collector { + constructor(message, filter, options = {}) { + super(message.client, filter, options); + this.message = message; + + this.users = new Collection(); + + this.total = 0; + + this.empty = this.empty.bind(this); + this._handleChannelDeletion = this._handleChannelDeletion.bind(this); + this._handleGuildDeletion = this._handleGuildDeletion.bind(this); + this._handleMessageDeletion = this._handleMessageDeletion.bind(this); + + this.client.incrementMaxListeners(); + this.client.on('selectMenu', this.handleCollect); + this.client.on(Events.MESSAGE_DELETE, this._handleMessageDeletion); + this.client.on(Events.CHANNEL_DELETE, this._handleChannelDeletion); + this.client.on(Events.GUILD_DELETE, this._handleGuildDeletion); + + this.once('end', () => { + this.client.removeListener('selectMenu', this.handleCollect); + this.client.removeListener(Events.MESSAGE_DELETE, this._handleMessageDeletion); + this.client.removeListener(Events.CHANNEL_DELETE, this._handleChannelDeletion); + this.client.removeListener(Events.GUILD_DELETE, this._handleGuildDeletion); + this.client.decrementMaxListeners(); + }); + + this.on('collect', (menu) => { + this.total++; + this.users.set(menu.clicker.user.id, menu.clicker.user); + }); + } + + collect(menu) { + if(this.message.unstable) return SelectMenuCollector.key(menu) + if (menu.message.id !== this.message.id) return null; + return SelectMenuCollector.key(menu); + } + + dispose() { + return null; + } + + empty() { + this.total = 0; + this.collected.clear(); + this.users.clear(); + this.checkEnd(); + } + + endReason() { + if (this.options.max && this.total >= this.options.max) return 'limit'; + if (this.options.maxEmojis && this.collected.size >= this.options.maxEmojis) return 'emojiLimit'; + if (this.options.maxUsers && this.users.size >= this.options.maxUsers) return 'userLimit'; + return null; + } + + _handleMessageDeletion(message) { + if (message.id === this.message.id) { + this.stop('messageDelete'); + } + } + + _handleChannelDeletion(channel) { + if (channel.id === this.message.channel.id) { + this.stop('channelDelete'); + } + } + + _handleGuildDeletion(guild) { + if (this.message.guild && guild.id === this.message.guild.id) { + this.stop('guildDelete'); + } + } + + static key(menu) { + return menu.selectMenuId; + } +} + +module.exports = SelectMenuCollector; \ No newline at end of file diff --git a/src/structures/v13/SelectMenuCollector.js b/src/structures/v13/SelectMenuCollector.js new file mode 100644 index 000000000..9ea1decce --- /dev/null +++ b/src/structures/v13/SelectMenuCollector.js @@ -0,0 +1,86 @@ +const { Collector } = require("discord.js"); +const Collection = require('discord.js').Collection; +const { Events } = require('discord.js').Constants; + +class SelectMenuCollector extends Collector { + constructor(message, filter, options = {}) { + super(message.client, filter, options); + this.message = message; + + this.users = new Collection(); + + this.total = 0; + + this.empty = this.empty.bind(this); + this._handleChannelDeletion = this._handleChannelDeletion.bind(this); + this._handleGuildDeletion = this._handleGuildDeletion.bind(this); + this._handleMessageDeletion = this._handleMessageDeletion.bind(this); + + this.client.incrementMaxListeners(); + this.client.on('selectMenu', this.handleCollect); + this.client.on(Events.MESSAGE_DELETE, this._handleMessageDeletion); + this.client.on(Events.CHANNEL_DELETE, this._handleChannelDeletion); + this.client.on(Events.GUILD_DELETE, this._handleGuildDeletion); + + this.once('end', () => { + this.client.removeListener('selectMenu', this.handleCollect); + this.client.removeListener(Events.MESSAGE_DELETE, this._handleMessageDeletion); + this.client.removeListener(Events.CHANNEL_DELETE, this._handleChannelDeletion); + this.client.removeListener(Events.GUILD_DELETE, this._handleGuildDeletion); + this.client.decrementMaxListeners(); + }); + + this.on('collect', (menu) => { + this.total++; + this.users.set(menu.clicker.user.id, menu.clicker.user); + }); + } + + collect(menu) { + if(this.message.unstable) return SelectMenuCollector.key(menu) + if (menu.message.id !== this.message.id) return null; + return SelectMenuCollector.key(menu); + } + + dispose() { + return null; + } + + empty() { + this.total = 0; + this.collected.clear(); + this.users.clear(); + this.checkEnd(); + } + + get endReason() { + if (this.options.max && this.total >= this.options.max) return 'limit'; + if (this.options.maxEmojis && this.collected.size >= this.options.maxEmojis) return 'emojiLimit'; + if (this.options.maxUsers && this.users.size >= this.options.maxUsers) return 'userLimit'; + return null; + } + + _handleMessageDeletion(message) { + if (message.id === this.message.id) { + this.stop('messageDelete'); + } + } + + _handleChannelDeletion(channel) { + if (channel.id === this.message.channel.id) { + this.stop('channelDelete'); + } + } + + _handleGuildDeletion(guild) { + if (this.message.guild && guild.id === this.message.guild.id) { + this.stop('guildDelete'); + } + } + + static key(menu) { + return menu.selectMenuId; + } +} + +module.exports = SelectMenuCollector; \ No newline at end of file