diff --git a/commands/deploy.js b/commands/deploy.js index 5a789658..e4d546b0 100644 --- a/commands/deploy.js +++ b/commands/deploy.js @@ -1,19 +1,18 @@ exports.run = async (client, message, args, level) => { // eslint-disable-line no-unused-vars -// Filter the slash commands to find guild only ones. - const guildCmds = client.container.slashcmds.filter(c => c.guildOnly).map(c => c.commandData); - // Now we filter out global commands by inverting the filter. - const globalCmds = client.container.slashcmds.filter(c => !c.guildOnly).map(c => c.commandData); + // We'll partition the slash commands based on the guildOnly boolean. + // Separating them into the correct objects defined in the array below. + const [globalCmds, guildCmds] = client.container.slashcmds.partition(c => !c.conf.guildOnly); // Give the user a notification the commands are deploying. await message.channel.send("Deploying commands!"); // We'll use set but please keep in mind that `set` is overkill for a singular command. // Set the guild commands like - await client.guilds.cache.get(message.guild.id)?.commands.set(guildCmds); + await client.guilds.cache.get(message.guild.id)?.commands.set(guildCmds.map(c => c.commandData)); // Then set the global commands like - await client.application?.commands.set(globalCmds).catch(e => console.log(e)); + await client.application?.commands.set(globalCmds.map(c => c.commandData)).catch(e => console.log(e)); // Reply to the user that the commands have been deployed. await message.channel.send("All commands deployed!"); diff --git a/commands/help.js b/commands/help.js index d847aa5e..41859ba8 100644 --- a/commands/help.js +++ b/commands/help.js @@ -46,11 +46,11 @@ exports.run = (client, message, args, level) => { } else { // Show individual command's help. let command = args[0]; - if (container.commands.has(command)) { - command = container.commands.get(command); + if (container.commands.has(command) || container.commands.has(container.aliases.get(command))) { + command = container.commands.get(command) ?? container.commands.get(container.aliases.get(command)); if (level < container.levelCache[command.conf.permLevel]) return; message.channel.send(codeBlock("asciidoc", `= ${command.help.name} = \n${command.help.description}\nusage:: ${command.help.usage}\naliases:: ${command.conf.aliases.join(", ")}`)); - } + } else return message.channel.send("No command with that name, or alias exists."); }}; exports.conf = { diff --git a/config.js.example b/config.js.example index a8a743bc..cd6d8af5 100644 --- a/config.js.example +++ b/config.js.example @@ -76,7 +76,10 @@ const config = { name: "Server Owner", // Simple check, if the guild owner id matches the message author's ID, then it will return true. // Otherwise it will return false. - check: (message) => message.channel.type === "text" ? (message.guild.ownerID === message.author.id ? true : false) : false + check: (message) => { + const serverOwner = message.author ?? message.user; + return message.guild?.ownerId === serverOwner.id; + } }, // Bot Support is a special in between level that has the equivalent of server owner access @@ -85,13 +88,19 @@ const config = { name: "Bot Support", // The check is by reading if an ID is part of this array. Yes, this means you need to // change this and reboot the bot to add a support user. Make it better yourself! - check: (message) => config.support.includes(message.author.id) + check: (message) => { + const botSupport = message.author ?? message.user; + return config.support.includes(botSupport.id); + } }, // Bot Admin has some limited access like rebooting the bot or reloading commands. { level: 9, name: "Bot Admin", - check: (message) => config.admins.includes(message.author.id) + check: (message) => { + const botAdmin = message.author ?? message.user; + return config.admins.includes(botAdmin.id); + } }, // This is the bot owner, this should be the highest permission level available. @@ -101,7 +110,10 @@ const config = { { level: 10, name: "Bot Owner", // Another simple check, compares the message author id to a list of owners found in the bot application. - check: (message) => message.author.id === process.env.OWNER + check: (message) => { + const owner = message.author ?? message.user; + return owner.id === process.env.OWNER; + } } ] }; diff --git a/events/interactionCreate.js b/events/interactionCreate.js index 9816b3ea..56e8c6e9 100644 --- a/events/interactionCreate.js +++ b/events/interactionCreate.js @@ -1,15 +1,42 @@ const logger = require("../modules/Logger.js"); +const { getSettings, permlevel } = require("../modules/functions.js"); +const config = require("../config.js"); + module.exports = async (client, interaction) => { // If it's not a command, stop. if (!interaction.isCommand()) return; + + // Grab the settings for this server from Enmap. + // If there is no guild, get default conf (DMs) + const settings = interaction.settings = getSettings(interaction.guild); + + // Get the user or member's permission level from the elevation + const level = permlevel(interaction); + // Grab the command data from the client.container.slashcmds Collection const cmd = client.container.slashcmds.get(interaction.commandName); + // If that command doesn't exist, silently exit and do nothing if (!cmd) return; - // Run the command + + // Since the permission system from Discord is rather limited in regarding to + // Slash Commands, we'll just utilise our permission checker. + if (level < client.container.levelCache[cmd.conf.permLevel]) { + // Due to the nature of interactions we **must** respond to them otherwise + // they will error out because we didn't respond to them. + return await interaction.reply({ + content: `This command can only be used by ${cmd.conf.permLevel}'s only`, + // This will basically set the ephemeral response to either announce + // to everyone, or just the command executioner. But we **HAVE** to + // respond. + ephemeral: settings.systemNotice !== "true" + }); + } + + // If everything checks out, run the command try { await cmd.run(client, interaction); - logger.log(`${interaction.user.id} ran slash command ${interaction.commandName}`, "cmd"); + logger.log(`${config.permLevels.find(l => l.level === level).name} ${interaction.user.id} ran slash command ${interaction.commandName}`, "cmd"); } catch (e) { console.error(e); diff --git a/events/messageCreate.js b/events/messageCreate.js index 36bb71cc..fa09e1df 100644 --- a/events/messageCreate.js +++ b/events/messageCreate.js @@ -77,6 +77,7 @@ This command requires level ${container.levelCache[cmd.conf.permLevel]} (${cmd.c await cmd.run(client, message, args, level); logger.log(`${config.permLevels.find(l => l.level === level).name} ${message.author.id} ran command ${cmd.help.name}`, "cmd"); } catch (e) { + console.error(e); message.channel.send({ content: `There was a problem with your request.\n\`\`\`${e.message}\`\`\`` }) .catch(e => console.error("An error occurred replying on an error", e)); } diff --git a/package-lock.json b/package-lock.json index d457f15a..3d4ace7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2922,9 +2922,9 @@ "dev": true }, "node_modules/tar": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz", - "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -5456,9 +5456,9 @@ } }, "tar": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz", - "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", diff --git a/slash/leave.js b/slash/leave.js index 51cac12e..0360f2eb 100644 --- a/slash/leave.js +++ b/slash/leave.js @@ -1,8 +1,8 @@ -const { Permissions } = require('discord.js'); +const { Permissions } = require("discord.js"); exports.run = async (client, interaction) => { // eslint-disable-line no-unused-vars await interaction.deferReply(); - if(!interaction.guild.me.permissions.has(Permissions.FLAGS.KICK_MEMBERS)) + if (!interaction.guild.me.permissions.has(Permissions.FLAGS.KICK_MEMBERS)) return await interaction.editReply("I do not have permission to kick members in this server."); await interaction.member.send("You requested to leave the server, if you change your mind you can rejoin at a later date."); await interaction.member.kick(`${interaction.member.displayName} wanted to leave.`); @@ -16,5 +16,9 @@ exports.commandData = { defaultPermission: true, }; -// Set this to false if you want it to be global. -exports.guildOnly = true; \ No newline at end of file +// Set guildOnly to true if you want it to be available on guilds only. +// Otherwise false is global. +exports.conf = { + permLevel: "User", + guildOnly: true +}; \ No newline at end of file diff --git a/slash/ping.js b/slash/ping.js index f3c08162..62ff1eda 100644 --- a/slash/ping.js +++ b/slash/ping.js @@ -11,5 +11,9 @@ exports.commandData = { defaultPermission: true, }; -// Set this to false if you want it to be global. -exports.guildOnly = false; \ No newline at end of file +// Set guildOnly to true if you want it to be available on guilds only. +// Otherwise false is global. +exports.conf = { + permLevel: "User", + guildOnly: false +}; \ No newline at end of file diff --git a/slash/stats.js b/slash/stats.js index 9f472512..91481ed7 100644 --- a/slash/stats.js +++ b/slash/stats.js @@ -23,5 +23,9 @@ exports.commandData = { defaultPermission: true, }; -// Set this to false if you want it to be global. -exports.guildOnly = false; \ No newline at end of file +// Set guildOnly to true if you want it to be available on guilds only. +// Otherwise false is global. +exports.conf = { + permLevel: "User", + guildOnly: false +}; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d1cf3338..0f72da29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1892,9 +1892,9 @@ "readable-stream" "^3.1.1" "tar@^6.1.0": - "integrity" "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==" - "resolved" "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz" - "version" "6.1.6" + "integrity" "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==" + "resolved" "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz" + "version" "6.1.11" dependencies: "chownr" "^2.0.0" "fs-minipass" "^2.0.0"