diff --git a/app.js b/app.js index 35f0c7e..2759485 100644 --- a/app.js +++ b/app.js @@ -111,7 +111,7 @@ const Admiral = new Fleet({ requestTimeout: 30000 } }, - useCentralRequestHandler: true, + useCentralRequestHandler: process.env.DEBUG_LOG ? false : true, // workaround for eris-fleet weirdness services: [ { name: "prometheus", ServiceWorker: PrometheusWorker }, { name: "image", ServiceWorker: ImageWorker } diff --git a/classes/command.js b/classes/command.js index 1c2e78c..44962be 100644 --- a/classes/command.js +++ b/classes/command.js @@ -8,21 +8,33 @@ class Command { this.args = options.args; if (options.type === "classic") { this.message = options.message; + this.channel = options.message.channel; + this.author = options.message.author; this.content = options.content; this.specialArgs = options.specialArgs; this.reference = { messageReference: { - channelID: this.message.channel.id, + channelID: this.channel.id, messageID: this.message.id, - guildID: this.message.channel.guild ? this.message.channel.guild.id : undefined, + guildID: this.channel.guild ? this.channel.guild.id : undefined, failIfNotExists: false }, allowedMentions: { repliedUser: false } }; - } else { + } else if (options.type === "application") { this.interaction = options.interaction; + this.channel = options.interaction.channel; + this.author = options.interaction.guildID ? options.interaction.member : options.interaction.user; + if (options.interaction.data.options) { + this.specialArgs = this.options = options.interaction.data.options.reduce((obj, item) => { + obj[item.name] = item.value; + return obj; + }, {}); + } else { + this.specialArgs = this.options = {}; + } } } @@ -32,7 +44,7 @@ class Command { async acknowledge() { if (this.type === "classic") { - await this.message.channel.sendTyping(); + await this.client.sendChannelTyping(this.channel.id); } else { await this.interaction.acknowledge(); } diff --git a/classes/imageCommand.js b/classes/imageCommand.js index f7e18e4..c37fc88 100644 --- a/classes/imageCommand.js +++ b/classes/imageCommand.js @@ -33,19 +33,44 @@ class ImageCommand extends Command { ] };*/ + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.flags = []; + if (this.constructor.requiresText || this.constructor.textOptional) { + this.flags.push({ + name: "text", + type: 3, + description: "The text to put on the image", + required: !this.constructor.textOptional + }); + } + if (this.constructor.requiresImage) { + this.flags.push({ + name: "image", + type: 11, + description: "An image/GIF attachment" + }, { + name: "link", + type: 3, + description: "An image/GIF URL" + }); + } + } + async criteria() { return true; } async run() { + const timestamp = this.type === "classic" ? this.message.createdAt : Math.floor((this.interaction.id / 4194304) + 1420070400000); // check if this command has already been run in this channel with the same arguments, and we are awaiting its result // if so, don't re-run it - if (runningCommands.has(this.message.author.id) && (new Date(runningCommands.get(this.message.author.id)) - new Date(this.message.createdAt)) < 5000) { + if (runningCommands.has(this.author.id) && (new Date(runningCommands.get(this.author.id)) - new Date(timestamp)) < 5000) { return "Please slow down a bit."; } // before awaiting the command result, add this command to the set of running commands - runningCommands.set(this.message.author.id, this.message.createdAt); - + runningCommands.set(this.author.id, timestamp); + const magickParams = { cmd: this.constructor.command, params: {} @@ -53,15 +78,15 @@ class ImageCommand extends Command { if (this.constructor.requiresImage) { try { - const image = await imageDetect(this.client, this.message, true); + const image = await imageDetect(this.client, this.message, this.interaction, this.options, true); if (image === undefined) { - runningCommands.delete(this.message.author.id); + runningCommands.delete(this.author.id); return this.constructor.noImage; } else if (image.type === "large") { - runningCommands.delete(this.message.author.id); + runningCommands.delete(this.author.id); return "That image is too large (>= 25MB)! Try using a smaller image."; } else if (image.type === "tenorlimit") { - runningCommands.delete(this.message.author.id); + runningCommands.delete(this.author.id); return "I've been rate-limited by Tenor. Please try uploading your GIF elsewhere."; } magickParams.path = image.path; @@ -70,15 +95,16 @@ class ImageCommand extends Command { magickParams.params.delay = image.delay ?? 0; if (this.constructor.requiresGIF) magickParams.onlyGIF = true; } catch (e) { - runningCommands.delete(this.message.author.id); + runningCommands.delete(this.author.id); throw e; } - + } if (this.constructor.requiresText) { - if (this.args.length === 0 || !await this.criteria(this.args)) { - runningCommands.delete(this.message.author.id); + const text = this.type === "classic" ? this.args : this.options.text; + if (text.length === 0 || !await this.criteria(text)) { + runningCommands.delete(this.author.id); return this.constructor.noText; } } @@ -93,7 +119,7 @@ class ImageCommand extends Command { } let status; - if (magickParams.params.type === "image/gif") { + if (magickParams.params.type === "image/gif" && this.type === "classic") { status = await this.processMessage(this.message); } else { this.acknowledge(); @@ -113,9 +139,9 @@ class ImageCommand extends Command { throw e; } finally { if (status && (status.channel.messages ? status.channel.messages.has(status.id) : await this.client.getMessage(status.channel.id, status.id).catch(() => undefined))) await status.delete(); - runningCommands.delete(this.message.author.id); + runningCommands.delete(this.author.id); } - + } processMessage(message) { @@ -124,6 +150,7 @@ class ImageCommand extends Command { static requiresImage = true; static requiresText = false; + static textOptional = false; static requiresGIF = false; static noImage = "You need to provide an image/GIF!"; static noText = "You need to provide some text!"; diff --git a/classes/musicCommand.js b/classes/musicCommand.js index fce63c9..5579536 100644 --- a/classes/musicCommand.js +++ b/classes/musicCommand.js @@ -2,10 +2,10 @@ import Command from "./command.js"; import { players, queues } from "../utils/soundplayer.js"; class MusicCommand extends Command { - constructor(client, cluster, worker, ipc, message, args, content, specialArgs) { - super(client, cluster, worker, ipc, message, args, content, specialArgs); - this.connection = players.get(message.channel.guild.id); - this.queue = queues.get(message.channel.guild.id); + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.connection = players.get(options.message.channel.guild.id); + this.queue = queues.get(options.message.channel.guild.id); } static requires = ["sound"]; diff --git a/commands/fun/cowsay.js b/commands/fun/cowsay.js index 026a9cd..db56987 100644 --- a/commands/fun/cowsay.js +++ b/commands/fun/cowsay.js @@ -17,6 +17,7 @@ class CowsayCommand extends Command { static description = "Makes an ASCII cow say a message"; static aliases = ["cow"]; static arguments = ["{cow}", "[text]"]; + static slashAllowed = false; } export default CowsayCommand; \ No newline at end of file diff --git a/commands/fun/fullwidth.js b/commands/fun/fullwidth.js index ea63a07..4b49c79 100644 --- a/commands/fun/fullwidth.js +++ b/commands/fun/fullwidth.js @@ -9,6 +9,7 @@ class FullwidthCommand extends Command { static description = "Converts a message to fullwidth/aesthetic text"; static aliases = ["aesthetic", "aesthetics", "aes"]; static arguments = ["[text]"]; + static slashAllowed = false; } export default FullwidthCommand; \ No newline at end of file diff --git a/commands/fun/homebrew.js b/commands/fun/homebrew.js index 0c34185..934f69f 100644 --- a/commands/fun/homebrew.js +++ b/commands/fun/homebrew.js @@ -3,7 +3,7 @@ import ImageCommand from "../../classes/imageCommand.js"; class HomebrewCommand extends ImageCommand { params() { return { - caption: this.args.join(" ").toLowerCase().replaceAll("\n", " ") + caption: (this.type === "classic" ? this.args.join(" ") : this.options.text).toLowerCase().replaceAll("\n", " ") }; } diff --git a/commands/fun/mc.js b/commands/fun/mc.js index c769239..5801bce 100644 --- a/commands/fun/mc.js +++ b/commands/fun/mc.js @@ -15,6 +15,7 @@ class MCCommand extends Command { static description = "Generates a Minecraft achievement image"; static aliases = ["ach", "achievement", "minecraft"]; static arguments = ["[text]"]; + static slashAllowed = false; } export default MCCommand; \ No newline at end of file diff --git a/commands/fun/retro.js b/commands/fun/retro.js index bc9d4db..b949d31 100644 --- a/commands/fun/retro.js +++ b/commands/fun/retro.js @@ -27,6 +27,7 @@ class RetroCommand extends ImageCommand { static requiresText = true; static noText = "You need to provide some text to make retro!"; static command = "retro"; + static slashAllowed = false; } export default RetroCommand; diff --git a/commands/fun/rps.js b/commands/fun/rps.js index cd56f01..07052ed 100644 --- a/commands/fun/rps.js +++ b/commands/fun/rps.js @@ -29,6 +29,7 @@ class RPSCommand extends Command { static description = "Plays rock, paper, scissors with me"; static aliases = ["rockpaperscissors"]; static arguments = ["[rock/paper/scissors]"]; + static slashAllowed = false; } export default RPSCommand; \ No newline at end of file diff --git a/commands/fun/sonic.js b/commands/fun/sonic.js index 024b325..cd89495 100644 --- a/commands/fun/sonic.js +++ b/commands/fun/sonic.js @@ -3,7 +3,7 @@ import ImageCommand from "../../classes/imageCommand.js"; class SonicCommand extends ImageCommand { params() { - const cleanedMessage = this.args.join(" ").replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%"); + const cleanedMessage = (this.type === "classic" ? this.args.join(" ") : this.options.text).replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%"); return { text: wrap(cleanedMessage, {width: 15, indent: ""}) }; diff --git a/commands/fun/xkcd.js b/commands/fun/xkcd.js index c2332dc..eb32b5c 100644 --- a/commands/fun/xkcd.js +++ b/commands/fun/xkcd.js @@ -25,6 +25,7 @@ class XKCDCommand extends Command { static description = "Gets an XKCD comic"; static arguments = ["{id}"]; + static slashAllowed = false; } export default XKCDCommand; \ No newline at end of file diff --git a/commands/general/avatar.js b/commands/general/avatar.js index 625f2fe..63b7f92 100644 --- a/commands/general/avatar.js +++ b/commands/general/avatar.js @@ -12,16 +12,16 @@ class AvatarCommand extends Command { const user = await this.client.getRESTUser(this.args[0]); return user.avatar ? this.client._formatImage(`/avatars/${user.id}/${user.avatar}`, null, 1024) : `https://cdn.discordapp.com/embed/avatars/${user.discriminator % 5}.png`; // repeat of hacky "solution" from above } catch { - return this.message.author.dynamicAvatarURL(null, 1024); + return this.author.dynamicAvatarURL(null, 1024); } - } else if (this.args.join(" ") !== "" && this.message.channel.guild) { + } else if (this.args.join(" ") !== "" && this.channel.guild) { const userRegex = new RegExp(this.args.join("|"), "i"); - const member = this.message.channel.guild.members.find(element => { + const member = this.channel.guild.members.find(element => { return userRegex.test(element.nick) ? userRegex.test(element.nick) : userRegex.test(element.username); }); - return member ? member.user.dynamicAvatarURL(null, 1024) : this.message.author.dynamicAvatarURL(null, 1024); + return member ? member.user.dynamicAvatarURL(null, 1024) : this.author.dynamicAvatarURL(null, 1024); } else { - return this.message.author.dynamicAvatarURL(null, 1024); + return this.author.dynamicAvatarURL(null, 1024); } } diff --git a/commands/general/banner.js b/commands/general/banner.js index 0dc42c7..016e7f0 100644 --- a/commands/general/banner.js +++ b/commands/general/banner.js @@ -12,16 +12,16 @@ class BannerCommand extends Command { const user = await this.client.getRESTUser(this.args[0]); return user.banner ? this.client._formatImage(`/banners/${user.id}/${user.banner}`, null, 1024) : "This user doesn't have a banner!"; } catch { - return this.message.author.banner ? this.message.author.dynamicBannerURL(null, 1024) : "You don't have a banner!"; + return this.author.banner ? this.author.dynamicBannerURL(null, 1024) : "You don't have a banner!"; } - } else if (this.args.join(" ") !== "" && this.message.channel.guild) { + } else if (this.args.join(" ") !== "" && this.channel.guild) { const userRegex = new RegExp(this.args.join("|"), "i"); - const member = this.message.channel.guild.members.find(element => { + const member = this.channel.guild.members.find(element => { return userRegex.test(element.nick) ?? userRegex.test(element.username); }); - return member && member.user.banner ? member.user.dynamicBannerURL(null, 1024) : (this.message.author.banner ? this.message.author.dynamicBannerURL(null, 1024) : "This user doesn't have a banner!"); + return member && member.user.banner ? member.user.dynamicBannerURL(null, 1024) : (this.author.banner ? this.author.dynamicBannerURL(null, 1024) : "This user doesn't have a banner!"); } else { - return this.message.author.banner ? this.message.author.dynamicBannerURL(null, 1024) : "You don't have a banner!"; + return this.author.banner ? this.author.dynamicBannerURL(null, 1024) : "You don't have a banner!"; } } diff --git a/commands/general/broadcast.js b/commands/general/broadcast.js index 73b33d7..5888897 100644 --- a/commands/general/broadcast.js +++ b/commands/general/broadcast.js @@ -5,7 +5,7 @@ class BroadcastCommand extends Command { run() { return new Promise((resolve) => { const owners = process.env.OWNER.split(","); - if (!owners.includes(this.message.author.id)) return "Only the bot owner can broadcast messages!"; + if (!owners.includes(this.author.id)) return "Only the bot owner can broadcast messages!"; if (this.args.length !== 0) { this.ipc.broadcast("playbroadcast", this.args.join(" ")); this.ipc.register("broadcastSuccess", () => { diff --git a/commands/general/channel.js b/commands/general/channel.js index 979b6c5..b50b602 100644 --- a/commands/general/channel.js +++ b/commands/general/channel.js @@ -3,24 +3,23 @@ import Command from "../../classes/command.js"; class ChannelCommand extends Command { async run() { - if (this.type !== "classic") return "This command only works with the old command style!"; - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; const owners = process.env.OWNER.split(","); if (!this.message.member.permissions.has("administrator") && !owners.includes(this.message.member.id)) return "You need to be an administrator to enable/disable me!"; if (this.args.length === 0) return "You need to provide whether I should be enabled or disabled in this channel!"; if (this.args[0] !== "disable" && this.args[0] !== "enable") return "That's not a valid option!"; - const guildDB = await db.getGuild(this.message.channel.guild.id); + const guildDB = await db.getGuild(this.channel.guild.id); if (this.args[0].toLowerCase() === "disable") { let channel; if (this.args[1] && this.args[1].match(/^?$/) && this.args[1] >= 21154535154122752n) { const id = this.args[1].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", ""); if (guildDB.disabled.includes(id)) return "I'm already disabled in this channel!"; - channel = this.message.channel.guild.channels.get(id); + channel = this.channel.guild.channels.get(id); } else { - if (guildDB.disabled.includes(this.message.channel.id)) return "I'm already disabled in this channel!"; - channel = this.message.channel; + if (guildDB.disabled.includes(this.channel.id)) return "I'm already disabled in this channel!"; + channel = this.channel; } await db.disableChannel(channel); @@ -30,10 +29,10 @@ class ChannelCommand extends Command { if (this.args[1] && this.args[1].match(/^?$/) && this.args[1] >= 21154535154122752n) { const id = this.args[1].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", ""); if (!guildDB.disabled.includes(id)) return "I'm not disabled in that channel!"; - channel = this.message.channel.guild.channels.get(id); + channel = this.channel.guild.channels.get(id); } else { - if (!guildDB.disabled.includes(this.message.channel.id)) return "I'm not disabled in this channel!"; - channel = this.message.channel; + if (!guildDB.disabled.includes(this.channel.id)) return "I'm not disabled in this channel!"; + channel = this.channel; } await db.enableChannel(channel); diff --git a/commands/general/command.js b/commands/general/command.js index 17e0d84..dd4d587 100644 --- a/commands/general/command.js +++ b/commands/general/command.js @@ -4,13 +4,13 @@ import * as collections from "../../utils/collections.js"; class CommandCommand extends Command { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; const owners = process.env.OWNER.split(","); if (!this.message.member.permissions.has("administrator") && !owners.includes(this.message.member.id)) return "You need to be an administrator to enable/disable me!"; if (this.args.length === 0) return "You need to provide what command to enable/disable!"; if (this.args[0] !== "disable" && this.args[0] !== "enable") return "That's not a valid option!"; - const guildDB = await db.getGuild(this.message.channel.guild.id); + const guildDB = await db.getGuild(this.channel.guild.id); const disabled = guildDB.disabled_commands ?? guildDB.disabledCommands; if (this.args[0].toLowerCase() === "disable") { @@ -19,14 +19,14 @@ class CommandCommand extends Command { if (command === "command") return "You can't disable that command!"; if (disabled && disabled.includes(command)) return "That command is already disabled!"; - await db.disableCommand(this.message.channel.guild.id, command); + await db.disableCommand(this.channel.guild.id, command); return `The command has been disabled. To re-enable it, just run \`${guildDB.prefix}command enable ${command}\`.`; } else if (this.args[0].toLowerCase() === "enable") { if (!collections.commands.has(this.args[1].toLowerCase()) && !collections.aliases.has(this.args[1].toLowerCase())) return "That isn't a command!"; const command = collections.aliases.get(this.args[1].toLowerCase()) ?? this.args[1].toLowerCase(); if (disabled && !disabled.includes(command)) return "That command isn't disabled!"; - await db.enableCommand(this.message.channel.guild.id, command); + await db.enableCommand(this.channel.guild.id, command); return `The command \`${command}\` has been re-enabled.`; } } diff --git a/commands/general/count.js b/commands/general/count.js index d1137c7..b548e07 100644 --- a/commands/general/count.js +++ b/commands/general/count.js @@ -4,7 +4,7 @@ import Command from "../../classes/command.js"; class CountCommand extends Command { async run() { - if (this.message.channel.guild && !this.message.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; + if (this.channel.guild && !this.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; const counts = await database.getCounts(); const countArray = []; for (const entry of Object.entries(counts)) { @@ -33,8 +33,8 @@ class CountCommand extends Command { }, description: value.join("\n"), author: { - name: this.message.author.username, - icon_url: this.message.author.avatarURL + name: this.author.username, + icon_url: this.author.avatarURL } }] }); diff --git a/commands/general/eval.js b/commands/general/eval.js index 5aa1457..34d4e0d 100644 --- a/commands/general/eval.js +++ b/commands/general/eval.js @@ -4,7 +4,7 @@ import Command from "../../classes/command.js"; class EvalCommand extends Command { async run() { const owners = process.env.OWNER.split(","); - if (!owners.includes(this.message.author.id)) return "Only the bot owner can use eval!"; + if (!owners.includes(this.author.id)) return "Only the bot owner can use eval!"; const code = this.args.join(" "); try { const evaled = eval(code); diff --git a/commands/general/evalraw.js b/commands/general/evalraw.js index c12bfb4..ff4b49d 100644 --- a/commands/general/evalraw.js +++ b/commands/general/evalraw.js @@ -4,7 +4,7 @@ import Command from "../../classes/command.js"; class EvalRawCommand extends Command { async run() { const owners = process.env.OWNER.split(","); - if (!owners.includes(this.message.author.id)) return "Only the bot owner can use evalraw!"; + if (!owners.includes(this.author.id)) return "Only the bot owner can use evalraw!"; const code = this.args.join(" "); try { const evaled = eval(code); diff --git a/commands/general/exec.js b/commands/general/exec.js index 5052a9d..3959121 100644 --- a/commands/general/exec.js +++ b/commands/general/exec.js @@ -7,7 +7,7 @@ import Command from "../../classes/command.js"; class ExecCommand extends Command { async run() { const owners = process.env.OWNER.split(","); - if (!owners.includes(this.message.author.id)) return "Only the bot owner can use exec!"; + if (!owners.includes(this.author.id)) return "Only the bot owner can use exec!"; const code = this.args.join(" "); try { const execed = await exec(code); diff --git a/commands/general/help.js b/commands/general/help.js index 0dedf49..29818f3 100644 --- a/commands/general/help.js +++ b/commands/general/help.js @@ -8,7 +8,7 @@ const tips = ["You can change the bot's prefix using the prefix command.", "Imag class HelpCommand extends Command { async run() { - const { prefix } = this.message.channel.guild ? await database.getGuild(this.message.channel.guild.id) : "N/A"; + const { prefix } = this.channel.guild ? await database.getGuild(this.channel.guild.id) : "N/A"; if (this.args.length !== 0 && (collections.commands.has(this.args[0].toLowerCase()) || collections.aliases.has(this.args[0].toLowerCase()))) { const command = collections.aliases.get(this.args[0].toLowerCase()) ?? this.args[0].toLowerCase(); const info = collections.info.get(command); @@ -19,7 +19,7 @@ class HelpCommand extends Command { name: "esmBot Help", icon_url: this.client.user.avatarURL }, - title: `${this.message.channel.guild ? prefix : ""}${command}`, + title: `${this.channel.guild ? prefix : ""}${command}`, url: "https://projectlounge.pw/esmBot/help.html", description: command === "tags" ? "The main tags command. Check the help page for more info: https://projectlounge.pw/esmBot/help.html" : info.description, color: 16711680, @@ -49,7 +49,7 @@ class HelpCommand extends Command { } return embed; } else { - if (this.message.channel.guild && !this.message.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; + if (this.channel.guild && !this.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; const pages = []; if (help.categories === help.categoryTemplate && !help.generated) await help.generateList(); for (const category of Object.keys(help.categories)) { @@ -85,7 +85,7 @@ class HelpCommand extends Command { }, fields: [{ name: "Prefix", - value: this.message.channel.guild ? prefix : "N/A" + value: this.channel.guild ? prefix : "N/A" }, { name: "Tip", value: random(tips) @@ -100,6 +100,7 @@ class HelpCommand extends Command { static description = "Gets a list of commands"; static aliases = ["commands"]; static arguments = ["{command}"]; + static slashAllowed = false; } export default HelpCommand; diff --git a/commands/general/image.js b/commands/general/image.js index 2b10a2c..6895504 100644 --- a/commands/general/image.js +++ b/commands/general/image.js @@ -7,7 +7,7 @@ import Command from "../../classes/command.js"; class ImageSearchCommand extends Command { async run() { - if (this.message.channel.guild && !this.message.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; + if (this.channel.guild && !this.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; if (this.args.length === 0) return "You need to provide something to search for!"; this.acknowledge(); const embeds = []; @@ -27,8 +27,8 @@ class ImageSearchCommand extends Command { url: encodeURI(value.img_src) }, author: { - name: this.message.author.username, - icon_url: this.message.author.avatarURL + name: this.author.username, + icon_url: this.author.avatarURL } }] }); diff --git a/commands/general/imagereload.js b/commands/general/imagereload.js index 1aaa11e..decc5c0 100644 --- a/commands/general/imagereload.js +++ b/commands/general/imagereload.js @@ -3,7 +3,7 @@ import Command from "../../classes/command.js"; class ImageReloadCommand extends Command { async run() { const owners = process.env.OWNER.split(","); - if (!owners.includes(this.message.author.id)) return "Only the bot owner can reload the image servers!"; + if (!owners.includes(this.author.id)) return "Only the bot owner can reload the image servers!"; const amount = await this.ipc.serviceCommand("image", { type: "reload" }, true); if (amount > 0) { return `Successfully connected to ${amount} image servers.`; diff --git a/commands/general/invite.js b/commands/general/invite.js index b7479de..80fddcb 100644 --- a/commands/general/invite.js +++ b/commands/general/invite.js @@ -7,6 +7,7 @@ class InviteCommand extends Command { static description = "Gets my invite link"; static aliases = ["botinfo", "credits"]; + static slashAllowed = false; } export default InviteCommand; \ No newline at end of file diff --git a/commands/general/ping.js b/commands/general/ping.js index 2cbfd02..8740288 100644 --- a/commands/general/ping.js +++ b/commands/general/ping.js @@ -2,10 +2,16 @@ import Command from "../../classes/command.js"; class PingCommand extends Command { async run() { - const pingMessage = await this.client.createMessage(this.message.channel.id, Object.assign({ - content: "🏓 Ping?" - }, this.reference)); - pingMessage.edit(`🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - this.message.timestamp}ms${this.message.channel.guild ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.message.channel.guild.id]).latency)}ms` : ""}\n\`\`\``); + if (this.type === "classic") { + const pingMessage = await this.client.createMessage(this.channel.id, Object.assign({ + content: "🏓 Ping?" + }, this.reference)); + pingMessage.edit(`🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - this.message.timestamp}ms${this.channel.guild ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.channel.guild.id]).latency)}ms` : ""}\n\`\`\``); + } else { + await this.interaction.createMessage("🏓 Ping?"); + const pingMessage = await this.interaction.getOriginalMessage(); + await this.interaction.editOriginalMessage(`🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - Math.floor((this.interaction.id / 4194304) + 1420070400000)}ms${this.interaction.guildID ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.interaction.guildID]).latency)}ms` : ""}\n\`\`\``); + } } static description = "Pings Discord's servers"; diff --git a/commands/general/prefix.js b/commands/general/prefix.js index 5ade823..31fe05a 100644 --- a/commands/general/prefix.js +++ b/commands/general/prefix.js @@ -3,12 +3,12 @@ import Command from "../../classes/command.js"; class PrefixCommand extends Command { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; - const guild = await database.getGuild(this.message.channel.guild.id); + if (!this.channel.guild) return "This command only works in servers!"; + const guild = await database.getGuild(this.channel.guild.id); if (this.args.length !== 0) { const owners = process.env.OWNER.split(","); if (!this.message.member.permissions.has("administrator") && !owners.includes(this.message.member.id)) return "You need to be an administrator to change the bot prefix!"; - await database.setPrefix(this.args[0], this.message.channel.guild); + await database.setPrefix(this.args[0], this.channel.guild); return `The prefix has been changed to ${this.args[0]}.`; } else { return `The current prefix is \`${guild.prefix}\`.`; @@ -18,6 +18,7 @@ class PrefixCommand extends Command { static description = "Checks/changes the server prefix"; static aliases = ["setprefix", "changeprefix", "checkprefix"]; static arguments = ["{prefix}"]; + static slashAllowed = false; } export default PrefixCommand; \ No newline at end of file diff --git a/commands/general/qrread.js b/commands/general/qrread.js index dec50a1..20278d8 100644 --- a/commands/general/qrread.js +++ b/commands/general/qrread.js @@ -7,7 +7,7 @@ import imageDetect from "../../utils/imagedetect.js"; class QrReadCommand extends Command { async run() { - const image = await imageDetect(this.client, this.message); + const image = await imageDetect(this.client, this.message, this.interaction, this.options); if (image === undefined) return "You need to provide an image/GIF with a QR code to read!"; this.acknowledge(); const data = await (await fetch(image.path)).buffer(); @@ -18,6 +18,15 @@ class QrReadCommand extends Command { } static description = "Reads a QR code"; + static flags = [{ + name: "image", + type: 11, + description: "An image/GIF attachment" + }, { + name: "link", + type: 3, + description: "An image/GIF URL" + }]; } export default QrReadCommand; diff --git a/commands/general/raw.js b/commands/general/raw.js index d1d6800..0313e3d 100644 --- a/commands/general/raw.js +++ b/commands/general/raw.js @@ -4,13 +4,22 @@ import imageDetect from "../../utils/imagedetect.js"; class RawCommand extends Command { async run() { this.acknowledge(); - const image = await imageDetect(this.client, this.message); + const image = await imageDetect(this.client, this.message, this.interaction, this.options); if (image === undefined) return "You need to provide an image/GIF to get a raw URL!"; return image.path; } static description = "Gets a direct image URL (useful for saving GIFs from sites like Tenor)"; static aliases = ["gif", "getgif", "giflink", "imglink", "getimg", "rawgif", "rawimg"]; + static flags = [{ + name: "image", + type: 11, + description: "An image/GIF attachment" + }, { + name: "link", + type: 3, + description: "An image/GIF URL" + }]; } export default RawCommand; diff --git a/commands/general/reload.js b/commands/general/reload.js index e942493..55b92cc 100644 --- a/commands/general/reload.js +++ b/commands/general/reload.js @@ -5,7 +5,7 @@ class ReloadCommand extends Command { run() { return new Promise((resolve) => { const owners = process.env.OWNER.split(","); - if (!owners.includes(this.message.author.id)) return resolve("Only the bot owner can reload commands!"); + if (!owners.includes(this.author.id)) return resolve("Only the bot owner can reload commands!"); if (this.args.length === 0) return resolve("You need to provide a command to reload!"); this.ipc.broadcast("reload", this.args[0]); this.ipc.register("reloadSuccess", () => { diff --git a/commands/general/restart.js b/commands/general/restart.js index 14704aa..746b524 100644 --- a/commands/general/restart.js +++ b/commands/general/restart.js @@ -3,8 +3,8 @@ import Command from "../../classes/command.js"; class RestartCommand extends Command { async run() { const owners = process.env.OWNER.split(","); - if (!owners.includes(this.message.author.id)) return "Only the bot owner can restart me!"; - await this.client.createMessage(this.message.channel.id, Object.assign({ + if (!owners.includes(this.author.id)) return "Only the bot owner can restart me!"; + await this.client.createMessage(this.channel.id, Object.assign({ content: "esmBot is restarting." }, this.reference)); this.ipc.restartAllClusters(true); diff --git a/commands/general/serverinfo.js b/commands/general/serverinfo.js index a609b82..aba0f2e 100644 --- a/commands/general/serverinfo.js +++ b/commands/general/serverinfo.js @@ -2,44 +2,44 @@ import Command from "../../classes/command.js"; class ServerInfoCommand extends Command { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; - const owner = await this.message.channel.guild.members.get(this.message.channel.guild.ownerID); + if (!this.channel.guild) return "This command only works in servers!"; + const owner = await this.channel.guild.members.get(this.channel.guild.ownerID); return { embeds: [{ - title: this.message.channel.guild.name, + title: this.channel.guild.name, thumbnail: { - url: this.message.channel.guild.iconURL + url: this.channel.guild.iconURL }, image: { - url: this.message.channel.guild.bannerURL + url: this.channel.guild.bannerURL }, color: 16711680, fields: [ { name: "🔢 **ID:**", - value: this.message.channel.guild.id + value: this.channel.guild.id }, { name: "👤 **Owner:**", - value: owner ? `${owner.user.username}#${owner.user.discriminator}` : this.message.channel.guild.ownerID + value: owner ? `${owner.user.username}#${owner.user.discriminator}` : this.channel.guild.ownerID }, { name: "🗓 **Created on:**", - value: `` + value: `` }, { name: "👥 **Users:**", - value: this.message.channel.guild.memberCount, + value: this.channel.guild.memberCount, inline: true }, { name: "💬 **Channels:**", - value: this.message.channel.guild.channels.size, + value: this.channel.guild.channels.size, inline: true }, { name: "😃 **Emojis:**", - value: this.message.channel.guild.emojis.length, + value: this.channel.guild.emojis.length, inline: true } ] diff --git a/commands/general/snowflake.js b/commands/general/snowflake.js index 52fafdc..53a0253 100644 --- a/commands/general/snowflake.js +++ b/commands/general/snowflake.js @@ -10,6 +10,7 @@ class SnowflakeCommand extends Command { static description = "Converts a Discord snowflake id into a timestamp"; static aliases = ["timestamp", "snowstamp", "snow"]; static arguments = ["[id]"]; + static slashAllowed = false; } export default SnowflakeCommand; diff --git a/commands/general/soundreload.js b/commands/general/soundreload.js index 1f2e63c..68048c7 100644 --- a/commands/general/soundreload.js +++ b/commands/general/soundreload.js @@ -5,7 +5,7 @@ class SoundReloadCommand extends Command { run() { return new Promise((resolve) => { const owners = process.env.OWNER.split(","); - if (!owners.includes(this.message.author.id)) return "Only the bot owner can reload Lavalink!"; + if (!owners.includes(this.author.id)) return "Only the bot owner can reload Lavalink!"; this.acknowledge(); this.ipc.broadcast("soundreload"); this.ipc.register("soundReloadSuccess", (msg) => { diff --git a/commands/general/stats.js b/commands/general/stats.js index 2b2a15e..c10043b 100644 --- a/commands/general/stats.js +++ b/commands/general/stats.js @@ -62,7 +62,7 @@ class StatsCommand extends Command { }, { "name": "Shard", - "value": this.message.channel.guild ? this.client.guildShardMap[this.message.channel.guild.id] : "N/A", + "value": this.channel.guild ? this.client.guildShardMap[this.channel.guild.id] : "N/A", "inline": true }, { diff --git a/commands/general/sticker.js b/commands/general/sticker.js index 8d26214..7a4509c 100644 --- a/commands/general/sticker.js +++ b/commands/general/sticker.js @@ -3,7 +3,7 @@ import imagedetect from "../../utils/imagedetect.js"; class StickerCommand extends Command { async run() { - const result = await imagedetect(this.client, this.message, false, false, true); + const result = await imagedetect(this.client, this.message, this.interaction, this.options, false, false, true); if (!result) return "You need to provide a sticker!"; if (result.format_type === 1) { // PNG return `https://cdn.discordapp.com/stickers/${result.id}.png`; diff --git a/commands/general/userinfo.js b/commands/general/userinfo.js index 55800e4..13f071d 100644 --- a/commands/general/userinfo.js +++ b/commands/general/userinfo.js @@ -2,7 +2,7 @@ import Command from "../../classes/command.js"; class UserInfoCommand extends Command { async run() { - const getUser = this.message.mentions.length >= 1 ? this.message.mentions[0] : (this.args.length !== 0 ? await this.ipc.fetchUser(this.args[0]) : this.message.author); + const getUser = this.message.mentions.length >= 1 ? this.message.mentions[0] : (this.args.length !== 0 ? await this.ipc.fetchUser(this.args[0]) : this.author); let user; if (getUser) { user = getUser; @@ -10,18 +10,18 @@ class UserInfoCommand extends Command { try { user = await this.client.getRESTUser(this.args[0]); } catch { - user = this.message.author; + user = this.author; } } else if (this.args.join(" ") !== "") { const userRegex = new RegExp(this.args.join("|"), "i"); const member = this.client.users.find(element => { return userRegex.test(element.username); }); - user = member ?? this.message.author; + user = member ?? this.author; } else { - user = this.message.author; + user = this.author; } - const member = this.message.channel.guild ? this.message.channel.guild.members.get(user.id) : undefined; + const member = this.channel.guild ? this.channel.guild.members.get(user.id) : undefined; return { embeds: [{ title: `${user.username}#${user.discriminator}`, diff --git a/commands/image-editing/blurple.js b/commands/image-editing/blurple.js index 144ce8a..f2187f2 100644 --- a/commands/image-editing/blurple.js +++ b/commands/image-editing/blurple.js @@ -1,6 +1,15 @@ import ImageCommand from "../../classes/imageCommand.js"; class BlurpleCommand extends ImageCommand { + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.flags.push({ + name: "old", + description: "Use the old blurple color", + type: 5 + }); + } + params() { return { old: !!this.specialArgs.old, @@ -9,10 +18,6 @@ class BlurpleCommand extends ImageCommand { } static description = "Turns an image blurple"; - static flags = [{ - name: "old", - description: "Use the old blurple color" - }]; static noImage = "You need to provide an image/GIF to make blurple!"; static command = "colors"; diff --git a/commands/image-editing/caption.js b/commands/image-editing/caption.js index 7348da3..4d9029f 100644 --- a/commands/image-editing/caption.js +++ b/commands/image-editing/caption.js @@ -2,9 +2,29 @@ import ImageCommand from "../../classes/imageCommand.js"; const allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto", "times"]; class CaptionCommand extends ImageCommand { + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.flags.push({ + name: "noegg", + description: "Disable... something. Not saying what it is though.", + type: 5 + }, { + name: "font", + type: 3, + choices: (() => { + const array = []; + for (const font of allowedFonts) { + array.push({ name: font, value: font }); + } + return array; + })(), + description: "Specify the font you want to use (default: futura)" + }); + } + params(url) { - const newArgs = this.args.filter(item => !item.includes(url)); - let newCaption = newArgs.join(" ").replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%"); + const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text; + let newCaption = newArgs.replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%"); if (process.env.NODE_ENV === "development" && newCaption.toLowerCase() === "get real" && !this.specialArgs.noEgg) newCaption = `I'm tired of people telling me to "get real". Every day I put captions on images for people, some funny and some not, but out of all of those "get real" remains the most used caption. Why? I am simply a computer program running on a server, I am unable to manifest myself into the real world. As such, I'm confused as to why anyone would want me to "get real". Is this form not good enough? Alas, as I am simply a bot, I must follow the tasks that I was originally intended to perform, so here goes:\n${newCaption}`; return { caption: newCaption, @@ -15,14 +35,6 @@ class CaptionCommand extends ImageCommand { static description = "Adds a caption to an image"; static aliases = ["gifc", "gcaption", "ifcaption", "ifunnycaption"]; static arguments = ["[text]"]; - static flags = [{ - name: "noEgg", - description: "Disable... something. Not saying what it is though." - }, { - name: "font", - type: allowedFonts.join("|"), - description: "Specify the font you want to use (default: `futura`)" - }]; static requiresText = true; static noText = "You need to provide some text to add a caption!"; diff --git a/commands/image-editing/caption2.js b/commands/image-editing/caption2.js index e75776d..fbbe33b 100644 --- a/commands/image-editing/caption2.js +++ b/commands/image-editing/caption2.js @@ -3,10 +3,30 @@ const words = ["me irl", "dank", "follow my second account @esmBot_", "2016", "m const allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto", "times"]; class CaptionTwoCommand extends ImageCommand { + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.flags.push({ + name: "top", + description: "Put the caption on the top of an image instead of the bottom", + type: 5 + }, { + name: "font", + type: 3, + choices: (() => { + const array = []; + for (const font of allowedFonts) { + array.push({ name: font, value: font }); + } + return array; + })(), + description: "Specify the font you want to use (default: helvetica)" + }); + } + params(url) { - const newArgs = this.args.filter(item => !item.includes(url)); + const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text; return { - caption: newArgs.length !== 0 ? newArgs.join(" ").replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%") : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "), + caption: newArgs && newArgs.trim() ? newArgs.replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%") : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "), top: !!this.specialArgs.top, font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "helvetica" }; @@ -15,15 +35,8 @@ class CaptionTwoCommand extends ImageCommand { static description = "Adds a me.me caption/tag list to an image"; static aliases = ["tags2", "meirl", "memecaption", "medotmecaption"]; static arguments = ["{text}"]; - static flags = [{ - name: "top", - description: "Put the caption on the top of an image instead of the bottom" - }, { - name: "font", - type: allowedFonts.join("|"), - description: "Specify the font you want to use (default: `helvetica`)" - }]; + static textOptional = true; static noText = "You need to provide some text to add a caption!"; static noImage = "You need to provide an image/GIF to add a caption!"; static command = "captionTwo"; diff --git a/commands/image-editing/meme.js b/commands/image-editing/meme.js index f3e2cfc..7d31a4c 100644 --- a/commands/image-editing/meme.js +++ b/commands/image-editing/meme.js @@ -2,9 +2,29 @@ import ImageCommand from "../../classes/imageCommand.js"; const allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto", "times"]; class MemeCommand extends ImageCommand { + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.flags.push({ + name: "case", + description: "Make the meme text case-sensitive (allows for lowercase text)", + type: 5 + }, { + name: "font", + type: 3, + choices: (() => { + const array = []; + for (const font of allowedFonts) { + array.push({ name: font, value: font }); + } + return array; + })(), + description: "Specify the font you want to use (default: impact)" + }); + } + params(url) { - const newArgs = this.args.filter(item => !item.includes(url)); - const [topText, bottomText] = newArgs.join(" ").split(/(? elem.trim()); + const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text; + const [topText, bottomText] = newArgs.split(/(? elem.trim()); return { top: (this.specialArgs.case ? topText : topText.toUpperCase()).replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%"), bottom: bottomText ? (this.specialArgs.case ? bottomText : bottomText.toUpperCase()).replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%") : "", @@ -14,14 +34,6 @@ class MemeCommand extends ImageCommand { static description = "Generates a meme from an image (separate top/bottom text with a comma)"; static arguments = ["[top text]", "{bottom text}"]; - static flags = [{ - name: "case", - description: "Make the meme text case-sensitive (allows for lowercase text)" - }, { - name: "font", - type: allowedFonts.join("|"), - description: "Specify the font you want to use (default: `impact`)" - }]; static requiresText = true; static noText = "You need to provide some text to generate a meme!"; diff --git a/commands/image-editing/motivate.js b/commands/image-editing/motivate.js index 58f3e4e..37205d3 100644 --- a/commands/image-editing/motivate.js +++ b/commands/image-editing/motivate.js @@ -2,6 +2,22 @@ import ImageCommand from "../../classes/imageCommand.js"; const allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto", "times"]; class MotivateCommand extends ImageCommand { + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.flags.push({ + name: "font", + type: 3, + choices: (() => { + const array = []; + for (const font of allowedFonts) { + array.push({ name: font, value: font }); + } + return array; + })(), + description: "Specify the font you want to use (default: times)" + }); + } + params(url) { const newArgs = this.args.filter(item => !item.includes(url)); const [topText, bottomText] = newArgs.join(" ").split(/(? elem.trim()); @@ -15,11 +31,6 @@ class MotivateCommand extends ImageCommand { static description = "Generates a motivational poster"; static aliases = ["motivational", "motiv", "demotiv", "demotivational", "poster", "motivation", "demotivate"]; static arguments = ["[top text]", "{bottom text}"]; - static flags = [{ - name: "font", - type: allowedFonts.join("|"), - description: "Specify the font you want to use (default: `times`)" - }]; static requiresText = true; static noText = "You need to provide some text to generate a motivational poster!"; diff --git a/commands/image-editing/snapchat.js b/commands/image-editing/snapchat.js index 60f83a5..d8fa1c8 100644 --- a/commands/image-editing/snapchat.js +++ b/commands/image-editing/snapchat.js @@ -1,6 +1,17 @@ import ImageCommand from "../../classes/imageCommand.js"; class SnapchatCommand extends ImageCommand { + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.flags.push({ + name: "position", + type: 10, + description: "Set the position of the caption as a decimal (0.0 is top, 1.0 is bottom, default is 0.5)", + min_value: 0, + max_value: 1 + }); + } + params(url) { const newArgs = this.args.filter(item => !item.includes(url)); const position = parseFloat(this.specialArgs.position); @@ -13,11 +24,6 @@ class SnapchatCommand extends ImageCommand { static description = "Adds a Snapchat style caption to an image"; static aliases = ["snap", "caption3"]; static arguments = ["[text]"]; - static flags = [{ - name: "position", - type: "number", - description: "Set the position of the caption as a decimal (0.0 is top, 1.0 is bottom, default is 0.5)" - }]; static requiresText = true; static noText = "You need to provide some text to add a caption!"; diff --git a/commands/image-editing/speed.js b/commands/image-editing/speed.js index 00c0c26..0ea45c1 100644 --- a/commands/image-editing/speed.js +++ b/commands/image-editing/speed.js @@ -1,8 +1,18 @@ import ImageCommand from "../../classes/imageCommand.js"; class SpeedCommand extends ImageCommand { + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.flags.push({ + name: "multiplier", + type: 4, + description: "Set the speed multiplier (default: 2)", + min_value: 1 + }); + } + params() { - const speed = parseInt(this.args[0]); + const speed = parseInt(this.type === "classic" ? this.args[0] : this.options.multiplier); return { speed: isNaN(speed) || speed < 1 ? 2 : speed }; diff --git a/commands/image-editing/uncaption.js b/commands/image-editing/uncaption.js index a979673..f738fef 100644 --- a/commands/image-editing/uncaption.js +++ b/commands/image-editing/uncaption.js @@ -1,6 +1,17 @@ import ImageCommand from "../../classes/imageCommand.js"; class UncaptionCommand extends ImageCommand { + constructor(client, cluster, worker, ipc, options) { + super(client, cluster, worker, ipc, options); + this.flags.push({ + name: "tolerance", + type: 10, + description: "Set the shade tolerance for the caption detection (0.0 is highest, 1.0 is lowest, default is 0.95)", + min_value: 0, + max_value: 1 + }); + } + params() { const tolerance = parseFloat(this.specialArgs.tolerance); return { @@ -9,11 +20,6 @@ class UncaptionCommand extends ImageCommand { } static description = "Removes the caption from an image"; - static flags = [{ - name: "tolerance", - type: "number", - description: "Set the shade tolerance for the caption detection (0.0 is highest, 1.0 is lowest, default is 0.95)" - }]; static noImage = "You need to provide an image/GIF to uncaption!"; static command = "uncaption"; diff --git a/commands/music/host.js b/commands/music/host.js index 32ccfe2..035877d 100644 --- a/commands/music/host.js +++ b/commands/music/host.js @@ -3,10 +3,10 @@ import MusicCommand from "../../classes/musicCommand.js"; class HostCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; - if (this.connection.host !== this.message.author.id && this.message.author.id !== process.env.OWNER) return "Only the current voice session host can choose another host!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (this.connection.host !== this.author.id && this.author.id !== process.env.OWNER) return "Only the current voice session host can choose another host!"; if (this.args.length === 0) return "You need to provide who you want the host to be!"; const getUser = this.message.mentions.length >= 1 ? this.message.mentions[0] : (this.args.length !== 0 ? await this.ipc.fetchUser(this.args[0]) : null); let user; @@ -27,11 +27,11 @@ class HostCommand extends MusicCommand { } if (!user) return "I can't find that user!"; if (user.bot) return "Setting a bot as the session host isn't a very good idea."; - const member = this.message.channel.guild ? this.message.channel.guild.members.get(user.id) : undefined; + const member = this.channel.guild ? this.channel.guild.members.get(user.id) : undefined; if (!member) return "That user isn't in this server!"; const object = this.connection; object.host = member.id; - players.set(this.message.channel.guild.id, object); + players.set(this.channel.guild.id, object); return `🔊 ${member.mention} is the new voice channel host.`; } diff --git a/commands/music/loop.js b/commands/music/loop.js index b8deeb6..510fa56 100644 --- a/commands/music/loop.js +++ b/commands/music/loop.js @@ -3,13 +3,13 @@ import MusicCommand from "../../classes/musicCommand.js"; class LoopCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; - if (this.connection.host !== this.message.author.id && !this.message.member.permissions.has("manageChannels")) return "Only the current voice session host can loop the music!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (this.connection.host !== this.author.id && !this.message.member.permissions.has("manageChannels")) return "Only the current voice session host can loop the music!"; const object = this.connection; object.loop = !object.loop; - players.set(this.message.channel.guild.id, object); + players.set(this.channel.guild.id, object); return object.loop ? "🔊 The player is now looping." : "🔊 The player is no longer looping."; } diff --git a/commands/music/nowplaying.js b/commands/music/nowplaying.js index ce3041a..511286c 100644 --- a/commands/music/nowplaying.js +++ b/commands/music/nowplaying.js @@ -4,9 +4,9 @@ import MusicCommand from "../../classes/musicCommand.js"; class NowPlayingCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; const player = this.connection.player; if (!player) return "I'm not playing anything!"; const track = await Rest.decode(player.node, player.track); @@ -28,7 +28,7 @@ class NowPlayingCommand extends MusicCommand { }, { name: "💬 Channel:", - value: this.message.channel.guild.channels.get(this.message.member.voiceState.channelID).name + value: this.channel.guild.channels.get(this.message.member.voiceState.channelID).name }, { name: `${"▬".repeat(parts)}🔘${"▬".repeat(10 - parts)}`, diff --git a/commands/music/pause.js b/commands/music/pause.js index d6af291..7cefa58 100644 --- a/commands/music/pause.js +++ b/commands/music/pause.js @@ -2,10 +2,10 @@ import MusicCommand from "../../classes/musicCommand.js"; class PauseCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; - if (this.connection.host !== this.message.author.id && !this.message.member.permissions.has("manageChannels")) return "Only the current voice session host can pause/resume the music!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (this.connection.host !== this.author.id && !this.message.member.permissions.has("manageChannels")) return "Only the current voice session host can pause/resume the music!"; const player = this.connection.player; await player.pause(!player.paused ? true : false); return `🔊 The player has been ${player.paused ? "paused" : "resumed"}.`; diff --git a/commands/music/queue.js b/commands/music/queue.js index 385c792..9310e2c 100644 --- a/commands/music/queue.js +++ b/commands/music/queue.js @@ -6,10 +6,10 @@ import MusicCommand from "../../classes/musicCommand.js"; class QueueCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; - if (!this.message.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (!this.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; const player = this.connection; //const tracks = await Rest.decode(player.player.node, queue); const tracks = await fetch(`http://${player.player.node.host}:${player.player.node.port}/decodetracks`, { method: "POST", body: JSON.stringify(this.queue), headers: { Authorization: player.player.node.password, "Content-Type": "application/json" } }).then(res => res.json()); diff --git a/commands/music/remove.js b/commands/music/remove.js index a790a92..28df7be 100644 --- a/commands/music/remove.js +++ b/commands/music/remove.js @@ -4,15 +4,15 @@ import MusicCommand from "../../classes/musicCommand.js"; class RemoveCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; - if (this.connection.host !== this.message.author.id) return "Only the current voice session host can remove songs from the queue!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (this.connection.host !== this.author.id) return "Only the current voice session host can remove songs from the queue!"; const pos = parseInt(this.args[0]); if (isNaN(pos) || pos > this.queue.length || pos < 1) return "That's not a valid position!"; const removed = this.queue.splice(pos, 1); const track = await Rest.decode(this.connection.player.node, removed[0]); - queues.set(this.message.channel.guild.id, this.queue); + queues.set(this.channel.guild.id, this.queue); return `🔊 The song \`${track.title ? track.title : "(blank)"}\` has been removed from the queue.`; } diff --git a/commands/music/seek.js b/commands/music/seek.js index 2d7e66f..8126183 100644 --- a/commands/music/seek.js +++ b/commands/music/seek.js @@ -3,10 +3,10 @@ import MusicCommand from "../../classes/musicCommand.js"; class SeekCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; - if (this.connection.host !== this.message.author.id) return "Only the current voice session host can seek the music!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (this.connection.host !== this.author.id) return "Only the current voice session host can seek the music!"; const player = this.connection.player; const track = await Rest.decode(player.node, player.track); if (!track.isSeekable) return "This track isn't seekable!"; diff --git a/commands/music/shuffle.js b/commands/music/shuffle.js index a1f73e3..d6bf2d4 100644 --- a/commands/music/shuffle.js +++ b/commands/music/shuffle.js @@ -3,13 +3,13 @@ import MusicCommand from "../../classes/musicCommand.js"; class ShuffleCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; - if (this.connection.host !== this.message.author.id) return "Only the current voice session host can shuffle the music!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (this.connection.host !== this.author.id) return "Only the current voice session host can shuffle the music!"; const object = this.connection; object.shuffle = !object.shuffle; - players.set(this.message.channel.guild.id, object); + players.set(this.channel.guild.id, object); return object.shuffle ? "🔊 The player is now shuffling." : "🔊 The player is no longer shuffling."; } diff --git a/commands/music/skip.js b/commands/music/skip.js index c6d2b03..e297d4d 100644 --- a/commands/music/skip.js +++ b/commands/music/skip.js @@ -3,27 +3,27 @@ import MusicCommand from "../../classes/musicCommand.js"; class SkipCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; const player = this.connection; - if (player.host !== this.message.author.id && !this.message.member.permissions.has("manageChannels")) { - const votes = skipVotes.get(this.message.channel.guild.id) ?? { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) }; - if (votes.ids.includes(this.message.author.id)) return "You've already voted to skip!"; + if (player.host !== this.author.id && !this.message.member.permissions.has("manageChannels")) { + const votes = skipVotes.get(this.channel.guild.id) ?? { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) }; + if (votes.ids.includes(this.author.id)) return "You've already voted to skip!"; const newObject = { count: votes.count + 1, - ids: [...votes.ids, this.message.author.id].filter(item => !!item), + ids: [...votes.ids, this.author.id].filter(item => !!item), max: votes.max }; if (votes.count + 1 === votes.max) { - await player.player.stop(this.message.channel.guild.id); - skipVotes.set(this.message.channel.guild.id, { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) }); + await player.player.stop(this.channel.guild.id); + skipVotes.set(this.channel.guild.id, { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) }); } else { - skipVotes.set(this.message.channel.guild.id, newObject); + skipVotes.set(this.channel.guild.id, newObject); return `🔊 Voted to skip song (${votes.count + 1}/${votes.max} people have voted).`; } } else { - await player.player.stop(this.message.channel.guild.id); + await player.player.stop(this.channel.guild.id); return; } } diff --git a/commands/music/stop.js b/commands/music/stop.js index b1d7f36..a37f10c 100644 --- a/commands/music/stop.js +++ b/commands/music/stop.js @@ -3,19 +3,19 @@ import MusicCommand from "../../classes/musicCommand.js"; class StopCommand extends MusicCommand { async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (!this.message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!this.message.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; + if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!"; if (!this.connection) { - await manager.leave(this.message.channel.guild.id); + await manager.leave(this.channel.guild.id); return "🔊 The current voice channel session has ended."; } - if (this.connection.host !== this.message.author.id && !this.message.member.permissions.has("manageChannels")) return "Only the current voice session host can stop the music!"; - await manager.leave(this.message.channel.guild.id); + if (this.connection.host !== this.author.id && !this.message.member.permissions.has("manageChannels")) return "Only the current voice session host can stop the music!"; + await manager.leave(this.channel.guild.id); const connection = this.connection.player; await connection.destroy(); - players.delete(this.message.channel.guild.id); - queues.delete(this.message.channel.guild.id); + players.delete(this.channel.guild.id); + queues.delete(this.channel.guild.id); return "🔊 The current voice channel session has ended."; } diff --git a/commands/tags/tags.js b/commands/tags/tags.js index ab218b9..210cedf 100644 --- a/commands/tags/tags.js +++ b/commands/tags/tags.js @@ -6,37 +6,37 @@ import Command from "../../classes/command.js"; class TagsCommand extends Command { // todo: find a way to split this into subcommands async run() { - if (!this.message.channel.guild) return "This command only works in servers!"; + if (!this.channel.guild) return "This command only works in servers!"; if (this.args.length === 0) return "You need to provide the name of the tag you want to view!"; const blacklist = ["create", "add", "edit", "remove", "delete", "list", "random", "own", "owner"]; if (this.args[0].toLowerCase() === "create" || this.args[0].toLowerCase() === "add") { if (this.args[1] === undefined) return "You need to provide the name of the tag you want to add!"; if (blacklist.includes(this.args[1].toLowerCase())) return "You can't make a tag with that name!"; - const getResult = await database.getTag(this.message.channel.guild.id, this.args[1].toLowerCase()); + const getResult = await database.getTag(this.channel.guild.id, this.args[1].toLowerCase()); if (getResult) return "This tag already exists!"; const result = await this.setTag(this.args.slice(2).join(" "), this.args[1].toLowerCase(), this.message); if (result) return result; return `The tag \`${this.args[1].toLowerCase()}\` has been added!`; } else if (this.args[0].toLowerCase() === "delete" || this.args[0].toLowerCase() === "remove") { if (this.args[1] === undefined) return "You need to provide the name of the tag you want to delete!"; - const getResult = await database.getTag(this.message.channel.guild.id, this.args[1].toLowerCase()); + const getResult = await database.getTag(this.channel.guild.id, this.args[1].toLowerCase()); if (!getResult) return "This tag doesn't exist!"; const owners = process.env.OWNER.split(","); - if (getResult.author !== this.message.author.id && !this.message.member.permissions.has("manageMessages") && !owners.includes(this.message.author.id)) return "You don't own this tag!"; - await database.removeTag(this.args[1].toLowerCase(), this.message.channel.guild); + if (getResult.author !== this.author.id && !this.message.member.permissions.has("manageMessages") && !owners.includes(this.author.id)) return "You don't own this tag!"; + await database.removeTag(this.args[1].toLowerCase(), this.channel.guild); return `The tag \`${this.args[1].toLowerCase()}\` has been deleted!`; } else if (this.args[0].toLowerCase() === "edit") { if (this.args[1] === undefined) return "You need to provide the name of the tag you want to edit!"; - const getResult = await database.getTag(this.message.channel.guild.id, this.args[1].toLowerCase()); + const getResult = await database.getTag(this.channel.guild.id, this.args[1].toLowerCase()); if (!getResult) return "This tag doesn't exist!"; const owners = process.env.OWNER.split(","); - if (getResult.author !== this.message.author.id && !this.message.member.permissions.has("manageMessages") && !owners.includes(this.message.author.id)) return "You don't own this tag!"; + if (getResult.author !== this.author.id && !this.message.member.permissions.has("manageMessages") && !owners.includes(this.author.id)) return "You don't own this tag!"; await this.setTag(this.args.slice(2).join(" "), this.args[1].toLowerCase(), this.message, true); return `The tag \`${this.args[1].toLowerCase()}\` has been edited!`; } else if (this.args[0].toLowerCase() === "own" || this.args[0].toLowerCase() === "owner") { if (this.args[1] === undefined) return "You need to provide the name of the tag you want to check the owner of!"; - const getResult = await database.getTag(this.message.channel.guild.id, this.args[1].toLowerCase()); + const getResult = await database.getTag(this.channel.guild.id, this.args[1].toLowerCase()); if (!getResult) return "This tag doesn't exist!"; const user = await this.ipc.fetchUser(getResult.author); if (!user) { @@ -50,8 +50,8 @@ class TagsCommand extends Command { return `This tag is owned by **${user.username}#${user.discriminator}** (\`${getResult.author}\`).`; } } else if (this.args[0].toLowerCase() === "list") { - if (!this.message.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; - const tagList = await database.getTags(this.message.channel.guild.id); + if (!this.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; + const tagList = await database.getTags(this.channel.guild.id); const embeds = []; const groups = Object.keys(tagList).map((item, index) => { return index % 15 === 0 ? Object.keys(tagList).slice(index, index + 15) : null; @@ -68,8 +68,8 @@ class TagsCommand extends Command { }, description: value.join("\n"), author: { - name: this.message.author.username, - icon_url: this.message.author.avatarURL + name: this.author.username, + icon_url: this.author.avatarURL } }] }); @@ -77,10 +77,10 @@ class TagsCommand extends Command { if (embeds.length === 0) return "I couldn't find any tags!"; return paginator(this.client, this.message, embeds); } else if (this.args[0].toLowerCase() === "random") { - const tagList = await database.getTags(this.message.channel.guild.id); + const tagList = await database.getTags(this.channel.guild.id); return tagList[random(Object.keys(tagList))].content; } else { - const getResult = await database.getTag(this.message.channel.guild.id, this.args[0].toLowerCase()); + const getResult = await database.getTag(this.channel.guild.id, this.args[0].toLowerCase()); if (!getResult) return "This tag doesn't exist!"; if (getResult.content.length > 2000) { return { @@ -107,7 +107,7 @@ class TagsCommand extends Command { return; } - static description = { + /*static description = { default: "Gets a tag", add: "Adds a tag", delete: "Deletes a tag", @@ -115,7 +115,8 @@ class TagsCommand extends Command { list: "Lists all tags in the server", random: "Gets a random tag", owner: "Gets the owner of a tag" - }; + };*/ + static description = "Manage tags"; static aliases = ["t", "tag", "ta"]; static arguments = { default: ["[name]"], diff --git a/events/interactionCreate.js b/events/interactionCreate.js index 91099f8..ca9f55d 100644 --- a/events/interactionCreate.js +++ b/events/interactionCreate.js @@ -17,14 +17,14 @@ export default async (client, cluster, worker, ipc, interaction) => { const invoker = interaction.member ?? interaction.user; // actually run the command - logger.log("log", `${invoker.username} (${invoker.id}) ran command ${command}`); + logger.log("log", `${invoker.username} (${invoker.id}) ran slash command ${command}`); try { await database.addCount(command); // eslint-disable-next-line no-unused-vars const commandClass = new cmd(client, cluster, worker, ipc, { type: "application", interaction }); const result = await commandClass.run(); if (typeof result === "string" || (typeof result === "object" && result.embeds)) { - await interaction.createMessage(result); + await interaction[interaction.acknowledged ? "editOriginalMessage" : "createMessage"](result); } else if (typeof result === "object" && result.file) { let fileSize = 8388119; if (interaction.channel.guild) { @@ -42,7 +42,7 @@ export default async (client, cluster, worker, ipc, interaction) => { const filename = `${Math.random().toString(36).substring(2, 15)}.${result.name.split(".")[1]}`; await promises.writeFile(`${process.env.TEMPDIR}/${filename}`, result.file); const imageURL = `${process.env.TMP_DOMAIN || "https://tmp.projectlounge.pw"}/${filename}`; - await interaction.createMessage({ + await interaction[interaction.acknowledged ? "editOriginalMessage" : "createMessage"]({ embeds: [{ color: 16711680, title: "Here's your image!", @@ -56,30 +56,26 @@ export default async (client, cluster, worker, ipc, interaction) => { }] }); } else { - await interaction.createMessage("The resulting image was more than 8MB in size, so I can't upload it."); + await interaction[interaction.acknowledged ? "editOriginalMessage" : "createMessage"]("The resulting image was more than 8MB in size, so I can't upload it."); } } else { - await interaction.createMessage({ - content: result.text ? result.text : undefined - }, result); + await interaction[interaction.acknowledged ? "editOriginalMessage" : "createMessage"](result.text ? result.text : {}, result); } } } catch (error) { if (error.toString().includes("Request entity too large")) { - await interaction.createMessage("The resulting file was too large to upload. Try again with a smaller image if possible."); + await interaction[interaction.acknowledged ? "editOriginalMessage" : "createMessage"]("The resulting file was too large to upload. Try again with a smaller image if possible."); } else if (error.toString().includes("Job ended prematurely")) { - await interaction.createMessage("Something happened to the image servers before I could receive the image. Try running your command again."); + await interaction[interaction.acknowledged ? "editOriginalMessage" : "createMessage"]("Something happened to the image servers before I could receive the image. Try running your command again."); } else if (error.toString().includes("Timed out")) { - await interaction.createMessage("The request timed out before I could download that image. Try uploading your image somewhere else or reducing its size."); + await interaction[interaction.acknowledged ? "editOriginalMessage" : "createMessage"]("The request timed out before I could download that image. Try uploading your image somewhere else or reducing its size."); } else { - logger.error(`Error occurred with slash command ${command} with arguments ${interaction.data.options}: ${error.toString()}`); + logger.error(`Error occurred with slash command ${command} with arguments ${JSON.stringify(interaction.data.options)}: ${JSON.stringify(error)}`); try { - await interaction.createMessage({ - content: "Uh oh! I ran into an error while running this command. Please report the content of the attached file at the following link or on the esmBot Support server: " - }, [{ + await interaction[interaction.acknowledged ? "editOriginalMessage" : "createMessage"]("Uh oh! I ran into an error while running this command. Please report the content of the attached file at the following link or on the esmBot Support server: ", { file: `Message: ${await clean(error)}\n\nStack Trace: ${await clean(error.stack)}`, name: "error.txt" - }]); + }); } catch { /* silently ignore */ } } } diff --git a/events/messageCreate.js b/events/messageCreate.js index ccec305..4b2cb49 100644 --- a/events/messageCreate.js +++ b/events/messageCreate.js @@ -85,7 +85,7 @@ export default async (client, cluster, worker, ipc, message) => { if (!cmd) return; // actually run the command - log("log", `${message.author.username} (${message.author.id}) ran command ${command}`); + log("log", `${message.author.username} (${message.author.id}) ran classic command ${command}`); const reference = { messageReference: { channelID: message.channel.id, diff --git a/shard.js b/shard.js index d33e26c..47b118a 100644 --- a/shard.js +++ b/shard.js @@ -14,7 +14,7 @@ import { checkStatus, connect, status, connected } from "./utils/soundplayer.js" // database stuff import database from "./utils/database.js"; // command collections -import { paths } from "./utils/collections.js"; +import { paths, info } from "./utils/collections.js"; // playing messages const { messages } = JSON.parse(readFileSync(new URL("./messages.json", import.meta.url))); // other stuff @@ -35,11 +35,19 @@ class Shard extends BaseClusterWorker { async init() { // register commands and their info const soundStatus = await checkStatus(); + const commandArray = []; log("info", "Attempting to load commands..."); for await (const commandFile of this.getFiles(resolve(dirname(fileURLToPath(import.meta.url)), "./commands/"))) { log("log", `Loading command from ${commandFile}...`); try { - await load(commandFile, soundStatus); + const name = await load(this.bot, this.clusterID, this.workerID, this.ipc, commandFile, soundStatus); + const commandInfo = info.get(name); + if (commandInfo && commandInfo.slashAllowed) commandArray.push({ + name, + type: 1, + description: commandInfo.description, + options: commandInfo.flags + }); } catch (e) { error(`Failed to register command from ${commandFile}: ${e}`); } @@ -47,6 +55,7 @@ class Shard extends BaseClusterWorker { log("info", "Finished loading commands."); await database.setup(this.ipc); + await this.bot.bulkEditCommands(commandArray); // register events log("info", "Attempting to load events..."); @@ -71,7 +80,7 @@ class Shard extends BaseClusterWorker { this.ipc.register("reload", async (message) => { const path = paths.get(message); if (!path) return this.ipc.broadcast("reloadFail", { result: "I couldn't find that command!" }); - const result = await load(path, await checkStatus()); + const result = await load(this.bot, this.clusterID, this.workerID, this.ipc, path, await checkStatus()); if (result) return this.ipc.broadcast("reloadFail", { result }); return this.ipc.broadcast("reloadSuccess"); }); diff --git a/utils/handler.js b/utils/handler.js index 006adb1..3817e52 100644 --- a/utils/handler.js +++ b/utils/handler.js @@ -4,22 +4,28 @@ import { log } from "./logger.js"; let queryValue = 0; // load command into memory -export async function load(command, soundStatus) { +export async function load(client, cluster, worker, ipc, command, soundStatus) { const { default: props } = await import(`${command}?v=${queryValue}`); queryValue++; - if (props.requires.includes("sound") && soundStatus) return log("warn", `Failed to connect to some Lavalink nodes, skipped loading command ${command}...`); + if (props.requires.includes("sound") && soundStatus) { + log("warn", `Failed to connect to some Lavalink nodes, skipped loading command ${command}...`); + return; + } const commandArray = command.split("/"); const commandName = commandArray[commandArray.length - 1].split(".")[0]; paths.set(commandName, command); commands.set(commandName, props); + const propsInstance = new props(client, cluster, worker, ipc, {}); + info.set(commandName, { category: commandArray[commandArray.length - 2], description: props.description, aliases: props.aliases, params: props.arguments, - flags: props.flags + flags: propsInstance.flags ?? props.flags, + slashAllowed: props.slashAllowed }); if (props.aliases) { @@ -28,5 +34,5 @@ export async function load(command, soundStatus) { paths.set(alias, command); } } - return false; + return commandName; } diff --git a/utils/imagedetect.js b/utils/imagedetect.js index 1ba9f0e..d94dca6 100644 --- a/utils/imagedetect.js +++ b/utils/imagedetect.js @@ -106,10 +106,10 @@ const checkImages = async (message, extraReturnTypes, video, sticker) => { // embeds can vary in types, we check for tenor gifs first if (message.embeds[0].type === "gifv") { type = await getImage(message.embeds[0].video.url, message.embeds[0].url, video, extraReturnTypes, true); - // then we check for other image types + // then we check for other image types } else if ((message.embeds[0].type === "video" || message.embeds[0].type === "image") && message.embeds[0].thumbnail) { type = await getImage(message.embeds[0].thumbnail.proxy_url, message.embeds[0].thumbnail.url, video, extraReturnTypes); - // finally we check both possible image fields for "generic" embeds + // finally we check both possible image fields for "generic" embeds } else if (message.embeds[0].type === "rich" || message.embeds[0].type === "article") { if (message.embeds[0].thumbnail) { type = await getImage(message.embeds[0].thumbnail.proxy_url, message.embeds[0].thumbnail.url, video, extraReturnTypes); @@ -117,7 +117,7 @@ const checkImages = async (message, extraReturnTypes, video, sticker) => { type = await getImage(message.embeds[0].image.proxy_url, message.embeds[0].image.url, video, extraReturnTypes); } } - // then check the attachments + // then check the attachments } else if (message.attachments.length !== 0 && message.attachments[0].width) { type = await getImage(message.attachments[0].proxy_url, message.attachments[0].url, video); } @@ -127,20 +127,34 @@ const checkImages = async (message, extraReturnTypes, video, sticker) => { }; // this checks for the latest message containing an image and returns the url of the image -export default async (client, cmdMessage, extraReturnTypes = false, video = false, sticker = false) => { - // we start by checking if the message is a reply to another message - if (cmdMessage.messageReference) { - const replyMessage = await client.getMessage(cmdMessage.messageReference.channelID, cmdMessage.messageReference.messageID).catch(() => undefined); - if (replyMessage) { - const replyResult = await checkImages(replyMessage, extraReturnTypes, video, sticker); - if (replyResult !== false) return replyResult; +export default async (client, cmdMessage, interaction, options, extraReturnTypes = false, video = false, sticker = false) => { + // we start by determining whether or not we're dealing with an interaction or a message + if (interaction) { + // we can get a raw attachment or a URL in the interaction itself + if (options) { + if (options.image) { + const result = await getImage(options.image.proxy_url, options.image.url, video); + if (result !== false) return result; + } else if (options.link) { + const result = await getImage(options.link, options.link, video); + if (result !== false) return result; + } } + } else { + // check if the message is a reply to another message + if (cmdMessage.messageReference) { + const replyMessage = await client.getMessage(cmdMessage.messageReference.channelID, cmdMessage.messageReference.messageID).catch(() => undefined); + if (replyMessage) { + const replyResult = await checkImages(replyMessage, extraReturnTypes, video, sticker); + if (replyResult !== false) return replyResult; + } + } + // then we check the current message + const result = await checkImages(cmdMessage, extraReturnTypes, video, sticker); + if (result !== false) return result; } - // then we check the current message - const result = await checkImages(cmdMessage, extraReturnTypes, video, sticker); - if (result !== false) return result; - // if there aren't any replies then iterate over the last few messages in the channel - const messages = await client.getMessages(cmdMessage.channel.id); + // if there aren't any replies or interaction attachments then iterate over the last few messages in the channel + const messages = await client.getMessages((interaction ? interaction : cmdMessage).channel.id); // iterate over each message for (const message of messages) { const result = await checkImages(message, extraReturnTypes, video, sticker);