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

18
app.js
View file

@ -35,6 +35,8 @@ import database from "./utils/database.js";
import { Api } from "@top-gg/sdk";
const dbl = process.env.NODE_ENV === "production" && process.env.DBL ? new Api(process.env.DBL) : null;
const { types } = JSON.parse(readFileSync(new URL("./config/commands.json", import.meta.url)));
if (isMaster) {
const esmBotVersion = JSON.parse(readFileSync(new URL("./package.json", import.meta.url))).version;
const erisFleetVersion = JSON.parse(readFileSync(new URL("./node_modules/eris-fleet/package.json", import.meta.url))).version; // a bit of a hacky way to get the eris-fleet version
@ -66,9 +68,16 @@ esmBot ${esmBotVersion} (${(await exec("git rev-parse HEAD").then(output => outp
const services = [
{ name: "image", ServiceWorker: ImageWorker }
];
if (process.env.METRICS && process.env.METRICS !== "") services.push({ name: "prometheus", ServiceWorker: PrometheusWorker });
const intents = [
"guilds",
"guildVoiceStates",
"guildMessages",
"directMessages"
];
if (types.classic) intents.push("messageContent");
const Admiral = new Fleet({
BotWorker: Shard,
token: `Bot ${process.env.TOKEN}`,
@ -92,12 +101,7 @@ const Admiral = new Fleet({
},
restMode: true,
messageLimit: 50,
intents: [
"guilds",
"guildVoiceStates",
"guildMessages",
"directMessages"
],
intents,
stats: {
requestTimeout: 30000
},

View file

@ -1,4 +1,5 @@
class Command {
success = true;
constructor(client, cluster, worker, ipc, options) {
this.client = client;
this.cluster = cluster;

View file

@ -4,40 +4,15 @@ import { runningCommands } from "../utils/collections.js";
import { readFileSync } from "fs";
const { emotes } = JSON.parse(readFileSync(new URL("../config/messages.json", import.meta.url)));
import { random } from "../utils/misc.js";
import { selectedImages } from "../utils/collections.js";
class ImageCommand extends Command {
/*this.embed = {
"title": "Your image is being generated! (PRELIMINARY EMBED)",
"description": "The current progress is outlined below:",
"color": 16711680,
"footer": {
"text": "Step 2/3"
},
"author": {
"name": "Processing...",
"icon_url": "https://cdn.discordapp.com/avatars/429305856241172480/a20f739886ae47cfb10fa069416e8ed3.jpg"
},
"fields": [
{
"name": "Downloading...",
"value": "✅ Done!"
},
{
"name": "Processing...",
"value": "<a:processing:818243325891051581> In progress"
},
{
"name": "Uploading...",
"value": "<a:processing:818243325891051581> Waiting for previous steps to complete"
}
]
};*/
async criteria() {
return true;
}
async run() {
this.success = false;
const timestamp = this.type === "classic" ? this.message.createdAt : Math.floor((this.interaction.id / 4194304) + 1420070400000);
// check if this command has already been run in this channel with the same arguments, and we are awaiting its result
// if so, don't re-run it
@ -58,7 +33,9 @@ class ImageCommand extends Command {
if (this.constructor.requiresImage) {
try {
const image = await imageDetect(this.client, this.message, this.interaction, this.options, true);
const selection = selectedImages.get(this.author.id);
const image = selection ?? await imageDetect(this.client, this.message, this.interaction, this.options, true);
if (selection) selectedImages.delete(this.author.id);
if (image === undefined) {
runningCommands.delete(this.author.id);
return this.constructor.noImage;
@ -101,7 +78,10 @@ class ImageCommand extends Command {
try {
const { buffer, type } = await this.ipc.serviceCommand("image", { type: "run", obj: magickParams }, true, 9000000);
if (type === "nogif" && this.constructor.requiresGIF) return "That isn't a GIF!";
if (type === "nogif" && this.constructor.requiresGIF) {
return "That isn't a GIF!";
}
this.success = true;
return {
file: Buffer.from(buffer.data),
name: `${this.constructor.command}.${type}`

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: [{

View file

@ -1,31 +1,40 @@
import database from "../utils/database.js";
import * as logger from "../utils/logger.js";
import { commands } from "../utils/collections.js";
import { CommandInteraction } from "eris";
import { commands, messageCommands } from "../utils/collections.js";
import { clean } from "../utils/misc.js";
import { upload } from "../utils/tempimages.js";
// run when a slash command is executed
export default async (client, cluster, worker, ipc, interaction) => {
if (!(interaction instanceof CommandInteraction)) return;
if (interaction?.type !== 2) return;
// check if command exists and if it's enabled
const command = interaction.data.name;
const cmd = commands.get(command);
let cmd = commands.get(command);
if (!cmd) {
cmd = messageCommands.get(command);
if (!cmd) return;
}
const invoker = interaction.member ?? interaction.user;
// actually run the command
logger.log("log", `${invoker.username} (${invoker.id}) ran slash command ${command}`);
logger.log("log", `${invoker.username} (${invoker.id}) ran application command ${command}`);
try {
await database.addCount(command);
// eslint-disable-next-line no-unused-vars
const commandClass = new cmd(client, cluster, worker, ipc, { type: "application", interaction });
const result = await commandClass.run();
const replyMethod = interaction.acknowledged ? "editOriginalMessage" : "createMessage";
if (typeof result === "string" || (typeof result === "object" && result.embeds)) {
await interaction[replyMethod](result);
if (typeof result === "string") {
await interaction[replyMethod]({
content: result,
flags: commandClass.success ? 0 : 64
});
} else if (typeof result === "object" && result.embeds) {
await interaction[replyMethod](Object.assign(result, {
flags: result.flags ?? (commandClass.success ? 0 : 64)
}));
} else if (typeof result === "object" && result.file) {
let fileSize = 8388119;
if (interaction.channel.guild) {
@ -42,7 +51,10 @@ export default async (client, cluster, worker, ipc, interaction) => {
if (process.env.TEMPDIR && process.env.TEMPDIR !== "") {
await upload(client, ipc, result, interaction, true);
} else {
await interaction[replyMethod]("The resulting image was more than 8MB in size, so I can't upload it.");
await interaction[replyMethod]({
content: "The resulting image was more than 8MB in size, so I can't upload it.",
flags: 64
});
}
} else {
await interaction[replyMethod](result.text ? result.text : {}, result);
@ -51,13 +63,13 @@ export default async (client, cluster, worker, ipc, interaction) => {
} catch (error) {
const replyMethod = interaction.acknowledged ? "editOriginalMessage" : "createMessage";
if (error.toString().includes("Request entity too large")) {
await interaction[replyMethod]("The resulting file was too large to upload. Try again with a smaller image if possible.");
await interaction[replyMethod]({ content: "The resulting file was too large to upload. Try again with a smaller image if possible.", flags: 64 });
} else if (error.toString().includes("Job ended prematurely")) {
await interaction[replyMethod]("Something happened to the image servers before I could receive the image. Try running your command again.");
await interaction[replyMethod]({ content: "Something happened to the image servers before I could receive the image. Try running your command again.", flags: 64 });
} else if (error.toString().includes("Timed out")) {
await interaction[replyMethod]("The request timed out before I could download that image. Try uploading your image somewhere else or reducing its size.");
await interaction[replyMethod]({ content: "The request timed out before I could download that image. Try uploading your image somewhere else or reducing its size.", flags: 64 });
} else {
logger.error(`Error occurred with slash command ${command} with arguments ${JSON.stringify(interaction.data.options)}: ${error.stack || error}`);
logger.error(`Error occurred with application command ${command} with arguments ${JSON.stringify(interaction.data.options)}: ${error.stack || error}`);
try {
await interaction[replyMethod]("Uh oh! I ran into an error while running this command. Please report the content of the attached file at the following link or on the esmBot Support server: <https://github.com/esmBot/esmBot/issues>", {
file: `Message: ${await clean(error)}\n\nStack Trace: ${await clean(error.stack)}`,

View file

@ -25,7 +25,7 @@
"dependencies": {
"@top-gg/sdk": "^3.1.3",
"cmake-js": "^6.3.2",
"dotenv": "^16.0.1",
"dotenv": "^16.0.2",
"emoji-regex": "^10.1.0",
"eris": "github:esmBot/eris#dev",
"eris-fleet": "github:esmBot/eris-fleet#dev",
@ -37,23 +37,23 @@
"qrcode": "^1.5.1",
"sharp": "^0.30.7",
"shoukaku": "github:Deivu/shoukaku",
"undici": "^5.8.2",
"undici": "^5.10.0",
"winston": "^3.8.1",
"winston-daily-rotate-file": "^4.7.1"
},
"devDependencies": {
"@babel/core": "^7.18.10",
"@babel/core": "^7.18.13",
"@babel/eslint-parser": "^7.18.9",
"@babel/eslint-plugin": "^7.18.10",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"eslint": "^8.21.0",
"eslint": "^8.23.0",
"eslint-plugin-unicorn": "^42.0.0"
},
"optionalDependencies": {
"better-sqlite3": "^7.6.2",
"bufferutil": "^4.0.6",
"erlpack": "github:abalabahaha/erlpack",
"pg": "^8.7.3",
"pg": "^8.8.0",
"uuid": "^8.3.2",
"ws": "^8.8.1",
"zlib-sync": "^0.1.7"

View file

@ -1,7 +1,7 @@
lockfileVersion: 5.4
specifiers:
'@babel/core': ^7.18.10
'@babel/core': ^7.18.13
'@babel/eslint-parser': ^7.18.9
'@babel/eslint-plugin': ^7.18.10
'@babel/plugin-proposal-class-properties': ^7.18.6
@ -9,23 +9,23 @@ specifiers:
better-sqlite3: ^7.6.2
bufferutil: ^4.0.6
cmake-js: ^6.3.2
dotenv: ^16.0.1
dotenv: ^16.0.2
emoji-regex: ^10.1.0
eris: github:esmBot/eris#dev
eris-fleet: github:esmBot/eris-fleet#dev
erlpack: github:abalabahaha/erlpack
eslint: ^8.21.0
eslint: ^8.23.0
eslint-plugin-unicorn: ^42.0.0
file-type: ^17.1.6
format-duration: ^2.0.0
jsqr: ^1.4.0
node-addon-api: ^5.0.0
node-emoji: ^1.11.0
pg: ^8.7.3
pg: ^8.8.0
qrcode: ^1.5.1
sharp: ^0.30.7
shoukaku: github:Deivu/shoukaku
undici: ^5.8.2
undici: ^5.10.0
uuid: ^8.3.2
winston: ^3.8.1
winston-daily-rotate-file: ^4.7.1
@ -35,10 +35,10 @@ specifiers:
dependencies:
'@top-gg/sdk': 3.1.3
cmake-js: 6.3.2
dotenv: 16.0.1
dotenv: 16.0.2
emoji-regex: 10.1.0
eris: github.com/esmBot/eris/a1a919a19af8e73443b826ea587116db93bcc7a7_bufferutil@4.0.6
eris-fleet: github.com/esmBot/eris-fleet/5eac4f4d7e99603fc59ed6d28b834279d95948d3_eris@0.17.2-dev
eris: github.com/esmBot/eris/fbc637e7f92963d7f9e57c86223e995269e70de0_bufferutil@4.0.6
eris-fleet: github.com/esmBot/eris-fleet/a19920fa0b2723e6afb6a9d31375b419a8743e82_eris@0.17.2-dev
file-type: 17.1.6
format-duration: 2.0.0
jsqr: 1.4.0
@ -47,7 +47,7 @@ dependencies:
qrcode: 1.5.1
sharp: 0.30.7
shoukaku: github.com/Deivu/shoukaku/b6c724bf6e72a5dcf14beb6f51d35bc4b6a17647_bufferutil@4.0.6
undici: 5.8.2
undici: 5.10.0
winston: 3.8.1
winston-daily-rotate-file: 4.7.1_winston@3.8.1
@ -55,18 +55,18 @@ optionalDependencies:
better-sqlite3: 7.6.2
bufferutil: 4.0.6
erlpack: github.com/abalabahaha/erlpack/f7d730debe32c416d1b55b4217f8aef2ade05874
pg: 8.7.3
pg: 8.8.0
uuid: 8.3.2
ws: 8.8.1_bufferutil@4.0.6
zlib-sync: 0.1.7
devDependencies:
'@babel/core': 7.18.10
'@babel/eslint-parser': 7.18.9_xqt7ek4fk233nrovqiamjvck4u
'@babel/eslint-plugin': 7.18.10_nydfxhofap6opqwg3seb7aoezu
'@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.18.10
eslint: 8.21.0
eslint-plugin-unicorn: 42.0.0_eslint@8.21.0
'@babel/core': 7.18.13
'@babel/eslint-parser': 7.18.9_f6qngz4m6vfa3g3bk2x4qa2a2q
'@babel/eslint-plugin': 7.18.10_4q6j6uxclgkhf7c4z5hym4nj7a
'@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.18.13
eslint: 8.23.0
eslint-plugin-unicorn: 42.0.0_eslint@8.23.0
packages:
@ -75,7 +75,7 @@ packages:
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/gen-mapping': 0.1.1
'@jridgewell/trace-mapping': 0.3.14
'@jridgewell/trace-mapping': 0.3.15
dev: true
/@babel/code-frame/7.18.6:
@ -85,25 +85,25 @@ packages:
'@babel/highlight': 7.18.6
dev: true
/@babel/compat-data/7.18.8:
resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==}
/@babel/compat-data/7.18.13:
resolution: {integrity: sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/core/7.18.10:
resolution: {integrity: sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==}
/@babel/core/7.18.13:
resolution: {integrity: sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.2.0
'@babel/code-frame': 7.18.6
'@babel/generator': 7.18.12
'@babel/helper-compilation-targets': 7.18.9_@babel+core@7.18.10
'@babel/generator': 7.18.13
'@babel/helper-compilation-targets': 7.18.9_@babel+core@7.18.13
'@babel/helper-module-transforms': 7.18.9
'@babel/helpers': 7.18.9
'@babel/parser': 7.18.11
'@babel/parser': 7.18.13
'@babel/template': 7.18.10
'@babel/traverse': 7.18.11
'@babel/types': 7.18.10
'@babel/traverse': 7.18.13
'@babel/types': 7.18.13
convert-source-map: 1.8.0
debug: 4.3.4
gensync: 1.0.0-beta.2
@ -113,37 +113,37 @@ packages:
- supports-color
dev: true
/@babel/eslint-parser/7.18.9_xqt7ek4fk233nrovqiamjvck4u:
/@babel/eslint-parser/7.18.9_f6qngz4m6vfa3g3bk2x4qa2a2q:
resolution: {integrity: sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ==}
engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
peerDependencies:
'@babel/core': '>=7.11.0'
eslint: ^7.5.0 || ^8.0.0
dependencies:
'@babel/core': 7.18.10
eslint: 8.21.0
'@babel/core': 7.18.13
eslint: 8.23.0
eslint-scope: 5.1.1
eslint-visitor-keys: 2.1.0
semver: 6.3.0
dev: true
/@babel/eslint-plugin/7.18.10_nydfxhofap6opqwg3seb7aoezu:
/@babel/eslint-plugin/7.18.10_4q6j6uxclgkhf7c4z5hym4nj7a:
resolution: {integrity: sha512-iV1OZj/7eg4wZIcsVEkXS3MUWdhmpLsu2h+9Zr2ppywKWdCRs6VfjxbRzmHHYeurTizrrnaJ9ZkbO8KOv4lauQ==}
engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
peerDependencies:
'@babel/eslint-parser': '>=7.11.0'
eslint: '>=7.5.0'
dependencies:
'@babel/eslint-parser': 7.18.9_xqt7ek4fk233nrovqiamjvck4u
eslint: 8.21.0
'@babel/eslint-parser': 7.18.9_f6qngz4m6vfa3g3bk2x4qa2a2q
eslint: 8.23.0
eslint-rule-composer: 0.3.0
dev: true
/@babel/generator/7.18.12:
resolution: {integrity: sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==}
/@babel/generator/7.18.13:
resolution: {integrity: sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.18.10
'@babel/types': 7.18.13
'@jridgewell/gen-mapping': 0.3.2
jsesc: 2.5.2
dev: true
@ -152,29 +152,29 @@ packages:
resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.18.10
'@babel/types': 7.18.13
dev: true
/@babel/helper-compilation-targets/7.18.9_@babel+core@7.18.10:
/@babel/helper-compilation-targets/7.18.9_@babel+core@7.18.13:
resolution: {integrity: sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/compat-data': 7.18.8
'@babel/core': 7.18.10
'@babel/compat-data': 7.18.13
'@babel/core': 7.18.13
'@babel/helper-validator-option': 7.18.6
browserslist: 4.21.3
semver: 6.3.0
dev: true
/@babel/helper-create-class-features-plugin/7.18.9_@babel+core@7.18.10:
resolution: {integrity: sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==}
/@babel/helper-create-class-features-plugin/7.18.13_@babel+core@7.18.13:
resolution: {integrity: sha512-hDvXp+QYxSRL+23mpAlSGxHMDyIGChm0/AwTfTAAK5Ufe40nCsyNdaYCGuK91phn/fVu9kqayImRDkvNAgdrsA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.18.10
'@babel/core': 7.18.13
'@babel/helper-annotate-as-pure': 7.18.6
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.18.9
@ -196,28 +196,28 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.18.10
'@babel/types': 7.18.10
'@babel/types': 7.18.13
dev: true
/@babel/helper-hoist-variables/7.18.6:
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.18.10
'@babel/types': 7.18.13
dev: true
/@babel/helper-member-expression-to-functions/7.18.9:
resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.18.10
'@babel/types': 7.18.13
dev: true
/@babel/helper-module-imports/7.18.6:
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.18.10
'@babel/types': 7.18.13
dev: true
/@babel/helper-module-transforms/7.18.9:
@ -230,8 +230,8 @@ packages:
'@babel/helper-split-export-declaration': 7.18.6
'@babel/helper-validator-identifier': 7.18.6
'@babel/template': 7.18.10
'@babel/traverse': 7.18.11
'@babel/types': 7.18.10
'@babel/traverse': 7.18.13
'@babel/types': 7.18.13
transitivePeerDependencies:
- supports-color
dev: true
@ -240,7 +240,7 @@ packages:
resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.18.10
'@babel/types': 7.18.13
dev: true
/@babel/helper-plugin-utils/7.18.9:
@ -255,8 +255,8 @@ packages:
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-member-expression-to-functions': 7.18.9
'@babel/helper-optimise-call-expression': 7.18.6
'@babel/traverse': 7.18.11
'@babel/types': 7.18.10
'@babel/traverse': 7.18.13
'@babel/types': 7.18.13
transitivePeerDependencies:
- supports-color
dev: true
@ -265,14 +265,14 @@ packages:
resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.18.10
'@babel/types': 7.18.13
dev: true
/@babel/helper-split-export-declaration/7.18.6:
resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.18.10
'@babel/types': 7.18.13
dev: true
/@babel/helper-string-parser/7.18.10:
@ -295,8 +295,8 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.18.10
'@babel/traverse': 7.18.11
'@babel/types': 7.18.10
'@babel/traverse': 7.18.13
'@babel/types': 7.18.13
transitivePeerDependencies:
- supports-color
dev: true
@ -310,22 +310,22 @@ packages:
js-tokens: 4.0.0
dev: true
/@babel/parser/7.18.11:
resolution: {integrity: sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==}
/@babel/parser/7.18.13:
resolution: {integrity: sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.18.10
'@babel/types': 7.18.13
dev: true
/@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.18.10:
/@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.18.13:
resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.18.10
'@babel/helper-create-class-features-plugin': 7.18.9_@babel+core@7.18.10
'@babel/core': 7.18.13
'@babel/helper-create-class-features-plugin': 7.18.13_@babel+core@7.18.13
'@babel/helper-plugin-utils': 7.18.9
transitivePeerDependencies:
- supports-color
@ -336,30 +336,30 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/parser': 7.18.11
'@babel/types': 7.18.10
'@babel/parser': 7.18.13
'@babel/types': 7.18.13
dev: true
/@babel/traverse/7.18.11:
resolution: {integrity: sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==}
/@babel/traverse/7.18.13:
resolution: {integrity: sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.18.12
'@babel/generator': 7.18.13
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.18.9
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.18.11
'@babel/types': 7.18.10
'@babel/parser': 7.18.13
'@babel/types': 7.18.13
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: true
/@babel/types/7.18.10:
resolution: {integrity: sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==}
/@babel/types/7.18.13:
resolution: {integrity: sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.18.10
@ -380,13 +380,13 @@ packages:
kuler: 2.0.0
dev: false
/@eslint/eslintrc/1.3.0:
resolution: {integrity: sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==}
/@eslint/eslintrc/1.3.1:
resolution: {integrity: sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4
espree: 9.3.3
espree: 9.4.0
globals: 13.17.0
ignore: 5.2.0
import-fresh: 3.3.0
@ -412,6 +412,11 @@ packages:
resolution: {integrity: sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==}
dev: true
/@humanwhocodes/module-importer/1.0.1:
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
engines: {node: '>=12.22'}
dev: true
/@humanwhocodes/object-schema/1.2.1:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
dev: true
@ -430,7 +435,7 @@ packages:
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.14
'@jridgewell/trace-mapping': 0.3.14
'@jridgewell/trace-mapping': 0.3.15
dev: true
/@jridgewell/resolve-uri/3.1.0:
@ -447,8 +452,8 @@ packages:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: true
/@jridgewell/trace-mapping/0.3.14:
resolution: {integrity: sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==}
/@jridgewell/trace-mapping/0.3.15:
resolution: {integrity: sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
@ -638,8 +643,8 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
caniuse-lite: 1.0.30001375
electron-to-chromium: 1.4.215
caniuse-lite: 1.0.30001387
electron-to-chromium: 1.4.237
node-releases: 2.0.6
update-browserslist-db: 1.0.5_browserslist@4.21.3
dev: true
@ -704,8 +709,8 @@ packages:
engines: {node: '>=6'}
dev: false
/caniuse-lite/1.0.30001375:
resolution: {integrity: sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==}
/caniuse-lite/1.0.30001387:
resolution: {integrity: sha512-fKDH0F1KOJvR+mWSOvhj8lVRr/Q/mc5u5nabU2vi1/sgvlSqEsE8dOq0Hy/BqVbDkCYQPRRHB1WRjW6PGB/7PA==}
dev: true
/chainsaw/0.1.0:
@ -924,8 +929,8 @@ packages:
esutils: 2.0.3
dev: true
/dotenv/16.0.1:
resolution: {integrity: sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==}
/dotenv/16.0.2:
resolution: {integrity: sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==}
engines: {node: '>=12'}
dev: false
@ -935,8 +940,8 @@ packages:
readable-stream: 2.1.5
dev: false
/electron-to-chromium/1.4.215:
resolution: {integrity: sha512-vqZxT8C5mlDZ//hQFhneHmOLnj1LhbzxV0+I1yqHV8SB1Oo4Y5Ne9+qQhwHl7O1s9s9cRuo2l5CoLEHdhMTwZg==}
/electron-to-chromium/1.4.237:
resolution: {integrity: sha512-vxVyGJcsgArNOVUJcXm+7iY3PJAfmSapEszQD1HbyPLl0qoCmNQ1o/EX3RI7Et5/88In9oLxX3SGF8J3orkUgA==}
dev: true
/emoji-regex/10.1.0:
@ -982,7 +987,7 @@ packages:
engines: {node: '>=10'}
dev: true
/eslint-plugin-unicorn/42.0.0_eslint@8.21.0:
/eslint-plugin-unicorn/42.0.0_eslint@8.23.0:
resolution: {integrity: sha512-ixBsbhgWuxVaNlPTT8AyfJMlhyC5flCJFjyK3oKE8TRrwBnaHvUbuIkCM1lqg8ryYrFStL/T557zfKzX4GKSlg==}
engines: {node: '>=12'}
peerDependencies:
@ -991,8 +996,8 @@ packages:
'@babel/helper-validator-identifier': 7.18.6
ci-info: 3.3.2
clean-regexp: 1.0.0
eslint: 8.21.0
eslint-utils: 3.0.0_eslint@8.21.0
eslint: 8.23.0
eslint-utils: 3.0.0_eslint@8.23.0
esquery: 1.4.0
indent-string: 4.0.0
is-builtin-module: 3.2.0
@ -1026,13 +1031,13 @@ packages:
estraverse: 5.3.0
dev: true
/eslint-utils/3.0.0_eslint@8.21.0:
/eslint-utils/3.0.0_eslint@8.23.0:
resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
peerDependencies:
eslint: '>=5'
dependencies:
eslint: 8.21.0
eslint: 8.23.0
eslint-visitor-keys: 2.1.0
dev: true
@ -1046,14 +1051,15 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/eslint/8.21.0:
resolution: {integrity: sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==}
/eslint/8.23.0:
resolution: {integrity: sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
'@eslint/eslintrc': 1.3.0
'@eslint/eslintrc': 1.3.1
'@humanwhocodes/config-array': 0.10.4
'@humanwhocodes/gitignore-to-minimatch': 1.0.2
'@humanwhocodes/module-importer': 1.0.1
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
@ -1061,9 +1067,9 @@ packages:
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.1.1
eslint-utils: 3.0.0_eslint@8.21.0
eslint-utils: 3.0.0_eslint@8.23.0
eslint-visitor-keys: 3.3.0
espree: 9.3.3
espree: 9.4.0
esquery: 1.4.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
@ -1089,13 +1095,12 @@ packages:
strip-ansi: 6.0.1
strip-json-comments: 3.1.1
text-table: 0.2.0
v8-compile-cache: 2.3.0
transitivePeerDependencies:
- supports-color
dev: true
/espree/9.3.3:
resolution: {integrity: sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==}
/espree/9.4.0:
resolution: {integrity: sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
acorn: 8.8.0
@ -1223,12 +1228,12 @@ packages:
resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
engines: {node: ^10.12.0 || >=12.0.0}
dependencies:
flatted: 3.2.6
flatted: 3.2.7
rimraf: 3.0.2
dev: true
/flatted/3.2.6:
resolution: {integrity: sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==}
/flatted/3.2.7:
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
dev: true
/fn.name/1.1.0:
@ -1822,12 +1827,6 @@ packages:
word-wrap: 1.2.3
dev: true
/opusscript/0.0.8:
resolution: {integrity: sha512-VSTi1aWFuCkRCVq+tx/BQ5q9fMnQ9pVZ3JU4UHKqTkf0ED3fKEPdr+gKAAl3IA2hj9rrP6iyq3hlcJq3HELtNQ==}
requiresBuild: true
dev: false
optional: true
/os-locale/1.4.0:
resolution: {integrity: sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==}
engines: {node: '>=0.10.0'}
@ -1925,12 +1924,12 @@ packages:
dev: false
optional: true
/pg-pool/3.5.1_pg@8.7.3:
resolution: {integrity: sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==}
/pg-pool/3.5.2_pg@8.8.0:
resolution: {integrity: sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==}
peerDependencies:
pg: '>=8.0'
dependencies:
pg: 8.7.3
pg: 8.8.0
dev: false
optional: true
@ -1951,12 +1950,12 @@ packages:
dev: false
optional: true
/pg/8.7.3:
resolution: {integrity: sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==}
/pg/8.8.0:
resolution: {integrity: sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==}
engines: {node: '>= 8.0.0'}
requiresBuild: true
peerDependencies:
pg-native: '>=2.0.0'
pg-native: '>=3.0.1'
peerDependenciesMeta:
pg-native:
optional: true
@ -1964,7 +1963,7 @@ packages:
buffer-writer: 2.0.0
packet-reader: 1.0.0
pg-connection-string: 2.5.0
pg-pool: 3.5.1_pg@8.7.3
pg-pool: 3.5.2_pg@8.8.0
pg-protocol: 1.5.0
pg-types: 2.2.0
pgpass: 1.0.5
@ -2332,7 +2331,7 @@ packages:
resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
dependencies:
spdx-expression-parse: 3.0.1
spdx-license-ids: 3.0.11
spdx-license-ids: 3.0.12
dev: true
/spdx-exceptions/2.3.0:
@ -2343,11 +2342,11 @@ packages:
resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
dependencies:
spdx-exceptions: 2.3.0
spdx-license-ids: 3.0.11
spdx-license-ids: 3.0.12
dev: true
/spdx-license-ids/3.0.11:
resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==}
/spdx-license-ids/3.0.12:
resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==}
dev: true
/split2/4.1.0:
@ -2544,12 +2543,6 @@ packages:
safe-buffer: 5.2.1
dev: false
/tweetnacl/1.0.3:
resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==}
requiresBuild: true
dev: false
optional: true
/type-check/0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@ -2572,8 +2565,8 @@ packages:
engines: {node: '>=8'}
dev: true
/undici/5.8.2:
resolution: {integrity: sha512-3KLq3pXMS0Y4IELV045fTxqz04Nk9Ms7yfBBHum3yxsTR4XNn+ZCaUbf/mWitgYDAhsplQ0B1G4S5D345lMO3A==}
/undici/5.10.0:
resolution: {integrity: sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==}
engines: {node: '>=12.18'}
dev: false
@ -2583,7 +2576,7 @@ packages:
dev: false
/unpipe/1.0.0:
resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=}
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
dev: false
@ -2633,10 +2626,6 @@ packages:
dev: false
optional: true
/v8-compile-cache/2.3.0:
resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
dev: true
/validate-npm-package-license/3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
dependencies:
@ -2838,7 +2827,7 @@ packages:
prepare: true
requiresBuild: true
dependencies:
undici: 5.8.2
undici: 5.10.0
ws: 8.8.1_bufferutil@4.0.6
transitivePeerDependencies:
- bufferutil
@ -2856,28 +2845,25 @@ packages:
dev: false
optional: true
github.com/esmBot/eris-fleet/5eac4f4d7e99603fc59ed6d28b834279d95948d3_eris@0.17.2-dev:
resolution: {tarball: https://codeload.github.com/esmBot/eris-fleet/tar.gz/5eac4f4d7e99603fc59ed6d28b834279d95948d3}
id: github.com/esmBot/eris-fleet/5eac4f4d7e99603fc59ed6d28b834279d95948d3
github.com/esmBot/eris-fleet/a19920fa0b2723e6afb6a9d31375b419a8743e82_eris@0.17.2-dev:
resolution: {tarball: https://codeload.github.com/esmBot/eris-fleet/tar.gz/a19920fa0b2723e6afb6a9d31375b419a8743e82}
id: github.com/esmBot/eris-fleet/a19920fa0b2723e6afb6a9d31375b419a8743e82
name: eris-fleet
version: 1.0.2
peerDependencies:
eris: ~0.16.0
dependencies:
eris: github.com/esmBot/eris/a1a919a19af8e73443b826ea587116db93bcc7a7_bufferutil@4.0.6
eris: github.com/esmBot/eris/fbc637e7f92963d7f9e57c86223e995269e70de0_bufferutil@4.0.6
dev: false
github.com/esmBot/eris/a1a919a19af8e73443b826ea587116db93bcc7a7_bufferutil@4.0.6:
resolution: {tarball: https://codeload.github.com/esmBot/eris/tar.gz/a1a919a19af8e73443b826ea587116db93bcc7a7}
id: github.com/esmBot/eris/a1a919a19af8e73443b826ea587116db93bcc7a7
github.com/esmBot/eris/fbc637e7f92963d7f9e57c86223e995269e70de0_bufferutil@4.0.6:
resolution: {tarball: https://codeload.github.com/esmBot/eris/tar.gz/fbc637e7f92963d7f9e57c86223e995269e70de0}
id: github.com/esmBot/eris/fbc637e7f92963d7f9e57c86223e995269e70de0
name: eris
version: 0.17.2-dev
engines: {node: '>=10.4.0'}
dependencies:
ws: 8.8.1_bufferutil@4.0.6
optionalDependencies:
opusscript: 0.0.8
tweetnacl: 1.0.3
transitivePeerDependencies:
- bufferutil
- utf-8-validate

View file

@ -55,8 +55,9 @@ class Shard extends BaseClusterWorker {
const commandArray = await update(this.bot, this.clusterID, this.workerID, this.ipc, soundStatus);
try {
await this.bot.bulkEditCommands(commandArray);
} catch {
log("error", "Failed to send command data to Discord, slash commands may be unavailable.");
} catch (e) {
log("error", e);
log("error", "Failed to send command data to Discord, slash/message commands may be unavailable.");
}
}
log("info", "Finished loading commands.");
@ -127,6 +128,14 @@ class Shard extends BaseClusterWorker {
// connect to lavalink
if (!status && !connected) connect(this.bot);
const broadcastMessage = await this.ipc.centralStore.get("broadcast");
if (broadcastMessage) {
broadcast = true;
this.bot.editStatus("dnd", {
name: `${broadcastMessage} | @${this.bot.user.username} help`,
});
}
this.activityChanger();
log("info", `Started worker ${this.workerID}.`);

View file

@ -1,4 +1,5 @@
export const commands = new Map();
export const messageCommands = new Map();
export const paths = new Map();
export const aliases = new Map();
export const info = new Map();
@ -6,15 +7,20 @@ export const sounds = new Map();
export const categories = new Map();
class TimedMap extends Map {
constructor(time, values) {
super(values);
this.time = time;
}
set(key, value) {
super.set(key, value);
setTimeout(() => {
if (super.has(key)) super.delete(key);
}, 5000);
}, this.time);
}
}
export const runningCommands = new TimedMap();
export const runningCommands = new TimedMap(5000);
export const selectedImages = new TimedMap(180000);
class Cache extends Map {
constructor(values) {

View file

@ -1,4 +1,4 @@
import { paths, commands, info, sounds, categories, aliases as _aliases } from "./collections.js";
import { paths, commands, messageCommands, info, sounds, categories, aliases as _aliases } from "./collections.js";
import { log } from "./logger.js";
import { readFileSync } from "fs";
@ -16,33 +16,41 @@ export async function load(client, cluster, worker, ipc, command, soundStatus, s
return;
}
const commandArray = command.split("/");
const commandName = commandArray[commandArray.length - 1].split(".")[0];
let commandName = commandArray[commandArray.length - 1].split(".")[0];
const category = commandArray[commandArray.length - 2];
if (blacklist.includes(commandName)) {
log("warn", `Skipped loading blacklisted command ${command}...`);
return;
}
if (category === "message") {
const nameStringArray = commandName.split("-");
for (const index of nameStringArray.keys()) {
nameStringArray[index] = nameStringArray[index].charAt(0).toUpperCase() + nameStringArray[index].slice(1);
}
commandName = nameStringArray.join(" ");
}
props.init();
paths.set(commandName, command);
commands.set(commandName, props);
if (Object.getPrototypeOf(props).name === "SoundboardCommand") sounds.set(commandName, props.file);
const category = commandArray[commandArray.length - 2];
info.set(commandName, {
const commandInfo = {
category: category,
description: props.description,
aliases: props.aliases,
params: props.arguments,
flags: props.flags,
slashAllowed: props.slashAllowed,
directAllowed: props.directAllowed
});
directAllowed: props.directAllowed,
type: 1
};
const categoryCommands = categories.get(category);
categories.set(category, categoryCommands ? [...categoryCommands, commandName] : [commandName]);
if (category === "message") {
messageCommands.set(commandName, props);
commandInfo.type = 3;
} else {
commands.set(commandName, props);
if (slashReload && props.slashAllowed) {
const commandList = await client.getCommands();
@ -56,6 +64,14 @@ export async function load(client, cluster, worker, ipc, command, soundStatus, s
options: props.flags
});
}
}
if (Object.getPrototypeOf(props).name === "SoundboardCommand") sounds.set(commandName, props.file);
info.set(commandName, commandInfo);
const categoryCommands = categories.get(category);
categories.set(category, categoryCommands ? [...categoryCommands, commandName] : [commandName]);
if (props.aliases) {
for (const alias of props.aliases) {
@ -68,11 +84,11 @@ export async function load(client, cluster, worker, ipc, command, soundStatus, s
export async function update() {
const commandArray = [];
for (const [name, command] of commands.entries()) {
const merged = new Map([...commands, ...messageCommands]);
for (const [name, command] of merged.entries()) {
let cmdInfo = info.get(name);
if (command.postInit) {
const cmd = command.postInit();
//commands.set(name, cmd);
cmdInfo = {
category: cmdInfo.category,
description: cmd.description,
@ -80,17 +96,26 @@ export async function update() {
params: cmd.arguments,
flags: cmd.flags,
slashAllowed: cmd.slashAllowed,
directAllowed: cmd.directAllowed
directAllowed: cmd.directAllowed,
type: cmdInfo.type
};
info.set(name, cmdInfo);
}
if (cmdInfo?.slashAllowed) commandArray.push({
if (cmdInfo?.type === 3) {
commandArray.push({
name: name,
type: cmdInfo.type,
dm_permission: cmdInfo.directAllowed
});
} else if (cmdInfo?.slashAllowed) {
commandArray.push({
name,
type: 1,
type: cmdInfo.type,
description: cmdInfo.description,
options: cmdInfo.flags,
dm_permission: cmdInfo.directAllowed
});
}
}
return commandArray;
}

View file

@ -133,7 +133,7 @@ const checkImages = async (message, extraReturnTypes, video, sticker) => {
};
// this checks for the latest message containing an image and returns the url of the image
export default async (client, cmdMessage, interaction, options, extraReturnTypes = false, video = false, sticker = false) => {
export default async (client, cmdMessage, interaction, options, extraReturnTypes = false, video = false, sticker = false, singleMessage = false) => {
// we start by determining whether or not we're dealing with an interaction or a message
if (interaction) {
// we can get a raw attachment or a URL in the interaction itself
@ -147,7 +147,8 @@ export default async (client, cmdMessage, interaction, options, extraReturnTypes
if (result !== false) return result;
}
}
} else {
}
if (cmdMessage) {
// check if the message is a reply to another message
if (cmdMessage.messageReference) {
const replyMessage = await client.getMessage(cmdMessage.messageReference.channelID, cmdMessage.messageReference.messageID).catch(() => undefined);
@ -160,6 +161,7 @@ export default async (client, cmdMessage, interaction, options, extraReturnTypes
const result = await checkImages(cmdMessage, extraReturnTypes, video, sticker);
if (result !== false) return result;
}
if (!singleMessage) {
// if there aren't any replies or interaction attachments then iterate over the last few messages in the channel
const messages = await client.getMessages((interaction ? interaction : cmdMessage).channel.id);
// iterate over each message
@ -171,4 +173,5 @@ export default async (client, cmdMessage, interaction, options, extraReturnTypes
return result;
}
}
}
};

View file

@ -60,13 +60,13 @@ export function reload() {
}
export async function play(client, sound, options, music = false) {
if (!manager) return "The sound commands are still starting up!";
if (!options.channel.guild) return "This command only works in servers!";
if (!options.member.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!";
if (!manager) return { content: "The sound commands are still starting up!", flags: 64 };
if (!options.channel.guild) return { content: "This command only works in servers!", flags: 64 };
if (!options.member.voiceState.channelID) return { content: "You need to be in a voice channel first!", flags: 64 };
if (!options.channel.guild.permissionsOf(client.user.id).has("voiceConnect")) return { content: "I can't join this voice channel!", flags: 64 };
const voiceChannel = options.channel.guild.channels.get(options.member.voiceState.channelID);
if (!voiceChannel.permissionsOf(client.user.id).has("voiceConnect")) return "I don't have permission to join this voice channel!";
if (!music && manager.players.has(options.channel.guild.id)) return "I can't play a sound effect while other audio is playing!";
if (!voiceChannel.permissionsOf(client.user.id).has("voiceConnect")) return { content: "I don't have permission to join this voice channel!", flags: 64 };
if (!music && manager.players.has(options.channel.guild.id)) return { content: "I can't play a sound effect while other audio is playing!", flags: 64 };
let node = manager.getNode();
if (!node) {
const status = await checkStatus();
@ -81,14 +81,14 @@ export async function play(client, sound, options, music = false) {
let response;
try {
response = await node.rest.resolve(sound);
if (!response) return "🔊 I couldn't get a response from the audio server.";
if (response.loadType === "NO_MATCHES" || response.loadType === "LOAD_FAILED") return "I couldn't find that song!";
if (!response) return { content: "🔊 I couldn't get a response from the audio server.", flags: 64 };
if (response.loadType === "NO_MATCHES" || response.loadType === "LOAD_FAILED") return { content: "I couldn't find that song!", flags: 64 };
} catch (e) {
logger.error(e);
return "🔊 Hmmm, seems that all of the audio servers are down. Try again in a bit.";
return { content: "🔊 Hmmm, seems that all of the audio servers are down. Try again in a bit.", flags: 64 };
}
const oldQueue = queues.get(voiceChannel.guild.id);
if (!response.tracks || response.tracks.length === 0) return "I couldn't find that song!";
if (!response.tracks || response.tracks.length === 0) return { content: "I couldn't find that song!", flags: 64 };
if (music) {
const sortedTracks = response.tracks.map((val) => { return val.track; });
const playlistTracks = response.playlistInfo.selectedTrack ? sortedTracks : [sortedTracks[0]];