feat: reporting

This commit is contained in:
dvelo 2024-08-20 21:32:27 -05:00
parent 79cef04e48
commit 7c2d7c61d0
7 changed files with 775 additions and 54 deletions

@ -22,6 +22,7 @@
"@unocss/postcss": "^0.61.5", "@unocss/postcss": "^0.61.5",
"@unocss/transformer-directives": "^0.61.5", "@unocss/transformer-directives": "^0.61.5",
"@unocss/webpack": "^0.61.5", "@unocss/webpack": "^0.61.5",
"discord.js": "^14.15.3",
"inngest": "^3.21.2", "inngest": "^3.21.2",
"input-otp": "^1.2.4", "input-otp": "^1.2.4",
"json-beautify": "^1.1.1", "json-beautify": "^1.1.1",

@ -1,7 +1,9 @@
import ClientFadeIn from "@/components/ClientFadeIn";
export default function ECA() { export default function ECA() {
return ( return (
<main> <main>
<div className="pt-[100px] pr-8 pl-8"> <div className="pt-[100px] p-[300px]">
<strong className="text-2xl">External Content Agreement (ECA)</strong> <strong className="text-2xl">External Content Agreement (ECA)</strong>
<br /> <br />
By making external content available for anyone to see, there needs to By making external content available for anyone to see, there needs to
@ -16,62 +18,96 @@ export default function ECA() {
uploaded onto the platform are. uploaded onto the platform are.
<br /> <br />
<br /> <br />
<ClientFadeIn>
<div>
<strong className="text-xl">Source Code</strong> <strong className="text-xl">Source Code</strong>
<br /> <br />
The source code for MHSF is defined by the{" "} The source code for MHSF is defined by the{" "}
<a href="https://github.com/DeveloLongScript/MHSF/blob/main/LICENSE"> <a href="https://github.com/DeveloLongScript/MHSF/blob/main/LICENSE">
MIT License MIT License
</a> </a>
. You are free to use MHSF for commercial use, and you may modify the . You are free to use MHSF for commercial use, and you may modify
software however you'd like. Taking copies of the software (aka the software however you'd like. Taking copies of the software (aka
<i>"forking"</i>) is also freely allowed. <i>"forking"</i>) is also freely allowed.
<br /> <br />
</div>
</ClientFadeIn>
<br /> <br />
<ClientFadeIn delay={200}>
<div>
<strong className="text-xl">What your limits are</strong> <strong className="text-xl">What your limits are</strong>
<br /> <br />
When creating content, if its a matter of making a profile picture, or When creating content, if its a matter of making a profile picture,
editing the description for a server, (and more), you must follow the or editing the description for a server, (and more), you must follow
underlying agreements below. the underlying agreements below.
<br /> <br />
For making banners & descriptions, you must follow{" "} For making banners & descriptions, you must follow{" "}
<a href="https://minehut.wiki.gg/wiki/Rules"> <a href="https://minehut.wiki.gg/wiki/Rules">
Minehuts Terms of Service Minehuts Terms of Service
</a>{" "} </a>{" "}
<i> <i>
as all content made is associated to Minehut (as the server is mostly as all content made is associated to Minehut (as the server is
on a community for Minehut). mostly on a community for Minehut).
</i>{" "} </i>{" "}
<br /> <br />
For making Discord server embeds, you must follow{" "} For making Discord server embeds, you must follow{" "}
<a href="https://discord.com/terms/">Discords Terms of Service</a>{" "} <a href="https://discord.com/terms/">
Discords Terms of Service
</a>{" "}
<i>as all content made is associated to Discord.</i> <br /> <i>as all content made is associated to Discord.</i> <br />
<strong> <strong>
For all other content, they must follow the following: <br /> For all other content, they must follow the following: <br />
</strong> </strong>
- No inappropriate/adult images <br /> - No inappropriate/adult images <br />
- No swear words of any kind or slurs <br />- Endorsing unethical client - No swear words of any kind or slurs <br />- Endorsing unethical
modifications (aka cheating or hacking) client modifications (aka cheating or hacking)
<br /> <br />
</div>
</ClientFadeIn>
<br /> <br />
<ClientFadeIn delay={400}>
<div>
<strong className="text-xl">When you agree to the ECA</strong> <strong className="text-xl">When you agree to the ECA</strong>
<br /> <br />
When you add customization to your server, or add a profile picture When you add customization to your server, or add a profile picture
(linking an account is included), you must follow the ECA. (linking an account is included), you must follow the ECA.
<br /> <br />
</div>
</ClientFadeIn>
<br /> <br />
<ClientFadeIn delay={600}>
<div>
<strong className="text-xl">When linking an account</strong> <strong className="text-xl">When linking an account</strong>
<br /> <br />
When linking an account, you must follow the privacy policy and terms of When linking an account, you must follow the privacy policy and
service to the associated service that you linked your MHSF account to. terms of service to the associated service that you linked your MHSF
Additionally, if you link an external account <i>after</i> account account to. Additionally, if you link an external account{" "}
creation, everything said before is still true. <i>after</i> account creation, everything said before is still true.
<br /> <br />
</div>
</ClientFadeIn>
<br /> <br />
<ClientFadeIn delay={800}>
<div>
<strong className="text-xl">Violations</strong> <strong className="text-xl">Violations</strong>
<br /> <br />
Violations from above have 1 warning. Your first interaction is a Violations from above have 1 warning. Your first interaction is a
warning by removing the content from MHSF, and your 2nd is warning by removing the content from MHSF, and your 2nd is
banning/deleting your account. banning/deleting your account. (some violations are an instant
delete)
</div>
</ClientFadeIn>
<br />
<ClientFadeIn delay={1000}>
<div>
<strong className="text-xl">Reporting</strong>
<br />
If you personally see a violation of the ECA, you can report it by
clicking the customization tab on a server, and hitting the Report
button (it doesn't appear when the server was never owned). If you
misuse this feature, you may get your account deleted.
</div>
</ClientFadeIn>
</div> </div>
</main> </main>
); );

@ -6,6 +6,7 @@ import { Dispatch, SetStateAction, useEffect, useState } from "react";
import { import {
getCustomization, getCustomization,
ownServer, ownServer,
reportServer,
setCustomization, setCustomization,
serverOwned as sOFunc, serverOwned as sOFunc,
unownServer, unownServer,
@ -43,6 +44,14 @@ import { useTheme } from "next-themes";
import { DiscordPopover } from "./misc/DiscordPopover"; import { DiscordPopover } from "./misc/DiscordPopover";
import { Spinner } from "./ui/spinner"; import { Spinner } from "./ui/spinner";
import { BannerPopover } from "./misc/BannerPopover"; import { BannerPopover } from "./misc/BannerPopover";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "./ui/dialog";
const formSchema = z.object({ const formSchema = z.object({
description: z description: z
@ -67,6 +76,7 @@ export default function ServerCustomize({
const [serverOwned, setServerOwned] = useState(false); const [serverOwned, setServerOwned] = useState(false);
const [userOwned, setUserOwned] = useState(false); const [userOwned, setUserOwned] = useState(false);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [reason, setReason] = useState("");
const [description, setDescription] = useState(""); const [description, setDescription] = useState("");
const [get, setGet] = useState<any>({}); const [get, setGet] = useState<any>({});
const [author, setAuthor] = useState<string | undefined>(""); const [author, setAuthor] = useState<string | undefined>("");
@ -134,6 +144,74 @@ export default function ServerCustomize({
customize it. customize it.
</div> </div>
)} )}
{serverOwned && !userOwned && (
<div>
<div className="font-bold">
Is this server in violation of the ECA?
</div>
Is this server in violation of the{" "}
<Link href="/legal/external-content-agreement">
External Content Agreement (aka ECA)
</Link>
? You can report the server to remove the customizations from the
server.
<Dialog>
<DialogTrigger>
<Button className="h-[30px] ml-2">Report</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Report Server</DialogTitle>
<DialogDescription>
This will send a notification to MHSF maintainers. This
server must be in violation of the{" "}
<Link href="/legal/external-content-agreement">ECA</Link> to
be a valid report. Typical response times include 1 hour to
1 day, and you will not be notified if your report is
successful or not.{" "}
<b>
Please do not spam this form with mindless reports. If you
do, your account will be banned. We are not Minehut
support, we cannot help you with a problem within the
Minehut platform or within the server, we can only
moderate the customization of the server.
</b>{" "}
(if the problem is within the server,{" "}
<Link href="https://support.minehut.com/hc/en-us/requests/new?tf_subject=Reporting%20Server&tf_27062997154195=reports_appeals&tf_27063229498259=report_server">
report it on Minehut
</Link>
)<br />
<br />
<b>Reason:</b>
</DialogDescription>
</DialogHeader>
<Textarea
value={reason}
onChange={(e) => setReason(e.target.value)}
/>
<div className="grid grid-flow-row gap-2">
<Button
onClick={async () => {
await toast.promise(
new Promise(async (g, b) => {
(await reportServer(server, reason)) ? g("") : b();
}),
{
success: "Report sent!",
loading: "Sending report...",
error: "Error while sending report",
}
);
}}
>
Report Server
</Button>
</div>
</DialogContent>
</Dialog>
<br />
</div>
)}
</SignedIn> </SignedIn>
{!serverOwned && minehutOwned && ( {!serverOwned && minehutOwned && (

@ -379,3 +379,26 @@ export async function sortedFavorites(): Promise<
throw Error("Error while running API"); throw Error("Error while running API");
} }
} }
export async function reportServer(
server: string,
reason: string
): Promise<boolean> {
try {
const response = await fetch(connector(`/report-server`, { version: 1 }), {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ server, reason }),
});
if (response.status == 400) {
return false;
}
return true;
} catch {
throw Error("Error while running API");
}
}

@ -1,8 +1,119 @@
import { OnlineServer } from "@/lib/types/mh-server"; import { OnlineServer } from "@/lib/types/mh-server";
import { Inngest } from "inngest"; import { Inngest } from "inngest";
import { serve } from "inngest/next"; import { serve } from "inngest/next";
import { MongoClient } from "mongodb"; import { Document, MongoClient, ObjectId, WithId } from "mongodb";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
Client,
EmbedBuilder,
GatewayIntentBits,
ModalBuilder,
REST,
Routes,
SlashCommandBuilder,
SlashCommandStringOption,
TextChannel,
TextInputBuilder,
TextInputStyle,
} from "discord.js";
let reporting = true;
let initalizedYet = false;
const commands = [
new SlashCommandBuilder()
.setName("realive")
.setDescription("Re-alive an existing report that was glitched")
.addStringOption(
new SlashCommandStringOption()
.setName("id")
.setRequired(true)
.setDescription("Report ID")
),
];
let client: Client;
// this isn't entirely necessary, to run each time the server starts
async function init() {
try {
const rest = new REST({ version: "10" }).setToken(
process.env.DISCORD_TOKEN as string
);
try {
console.log("[REPORTING] Started refreshing application (/) commands.");
await rest.put(
Routes.applicationCommands(process.env.DISCORD_APP_ID as string),
{ body: commands }
);
console.log(
"[REPORTING] Successfully reloaded application (/) commands."
);
} catch (error) {
console.error(error);
}
} catch {
console.log(
"[REPORTING] Discord API token not found, skipping reporting..."
);
reporting = false;
}
client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
});
client.on("interactionCreate", async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === "realive") {
await interaction.reply({
embeds: [
new EmbedBuilder()
.setTitle("Re-creating report")
.setColor("Blurple")
.setDescription("This may take a few seconds.."),
],
ephemeral: true,
});
const mongo = new MongoClient(process.env.MONGO_DB as string);
const db = mongo.db("mhsf");
const collection = db.collection("reports");
let reportDoc: WithId<Document> | null;
try {
reportDoc = await collection.findOne({
_id: new ObjectId(interaction.options.getString("id") as string),
});
} catch {
await interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle("Report ID not valid")
.setColor("Red")
.setDescription("This report could not be found."),
],
});
return;
}
if (reportDoc === null) {
await interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle("Report not found")
.setColor("Red")
.setDescription("This report could not be found."),
],
});
return;
}
report({ data: { ...reportDoc, id: reportDoc._id.toString() } });
}
});
return;
}
// Create a client to send and receive events // Create a client to send and receive events
export const inngest = new Inngest({ id: "mhsf" }); export const inngest = new Inngest({ id: "mhsf" });
@ -10,6 +121,33 @@ export const inngest = new Inngest({ id: "mhsf" });
export default serve({ export default serve({
client: inngest, client: inngest,
functions: [ functions: [
inngest.createFunction(
{ id: "report" },
{ event: "report-server" },
async ({ event, step }) => {
if (!reporting) {
throw new Error("Cannot report server: Discord API token not found");
}
if (!initalizedYet) {
await init();
initalizedYet = true;
client.login(process.env.DISCORD_TOKEN);
console.log(`[REPORTING] Waiting for bot to be ready`);
client.on("ready", () => {
console.log(
`[REPORTING] Bot logged in as ${client.user?.displayName}`
);
report(event);
});
return;
}
report(event);
}
),
inngest.createFunction( inngest.createFunction(
{ id: "short-term-data" }, { id: "short-term-data" },
[{ cron: "*/30 * * * *" }, { event: "test/30-min" }], [{ cron: "*/30 * * * *" }, { event: "test/30-min" }],
@ -80,3 +218,238 @@ export default serve({
), ),
], ],
}); });
async function report(event: any) {
const isTextBased = client.channels.cache.get(
process.env.REPORTS_CHANNEL as string
)?.isTextBased;
if (!isTextBased) {
throw new Error(
"Cannot report server: Report channel not found or not a text channel."
);
}
const channel = client.channels.cache.get(
process.env.REPORTS_CHANNEL as string
) as TextChannel;
const goToServer = new ButtonBuilder()
.setLabel("Go to server")
.setStyle(ButtonStyle.Link)
.setURL("https://list.mlnehut.com/server/" + event.data.server);
const confirm = new ButtonBuilder()
.setCustomId("resolve")
.setLabel("Resolve")
.setStyle(ButtonStyle.Primary);
const typed = (name: string) =>
new ButtonBuilder()
.setCustomId("typed")
.setLabel(name + " is typing")
.setStyle(ButtonStyle.Secondary)
.setDisabled(true);
const cancel = new ButtonBuilder()
.setCustomId("cancel")
.setLabel("Cancel")
.setStyle(ButtonStyle.Secondary);
const undo = new ButtonBuilder()
.setCustomId("undo")
.setLabel("Undo")
.setStyle(ButtonStyle.Danger);
const undor = new ActionRowBuilder<ButtonBuilder>().addComponents(
undo,
goToServer
);
const rowN = new ActionRowBuilder<ButtonBuilder>().addComponents(
confirm,
cancel,
goToServer
);
const message = await channel.send({
embeds: [
new EmbedBuilder()
.setColor("Orange")
.setTitle("New report to server")
.setTimestamp()
.setDescription(
"A server has been reported by a player. \n Reason: " +
event.data.reason +
(event.data.reason == "" ? "*<empty>*" : "")
)
.setFields([
{ name: "User ID", value: `\`${event.data.userId}\`` },
{ name: "Server", value: `${event.data.server}` },
])
.setFooter({
text:
"Is this report glitched? Use the command /realive id:" +
event.data._id +
" to do actions on this report.",
}),
],
components: [rowN],
});
setTimeout(async () => {
await messageLoop();
async function messageLoop() {
const confirmation = await message.awaitMessageComponent({});
if (confirmation.customId == "undo") {
(await confirmation.reply({ content: "Done!" })).delete();
await message.edit({
embeds: [
new EmbedBuilder()
.setColor("Orange")
.setTitle("New report to server")
.setTimestamp()
.setDescription(
"A server has been reported by a player. \n Reason: " +
event.data.reason +
(event.data.reason == "" ? "*<empty>*" : "")
)
.setFields([
{ name: "User ID", value: `\`${event.data.userId}\`` },
{ name: "Server", value: `${event.data.server}` },
])
.setFooter({
text:
"Is this report glitched? Use the command /realive id:" +
event.data._id +
" to do actions on this report.",
}),
],
components: [rowN],
});
}
if (confirmation.customId == "resolve") {
await message.edit({
embeds: [
new EmbedBuilder()
.setColor("Green")
.setTitle("Server report resolved")
.setTimestamp()
.setDescription(
"The server report has been resolved by <@" +
confirmation.user.id +
"> \n Reason: " +
event.data.reason +
(event.data.reason == "" ? "*<empty>*" : "")
)
.setFields([
{ name: "User ID", value: `\`${event.data.userId}\`` },
{ name: "Server", value: `${event.data.server}` },
])
.setFooter({
text:
"Is this report glitched? Use the command /realive id:" +
event.data._id +
" to do actions on this report.",
}),
],
components: [undor],
});
(
await confirmation.reply({ content: "Done!", ephemeral: true })
).delete();
}
if (confirmation.customId == "cancel") {
const modal = new ModalBuilder().setCustomId("why").setTitle("MHSF");
const favoriteColorInput = new TextInputBuilder()
.setCustomId("whyToCancel")
.setLabel("Cancelation reason")
.setStyle(TextInputStyle.Short);
const row = new ActionRowBuilder<TextInputBuilder>().addComponents(
favoriteColorInput
);
modal.addComponents(row);
confirmation.showModal(modal);
try {
let reportedYet = false;
await message.edit({
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents(
typed(
confirmation.user.globalName || confirmation.user.username
)
),
],
});
setTimeout(async () => {
if (reportedYet == false) {
await message.edit({
components: [rowN],
});
await messageLoop();
}
return;
}, 60_000);
const submission = await confirmation.awaitModalSubmit({
time: 60_000,
});
const text = submission.fields.getTextInputValue("whyToCancel");
if (text == "") {
await submission.reply({ content: "Done!", ephemeral: true });
await messageLoop();
}
reportedYet = true;
await message.edit({
embeds: [
new EmbedBuilder()
.setColor("Red")
.setTitle("Server report cancelled")
.setTimestamp()
.setDescription(
"The server report has been cancelled by <@" +
confirmation.user.id +
"> \n Reason of cancelation: " +
text +
"\nReason of report: " +
event.data.reason +
(event.data.reason == "" ? "*<empty>*" : "")
)
.setFields([
{ name: "User ID", value: `\`${event.data.userId}\`` },
{ name: "Server", value: `${event.data.server}` },
])
.setFooter({
text:
"Is this report glitched? Use the command /realive id:" +
event.data._id +
" to do actions on this report.",
}),
],
components: [undor],
});
await submission.reply({ content: "Done!", ephemeral: true });
} catch (e) {
await message.edit({
components: [rowN],
});
await confirmation.reply({
embeds: [
new EmbedBuilder()
.setTitle("You took too long!")
.setDescription("Please try again.")
.setColor("Red"),
],
ephemeral: true,
});
}
}
await messageLoop();
}
}, 0);
}

@ -0,0 +1,50 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getAuth } from "@clerk/nextjs/server";
import { MongoClient } from "mongodb";
import { inngest } from "../inngest";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { userId } = getAuth(req);
const { server } = req.body;
if (server == null) {
res.status(400).send({ message: "Couldn't find data" });
return;
}
const { reason } = req.body;
if (reason == null) {
res.status(400).send({ message: "Couldn't find data" });
return;
}
if (!userId) {
return res.status(401).json({ error: "Unauthorized" });
}
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
const db = client.db("mhsf");
const collection = db.collection("reports");
const entry = await collection.insertOne({
server: server,
reason: reason,
userId: userId,
});
// Don't wait for this to finish, just continue anyway
inngest.send({
name: "report-server",
data: {
_id: entry.insertedId.toString(),
server,
reason,
userId,
},
});
client.close();
res.send({ msg: "Successfully reported server!" });
}

