Added (hacky) message command support, lots of work to prepare for message content intent enforcement, improve broadcast, remove evalraw, update packages

This commit is contained in:
Essem 2022-08-31 20:00:34 -05:00
parent 3392c3c89e
commit d33a7804d7
No known key found for this signature in database
GPG key ID: 7D497397CC3A2A8C
56 changed files with 443 additions and 315 deletions

View file

@ -14,6 +14,7 @@ class AncientCommand extends Command {
return data.headers.location;
} catch (e) {
if (e.name === "AbortError") {
this.success = false;
return "I couldn't get a meme in time. Maybe try again?";
}
}

View file

@ -15,6 +15,7 @@ class BirdCommand extends Command {
return json[0];
} catch (e) {
if (e.name === "AbortError") {
this.success = false;
return "I couldn't get a bird image in time. Maybe try again?";
}
}

View file

@ -14,6 +14,7 @@ class CatCommand extends Command {
return data.headers.location;
} catch (e) {
if (e.name === "AbortError") {
this.success = false;
return "I couldn't get a cat image in time. Maybe try again?";
}
}

View file

@ -15,6 +15,7 @@ class DogCommand extends Command {
return json.message;
} catch (e) {
if (e.name === "AbortError") {
this.success = false;
return "I couldn't get a dog image in time. Maybe try again?";
}
}

View file

@ -21,6 +21,7 @@ class WikihowCommand extends Command {
}
} catch (e) {
if (e.name === "AbortError") {
this.success = false;
return "I couldn't get a WikiHow image in time. Maybe try again?";
}
}

View file

