diff --git a/assets/images/speechbubble.png b/assets/images/speechbubble.png index f28e70d..72c67f0 100644 Binary files a/assets/images/speechbubble.png and b/assets/images/speechbubble.png differ diff --git a/classes/command.js b/classes/command.js index 44962be..56729d7 100644 --- a/classes/command.js +++ b/classes/command.js @@ -4,12 +4,14 @@ class Command { this.cluster = cluster; this.worker = worker; this.ipc = ipc; + this.origOptions = options; this.type = options.type; this.args = options.args; if (options.type === "classic") { this.message = options.message; this.channel = options.message.channel; this.author = options.message.author; + this.member = options.message.member; this.content = options.content; this.specialArgs = options.specialArgs; this.reference = { @@ -26,12 +28,13 @@ class Command { } 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; + this.author = this.member = 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; }, {}); + this.optionsArray = options.interaction.data.options; } else { this.specialArgs = this.options = {}; } @@ -50,6 +53,10 @@ class Command { } } + static init() { + return this; + } + static description = "No description found"; static aliases = []; static arguments = []; diff --git a/classes/imageCommand.js b/classes/imageCommand.js index c37fc88..5d0c5c5 100644 --- a/classes/imageCommand.js +++ b/classes/imageCommand.js @@ -33,30 +33,6 @@ 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; } @@ -98,7 +74,6 @@ class ImageCommand extends Command { runningCommands.delete(this.author.id); throw e; } - } if (this.constructor.requiresText) { @@ -122,7 +97,7 @@ class ImageCommand extends Command { if (magickParams.params.type === "image/gif" && this.type === "classic") { status = await this.processMessage(this.message); } else { - this.acknowledge(); + await this.acknowledge(); } try { @@ -148,6 +123,30 @@ class ImageCommand extends Command { return this.client.createMessage(message.channel.id, `${random(emotes) || process.env.PROCESSING_EMOJI || ""} Processing... This might take a while`); } + static init() { + this.flags = []; + if (this.requiresText || this.textOptional) { + this.flags.push({ + name: "text", + type: 3, + description: "The text to put on the image", + required: !this.textOptional + }); + } + if (this.requiresImage) { + this.flags.push({ + name: "image", + type: 11, + description: "An image/GIF attachment" + }, { + name: "link", + type: 3, + description: "An image/GIF URL" + }); + } + return this; + } + static requiresImage = true; static requiresText = false; static textOptional = false; diff --git a/classes/soundboardCommand.js b/classes/soundboardCommand.js new file mode 100644 index 0000000..cd47731 --- /dev/null +++ b/classes/soundboardCommand.js @@ -0,0 +1,14 @@ +import Command from "./command.js"; +import { play } from "../utils/soundplayer.js"; + +// only exists to sort the various soundboard commands +class SoundboardCommand extends Command { + async run() { + return await play(this.client, this.constructor.file, { channel: this.channel, author: this.author, type: this.type, interaction: this.interaction }); + } + + static requires = ["sound"]; + static slashAllowed = false; +} + +export default SoundboardCommand; diff --git a/commands/fun/8ball.js b/commands/fun/8ball.js index d84594d..65e53e1 100644 --- a/commands/fun/8ball.js +++ b/commands/fun/8ball.js @@ -29,6 +29,12 @@ class EightBallCommand extends Command { return `🎱 ${random(EightBallCommand.responses)}`; } + static flags = [{ + name: "question", + type: 3, + description: "A question you want to ask the ball" + }]; + static description = "Asks the magic 8-ball a question"; static aliases = ["magicball", "magikball", "magic8ball", "magik8ball", "eightball"]; static arguments = ["{text}"]; diff --git a/commands/fun/ancient.js b/commands/fun/ancient.js index a9ad913..224507a 100644 --- a/commands/fun/ancient.js +++ b/commands/fun/ancient.js @@ -3,7 +3,7 @@ import Command from "../../classes/command.js"; class AncientCommand extends Command { async run() { - this.acknowledge(); + await this.acknowledge(); const controller = new AbortController(); // eslint-disable-line no-undef const timeout = setTimeout(() => { controller.abort(); diff --git a/commands/fun/bird.js b/commands/fun/bird.js index cfd5b27..fb558d8 100644 --- a/commands/fun/bird.js +++ b/commands/fun/bird.js @@ -3,7 +3,7 @@ import Command from "../../classes/command.js"; class BirdCommand extends Command { async run() { - this.acknowledge(); + await this.acknowledge(); const imageData = await fetch("http://shibe.online/api/birds"); const json = await imageData.json(); return { diff --git a/commands/fun/cat.js b/commands/fun/cat.js index 62451cd..4ad1734 100644 --- a/commands/fun/cat.js +++ b/commands/fun/cat.js @@ -3,7 +3,7 @@ import Command from "../../classes/command.js"; class CatCommand extends Command { async run() { - this.acknowledge(); + await this.acknowledge(); const controller = new AbortController(); // eslint-disable-line no-undef const timeout = setTimeout(() => { controller.abort(); diff --git a/commands/fun/dog.js b/commands/fun/dog.js index 8014fd2..1b7158b 100644 --- a/commands/fun/dog.js +++ b/commands/fun/dog.js @@ -3,7 +3,7 @@ import Command from "../../classes/command.js"; class DogCommand extends Command { async run() { - this.acknowledge(); + await this.acknowledge(); const imageData = await fetch("https://dog.ceo/api/breeds/image/random"); const json = await imageData.json(); return { diff --git a/commands/fun/mc.js b/commands/fun/mc.js index 5801bce..6e97c12 100644 --- a/commands/fun/mc.js +++ b/commands/fun/mc.js @@ -4,7 +4,7 @@ import Command from "../../classes/command.js"; class MCCommand extends Command { async run() { if (this.args.length === 0) return "You need to provide some text to generate a Minecraft achievement!"; - this.acknowledge(); + await this.acknowledge(); const request = await fetch(`https://www.minecraftskinstealer.com/achievement/a.php?i=13&h=Achievement+get%21&t=${encodeURIComponent(this.args.join("+"))}`); return { file: Buffer.from(await request.arrayBuffer()), diff --git a/commands/fun/wikihow.js b/commands/fun/wikihow.js index 09d0366..65f543f 100644 --- a/commands/fun/wikihow.js +++ b/commands/fun/wikihow.js @@ -3,7 +3,7 @@ import Command from "../../classes/command.js"; class WikihowCommand extends Command { async run() { - this.acknowledge(); + await this.acknowledge(); const request = await fetch("https://www.wikihow.com/api.php?action=query&generator=random&prop=imageinfo&format=json&iiprop=url&grnnamespace=6"); const json = await request.json(); const id = Object.keys(json.query.pages)[0]; diff --git a/commands/general/channel.js b/commands/general/channel.js index b50b602..b11c651 100644 --- a/commands/general/channel.js +++ b/commands/general/channel.js @@ -5,7 +5,7 @@ class ChannelCommand extends Command { async run() { 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.member.permissions.has("administrator") && !owners.includes(this.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!"; diff --git a/commands/general/command.js b/commands/general/command.js index dd4d587..7bcdd56 100644 --- a/commands/general/command.js +++ b/commands/general/command.js @@ -6,7 +6,7 @@ class CommandCommand extends Command { async run() { 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.member.permissions.has("administrator") && !owners.includes(this.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!"; diff --git a/commands/general/donate.js b/commands/general/donate.js index 5c2016b..2b6e74b 100644 --- a/commands/general/donate.js +++ b/commands/general/donate.js @@ -3,7 +3,7 @@ import Command from "../../classes/command.js"; class DonateCommand extends Command { async run() { - this.acknowledge(); + await this.acknowledge(); let prefix = ""; const controller = new AbortController(); // eslint-disable-line no-undef const timeout = setTimeout(() => { diff --git a/commands/general/emote.js b/commands/general/emote.js index 8ab6af6..0205f10 100644 --- a/commands/general/emote.js +++ b/commands/general/emote.js @@ -3,12 +3,13 @@ import Command from "../../classes/command.js"; class EmoteCommand extends Command { async run() { - if (this.args.length === 0) return "You need to provide an emoji!"; - if (this.content.split(" ")[0].match(/^$/)) { - return `https://cdn.discordapp.com/emojis/${this.content.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$2")}.${this.content.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$1") === "a" ? "gif" : "png"}`; - } else if (this.args[0].match(emojiRegex)) { + const emoji = this.type === "classic" ? this.args.join(" ") : this.options.emoji; + if (!emoji || !emoji.trim()) return "You need to provide an emoji!"; + if (emoji.split(" ")[0].match(/^$/)) { + return `https://cdn.discordapp.com/emojis/${emoji.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$2")}.${emoji.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$1") === "a" ? "gif" : "png"}`; + } else if (emoji.match(emojiRegex)) { const codePoints = []; - for (const codePoint of this.args[0]) { + for (const codePoint of emoji) { codePoints.push(codePoint.codePointAt(0).toString(16)); } return `https://twemoji.maxcdn.com/v/latest/72x72/${codePoints.join("-").replace("-fe0f", "")}.png`; @@ -17,6 +18,13 @@ class EmoteCommand extends Command { } } + static flags = [{ + name: "emoji", + type: 3, + description: "The emoji you want to get", + required: true + }]; + static description = "Gets a raw emote image"; static aliases = ["e", "em", "hugemoji", "hugeemoji", "emoji"]; static arguments = ["[emote]"]; diff --git a/commands/general/image.js b/commands/general/image.js index e85744a..42bb546 100644 --- a/commands/general/image.js +++ b/commands/general/image.js @@ -10,7 +10,7 @@ class ImageSearchCommand extends Command { if (this.channel.guild && !this.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!"; const query = this.type === "classic" ? this.args.join(" ") : this.options.query; if (!query || !query.trim()) return "You need to provide something to search for!"; - this.acknowledge(); + await this.acknowledge(); const embeds = []; const rawImages = await fetch(`${random(searx)}/search?format=json&safesearch=2&categories=images&q=!goi%20!ddi%20${encodeURIComponent(query)}`).then(res => res.json()); if (rawImages.results.length === 0) return "I couldn't find any results!"; diff --git a/commands/general/lengthen.js b/commands/general/lengthen.js index 6fd0542..a19a920 100644 --- a/commands/general/lengthen.js +++ b/commands/general/lengthen.js @@ -4,16 +4,24 @@ import Command from "../../classes/command.js"; class LengthenCommand extends Command { async run() { - this.acknowledge(); - if (this.args.length === 0 || !urlCheck(this.args[0])) return "You need to provide a short URL to lengthen!"; - if (urlCheck(this.args[0])) { - const url = await fetch(encodeURI(this.args[0]), { redirect: "manual" }); - return url.headers.get("location") || this.args[0]; + await this.acknowledge(); + const input = this.type === "classic" ? this.args.join(" ") : this.options.url; + if (!input || !input.trim() || !urlCheck(input)) return "You need to provide a short URL to lengthen!"; + if (urlCheck(input)) { + const url = await fetch(encodeURI(input), { redirect: "manual" }); + return url.headers.get("location") || input; } else { return "That isn't a URL!"; } } + static flags = [{ + name: "url", + type: 3, + description: "The URL you want to lengthen", + required: true + }]; + static description = "Lengthens a short URL"; static aliases = ["longurl", "lengthenurl", "longuri", "lengthenuri", "unshorten"]; static arguments = ["[url]"]; diff --git a/commands/general/prefix.js b/commands/general/prefix.js index 31fe05a..a671b2d 100644 --- a/commands/general/prefix.js +++ b/commands/general/prefix.js @@ -7,7 +7,7 @@ class PrefixCommand extends Command { 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!"; + if (!this.member.permissions.has("administrator") && !owners.includes(this.member.id)) return "You need to be an administrator to change the bot prefix!"; await database.setPrefix(this.args[0], this.channel.guild); return `The prefix has been changed to ${this.args[0]}.`; } else { diff --git a/commands/general/qrcreate.js b/commands/general/qrcreate.js index a2f27da..4864bda 100644 --- a/commands/general/qrcreate.js +++ b/commands/general/qrcreate.js @@ -5,7 +5,7 @@ import Command from "../../classes/command.js"; class QrCreateCommand extends Command { async run() { if (this.args.length === 0) return "You need to provide some text to generate a QR code!"; - this.acknowledge(); + await this.acknowledge(); const writable = new PassThrough(); qrcode.toFileStream(writable, this.content, { margin: 1 }); const file = await this.streamToBuf(writable); diff --git a/commands/general/qrread.js b/commands/general/qrread.js index 20278d8..48c82c4 100644 --- a/commands/general/qrread.js +++ b/commands/general/qrread.js @@ -9,8 +9,8 @@ class QrReadCommand extends Command { async run() { 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(); + await this.acknowledge(); + const data = Buffer.from(await (await fetch(image.path)).arrayBuffer()); const rawData = await sharp(data).ensureAlpha().raw().toBuffer({ resolveWithObject: true }); const qrBuffer = jsqr(rawData.data, rawData.info.width, rawData.info.height); if (!qrBuffer) return "I couldn't find a QR code!"; diff --git a/commands/general/raw.js b/commands/general/raw.js index 0313e3d..4337569 100644 --- a/commands/general/raw.js +++ b/commands/general/raw.js @@ -3,7 +3,7 @@ import imageDetect from "../../utils/imagedetect.js"; class RawCommand extends Command { async run() { - this.acknowledge(); + await this.acknowledge(); 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; diff --git a/commands/general/reload.js b/commands/general/reload.js index 5288db7..3c4afd2 100644 --- a/commands/general/reload.js +++ b/commands/general/reload.js @@ -8,17 +8,18 @@ class ReloadCommand extends Command { if (!owners.includes(this.author.id)) return resolve("Only the bot owner can reload commands!"); const commandName = this.type === "classic" ? this.args.join(" ") : this.options.cmd; if (!commandName || !commandName.trim()) return resolve("You need to provide a command to reload!"); - this.acknowledge(); - this.ipc.broadcast("reload", commandName); - this.ipc.register("reloadSuccess", () => { - this.ipc.unregister("reloadSuccess"); - this.ipc.unregister("reloadFail"); - resolve(`The command \`${commandName}\` has been reloaded.`); - }); - this.ipc.register("reloadFail", (message) => { - this.ipc.unregister("reloadSuccess"); - this.ipc.unregister("reloadFail"); - resolve(message.result); + this.acknowledge().then(() => { + this.ipc.broadcast("reload", commandName); + this.ipc.register("reloadSuccess", () => { + this.ipc.unregister("reloadSuccess"); + this.ipc.unregister("reloadFail"); + resolve(`The command \`${commandName}\` has been reloaded.`); + }); + this.ipc.register("reloadFail", (message) => { + this.ipc.unregister("reloadSuccess"); + this.ipc.unregister("reloadFail"); + resolve(message.result); + }); }); }); } diff --git a/commands/general/soundreload.js b/commands/general/soundreload.js index 68048c7..bbba443 100644 --- a/commands/general/soundreload.js +++ b/commands/general/soundreload.js @@ -6,18 +6,20 @@ class SoundReloadCommand extends Command { return new Promise((resolve) => { const owners = process.env.OWNER.split(","); 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) => { - this.ipc.unregister("soundReloadSuccess"); - this.ipc.unregister("soundReloadFail"); - resolve(`Successfully connected to ${msg.length} Lavalink node(s).`); - }); - this.ipc.register("soundReloadFail", () => { - this.ipc.unregister("soundReloadSuccess"); - this.ipc.unregister("soundReloadFail"); - resolve("I couldn't connect to any Lavalink nodes!"); + this.acknowledge().then(() => { + this.ipc.broadcast("soundreload"); + this.ipc.register("soundReloadSuccess", (msg) => { + this.ipc.unregister("soundReloadSuccess"); + this.ipc.unregister("soundReloadFail"); + resolve(`Successfully connected to ${msg.length} Lavalink node(s).`); + }); + this.ipc.register("soundReloadFail", () => { + this.ipc.unregister("soundReloadSuccess"); + this.ipc.unregister("soundReloadFail"); + resolve("I couldn't connect to any Lavalink nodes!"); + }); }); + }); } diff --git a/commands/general/youtube.js b/commands/general/youtube.js index a19d81c..79d754c 100644 --- a/commands/general/youtube.js +++ b/commands/general/youtube.js @@ -9,7 +9,7 @@ class YouTubeCommand extends Command { async run() { const query = this.type === "classic" ? this.args.join(" ") : this.options.query; if (!query || !query.trim()) return "You need to provide something to search for!"; - this.acknowledge(); + await this.acknowledge(); const messages = []; const videos = await fetch(`${random(searx)}/search?format=json&safesearch=1&categories=videos&q=!youtube%20${encodeURIComponent(query)}`).then(res => res.json()); if (videos.results.length === 0) return "I couldn't find any results!"; diff --git a/commands/image-editing/blurple.js b/commands/image-editing/blurple.js index f2187f2..e4b39dc 100644 --- a/commands/image-editing/blurple.js +++ b/commands/image-editing/blurple.js @@ -1,15 +1,6 @@ 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, @@ -19,6 +10,16 @@ class BlurpleCommand extends ImageCommand { static description = "Turns an image blurple"; + static init() { + super.init(); + this.flags.push({ + name: "old", + description: "Use the old blurple color", + type: 5 + }); + return this; + } + static noImage = "You need to provide an image/GIF to make blurple!"; static command = "colors"; static aliases = ["blurp"]; diff --git a/commands/image-editing/caption.js b/commands/image-editing/caption.js index 4d9029f..4d2104f 100644 --- a/commands/image-editing/caption.js +++ b/commands/image-editing/caption.js @@ -2,8 +2,18 @@ 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); + params(url) { + 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, + font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "futura" + }; + } + + static init() { + super.init(); this.flags.push({ name: "noegg", description: "Disable... something. Not saying what it is though.", @@ -20,16 +30,7 @@ class CaptionCommand extends ImageCommand { })(), description: "Specify the font you want to use (default: futura)" }); - } - - params(url) { - 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, - font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "futura" - }; + return this; } static description = "Adds a caption to an image"; diff --git a/commands/image-editing/caption2.js b/commands/image-editing/caption2.js index fbbe33b..d4beb26 100644 --- a/commands/image-editing/caption2.js +++ b/commands/image-editing/caption2.js @@ -3,8 +3,17 @@ 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); + params(url) { + const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text; + return { + 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" + }; + } + + static init() { + super.init(); this.flags.push({ name: "top", description: "Put the caption on the top of an image instead of the bottom", @@ -21,15 +30,7 @@ class CaptionTwoCommand extends ImageCommand { })(), description: "Specify the font you want to use (default: helvetica)" }); - } - - params(url) { - const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text; - return { - 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" - }; + return this; } static description = "Adds a me.me caption/tag list to an image"; diff --git a/commands/image-editing/freeze.js b/commands/image-editing/freeze.js index 3e5f9cd..546e687 100644 --- a/commands/image-editing/freeze.js +++ b/commands/image-editing/freeze.js @@ -1,16 +1,6 @@ import ImageCommand from "../../classes/imageCommand.js"; class FreezeCommand extends ImageCommand { - constructor(client, cluster, worker, ipc, options) { - super(client, cluster, worker, ipc, options); - this.flags.push({ - name: "endframe", - type: 4, - description: "Set the end frame (default: last frame)", - min_value: 0 - }); - } - params() { const frameCount = parseInt(this.type === "classic" ? this.args[0] : this.options.endframe); return { @@ -19,6 +9,17 @@ class FreezeCommand extends ImageCommand { }; } + static init() { + super.init(); + this.flags.push({ + name: "endframe", + type: 4, + description: "Set the end frame (default: last frame)", + min_value: 0 + }); + return this; + } + static description = "Makes an image sequence only play once"; static aliases = ["noloop", "once"]; static arguments = ["{end frame number}"]; diff --git a/commands/image-editing/jpeg.js b/commands/image-editing/jpeg.js index af480ad..7125626 100644 --- a/commands/image-editing/jpeg.js +++ b/commands/image-editing/jpeg.js @@ -1,8 +1,15 @@ import ImageCommand from "../../classes/imageCommand.js"; class JPEGCommand extends ImageCommand { - constructor(client, cluster, worker, ipc, options) { - super(client, cluster, worker, ipc, options); + params() { + const quality = parseInt(this.type === "classic" ? this.args[0] : this.options.quality); + return { + quality: isNaN(quality) ? 1 : Math.max(1, Math.min(quality, 100)) + }; + } + + static init() { + super.init(); this.flags.push({ name: "quality", type: 4, @@ -10,13 +17,7 @@ class JPEGCommand extends ImageCommand { min_value: 1, max_value: 100 }); - } - - params() { - const quality = parseInt(this.type === "classic" ? this.args[0] : this.options.quality); - return { - quality: isNaN(quality) ? 1 : Math.max(1, Math.min(quality, 100)) - }; + return this; } static description = "Adds JPEG compression to an image"; diff --git a/commands/image-editing/meme.js b/commands/image-editing/meme.js index 7d31a4c..c78e203 100644 --- a/commands/image-editing/meme.js +++ b/commands/image-editing/meme.js @@ -2,8 +2,18 @@ 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); + params(url) { + 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("%", "\\%") : "", + font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "impact" + }; + } + + static init() { + super.init(); this.flags.push({ name: "case", description: "Make the meme text case-sensitive (allows for lowercase text)", @@ -20,16 +30,7 @@ class MemeCommand extends ImageCommand { })(), description: "Specify the font you want to use (default: impact)" }); - } - - params(url) { - 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("%", "\\%") : "", - font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "impact" - }; + return this; } static description = "Generates a meme from an image (separate top/bottom text with a comma)"; diff --git a/commands/image-editing/motivate.js b/commands/image-editing/motivate.js index e8e8e55..8af4722 100644 --- a/commands/image-editing/motivate.js +++ b/commands/image-editing/motivate.js @@ -2,8 +2,18 @@ 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); + params(url) { + 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: topText.replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%"), + bottom: bottomText ? bottomText.replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%") : "", + font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "times" + }; + } + + static init() { + super.init(); this.flags.push({ name: "font", type: 3, @@ -16,16 +26,7 @@ class MotivateCommand extends ImageCommand { })(), description: "Specify the font you want to use (default: times)" }); - } - - params(url) { - 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: topText.replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%"), - bottom: bottomText ? bottomText.replaceAll("&", "\\&").replaceAll(">", "\\>").replaceAll("<", "\\<").replaceAll("\"", "\\"").replaceAll("'", "\\'").replaceAll("%", "\\%") : "", - font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "times" - }; + return this; } static description = "Generates a motivational poster"; diff --git a/commands/image-editing/slow.js b/commands/image-editing/slow.js index e85293f..43a7e6c 100644 --- a/commands/image-editing/slow.js +++ b/commands/image-editing/slow.js @@ -1,16 +1,6 @@ import ImageCommand from "../../classes/imageCommand.js"; class SlowCommand 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.type === "classic" ? this.args[0] : this.options.multiplier); return { @@ -19,6 +9,17 @@ class SlowCommand extends ImageCommand { }; } + static init() { + super.init(); + this.flags.push({ + name: "multiplier", + type: 4, + description: "Set the speed multiplier (default: 2)", + min_value: 1 + }); + return this; + } + static description = "Makes an image sequence slower"; static aliases = ["slowdown", "slower", "gifspeed2"]; static arguments = ["{multiplier}"]; diff --git a/commands/image-editing/snapchat.js b/commands/image-editing/snapchat.js index a14b6be..79ebd1f 100644 --- a/commands/image-editing/snapchat.js +++ b/commands/image-editing/snapchat.js @@ -1,17 +1,6 @@ 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.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text; const position = parseFloat(this.specialArgs.position); @@ -21,6 +10,18 @@ class SnapchatCommand extends ImageCommand { }; } + static init() { + super.init(); + 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 + }); + return this; + } + static description = "Adds a Snapchat style caption to an image"; static aliases = ["snap", "caption3"]; static arguments = ["[text]"]; diff --git a/commands/image-editing/speed.js b/commands/image-editing/speed.js index 0ea45c1..c2b1030 100644 --- a/commands/image-editing/speed.js +++ b/commands/image-editing/speed.js @@ -1,21 +1,22 @@ import ImageCommand from "../../classes/imageCommand.js"; class SpeedCommand extends ImageCommand { - constructor(client, cluster, worker, ipc, options) { - super(client, cluster, worker, ipc, options); + params() { + const speed = parseInt(this.type === "classic" ? this.args[0] : this.options.multiplier); + return { + speed: isNaN(speed) || speed < 1 ? 2 : speed + }; + } + + static init() { + super.init(); this.flags.push({ name: "multiplier", type: 4, description: "Set the speed multiplier (default: 2)", min_value: 1 }); - } - - params() { - const speed = parseInt(this.type === "classic" ? this.args[0] : this.options.multiplier); - return { - speed: isNaN(speed) || speed < 1 ? 2 : speed - }; + return this; } static description = "Makes an image sequence faster"; diff --git a/commands/image-editing/uncaption.js b/commands/image-editing/uncaption.js index f738fef..324c456 100644 --- a/commands/image-editing/uncaption.js +++ b/commands/image-editing/uncaption.js @@ -1,8 +1,15 @@ import ImageCommand from "../../classes/imageCommand.js"; class UncaptionCommand extends ImageCommand { - constructor(client, cluster, worker, ipc, options) { - super(client, cluster, worker, ipc, options); + params() { + const tolerance = parseFloat(this.specialArgs.tolerance); + return { + tolerance: isNaN(tolerance) ? 0.95 : tolerance + }; + } + + static init() { + super.init(); this.flags.push({ name: "tolerance", type: 10, @@ -10,13 +17,7 @@ class UncaptionCommand extends ImageCommand { min_value: 0, max_value: 1 }); - } - - params() { - const tolerance = parseFloat(this.specialArgs.tolerance); - return { - tolerance: isNaN(tolerance) ? 0.95 : tolerance - }; + return this; } static description = "Removes the caption from an image"; diff --git a/commands/music/host.js b/commands/music/host.js index 035877d..4df7be3 100644 --- a/commands/music/host.js +++ b/commands/music/host.js @@ -4,26 +4,31 @@ import MusicCommand from "../../classes/musicCommand.js"; class HostCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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); + const input = this.type === "classic" ? this.args.join(" ") : this.options.user; + if (!input || !input.trim()) return "You need to provide who you want the host to be!"; let user; - if (getUser) { - user = getUser; - } else if (this.args[0].match(/^?$/) && this.args[0] >= 21154535154122752n) { - try { - user = await this.client.getRESTUser(this.args[0]); - } catch { - // no-op + if (this.type === "classic") { + const getUser = this.message.mentions.length >= 1 ? this.message.mentions[0] : (await this.ipc.fetchUser(input)); + if (getUser) { + user = getUser; + } else if (input.match(/^?$/) && input >= 21154535154122752n) { + try { + user = await this.client.getRESTUser(input); + } catch { + // no-op + } + } else { + const userRegex = new RegExp(input.split(" ").join("|"), "i"); + const member = this.client.users.find(element => { + return userRegex.test(element.username); + }); + user = member; } - } 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; + } else { + user = input; } 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."; @@ -35,6 +40,12 @@ class HostCommand extends MusicCommand { return `🔊 ${member.mention} is the new voice channel host.`; } + static flags = [{ + name: "user", + type: 6, + description: "The user you want the new host to be", + required: true + }]; static description = "Changes the host of the current voice session"; static aliases = ["sethost"]; } diff --git a/commands/music/loop.js b/commands/music/loop.js index 510fa56..fe5c2d6 100644 --- a/commands/music/loop.js +++ b/commands/music/loop.js @@ -4,9 +4,9 @@ import MusicCommand from "../../classes/musicCommand.js"; class LoopCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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!"; + if (this.connection.host !== this.author.id && !this.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.channel.guild.id, object); diff --git a/commands/music/music.js b/commands/music/music.js new file mode 100644 index 0000000..9d8da7d --- /dev/null +++ b/commands/music/music.js @@ -0,0 +1,43 @@ +import Command from "../../classes/command.js"; +import { commands, aliases, info, categories } from "../../utils/collections.js"; + +// all-in-one music command +class MusicAIOCommand extends Command { + async run() { + let cmd = this.type === "classic" ? this.args[0] : this.optionsArray[0].name; + if (cmd === "music" || this.constructor.aliases.includes(cmd)) return "How dare you recurse me!"; + if (this.type === "classic") { + this.origOptions.args.shift(); + } else { + this.origOptions.interaction.data.options = this.origOptions.interaction.data.options[0].options; + } + if (aliases.has(cmd)) cmd = aliases.get(cmd); + if (commands.has(cmd) && info.get(cmd).category === "music") { + const command = commands.get(cmd); + return await (new command(this.client, this.cluster, this.worker, this.ipc, this.origOptions)).run(); + } else { + return "That isn't a valid music command!"; + } + } + + static postInit() { + this.flags = []; + for (const cmd of categories.get("music")) { + if (cmd === "music") continue; + const cmdInfo = info.get(cmd); + this.flags.push({ + name: cmd, + type: 1, + description: cmdInfo.description, + options: cmdInfo.flags + }); + } + return this; + } + + static description = "Handles music playback"; + static requires = ["sound"]; + static aliases = ["m"]; +} + +export default MusicAIOCommand; diff --git a/commands/music/nowplaying.js b/commands/music/nowplaying.js index 511286c..0de811c 100644 --- a/commands/music/nowplaying.js +++ b/commands/music/nowplaying.js @@ -5,7 +5,7 @@ import MusicCommand from "../../classes/musicCommand.js"; class NowPlayingCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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!"; @@ -28,7 +28,7 @@ class NowPlayingCommand extends MusicCommand { }, { name: "💬 Channel:", - value: this.channel.guild.channels.get(this.message.member.voiceState.channelID).name + value: this.channel.guild.channels.get(this.member.voiceState.channelID).name }, { name: `${"▬".repeat(parts)}🔘${"▬".repeat(10 - parts)}`, diff --git a/commands/music/play.js b/commands/music/play.js index 7740238..d224faf 100644 --- a/commands/music/play.js +++ b/commands/music/play.js @@ -3,9 +3,10 @@ import MusicCommand from "../../classes/musicCommand.js"; class PlayCommand extends MusicCommand { async run() { - if (!this.args[0] && this.message.attachments.length <= 0) return "You need to provide what you want to play!"; - let query = this.args.join(" ").trim(); - const attachment = this.message.attachments[0]; + const input = this.type === "classic" ? this.args.join(" ") : this.options.query; + if (!input && (this.type === "classic" ? (!this.message || this.message.attachments.length <= 0) : !this.options.file)) return "You need to provide what you want to play!"; + let query = input ? input.trim() : ""; + const attachment = this.type === "classic" ? this.message.attachments[0] : this.interaction.data.resolved.attachments[this.options.file]; if (query.startsWith("||") && query.endsWith("||")) { query = query.substring(2, query.length - 2); } @@ -14,13 +15,22 @@ class PlayCommand extends MusicCommand { } try { const url = new URL(query); - return await play(this.client, url, this.message, true); + return await play(this.client, url, { channel: this.channel, author: this.author, type: this.type, interaction: this.interaction }, true); } catch { - const search = query.startsWith("ytsearch:") ? query : !this.args[0] && attachment ? attachment.url : `ytsearch:${query}`; - return await play(this.client, search, this.message, true); + const search = query.startsWith("ytsearch:") ? query : !query && attachment ? attachment.url : `ytsearch:${query}`; + return await play(this.client, search, { channel: this.channel, author: this.author, type: this.type, interaction: this.interaction }, true); } } + static flags = [{ + name: "file", + type: 11, + description: "An audio file attachment" + }, { + name: "query", + type: 3, + description: "An audio search query or URL" + }]; static description = "Plays a song or adds it to the queue"; static aliases = ["p"]; static arguments = ["[url]"]; diff --git a/commands/music/queue.js b/commands/music/queue.js index 980bcba..0537193 100644 --- a/commands/music/queue.js +++ b/commands/music/queue.js @@ -7,7 +7,7 @@ import MusicCommand from "../../classes/musicCommand.js"; class QueueCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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; diff --git a/commands/music/remove.js b/commands/music/remove.js index 28df7be..df24fb1 100644 --- a/commands/music/remove.js +++ b/commands/music/remove.js @@ -5,10 +5,10 @@ import MusicCommand from "../../classes/musicCommand.js"; class RemoveCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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]); + const pos = parseInt(this.type === "classic" ? this.args[0] : this.options.position); 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]); @@ -16,6 +16,13 @@ class RemoveCommand extends MusicCommand { return `🔊 The song \`${track.title ? track.title : "(blank)"}\` has been removed from the queue.`; } + static flags = [{ + name: "position", + type: 4, + description: "The queue position you want to remove", + min_value: 1, + required: true + }]; static description = "Removes a song from the queue"; static aliases = ["rm"]; } diff --git a/commands/music/seek.js b/commands/music/seek.js index 8126183..0ba7ff0 100644 --- a/commands/music/seek.js +++ b/commands/music/seek.js @@ -4,18 +4,25 @@ import MusicCommand from "../../classes/musicCommand.js"; class SeekCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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!"; - const seconds = parseFloat(this.args[0]); + const seconds = parseFloat(this.type === "classic" ? this.args[0] : this.options.position); if (isNaN(seconds) || (seconds * 1000) > track.length || (seconds * 1000) < 0) return "That's not a valid position!"; await player.seek(seconds * 1000); return `🔊 Seeked track to ${seconds} second(s).`; } + static flags = [{ + name: "position", + type: 10, + description: "Seek to this position", + required: true, + min_value: 0 + }]; static description = "Seeks to a different position in the music"; static aliases = ["pos"]; static arguments = ["[seconds]"]; diff --git a/commands/music/shuffle.js b/commands/music/shuffle.js index d6bf2d4..28536d5 100644 --- a/commands/music/shuffle.js +++ b/commands/music/shuffle.js @@ -4,7 +4,7 @@ import MusicCommand from "../../classes/musicCommand.js"; class ShuffleCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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; diff --git a/commands/music/skip.js b/commands/music/skip.js index e297d4d..e2acd2b 100644 --- a/commands/music/skip.js +++ b/commands/music/skip.js @@ -4,10 +4,10 @@ import MusicCommand from "../../classes/musicCommand.js"; class SkipCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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.author.id && !this.message.member.permissions.has("manageChannels")) { + if (player.host !== this.author.id && !this.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 = { diff --git a/commands/music/stop.js b/commands/music/stop.js index a37f10c..931e5f7 100644 --- a/commands/music/stop.js +++ b/commands/music/stop.js @@ -4,13 +4,13 @@ import MusicCommand from "../../classes/musicCommand.js"; class StopCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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.channel.guild.id); return "🔊 The current voice channel session has ended."; } - if (this.connection.host !== this.author.id && !this.message.member.permissions.has("manageChannels")) return "Only the current voice session host can stop the music!"; + if (this.connection.host !== this.author.id && !this.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(); diff --git a/commands/music/pause.js b/commands/music/toggle.js similarity index 57% rename from commands/music/pause.js rename to commands/music/toggle.js index 7cefa58..5492b66 100644 --- a/commands/music/pause.js +++ b/commands/music/toggle.js @@ -1,18 +1,18 @@ import MusicCommand from "../../classes/musicCommand.js"; -class PauseCommand extends MusicCommand { +class ToggleCommand extends MusicCommand { async run() { 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.member.voiceState.channelID) return "You need to be in a voice channel first!"; 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!"; + if (this.connection.host !== this.author.id && !this.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"}.`; } static description = "Pauses/resumes the current song"; - static aliases = ["resume"]; + static aliases = ["pause", "resume"]; } -export default PauseCommand; +export default ToggleCommand; diff --git a/commands/soundboard/boi.js b/commands/soundboard/boi.js index 3ccf123..4ba0f90 100644 --- a/commands/soundboard/boi.js +++ b/commands/soundboard/boi.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class BoiCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/boi.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class BoiCommand extends SoundboardCommand { + static file = "./assets/audio/boi.ogg"; static description = "Plays the \"boi\" sound effect"; static aliases = ["boy", "neutron", "hugh"]; } diff --git a/commands/soundboard/boom.js b/commands/soundboard/boom.js index 7a1b529..76273dc 100644 --- a/commands/soundboard/boom.js +++ b/commands/soundboard/boom.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class BoomCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/boom.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class BoomCommand extends SoundboardCommand { + static file = "./assets/audio/boom.ogg"; static description = "Plays the Vine boom sound effect"; static aliases = ["thud", "vine"]; } diff --git a/commands/soundboard/bruh.js b/commands/soundboard/bruh.js index 93790de..06e019b 100644 --- a/commands/soundboard/bruh.js +++ b/commands/soundboard/bruh.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class BruhCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/bruh.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class BruhCommand extends SoundboardCommand { + static file = "./assets/audio/bruh.ogg"; static description = "Plays the \"bruh\" sound effect"; static aliases = ["bro"]; } diff --git a/commands/soundboard/damndaniel.js b/commands/soundboard/damndaniel.js index 05938e6..049b0e7 100644 --- a/commands/soundboard/damndaniel.js +++ b/commands/soundboard/damndaniel.js @@ -1,13 +1,9 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; +import SoundboardCommand from "../../classes/soundboardCommand.js"; -class DamnDanielCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/damndaniel.ogg", this.message); - } - - static description = "Plays the \"damn daniel\" sound effect"; - static aliases = ["daniel", "damn"]; +class DamnDanielCommand extends SoundboardCommand { + static file = "./assets/audio/damndaniel.ogg"; + static description = "Plays the \"damn daniel\" sound effect"; + static aliases = ["daniel", "damn"]; } export default DamnDanielCommand; diff --git a/commands/soundboard/explosion.js b/commands/soundboard/explosion.js index 826cbe7..e9dd261 100644 --- a/commands/soundboard/explosion.js +++ b/commands/soundboard/explosion.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class ExplosionCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/explosion.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class ExplosionCommand extends SoundboardCommand { + static file = "./assets/audio/explosion.ogg"; static description = "Plays an explosion sound effect"; } diff --git a/commands/soundboard/fakeping.js b/commands/soundboard/fakeping.js index 2bc0a7d..669e612 100644 --- a/commands/soundboard/fakeping.js +++ b/commands/soundboard/fakeping.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class FakePingCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/ping.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class FakePingCommand extends SoundboardCommand { + static file = "./assets/audio/ping.ogg"; static description = "Plays a Discord ping sound effect"; static aliases = ["notification", "notif"]; } diff --git a/commands/soundboard/fart.js b/commands/soundboard/fart.js index 16217da..8f75a91 100644 --- a/commands/soundboard/fart.js +++ b/commands/soundboard/fart.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class FartCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/fart.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class FartCommand extends SoundboardCommand { + static file = "./assets/audio/fart.ogg"; static description = "Plays a fart sound effect"; static aliases = ["toot"]; } diff --git a/commands/soundboard/fartreverb.js b/commands/soundboard/fartreverb.js index 15a2fb2..2c470f0 100644 --- a/commands/soundboard/fartreverb.js +++ b/commands/soundboard/fartreverb.js @@ -1,12 +1,7 @@ -// shoutouts to dairyorange, you're a real one -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class FartReverbCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/fart2.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class FartReverbCommand extends SoundboardCommand { + static file = "./assets/audio/fart2.ogg"; static description = "Plays a fart sound effect with extra reverb"; static aliases = ["fart2"]; } diff --git a/commands/soundboard/fbi.js b/commands/soundboard/fbi.js index f7932ab..eb1a7f4 100644 --- a/commands/soundboard/fbi.js +++ b/commands/soundboard/fbi.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class FBICommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/fbi.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class FBICommand extends SoundboardCommand { + static file = "./assets/audio/fbi.ogg"; static description = "Plays the \"FBI OPEN UP\" sound effect"; static aliases = ["openup"]; } diff --git a/commands/soundboard/mail.js b/commands/soundboard/mail.js index 2a5f4a1..f0ae1e4 100644 --- a/commands/soundboard/mail.js +++ b/commands/soundboard/mail.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class MailCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/mail.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class MailCommand extends SoundboardCommand { + static file = "./assets/audio/mail.ogg"; static description = "Plays the \"You've got mail\" sound effect"; static aliases = ["yougotmail", "youvegotmail", "aol"]; } diff --git a/commands/soundboard/oof.js b/commands/soundboard/oof.js index bccd89f..f4e5b89 100644 --- a/commands/soundboard/oof.js +++ b/commands/soundboard/oof.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class OofCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/oof.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class OofCommand extends SoundboardCommand { + static file = "./assets/audio/oof.ogg"; static description = "Plays the Roblox \"oof\" sound"; static aliases = ["roblox", "commitdie"]; } diff --git a/commands/soundboard/soundboard.js b/commands/soundboard/soundboard.js new file mode 100644 index 0000000..76810f5 --- /dev/null +++ b/commands/soundboard/soundboard.js @@ -0,0 +1,31 @@ +import { play } from "../../utils/soundplayer.js"; +import Command from "../../classes/command.js"; +import { sounds, info } from "../../utils/collections.js"; + +// all-in-one soundboard command +class SoundboardAIOCommand extends Command { + async run() { + const soundName = this.type === "classic" ? this.args[0] : this.optionsArray[0].name; + if (!sounds.has(soundName)) return "You need to provide a sound to play!"; + const name = sounds.get(soundName); + return await play(this.client, name, { channel: this.channel, author: this.author, type: this.type, interaction: this.interaction }); + } + + static postInit() { + this.flags = []; + for (const sound of sounds.keys()) { + this.flags.push({ + name: sound, + type: 1, + description: info.get(sound).description + }); + } + return this; + } + + static description = "Plays a sound effect"; + static requires = ["sound"]; + static aliases = ["sound", "sb"]; +} + +export default SoundboardAIOCommand; \ No newline at end of file diff --git a/commands/soundboard/winxp.js b/commands/soundboard/winxp.js index d51320e..56f4a81 100644 --- a/commands/soundboard/winxp.js +++ b/commands/soundboard/winxp.js @@ -1,11 +1,7 @@ -import { play } from "../../utils/soundplayer.js"; -import MusicCommand from "../../classes/musicCommand.js"; - -class WinXPCommand extends MusicCommand { - async run() { - return await play(this.client, "./assets/audio/winxp.ogg", this.message); - } +import SoundboardCommand from "../../classes/soundboardCommand.js"; +class WinXPCommand extends SoundboardCommand { + static file = "./assets/audio/winxp.ogg"; static description = "Plays the Windows XP startup sound"; static aliases = ["windows", "xp"]; } diff --git a/commands/tags/tags.js b/commands/tags/tags.js index db6293a..04faf55 100644 --- a/commands/tags/tags.js +++ b/commands/tags/tags.js @@ -23,7 +23,7 @@ class TagsCommand extends Command { 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.author.id && !this.message.member.permissions.has("manageMessages") && !owners.includes(this.author.id)) return "You don't own this tag!"; + if (getResult.author !== this.author.id && !this.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") { @@ -31,7 +31,7 @@ class TagsCommand extends Command { 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.author.id && !this.message.member.permissions.has("manageMessages") && !owners.includes(this.author.id)) return "You don't own this tag!"; + if (getResult.author !== this.author.id && !this.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") { diff --git a/messages.json b/messages.json index 7362976..6a8879f 100644 --- a/messages.json +++ b/messages.json @@ -110,7 +110,7 @@ "MilkyTracker", "with chimps", "with the TF2 source code", - "alvin the chipmunk nightcore", + "alvin chipmunk nightcore", "Troll", "ay yo the pizza here", "100 gecs", diff --git a/shard.js b/shard.js index 4e56b98..b362e33 100644 --- a/shard.js +++ b/shard.js @@ -8,13 +8,13 @@ import { fileURLToPath } from "url"; // fancy loggings import { log, error } from "./utils/logger.js"; // initialize command loader -import { load } from "./utils/handler.js"; +import { load, update } from "./utils/handler.js"; // lavalink stuff import { checkStatus, connect, status, connected } from "./utils/soundplayer.js"; // database stuff import database from "./utils/database.js"; // command collections -import { paths, info } from "./utils/collections.js"; +import { paths } from "./utils/collections.js"; // playing messages const { messages } = JSON.parse(readFileSync(new URL("./messages.json", import.meta.url))); // other stuff @@ -35,23 +35,16 @@ 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 { - 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 - }); + await load(this.bot, this.clusterID, this.workerID, this.ipc, commandFile, soundStatus); } catch (e) { error(`Failed to register command from ${commandFile}: ${e}`); } } + const commandArray = await update(this.bot, this.clusterID, this.workerID, this.ipc, soundStatus); log("info", "Finished loading commands."); await database.setup(this.ipc); diff --git a/utils/collections.js b/utils/collections.js index 0cd93c4..43bcfa7 100644 --- a/utils/collections.js +++ b/utils/collections.js @@ -2,6 +2,8 @@ export const commands = new Map(); export const paths = new Map(); export const aliases = new Map(); export const info = new Map(); +export const sounds = new Map(); +export const categories = new Map(); class TimedMap extends Map { set(key, value) { diff --git a/utils/handler.js b/utils/handler.js index 7e8670a..7c88401 100644 --- a/utils/handler.js +++ b/utils/handler.js @@ -1,4 +1,4 @@ -import { paths, commands, info, aliases as _aliases } from "./collections.js"; +import { paths, commands, info, sounds, categories, aliases as _aliases } from "./collections.js"; import { log } from "./logger.js"; let queryValue = 0; @@ -13,21 +13,27 @@ export async function load(client, cluster, worker, ipc, command, soundStatus, s } const commandArray = command.split("/"); const commandName = commandArray[commandArray.length - 1].split(".")[0]; + + props.init(); paths.set(commandName, command); commands.set(commandName, props); - const propsInstance = new props(client, cluster, worker, ipc, {}); + if (Object.getPrototypeOf(props).name === "SoundboardCommand") sounds.set(commandName, props.file); + const category = commandArray[commandArray.length - 2]; info.set(commandName, { - category: commandArray[commandArray.length - 2], + category: category, description: props.description, aliases: props.aliases, params: props.arguments, - flags: propsInstance.flags ?? props.flags, + flags: props.flags, slashAllowed: props.slashAllowed }); + const categoryCommands = categories.get(category); + categories.set(category, categoryCommands ? [...categoryCommands, commandName] : [commandName]); + if (slashReload && props.slashAllowed) { const commandList = await client.getCommands(); const oldCommand = commandList.filter((item) => { @@ -37,7 +43,7 @@ export async function load(client, cluster, worker, ipc, command, soundStatus, s name: commandName, type: 1, description: props.description, - options: propsInstance.flags ?? props.flags + options: props.flags }); } @@ -49,3 +55,30 @@ export async function load(client, cluster, worker, ipc, command, soundStatus, s } return commandName; } + +export async function update() { + const commandArray = []; + for (const [name, command] of commands.entries()) { + let cmdInfo = info.get(name); + if (command.postInit) { + const cmd = command.postInit(); + //commands.set(name, cmd); + cmdInfo = { + category: cmdInfo.category, + description: cmd.description, + aliases: cmd.aliases, + params: cmd.arguments, + flags: cmd.flags, + slashAllowed: cmd.slashAllowed + }; + info.set(name, cmdInfo); + } + if (cmdInfo && cmdInfo.slashAllowed) commandArray.push({ + name, + type: 1, + description: cmdInfo.description, + options: cmdInfo.flags + }); + } + return commandArray; +} \ No newline at end of file diff --git a/utils/imagedetect.js b/utils/imagedetect.js index d94dca6..3096344 100644 --- a/utils/imagedetect.js +++ b/utils/imagedetect.js @@ -133,7 +133,7 @@ export default async (client, cmdMessage, interaction, options, extraReturnTypes // 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); + const result = await getImage(interaction.data.resolved.attachments[options.image].proxy_url, interaction.data.resolved.attachments[options.image].url, video); if (result !== false) return result; } else if (options.link) { const result = await getImage(options.link, options.link, video); diff --git a/utils/pagination/awaitmessages.js b/utils/pagination/awaitmessages.js deleted file mode 100644 index 7cb3294..0000000 --- a/utils/pagination/awaitmessages.js +++ /dev/null @@ -1,37 +0,0 @@ -// eris doesn't come with an awaitMessages method by default, so we make our own -import { EventEmitter } from "events"; - -class MessageCollector extends EventEmitter { - constructor(client, channel, filter, options = {}) { - super(); - this.filter = filter; - this.channel = channel; - this.options = options; - this.ended = false; - this.collected = []; - this.bot = client; - this.listener = message => this.verify(message); - this.bot.on("messageCreate", this.listener); - if (options.time) setTimeout(() => this.stop("time"), options.time); - } - - verify(message) { - if (this.channel.id !== message.channel.id) return false; - if (this.filter(message)) { - this.collected.push(message); - this.emit("message", message); - if (this.collected.length >= this.options.maxMatches) this.stop("maxMatches"); - return true; - } - return false; - } - - stop(reason) { - if (this.ended) return; - this.ended = true; - this.bot.removeListener("messageCreate", this.listener); - this.emit("end", this.collected, reason); - } -} - -export default MessageCollector; diff --git a/utils/soundplayer.js b/utils/soundplayer.js index 23f3342..72289e7 100644 --- a/utils/soundplayer.js +++ b/utils/soundplayer.js @@ -50,15 +50,15 @@ export async function connect(client) { return length; } -export async function play(client, sound, message, music = false) { +export async function play(client, sound, options, music = false) { if (!manager) return "The sound commands are still starting up!"; - if (!message.channel.guild) return "This command only works in servers!"; - if (!message.member.voiceState.channelID) return "You need to be in a voice channel first!"; - if (!message.channel.guild.permissionsOf(client.user.id).has("voiceConnect")) return "I can't join this voice channel!"; - const voiceChannel = message.channel.guild.channels.get(message.member.voiceState.channelID); + if (!options.channel.guild) return "This command only works in servers!"; + if (!options.author.voiceState.channelID) return "You need to be in a voice channel first!"; + if (!options.channel.guild.permissionsOf(client.user.id).has("voiceConnect")) return "I can't join this voice channel!"; + const voiceChannel = options.channel.guild.channels.get(options.author.voiceState.channelID); if (!voiceChannel.permissionsOf(client.user.id).has("voiceConnect")) return "I don't have permission to join this voice channel!"; - const player = players.get(message.channel.guild.id); - if (!music && manager.voiceStates.has(message.channel.guild.id) && (player && player.type === "music")) return "I can't play a sound effect while playing music!"; + const player = players.get(options.channel.guild.id); + if (!music && manager.voiceStates.has(options.channel.guild.id) && (player && player.type === "music")) return "I can't play a sound effect while playing music!"; let node = manager.idealNodes[0]; if (!node) { const status = await checkStatus(); @@ -92,12 +92,12 @@ export async function play(client, sound, message, music = false) { if (oldQueue && oldQueue.length !== 0 && music) { return `Your ${playlistInfo.name ? "playlist" : "tune"} \`${playlistInfo.name ? playlistInfo.name.trim() : (tracks[0].info.title !== "" ? tracks[0].info.title.trim() : "(blank)")}\` has been added to the queue!`; } else { - nextSong(client, message, connection, tracks[0].track, tracks[0].info, music, voiceChannel, player ? player.host : message.author.id, player ? player.loop : false, player ? player.shuffle : false); + nextSong(client, options, connection, tracks[0].track, tracks[0].info, music, voiceChannel, player ? player.host : options.author.id, player ? player.loop : false, player ? player.shuffle : false); return; } } -export async function nextSong(client, message, connection, track, info, music, voiceChannel, host, loop = false, shuffle = false, lastTrack = null) { +export async function nextSong(client, options, connection, track, info, music, voiceChannel, host, loop = false, shuffle = false, lastTrack = null) { skipVotes.delete(voiceChannel.guild.id); const parts = Math.floor((0 / info.length) * 10); let playingMessage; @@ -114,7 +114,7 @@ export async function nextSong(client, message, connection, track, info, music, playingMessage = players.get(voiceChannel.guild.id).playMessage; } else { try { - playingMessage = await client.createMessage(message.channel.id, !music ? "🔊 Playing sound..." : { + const content = !music ? "🔊 Playing sound..." : { embeds: [{ color: 16711680, author: { @@ -138,7 +138,13 @@ export async function nextSong(client, message, connection, track, info, music, value: `0:00/${info.isStream ? "∞" : format(info.length)}` }] }] - }); + }; + if (options.type === "classic") { + playingMessage = await client.createMessage(options.channel.id, content); + } else { + await options.interaction[options.interaction.acknowledged ? "editOriginalMessage" : "createMessage"](content); + playingMessage = await options.interaction.getOriginalMessage(); + } } catch { // no-op } @@ -147,7 +153,7 @@ export async function nextSong(client, message, connection, track, info, music, connection.removeAllListeners("end"); await connection.play(track); await connection.volume(75); - players.set(voiceChannel.guild.id, { player: connection, type: music ? "music" : "sound", host: host, voiceChannel: voiceChannel, originalChannel: message.channel, loop: loop, shuffle: shuffle, playMessage: playingMessage }); + players.set(voiceChannel.guild.id, { player: connection, type: music ? "music" : "sound", host: host, voiceChannel: voiceChannel, originalChannel: options.channel, loop: loop, shuffle: shuffle, playMessage: playingMessage }); connection.once("error", async (error) => { try { if (playingMessage.channel.messages.has(playingMessage.id)) await playingMessage.delete(); @@ -166,7 +172,7 @@ export async function nextSong(client, message, connection, track, info, music, players.delete(voiceChannel.guild.id); queues.delete(voiceChannel.guild.id); logger.error(error); - await client.createMessage(message.channel.id, `🔊 Looks like there was an error regarding sound playback:\n\`\`\`${error.type}: ${error.error}\`\`\``); + await (options.type === "classic" ? options.channel.createMessage : options.interaction.createMessage)(`🔊 Looks like there was an error regarding sound playback:\n\`\`\`${error.type}: ${error.error}\`\`\``); }); connection.on("end", async (data) => { if (data.reason === "REPLACED") return; @@ -194,7 +200,7 @@ export async function nextSong(client, message, connection, track, info, music, queues.set(voiceChannel.guild.id, newQueue); if (newQueue.length !== 0) { const newTrack = await Rest.decode(connection.node, newQueue[0]); - nextSong(client, message, connection, newQueue[0], newTrack, music, voiceChannel, host, player.loop, player.shuffle, track); + nextSong(client, options, connection, newQueue[0], newTrack, music, voiceChannel, host, player.loop, player.shuffle, track); try { if (newQueue[0] !== track && playingMessage.channel.messages.has(playingMessage.id)) await playingMessage.delete(); if (newQueue[0] !== track && player.playMessage.channel.messages.has(player.playMessage.id)) await player.playMessage.delete(); @@ -207,7 +213,7 @@ export async function nextSong(client, message, connection, track, info, music, players.delete(voiceChannel.guild.id); queues.delete(voiceChannel.guild.id); skipVotes.delete(voiceChannel.guild.id); - if (music) await client.createMessage(message.channel.id, "🔊 The current voice channel session has ended."); + if (music) await (options.type === "classic" ? options.channel.createMessage : options.interaction.createMessage)("🔊 The current voice channel session has ended."); try { if (playingMessage.channel.messages.has(playingMessage.id)) await playingMessage.delete(); if (player && player.playMessage.channel.messages.has(player.playMessage.id)) await player.playMessage.delete();