162
yarn.lock

@ -209,6 +209,71 @@
dependencies: dependencies:
csstype "3.1.1" csstype "3.1.1"
"@discordjs/builders@^1.8.2":
version "1.8.2"
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.8.2.tgz#535d970331ee40f20dec9ef8079e43092f323ce9"
integrity sha512-6wvG3QaCjtMu0xnle4SoOIeFB4y6fKMN6WZfy3BMKJdQQtPLik8KGzDwBVL/+wTtcE/ZlFjgEk74GublyEVZ7g==
dependencies:
"@discordjs/formatters" "^0.4.0"
"@discordjs/util" "^1.1.0"
"@sapphire/shapeshift" "^3.9.7"
discord-api-types "0.37.83"
fast-deep-equal "^3.1.3"
ts-mixer "^6.0.4"
tslib "^2.6.2"
"@discordjs/collection@1.5.3":
version "1.5.3"
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.3.tgz#5a1250159ebfff9efa4f963cfa7e97f1b291be18"
integrity sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==
"@discordjs/collection@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-2.1.0.tgz#f327d944ab2dcf9a1f674470a481f78a120a5e3b"
integrity sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw==
"@discordjs/formatters@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.4.0.tgz#066a2c2163b26ac066e6f621f17445be9690c6a9"
integrity sha512-fJ06TLC1NiruF35470q3Nr1bi95BdvKFAF+T5bNfZJ4bNdqZ3VZ+Ttg6SThqTxm6qumSG3choxLBHMC69WXNXQ==
dependencies:
discord-api-types "0.37.83"
"@discordjs/rest@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-2.3.0.tgz#06d37c7fb54a9be61134b5bbb201abd760343472"
integrity sha512-C1kAJK8aSYRv3ZwMG8cvrrW4GN0g5eMdP8AuN8ODH5DyOCbHgJspze1my3xHOAgwLJdKUbWNVyAeJ9cEdduqIg==
dependencies:
"@discordjs/collection" "^2.1.0"
"@discordjs/util" "^1.1.0"
"@sapphire/async-queue" "^1.5.2"
"@sapphire/snowflake" "^3.5.3"
"@vladfrangu/async_event_emitter" "^2.2.4"
discord-api-types "0.37.83"
magic-bytes.js "^1.10.0"
tslib "^2.6.2"
undici "6.13.0"
"@discordjs/util@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-1.1.0.tgz#dcffd2b61aab8eadd66bea67811bc34fc769bb2a"
integrity sha512-IndcI5hzlNZ7GS96RV3Xw1R2kaDuXEp7tRIy/KlhidpN/BQ1qh1NZt3377dMLTa44xDUNKT7hnXkA/oUAzD/lg==
"@discordjs/ws@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-1.1.1.tgz#bffbfd46838258ab09054ed98ddef1a36f6507a3"
integrity sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==
dependencies:
"@discordjs/collection" "^2.1.0"
"@discordjs/rest" "^2.3.0"
"@discordjs/util" "^1.1.0"
"@sapphire/async-queue" "^1.5.2"
"@types/ws" "^8.5.10"
"@vladfrangu/async_event_emitter" "^2.2.4"
discord-api-types "0.37.83"
tslib "^2.6.2"
ws "^8.16.0"
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0" version "4.4.0"
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
@ -1135,6 +1200,24 @@
resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz" resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz"
integrity sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg== integrity sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==
"@sapphire/async-queue@^1.5.2":
version "1.5.3"
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.3.tgz#03cd2a2f3665068f314736bdc56eee2025352422"
integrity sha512-x7zadcfJGxFka1Q3f8gCts1F0xMwCKbZweM85xECGI0hBTeIZJGGCrHgLggihBoprlQ/hBmDR5LKfIPqnmHM3w==
"@sapphire/shapeshift@^3.9.7":
version "3.9.7"
resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.9.7.tgz#43e23243cac8a0c046bf1e73baf3dbf407d33a0c"
integrity sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g==
dependencies:
fast-deep-equal "^3.1.3"
lodash "^4.17.21"
"@sapphire/snowflake@3.5.3", "@sapphire/snowflake@^3.5.3":
version "3.5.3"
resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.3.tgz#0c102aa2ec5b34f806e9bc8625fc6a5e1d0a0c6a"
integrity sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==
"@swc/counter@^0.1.3": "@swc/counter@^0.1.3":
version "0.1.3" version "0.1.3"
resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz" resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz"
@ -1288,6 +1371,13 @@
resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz" resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz"
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
"@types/node@*":
version "22.4.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.4.1.tgz#9b595d292c65b94c20923159e2ce947731b6fdce"
integrity sha512-1tbpb9325+gPnKK0dMm+/LMriX0vKxf6RnB0SZUqfyVkQ4fMgUSySqhxE/y8Jvs4NyF1yHzTfG9KlnkIODxPKg==
dependencies:
undici-types "~6.19.2"
"@types/node@^20": "@types/node@^20":
version "20.13.0" version "20.13.0"
resolved "https://registry.npmjs.org/@types/node/-/node-20.13.0.tgz" resolved "https://registry.npmjs.org/@types/node/-/node-20.13.0.tgz"
@ -1349,6 +1439,13 @@
dependencies: dependencies:
"@types/webidl-conversions" "*" "@types/webidl-conversions" "*"
"@types/ws@^8.5.10":
version "8.5.12"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e"
integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==
dependencies:
"@types/node" "*"
"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0": "@typescript-eslint/parser@^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0":
version "7.2.0" version "7.2.0"
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz"
@ -1575,6 +1672,11 @@
resolved "https://registry.yarnpkg.com/@vercel/speed-insights/-/speed-insights-1.0.12.tgz#71c2edffdedae98d34e306d7b0a573e6816898b4" resolved "https://registry.yarnpkg.com/@vercel/speed-insights/-/speed-insights-1.0.12.tgz#71c2edffdedae98d34e306d7b0a573e6816898b4"
integrity sha512-ZGQ+a7bcfWJD2VYEp2R1LHvRAMyyaFBYytZXsfnbOMkeOvzGNVxUL7aVUvisIrTZjXTSsxG45DKX7yiw6nq2Jw== integrity sha512-ZGQ+a7bcfWJD2VYEp2R1LHvRAMyyaFBYytZXsfnbOMkeOvzGNVxUL7aVUvisIrTZjXTSsxG45DKX7yiw6nq2Jw==
"@vladfrangu/async_event_emitter@^2.2.4":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.5.tgz#7bc35026fdc3398a5e1aac801edd21b28cdf4cfa"
integrity sha512-J7T3gUr3Wz0l7Ni1f9upgBZ7+J22/Q1B7dl0X6fG+fTsD+H+31DIosMHj4Um1dWQwqbcQ3oQf+YS2foYkDc9cQ==
acorn-jsx@^5.3.2: acorn-jsx@^5.3.2:
version "5.3.2" version "5.3.2"
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
@ -2331,6 +2433,29 @@ dir-glob@^3.0.1:
dependencies: dependencies:
path-type "^4.0.0" path-type "^4.0.0"
discord-api-types@0.37.83:
version "0.37.83"
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.83.tgz#a22a799729ceded8176ea747157837ddf4708b1f"
integrity sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==
discord.js@^14.15.3:
version "14.15.3"
resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.15.3.tgz#b2a67a1a4ef192be498fb8b6784224a42906f1be"
integrity sha512-/UJDQO10VuU6wQPglA4kz2bw2ngeeSbogiIPx/TsnctfzV/tNf+q+i1HlgtX1OGpeOBpJH9erZQNO5oRM2uAtQ==
dependencies:
"@discordjs/builders" "^1.8.2"
"@discordjs/collection" "1.5.3"
"@discordjs/formatters" "^0.4.0"
"@discordjs/rest" "^2.3.0"
"@discordjs/util" "^1.1.0"
"@discordjs/ws" "^1.1.1"
"@sapphire/snowflake" "3.5.3"
discord-api-types "0.37.83"
fast-deep-equal "3.1.3"
lodash.snakecase "4.1.1"
tslib "2.6.2"
undici "6.13.0"
dlv@^1.1.3: dlv@^1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz"
@ -2808,7 +2933,7 @@ extend@^3.0.0, extend@^3.0.1:
resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: fast-deep-equal@3.1.3, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3" version "3.1.3"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@ -3715,6 +3840,11 @@ lodash.merge@^4.6.2:
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.snakecase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==
lodash@^4.17.21: lodash@^4.17.21:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@ -3749,6 +3879,11 @@ lucide-react@^0.416.0:
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.416.0.tgz#657da248f9b862703d7d80aafb912e79ad886313" resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.416.0.tgz#657da248f9b862703d7d80aafb912e79ad886313"
integrity sha512-wPWxTzdss1CTz2aqcNWNlbh4YSnH9neJWP3RaeXepxpLCTW+pmu7WcT/wxJe+Q7Y7DqGOxAqakJv0pIK3431Ag== integrity sha512-wPWxTzdss1CTz2aqcNWNlbh4YSnH9neJWP3RaeXepxpLCTW+pmu7WcT/wxJe+Q7Y7DqGOxAqakJv0pIK3431Ag==
magic-bytes.js@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz#c41cf4bc2f802992b05e64962411c9dd44fdef92"
integrity sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==
magic-string@^0.30.10: magic-string@^0.30.10:
version "0.30.10" version "0.30.10"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e"
@ -5685,6 +5820,11 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
ts-mixer@^6.0.4:
version "6.0.4"
resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.4.tgz#1da39ceabc09d947a82140d9f09db0f84919ca28"
integrity sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==
tsconfig-paths@^3.15.0: tsconfig-paths@^3.15.0:
version "3.15.0" version "3.15.0"
resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz"
@ -5700,6 +5840,11 @@ tslib@2.4.1:
resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz" resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz"
integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
tslib@2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2: tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2:
version "2.6.3" version "2.6.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
@ -5851,6 +5996,16 @@ undici-types@~5.26.4:
resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
undici-types@~6.19.2:
version "6.19.8"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
undici@6.13.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-6.13.0.tgz#7edbf4b7f3aac5f8a681d515151bf55cb3589d72"
integrity sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==
unified@^11.0.0: unified@^11.0.0:
version "11.0.4" version "11.0.4"
resolved "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz" resolved "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz"
@ -6149,6 +6304,11 @@ wrappy@1:
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
ws@^8.16.0:
version "8.18.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==
y18n@^5.0.5: y18n@^5.0.5:
version "5.0.8" version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"