@ -8,11 +8,15 @@ class AvatarCommand extends Command {
if (this.type === "classic" && this.message.mentions[0]) {
return this.message.mentions[0].dynamicAvatarURL(null, 512);
} else if (await this.ipc.fetchUser(member)) {
const user = await this.ipc.fetchUser(member);
return user.avatar ? this.client._formatImage(`/avatars/${user.id}/${user.avatar}`, null, 512) : `https://cdn.discordapp.com/embed/avatars/${user.discriminator % 5}.png`; // hacky "solution"
let user = await this.ipc.fetchUser(member);
if (!user) user = await this.client.getRESTUser(member);
return user?.avatar ? this.client._formatImage(`/avatars/${user.id}/${user.avatar}`, null, 512) : `https://cdn.discordapp.com/embed/avatars/${user.discriminator % 5}.png`; // hacky "solution"
} else if (mentionRegex.test(member)) {
const id = member.match(mentionRegex)[1];
if (id < 21154535154122752n) return "That's not a valid mention!";
if (id < 21154535154122752n) {
this.success = false;
return "That's not a valid mention!";
}
try {
const user = await this.client.getRESTUser(id);
return user.avatar ? this.client._formatImage(`/avatars/${user.id}/${user.avatar}`, null, 512) : `https://cdn.discordapp.com/embed/avatars/${user.discriminator % 5}.png`; // repeat of hacky "solution" from above

View file

@ -12,7 +12,10 @@ class BannerCommand extends Command {
return user.dynamicBannerURL(null, 512) ?? "This user doesn't have a banner!";
} else if (mentionRegex.test(member)) {
const id = member.match(mentionRegex)[1];
if (id < 21154535154122752n) return "That's not a valid mention!";
if (id < 21154535154122752n) {
this.success = false;
return "That's not a valid mention!";
}
try {
const user = await this.client.getRESTUser(id);
return user.dynamicBannerURL(null, 512) ?? "This user doesn't have a banner!";

View file

@ -3,11 +3,13 @@ import { clean } from "../../utils/misc.js";
class Base64Command extends Command {
async run() {
this.success = false;
if (this.type === "classic" && this.args.length === 0) return "You need to provide whether you want to encode or decode the text!";
const command = this.type === "classic" ? this.args[0].toLowerCase() : this.optionsArray[0].name.toLowerCase();
if (command !== "decode" && command !== "encode") return "You need to provide whether you want to encode or decode the text!";
const string = this.options.text ?? this.args.slice(1).join(" ");
if (!string || !string.trim()) return `You need to provide a string to ${command}!`;
this.success = true;
if (command === "decode") {
const b64Decoded = Buffer.from(string, "base64").toString("utf8");
return `\`\`\`\n${await clean(b64Decoded)}\`\`\``;

View file

@ -5,15 +5,20 @@ class BroadcastCommand extends Command {
run() {
return new Promise((resolve) => {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can broadcast messages!";
if (!owners.includes(this.author.id)) {
this.success = false;
resolve("Only the bot owner can broadcast messages!");
}
const message = this.options.message ?? this.args.join(" ");
if (message?.trim()) {
this.ipc.centralStore.set("broadcast", message);
this.ipc.broadcast("playbroadcast", message);
this.ipc.register("broadcastSuccess", () => {
this.ipc.unregister("broadcastSuccess");
resolve("Successfully broadcasted message.");
});
} else {
this.ipc.centralStore.delete("broadcast");
this.ipc.broadcast("broadcastend");
this.ipc.register("broadcastEnd", () => {
this.ipc.unregister("broadcastEnd");

View file

@ -3,6 +3,7 @@ import Command from "../../classes/command.js";
class ChannelCommand extends Command {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
const owners = process.env.OWNER.split(",");
if (!this.member.permissions.has("administrator") && !owners.includes(this.member.id)) return "You need to be an administrator to enable/disable me!";
@ -23,6 +24,7 @@ class ChannelCommand extends Command {
}
await db.disableChannel(channel);
this.success = true;
return `I have been disabled in this channel. To re-enable me, just run \`${guildDB.prefix}channel enable\`.`;
} else if (this.args[0].toLowerCase() === "enable") {
let channel;
@ -36,11 +38,12 @@ class ChannelCommand extends Command {
}
await db.enableChannel(channel);
this.success = true;
return "I have been re-enabled in this channel.";
}
}
static description = "Enables/disables me in a channel (does not work with slash commands)";
static description = "Enables/disables classic commands in a channel (use server settings for slash commands)";
static arguments = ["[enable/disable]", "{id}"];
static slashAllowed = false;
static directAllowed = false;

View file

@ -4,6 +4,7 @@ import * as collections from "../../utils/collections.js";
class CommandCommand extends Command {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
const owners = process.env.OWNER.split(",");
if (!this.member.permissions.has("administrator") && !owners.includes(this.member.id)) return "You need to be an administrator to enable/disable me!";
@ -21,16 +22,18 @@ class CommandCommand extends Command {
if (disabled?.includes(command)) return "That command is already disabled!";
await db.disableCommand(this.channel.guild.id, command);
this.success = true;
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 (!disabled?.includes(command)) return "That command isn't disabled!";
await db.enableCommand(this.channel.guild.id, command);
this.success = true;
return `The command \`${command}\` has been re-enabled.`;
}
}
static description = "Enables/disables a command for a server";
static description = "Enables/disables a classic command for a server (use server settings for slash commands)";
static aliases = ["cmd"];
static arguments = ["[enable/disable]", "[command]"];
static slashAllowed = false;

View file

@ -4,7 +4,10 @@ import Command from "../../classes/command.js";
class CountCommand extends Command {
async run() {
if (this.channel.guild && !this.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")) {
this.success = false;
return "I don't have the `Embed Links` permission!";
}
const counts = await database.getCounts();
const countArray = [];
for (const entry of Object.entries(counts)) {

View file

@ -4,8 +4,7 @@ import Command from "../../classes/command.js";
class EmoteCommand extends Command {
async run() {
const emoji = this.options.emoji ?? this.content;
if (!emoji || !emoji.trim()) return "You need to provide an emoji!";
if (emoji.split(" ")[0].match(/^<a?:.+:\d+>$/)) {
if (emoji && emoji.trim() && emoji.split(" ")[0].match(/^<a?:.+:\d+>$/)) {
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 = [];
@ -14,6 +13,7 @@ class EmoteCommand extends Command {
}
return `https://twemoji.maxcdn.com/v/latest/72x72/${codePoints.join("-").replace("-fe0f", "")}.png`;
} else {
this.success = false;
return "You need to provide a valid emoji to get an image!";
}
}

View file

@ -4,7 +4,10 @@ import Command from "../../classes/command.js";
class EvalCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can use eval!";
if (!owners.includes(this.author.id)) {
this.success = false;
return "Only the bot owner can use eval!";
}
await this.acknowledge();
const code = this.options.code ?? this.args.join(" ");
try {

View file

@ -1,31 +0,0 @@
import { clean } from "../../utils/misc.js";
import Command from "../../classes/command.js";
class EvalRawCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can use evalraw!";
const code = this.args.join(" ");
try {
const evaled = eval(code);
if (evaled.length >= 2000) {
return {
text: "The result was too large, so here it is as a file:",
file: evaled,
name: "result.txt"
};
} else {
return evaled;
}
} catch (err) {
return `\`ERROR\` \`\`\`xl\n${await clean(err)}\n\`\`\``;
}
}
static description = "Executes JavaScript code (with raw output)";
static aliases = ["run"];
static arguments = ["[code]"];
static slashAllowed = false;
}
export default EvalRawCommand;

View file

@ -7,7 +7,10 @@ import Command from "../../classes/command.js";
class ExecCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can use exec!";
if (!owners.includes(this.author.id)) {
this.success = false;
return "Only the bot owner can use exec!";
}
await this.acknowledge();
const code = this.options.cmd ?? this.args.join(" ");
try {

View file

@ -54,7 +54,10 @@ class HelpCommand extends Command {
}
return embed;
} else {
if (this.channel.guild && !this.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")) {
this.success = false;
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)) {

View file

@ -7,6 +7,7 @@ import Command from "../../classes/command.js";
class ImageSearchCommand extends Command {
async run() {
this.success = false;
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.options.query ?? this.args.join(" ");
if (!query || !query.trim()) return "You need to provide something to search for!";
@ -34,6 +35,7 @@ class ImageSearchCommand extends Command {
}]
});
}
this.success = true;
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
}

View file

@ -3,7 +3,10 @@ import Command from "../../classes/command.js";
class ImageReloadCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can reload the image servers!";
if (!owners.includes(this.author.id)) {
this.success = false;
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.`;

View file

@ -6,9 +6,11 @@ class LengthenCommand extends Command {
async run() {
await this.acknowledge();
const input = this.options.url ?? this.args.join(" ");
this.success = false;
if (!input || !input.trim() || !urlCheck(input)) return "You need to provide a short URL to lengthen!";
if (urlCheck(input)) {
const url = await request(encodeURI(input), { method: "HEAD" });
this.success = true;
return url.headers.location || input;
} else {
return "That isn't a URL!";

View file

@ -7,7 +7,10 @@ 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.member.permissions.has("administrator") && !owners.includes(this.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)) {
this.success = false;
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 {

View file

@ -4,7 +4,10 @@ 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!";
if (this.args.length === 0) {
this.success = false;
return "You need to provide some text to generate a QR code!";
}
await this.acknowledge();
const writable = new PassThrough();
qrcode.toFileStream(writable, this.content, { margin: 1 });

View file

@ -8,12 +8,14 @@ import imageDetect from "../../utils/imagedetect.js";
class QrReadCommand extends Command {
async run() {
const image = await imageDetect(this.client, this.message, this.interaction, this.options);
this.success = false;
if (image === undefined) return "You need to provide an image/GIF with a QR code to read!";
await this.acknowledge();
const data = Buffer.from(await (await request(image.path)).body.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!";
this.success = true;
return `\`\`\`\n${await clean(qrBuffer.data)}\n\`\`\``;
}

View file

@ -5,7 +5,10 @@ class RawCommand extends Command {
async run() {
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!";
if (image === undefined) {
this.success = false;
return "You need to provide an image/GIF to get a raw URL!";
}
return image.path;
}

View file

@ -3,7 +3,10 @@ import Command from "../../classes/command.js";
class RestartCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can restart me!";
if (!owners.includes(this.author.id)) {
this.success = false;
return "Only the bot owner can restart me!";
}
await this.client.createMessage(this.channel.id, Object.assign({
content: "esmBot is restarting."
}, this.reference));

View file

@ -2,7 +2,10 @@ import Command from "../../classes/command.js";
class ServerInfoCommand extends Command {
async run() {
if (!this.channel.guild) return "This command only works in servers!";
if (!this.channel.guild) {
this.success = false;
return "This command only works in servers!";
}
const owner = await this.channel.guild.members.get(this.channel.guild.ownerID);
return {
embeds: [{

View file

@ -2,8 +2,10 @@ import Command from "../../classes/command.js";
class SnowflakeCommand extends Command {
async run() {
this.success = false;
if (!this.args[0]) return "You need to provide a snowflake ID!";
if (!this.args[0].match(/^<?[@#]?[&!]?\d+>?$/) && this.args[0] < 21154535154122752n) return "That's not a valid snowflake!";
this.success = true;
return `<t:${Math.floor(((this.args[0].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "") / 4194304) + 1420070400000) / 1000)}:F>`;
}

View file

@ -5,7 +5,10 @@ class SoundReloadCommand extends Command {
run() {
return new Promise((resolve) => {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can reload Lavalink!";
if (!owners.includes(this.author.id)) {
this.success = false;
return "Only the bot owner can reload Lavalink!";
}
this.acknowledge().then(() => {
this.ipc.broadcast("soundreload");
this.ipc.register("soundReloadSuccess", (msg) => {

View file

@ -4,10 +4,13 @@ import imagedetect from "../../utils/imagedetect.js";
class StickerCommand extends Command {
async run() {
const result = await imagedetect(this.client, this.message, this.interaction, this.options, false, false, true);
this.success = false;
if (!result) return "You need to provide a sticker!";
if (result.format_type === 1) { // PNG
this.success = true;
return `https://cdn.discordapp.com/stickers/${result.id}.png`;
} else if (result.format_type === 2) { // APNG
this.success = true;
return {
embeds: [{
color: 16711680,
@ -18,6 +21,7 @@ class StickerCommand extends Command {
}]
};
} else if (result.format_type === 3) { // Lottie
this.success = true;
return `I can't display this sticker because it uses the Lottie animation format; however, I can give you the raw JSON link to it: https://cdn.discordapp.com/stickers/${result.id}.json`;
} else {
return "I don't recognize that sticker format!";

View file

@ -8,6 +8,7 @@ import Command from "../../classes/command.js";
class YouTubeCommand extends Command {
async run() {
const query = this.options.query ?? this.args.join(" ");
this.success = false;
if (!query || !query.trim()) return "You need to provide something to search for!";
await this.acknowledge();
const messages = [];
@ -16,6 +17,7 @@ class YouTubeCommand extends Command {
for (const [i, value] of videos.results.entries()) {
messages.push({ content: `Page ${i + 1} of ${videos.results.length}\n<:youtube:637020823005167626> **${value.title.replaceAll("*", "\\*")}**\nUploaded by **${value.author.replaceAll("*", "\\*")}**\n${value.url}` });
}
this.success = true;
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, messages);
}

View file

@ -0,0 +1,23 @@
import Command from "../../classes/command.js";
import imageDetect from "../../utils/imagedetect.js";
import { selectedImages } from "../../utils/collections.js";
class SelectImageCommand extends Command {
async run() {
const message = this.interaction.data.resolved.messages.get(this.interaction.data.target_id);
const image = await imageDetect(this.client, message, this.interaction, this.options, true, false, false, true);
this.success = false;
if (image === undefined) {
return "I couldn't find an image in that message!";
} else if (image.type === "large") {
return "That image is too large (>= 25MB)! Try using a smaller image.";
} else if (image.type === "tenorlimit") {
return "I've been rate-limited by Tenor. Please try uploading your GIF elsewhere.";
}
selectedImages.set(this.author.id, image);
this.success = true;
return "The image has been selected for your next command.";
}
}
export default SelectImageCommand;

View file

@ -3,6 +3,7 @@ import MusicCommand from "../../classes/musicCommand.js";
class HostCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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!";
@ -37,9 +38,11 @@ class HostCommand extends MusicCommand {
const object = this.connection;
object.host = member.id;
players.set(this.channel.guild.id, object);
this.success = true;
return `🔊 ${member.mention} is the new voice channel host.`;
} else {
const member = this.channel.guild ? this.channel.guild.members.get(players.get(this.channel.guild.id).host) : undefined;
this.success = true;
return `🔊 The current voice channel host is **${member?.username}#${member?.discriminator}**.`;
}
}

View file

@ -3,6 +3,7 @@ import MusicCommand from "../../classes/musicCommand.js";
class LoopCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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!";
@ -10,6 +11,7 @@ class LoopCommand extends MusicCommand {
const object = this.connection;
object.loop = !object.loop;
players.set(this.channel.guild.id, object);
this.success = true;
return object.loop ? "🔊 The player is now looping." : "🔊 The player is no longer looping.";
}

View file

@ -5,7 +5,7 @@ import { commands, aliases, info, categories } from "../../utils/collections.js"
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 "https://media.discordapp.net/attachments/322114245632327703/941958748874887178/robot_dance-trans.gif";
if (cmd === "music" || this.constructor.aliases.includes(cmd)) return "https://projectlounge.pw/robotdance.gif";
if (this.type === "classic") {
this.origOptions.args.shift();
} else {
@ -14,8 +14,12 @@ class MusicAIOCommand extends Command {
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();
const inst = new command(this.client, this.cluster, this.worker, this.ipc, this.origOptions);
const result = await inst.run();
this.success = inst.success;
return result;
} else {
this.success = false;
return "That isn't a valid music command!";
}
}

View file

@ -3,6 +3,7 @@ import MusicCommand from "../../classes/musicCommand.js";
class NowPlayingCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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!";
@ -10,6 +11,7 @@ class NowPlayingCommand extends MusicCommand {
if (!player) return "I'm not playing anything!";
const track = await player.node.rest.decode(player.track);
const parts = Math.floor((player.position / track.length) * 10);
this.success = true;
return {
embeds: [{
color: 16711680,

View file

@ -5,9 +5,12 @@ const prefixes = ["ytsearch:", "ytmsearch:", "scsearch:", "spsearch:", "amsearch
class PlayCommand extends MusicCommand {
async run() {
const input = this.options.query ?? this.args.join(" ");
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!";
if (!input && ((!this.message || this.message?.attachments.length <= 0))) {
this.success = false;
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.options.file ? this.interaction.data.resolved.attachments[this.options.file] : null);
const attachment = this.type === "classic" ? this.message.attachments[0] : null;
if (query.startsWith("||") && query.endsWith("||")) {
query = query.substring(2, query.length - 2);
}
@ -24,13 +27,10 @@ class PlayCommand extends MusicCommand {
}
static flags = [{
name: "file",
type: 11,
description: "An audio file attachment"
}, {
name: "query",
type: 3,
description: "An audio search query or URL"
description: "An audio search query or URL",
required: true
}];
static description = "Plays a song or adds it to the queue";
static aliases = ["p"];

View file

@ -6,6 +6,7 @@ import MusicCommand from "../../classes/musicCommand.js";
class QueueCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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!";
@ -52,6 +53,7 @@ class QueueCommand extends MusicCommand {
});
}
if (embeds.length === 0) return "There's nothing in the queue!";
this.success = true;
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
}

View file

@ -3,6 +3,7 @@ import MusicCommand from "../../classes/musicCommand.js";
class RemoveCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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!";
@ -13,6 +14,7 @@ class RemoveCommand extends MusicCommand {
if (removed.length === 0) return "That's not a valid position!";
const track = await this.connection.player.node.rest.decode(removed[0]);
queues.set(this.channel.guild.id, this.queue);
this.success = true;
return `🔊 The song \`${track.title ? track.title : "(blank)"}\` has been removed from the queue.`;
}

View file

@ -2,6 +2,7 @@ import MusicCommand from "../../classes/musicCommand.js";
class SeekCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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!";
@ -11,13 +12,14 @@ class SeekCommand extends MusicCommand {
if (!track.isSeekable) return "This track isn't seekable!";
const pos = this.options.position ?? this.args[0];
let seconds;
if (pos.includes(":")) {
if (typeof pos === "string" && pos.includes(":")) {
seconds = +(pos.split(":").reduce((acc, time) => (60 * acc) + +time));
} else {
seconds = parseFloat(pos);
}
if (isNaN(seconds) || (seconds * 1000) > track.length || (seconds * 1000) < 0) return "That's not a valid position!";
player.seekTo(seconds * 1000);
this.success = true;
return `🔊 Seeked track to ${seconds} second(s).`;
}

View file

@ -3,6 +3,7 @@ import MusicCommand from "../../classes/musicCommand.js";
class ShuffleCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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!";
@ -10,6 +11,7 @@ class ShuffleCommand extends MusicCommand {
const object = this.connection;
object.shuffle = !object.shuffle;
players.set(this.channel.guild.id, object);
this.success = true;
return object.shuffle ? "🔊 The player is now shuffling." : "🔊 The player is no longer shuffling.";
}

View file

@ -3,6 +3,7 @@ import MusicCommand from "../../classes/musicCommand.js";
class SkipCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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!";
@ -18,13 +19,16 @@ class SkipCommand extends MusicCommand {
if (votes.count + 1 === votes.max) {
await player.player.stopTrack(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) });
this.success = true;
if (this.type === "application") return "🔊 The current song has been skipped.";
} else {
skipVotes.set(this.channel.guild.id, newObject);
this.success = true;
return `🔊 Voted to skip song (${votes.count + 1}/${votes.max} people have voted).`;
}
} else {
await player.player.stopTrack();
this.success = true;
if (this.type === "application") return "🔊 The current song has been skipped.";
}
}

View file

@ -3,11 +3,13 @@ import MusicCommand from "../../classes/musicCommand.js";
class StopCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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.getNode().leaveChannel(this.channel.guild.id);
this.success = true;
return "🔊 The current voice channel session has ended.";
}
if (this.connection.host !== this.author.id && !this.member.permissions.has("manageChannels")) return "Only the current voice session host can stop the music!";
@ -15,6 +17,7 @@ class StopCommand extends MusicCommand {
connection.node.leaveChannel(this.channel.guild.id);
players.delete(this.channel.guild.id);
queues.delete(this.channel.guild.id);
this.success = true;
return "🔊 The current voice channel session has ended.";
}

View file

@ -2,12 +2,14 @@ import MusicCommand from "../../classes/musicCommand.js";
class ToggleCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
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.member.permissions.has("manageChannels")) return "Only the current voice session host can pause/resume the music!";
const player = this.connection.player;
player.setPaused(!player.paused ? true : false);
this.success = true;
return `🔊 The player has been ${player.paused ? "paused" : "resumed"}.`;
}

View file

@ -6,7 +6,10 @@ import { sounds, info } from "../../utils/collections.js";
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!";
if (!sounds.has(soundName)) {
this.success = false;
return "You need to provide a sound to play!";
}
const name = sounds.get(soundName);
return await play(this.client, name, { channel: this.channel, member: this.member, type: this.type, interaction: this.interaction });
}

View file

@ -7,6 +7,7 @@ const blacklist = ["create", "add", "edit", "remove", "delete", "list", "random"
class TagsCommand extends Command {
// todo: attempt to not make this file the worst thing that human eyes have ever seen
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
const cmd = this.type === "classic" ? (this.args[0] ?? "").toLowerCase() : this.optionsArray[0].name;
if (!cmd || !cmd.trim()) return "You need to provide the name of the tag you want to view!";
@ -18,6 +19,7 @@ class TagsCommand extends Command {
const getResult = await database.getTag(this.channel.guild.id, tagName);
if (getResult) return "This tag already exists!";
const result = await database.setTag(tagName, { content: this.type === "classic" ? this.args.slice(2).join(" ") : this.optionsArray[0].options[1].value, author: this.member.id }, this.channel.guild);
this.success = true;
if (result) return result;
return `The tag \`${tagName}\` has been added!`;
} else if (cmd === "delete" || cmd === "remove") {
@ -27,6 +29,7 @@ class TagsCommand extends Command {
const owners = process.env.OWNER.split(",");
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(tagName, this.channel.guild);
this.success = true;
return `The tag \`${tagName}\` has been deleted!`;
} else if (cmd === "edit") {
if (!tagName || !tagName.trim()) return "You need to provide the name of the tag you want to edit!";
@ -35,12 +38,14 @@ class TagsCommand extends Command {
const owners = process.env.OWNER.split(",");
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.editTag(tagName, { content: this.type === "classic" ? this.args.slice(2).join(" ") : this.optionsArray[0].options[1].value, author: this.member.id }, this.channel.guild);
this.success = true;
return `The tag \`${tagName}\` has been edited!`;
} else if (cmd === "own" || cmd === "owner") {
if (!tagName || !tagName.trim()) return "You need to provide the name of the tag you want to check the owner of!";
const getResult = await database.getTag(this.channel.guild.id, tagName);
if (!getResult) return "This tag doesn't exist!";
const user = await this.ipc.fetchUser(getResult.author);
this.success = true;
if (!user) {
try {
const restUser = await this.client.getRESTUser(getResult.author);
@ -77,6 +82,7 @@ class TagsCommand extends Command {
});
}
if (embeds.length === 0) return "I couldn't find any tags!";
this.success = true;
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
} else {
let getResult;
@ -87,6 +93,7 @@ class TagsCommand extends Command {
getResult = await database.getTag(this.channel.guild.id, this.type === "classic" ? cmd : tagName);
}
if (!getResult) return "This tag doesn't exist!";
this.success = true;
if (getResult.content.length > 2000) {
return {
embeds: [{