aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.DS_Storebin0 -> 6148 bytes
-rw-r--r--.gitignore8
-rw-r--r--.idea/.gitignore5
-rw-r--r--.idea/DJ Pon-3.iml12
-rw-r--r--.idea/discord.xml7
-rw-r--r--.idea/inspectionProfiles/Project_Default.xml6
-rw-r--r--.idea/modules.xml8
-rw-r--r--README.md3
-rw-r--r--TODO.md91
-rw-r--r--assets/questions.json70
-rw-r--r--commands/about.ts62
-rw-r--r--commands/ping.ts33
-rwxr-xr-xcooler-pony19
-rw-r--r--core/CommandAction.ts46
-rw-r--r--core/CommandBase.ts8
-rw-r--r--core/CommandInteractionManager.ts21
-rw-r--r--core/CommandsLoader.ts37
-rw-r--r--core/CoolerPony.ts80
-rw-r--r--core/ErrorParser.ts67
-rw-r--r--core/InteractionManager.ts3
-rw-r--r--core/LogManager.ts84
-rw-r--r--core/ManagedChannelManager.ts41
-rw-r--r--core/PresenceManager.ts7
-rw-r--r--core/SlashCommandsRefresher.ts35
-rw-r--r--core/Welcome.ts50
-rw-r--r--index.ts65
-rw-r--r--package-lock.json724
-rw-r--r--package.json25
-rw-r--r--tsconfig.json13
29 files changed, 1630 insertions, 0 deletions
diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..ff230cc
--- /dev/null
+++ b/.DS_Store
Binary files differ
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..981d0c7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+token.txt
+node_modules
+**/*.js
+**/*.js.map
+logs
+build
+data
+linesOfCode.js \ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/DJ Pon-3.iml b/.idea/DJ Pon-3.iml
new file mode 100644
index 0000000..0c8867d
--- /dev/null
+++ b/.idea/DJ Pon-3.iml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+ <component name="NewModuleRootManager">
+ <content url="file://$MODULE_DIR$">
+ <excludeFolder url="file://$MODULE_DIR$/temp" />
+ <excludeFolder url="file://$MODULE_DIR$/.tmp" />
+ <excludeFolder url="file://$MODULE_DIR$/tmp" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module> \ No newline at end of file
diff --git a/.idea/discord.xml b/.idea/discord.xml
new file mode 100644
index 0000000..d8e9561
--- /dev/null
+++ b/.idea/discord.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="DiscordProjectSettings">
+ <option name="show" value="PROJECT_FILES" />
+ <option name="description" value="" />
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..4ed2ac0
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+ <profile version="1.0">
+ <option name="myName" value="Project Default" />
+ <inspection_tool class="JSIgnoredPromiseFromCall" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
+ </profile>
+</component> \ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..2ec6503
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/.idea/DJ Pon-3.iml" filepath="$PROJECT_DIR$/.idea/DJ Pon-3.iml" />
+ </modules>
+ </component>
+</project> \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0fb9a50
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# Cooler Pony
+
+A general-purpose bot for Discord, forked from DJ Pon-3. \ No newline at end of file
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..091c5cc
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,91 @@
+# To-Do List
+
+## To be implemented
+
+Note that the tasks described here may not exactly be the same once implemented on the bot (for example command names or behavior can change).
+
+### Features
+
+- [ ] Make `/info` and `/remove` autocomplete with items from the queue
+
+### Bug Fixes
+
+- [ ] Enormous delay between each song
+
+### Performance Optimizations
+
+- *nothing*
+
+### New Commands
+
+- [ ] `/rmdupes`: removes duplicate songs from the queue
+- [ ] `/lock`: locks the bot to only the user who executed this command and administrators
+- [ ] `/save`: save the current queue to a playlist
+ - [ ] `/load`: loads a saved playlist
+ - [ ] `/list`: lists all saved playlists
+ - [ ] `/delete`: deletes a saved playlist
+
+----
+
+## Completed
+
+The completed tasks are ordered by completion date (most recent is at the bottom).
+
+### Features
+
+- [x] <s>A feature to restart the bot (so the CI server can restart it after deploying)</s>
+- [x] <s>Add buttons to command outputs</s>
+ * <s>Create a `CommandAction` class that allows to emulate slash commands</s>
+- [x] <s>Support for Argon</s>
+- [x] <s>Support for Bandcamp</s>
+- [x] <s>Support for SoundCloud</s>
+- [x] <s>Disconnect the bot after 30 minutes of inactivity (`VoiceBase.playing` is `false`; do NOT check
+ for `VoiceBase.paused`)</s>
+- [x] <s>Support for HTTP files</s>
+- [x] <s>If the URL given to `/add` is a playlist, add all the videos in that playlist to the queue</s>
+- [x] <s>Save history of played songs so it is easier to find songs that have already been played</s>
+ * <s>The `/search` command can use autocompletion and complete with history results</s>
+- [x] <s>Pass titles through [YouToo](https://gitlab.minteck.org/minteck/youtoo)'s "magic" title parser</s>
+- [x] <s>`/queue` gives the duration (in minutes) of the entire queue</s>
+- [x] <s>Block playing songs that are longer than 10 minutes</s>
+- [x] <s>Volume normalization</s>
+- [x] <s>Make `/queue` more consistent</s>
+ - <s>`01.` (`00:00`) Song name</s>
+- [x] <s>On `/queue`, show time when the playlist will be done</s>
+- [x] <s>Make `/lyrics` fetch lyrics from Bandcamp</s>
+- [x] <s>Prevent `RESTART` when the bot is playing</s>
+- [x] <s>Properly handle connection issues</s>
+- [x] <s>More logging</s>
+- [x] <s>History matches from SoundCloud result in corrupt entries</s>
+- [x] <s>Better loading items with cool messages (like what Discord did)</s>
+
+### Bug Fixes
+
+- [x] <s>Connection reset for random reasons</s>
+- [x] <s>Webhook errors for random reasons</s>
+- [x] <s>Handle being disconnected by a server moderator (right click > Disconnect)</s>
+ * <s>This will be harder than expected: class `VoiceConnectionEvents` in `@discordjs/voice` is totally undocumented.</s>
+- [x] <s>`/tmp/stream.ogg` is not deleted automatically</s>
+- [x] <s>Playing songs from Argon 2 times without wiping /tmp results in an error (BugCheck 0xD460A59A)</s>
+- [x] <s>Detect and prevent restart loops (more than 1 restart within 1 minute)</s>
+
+### Performance Optimizations
+
+- [x] <s>On `QueueManager.add`, don't use both `ytdl-core` and the `yt-dlp` command to get metadata</s>
+
+### New Commands
+
+- [x] <s>`/search`: search on YouTube instead of using the full video URL</s>
+- [x] <s>`/logs`: displays the bots logs</s>
+- [x] <s>`/lyrics`: get lyrics for the currently playing song (probably
+ use [`genius-api-client`](https://npmjs.com/package/genius-api-client))</s>
+- [x] <s>`/shuffle`: randomly changes the queue's order</s>
+- [x] <s>`/restart`: restart the bot</s>
+- [x] <s>`/random`: picks a random item from history and adds it to the queue</s>
+- [x] <s>`/about`: more user-friendly debugging information</s>
+- [x] <s>`/skipto`: skip to a certain item in the queue</s>
+- [x] <s>`/loopone`: repeat the current playing song instead of the whole queue (`/loop`)</s>
+- [x] <s>`/nowplaying`: shows info about the currently playing song</s>
+- [x] <s>`/info`: gets info about a song in the queue (who added, when, ...)</s>
+- [x] <s>`/clear`: clears all songs in the queue</s>
+- [x] <s>`/autodj`: Auto-DJ</s>
diff --git a/assets/questions.json b/assets/questions.json
new file mode 100644
index 0000000..103a7f2
--- /dev/null
+++ b/assets/questions.json
@@ -0,0 +1,70 @@
+[
+ {
+ "question": "Are fictives welcome on the server?",
+ "response": ["Yes", "True", "They are", "They are more than welcome", "They are definitely!"]
+ },
+ {
+ "question": "Are ponykin creatures welcome here?",
+ "response": ["Yes", "True", "They are", "They are more than welcome", "They are definitely!"]
+ },
+ {
+ "question": "Plural systems and singlets should be treated...",
+ "response": ["equally", "the same", "equal", "likewise", "Like they are the same"]
+ },
+ {
+ "question": "MLP content must be hidden behind...",
+ "response": ["spoilers", "spoiler tags", "hidden tags", "content warnings"]
+ },
+ {
+ "question": "Is sexually explicit or NSFW content allowed?",
+ "response": ["No", "False", "Not at all", "It is not", "Not allowed", "It is not allowed"]
+ },
+ {
+ "question": "Is spam or shitposting allowed?",
+ "response": ["No", "False", "Not at all", "It is not", "Not allowed", "It is not allowed"]
+ },
+ {
+ "question": "Is advertising in any kind allowed?",
+ "response": ["No", "False", "Not at all", "It is not", "Not allowed", "It is not allowed", "Not Discord servers", "Only 24 hours", "Every 24 hours", "Only every 24 hours", "If it doesn't disturb", "If it doesn't disturb an ongoing conversation", "No money", "Not for money"]
+ },
+ {
+ "question": "Which types/origins of plural systems are allowed?",
+ "response": ["All of them", "Everything", "All origins", "All origins are allowed", "They are all", "They are all allowed"]
+ },
+ {
+ "question": "What does \"MLP\" stand for?",
+ "response": ["My Little Pony"]
+ },
+ {
+ "question": "Is plurality okay on this server?",
+ "response": ["Yes", "True", "It is", "It is more than welcome", "It is definitely!", "It's okay", "It is okay"]
+ },
+ {
+ "question": "Are mods willing to help with questions?",
+ "response": ["Yes", "True", "They are", "They are more than willing", "They are definitely!"]
+ },
+ {
+ "question": "Where are memes allowed?",
+ "response": ["#memes", "Memes channel", "<#996408303523479623>", "They are not allowed", "Not allowed", "Not in general"]
+ },
+ {
+ "question": "Is roleplay allowed?",
+ "response": ["No", "False", "Not at all", "It is not", "Not allowed", "It is not allowed", "No except fictives", "Not at all except fictives", "It is not except fictives", "Not allowed except fictives", "It is not allowed except fictives", "No except with fictives", "Not at all except with fictives", "It is not except with fictives", "Not allowed except with fictives", "It is not allowed except with fictives"]
+ },
+ {
+ "question": "Are humans allowed on this server?",
+ "response": ["Yes", "True", "They are", "I don't know", "idk", "Nothing prevents them", "Nothing prevents that", "Why not", "I guess", "I think", "I think so", "We don't know", "We guess", "We think", "We think so", "Of course", "They are as well"]
+ },
+ {
+ "question": "Why must your username or nickname contain a few English-common characters?",
+ "response": ["Ping", "@", "So it's easier to ping", "To ping it", "It's easier to ping", "Pingable", "Mention", "Mentionable", "So it's easier to mention", "To mention it", "It's easier to mention", "So it's easier to @", "To @ it", "It's easier to @", "For the mods", "The mods", "Mods", "Moderators", "For the moderators", "The moderators", "For the admins", "The admins", "Admins", "Administrators", "For the administrators", "The administrators"]
+ },
+ {
+ "question": "Are mods forced to provide a reason to their action?",
+ "response": ["No", "False", "They are not", "No", "I don't know", "I don't think so", "Don't think so", "We don't know", "We don't think so", "They don't have to", "It's recommended", "Recommended", "Maybe", "Perhaps", "idk"]
+ },
+ {
+ "question": "Are littles considered roleplay?",
+ "response": ["No", "False", "Not at all", "They are not", "Of course not", "It's not roleplay", "They are not roleplaying", "They are not roleplay", "They are not doing roleplay"]
+ }
+] \ No newline at end of file
diff --git a/commands/about.ts b/commands/about.ts
new file mode 100644
index 0000000..96ba94a
--- /dev/null
+++ b/commands/about.ts
@@ -0,0 +1,62 @@
+import {CommandBase} from "../core/CommandBase";
+import {SlashCommandBuilder} from "@discordjs/builders";
+import * as os from 'os';
+import * as fs from 'fs';
+import {CommandAction} from "../core/CommandAction";
+import {MessageActionRow, MessageButton, MessageEmbed} from "discord.js";
+import {LogManager} from "../core/LogManager";
+
+export class AboutCommand extends CommandBase {
+ constructor() {
+ super();
+ this.slashCommandData = new SlashCommandBuilder()
+ .setName("about")
+ .setDescription("Gets diagnostics information from the bot")
+ }
+
+ public handle(action: CommandAction) {
+ let interaction = action.getInteraction();
+
+ interaction.reply({
+ embeds: [
+ new MessageEmbed()
+ .setTitle(":sos: Diagnostics Information")
+ .setDescription(global.client.user.username + " version " + global.version + ", build " + global.build + "\n    on Node.js " + process.versions.node + " " + process.arch + "\n    on " + os.type() + " " + os.arch() + " " + os.release() + "\n    on " + os.hostname() + "\n" + (
+ fs.existsSync("./RESTART") ? (
+ ":warning: Update to version " + (fs.existsSync("./.git/refs/heads/mane") ? fs.readFileSync("./.git/refs/heads/mane").toString().trim().substring(0, 8) : (fs.existsSync("../.git/refs/heads/mane") ? fs.readFileSync("../.git/refs/heads/mane").toString().trim().substring(0, 8) : (fs.existsSync("./version.txt") ? fs.readFileSync("./version.txt").toString().trim() : (fs.existsSync("../version.txt") ? fs.readFileSync("../version.txt").toString().trim() : "live")))) + ", build " + (fs.existsSync("./build.txt") ? fs.readFileSync("./build.txt").toString().trim() : (fs.existsSync("../build.txt") ? fs.readFileSync("../build.txt").toString().trim() : "dev")) + " pending."
+ ) : ("")
+ ))
+ .addField("System Information", "" +
+ "**PID:** " + process.pid + ", running as " + os.userInfo().username + " (" + os.userInfo().uid + ":" + os.userInfo().gid + ")\n" +
+ "**Path:** " + global.systemRoot + "\n"
+ )
+ .addField("Bot Information", "" +
+ "**User ID:** " + global.client.user.id + "\n" +
+ "**Imported modules:** " + Object.keys(require.cache).length + "\n" +
+ "**Used RAM:** " + Math.round(process.memoryUsage().heapTotal / (1024 ** 2)) + " MiB\n" +
+ "**Connection date:** " + global.client.readyAt.toISOString()
+ )
+ ],
+ components: [
+ new MessageActionRow()
+ .addComponents(
+ new MessageButton()
+ .setLabel('Source Code')
+ .setStyle('LINK')
+ .setURL("https://git.equestria.dev/equestria.dev/dj-pon-3"),
+ new MessageButton()
+ .setLabel('Roadmap')
+ .setStyle('LINK')
+ .setURL("https://git.equestria.dev/equestria.dev/dj-pon-3/src/branch/mane/TODO.md"),
+ new MessageButton()
+ .setLabel('General Information')
+ .setStyle('LINK')
+ .setURL("https://git.equestria.dev/equestria.dev/dj-pon-3/src/branch/mane/README.md"),
+ )
+ ]
+ }).then(() => {
+ LogManager.info("/about: dispatched GUI info");
+ })
+ return;
+ }
+}
diff --git a/commands/ping.ts b/commands/ping.ts
new file mode 100644
index 0000000..5b79231
--- /dev/null
+++ b/commands/ping.ts
@@ -0,0 +1,33 @@
+import {CommandBase} from "../core/CommandBase";
+import {MessageEmbed} from "discord.js";
+import {SlashCommandBuilder} from "@discordjs/builders";
+import {CommandAction} from "../core/CommandAction";
+
+export class PingCommand extends CommandBase {
+ constructor() {
+ super();
+ this.slashCommandData = new SlashCommandBuilder()
+ .setName("ping")
+ .setDescription("Shows the latency between the bot and Discord")
+ }
+
+ public handle(action: CommandAction) {
+ let interaction = action.getInteraction();
+ let VoiceBase = global.VoiceBase;
+ let processing = new Date().getTime() - global.processingStart;
+ let interactionTime = new Date().getTime() - interaction.createdAt.getTime();
+ let apiTime = global.client.ws.ping;
+
+ interaction.reply({
+ embeds: [
+ new MessageEmbed()
+ .setTitle(":rocket: Pong!")
+ .addFields(
+ {name: "Processing", value: processing.toFixed(2) + "ms", inline: true},
+ {name: "Interaction", value: interactionTime.toFixed(2) + "ms", inline: true},
+ {name: "API", value: apiTime.toFixed(2) + "ms", inline: true}
+ )
+ ]
+ });
+ }
+} \ No newline at end of file
diff --git a/cooler-pony b/cooler-pony
new file mode 100755
index 0000000..61e2538
--- /dev/null
+++ b/cooler-pony
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+cd build || (echo "Cannot find build directory, please run 'npm run build'." && exit)
+
+if ! command -v node &> /dev/null
+then
+ echo "'node' not found in PATH, please install NodeJS."
+ exit
+fi
+
+x_run () {
+ node --enable-source-maps index.js
+ if [[ $? -eq 14 ]]; then
+ x_run
+ else
+ exit $?
+ fi
+}
+
+x_run \ No newline at end of file
diff --git a/core/CommandAction.ts b/core/CommandAction.ts
new file mode 100644
index 0000000..ccc997b
--- /dev/null
+++ b/core/CommandAction.ts
@@ -0,0 +1,46 @@
+import {
+ CommandInteraction,
+ CommandInteractionOptionResolver,
+ MessageComponentInteraction,
+ ModalSubmitInteraction
+} from "discord.js";
+
+class CommandName extends String {
+ constructor(...args) {
+ super(...args);
+ }
+}
+
+export class CommandAction {
+ private readonly command: CommandName;
+ private readonly args: object | CommandInteractionOptionResolver;
+ private readonly interaction: CommandInteraction | ModalSubmitInteraction | MessageComponentInteraction;
+
+ constructor(command: string, interaction: CommandInteraction | ModalSubmitInteraction | MessageComponentInteraction, args?: object) {
+ this.command = new CommandName(command);
+ this.interaction = interaction;
+ if (args) {
+ this.args = args;
+ } else if (interaction instanceof CommandInteraction) {
+ this.args = interaction.options;
+ } else {
+ this.args = {};
+ }
+ }
+
+ public getCommand(): CommandName {
+ return this.command;
+ }
+
+ public getInteraction(): CommandInteraction | ModalSubmitInteraction | MessageComponentInteraction {
+ return this.interaction;
+ }
+
+ public getArgument(argument: string): any {
+ if (this.args instanceof CommandInteractionOptionResolver) {
+ return this.args.get(argument, true).value;
+ } else {
+ return this.args[argument];
+ }
+ }
+} \ No newline at end of file
diff --git a/core/CommandBase.ts b/core/CommandBase.ts
new file mode 100644
index 0000000..8be766b
--- /dev/null
+++ b/core/CommandBase.ts
@@ -0,0 +1,8 @@
+import {SlashCommandBuilder} from "@discordjs/builders";
+
+export class CommandBase {
+ public slashCommandData: Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
+
+ constructor() {
+ }
+} \ No newline at end of file
diff --git a/core/CommandInteractionManager.ts b/core/CommandInteractionManager.ts
new file mode 100644
index 0000000..0f7cb49
--- /dev/null
+++ b/core/CommandInteractionManager.ts
@@ -0,0 +1,21 @@
+import {InteractionManager} from "./InteractionManager";
+import {CommandInteraction} from "discord.js";
+import {LogManager} from "./LogManager";
+import {CommandsLoader} from "./CommandsLoader";
+import {CommandAction} from "./CommandAction";
+
+export class CommandInteractionManager extends InteractionManager {
+ public static commands = new CommandsLoader().getCommands();
+
+ constructor(interaction: CommandInteraction) {
+ super();
+ LogManager.verbose("CommandInteractionManager: " + interaction.commandName);
+
+ if (Object.keys(CommandInteractionManager.commands).includes(interaction.commandName)) {
+ CommandInteractionManager.commands[interaction.commandName].handle(new CommandAction(interaction.commandName, interaction));
+ } else {
+ LogManager.error("Command not found: " + interaction.commandName);
+ interaction.reply(":x: Command not found: `" + interaction.commandName + "`, this is most likely a bug.");
+ }
+ }
+} \ No newline at end of file
diff --git a/core/CommandsLoader.ts b/core/CommandsLoader.ts
new file mode 100644
index 0000000..4d435ae
--- /dev/null
+++ b/core/CommandsLoader.ts
@@ -0,0 +1,37 @@
+import {LogManager} from "./LogManager";
+import * as fs from 'fs';
+
+import {SlashCommandBuilder} from "@discordjs/builders";
+
+export class CommandsLoader {
+ private commands: object = {};
+
+ constructor() {
+ LogManager.verbose("Generate CommandsLoader list");
+ let list = fs.readdirSync("./commands").filter((i: string) => i.endsWith(".js"));
+
+ for (let item of list) {
+ LogManager.verbose(" load: " + item);
+ let imported = require('../commands/' + item);
+ let cmd = imported[Object.keys(imported)[0]];
+ this.commands[item.substring(0, item.length - 3)] = new cmd();
+ }
+ }
+
+ public slashCommands(): SlashCommandBuilder[] {
+ let slashCommands = [];
+
+ for (let name of Object.keys(this.commands)) {
+ let command = this.commands[name];
+ LogManager.verbose("CommandsLoader: " + name);
+
+ slashCommands.push(command.slashCommandData);
+ }
+
+ return slashCommands;
+ }
+
+ public getCommands(): object {
+ return this.commands;
+ }
+} \ No newline at end of file
diff --git a/core/CoolerPony.ts b/core/CoolerPony.ts
new file mode 100644
index 0000000..8a02f48
--- /dev/null
+++ b/core/CoolerPony.ts
@@ -0,0 +1,80 @@
+import {Client, GuildMember, Message, MessageEmbed} from 'discord.js';
+import * as fs from 'fs';
+import * as crypto from 'crypto';
+import {LogManager} from "./LogManager";
+import {CommandInteractionManager} from "./CommandInteractionManager";
+import {PresenceManager} from "./PresenceManager";
+import {SlashCommandsRefresher} from "./SlashCommandsRefresher";
+import {ErrorParser} from "./ErrorParser";
+import {Welcome} from "./Welcome";
+import {ManagedChannelManager} from "./ManagedChannelManager";
+
+process.on('uncaughtException', (error) => {
+ if (error.message === "WebSocket was closed before the connection was established") {
+ LogManager.error("Bot has encountered a connection error, restarting.");
+ fs.writeFileSync("./RESTART-FORCE", "");
+ return;
+ }
+ LogManager.error(error.stack);
+
+ let message = {
+ embeds: [
+ new MessageEmbed()
+ .setTitle(":no_entry: An internal error occurred with the bot")
+ .setDescription("We are sorry, but an error has occurred and the bot cannot process your request for now. This is most likely a problem on our side, please try again later.\n\n> " + ErrorParser.parse(error.name, error.message) + "\n\n\\*\\*\\*\\* BugCheck 0x" + crypto.createHash('md5').update(error.name + error.message).digest('hex').substring(0, 8).toUpperCase())
+ .addField("Are you a developer?", "Check the bot logs (e.g. with `/logs`) for additional details and a stack trace.")
+ .addField("Version information", "version " + (fs.existsSync("./.git/refs/heads/mane") ? fs.readFileSync("./.git/refs/heads/mane").toString().trim().substring(0, 8) : (fs.existsSync("../.git/refs/heads/mane") ? fs.readFileSync("../.git/refs/heads/mane").toString().trim().substring(0, 8) : (fs.existsSync("./version.txt") ? fs.readFileSync("./version.txt").toString().trim() : (fs.existsSync("../version.txt") ? fs.readFileSync("../version.txt").toString().trim() : "live")))) + ", build " + (fs.existsSync("./build.txt") ? fs.readFileSync("./build.txt").toString().trim() : (fs.existsSync("../build.txt") ? fs.readFileSync("../build.txt").toString().trim() : "dev")) + " (use `/about` for more details)")
+ ]
+ }
+
+ if (global.lastKnownInteraction) {
+ global.lastKnownInteraction.channel.send(message);
+ }
+})
+
+export class CoolerPony {
+ constructor(token: string) {
+ const client = global.client = new Client({intents: ['GUILD_VOICE_STATES', 'GUILD_MESSAGES', 'GUILDS', 'GUILD_MEMBERS']});
+
+ client.on('ready', () => {
+ LogManager.info(`Logged in as ${client.user.tag}!`);
+ PresenceManager.start(client);
+ SlashCommandsRefresher.refresh(client.user.id, token);
+ });
+
+ client.on('guildMemberAdd', (member: GuildMember) => {
+ Welcome.welcome(member);
+ })
+
+ client.on('guildMemberRemove', (member: GuildMember) => {
+ Welcome.unwelcome(member);
+ })
+
+ client.on('messageCreate', (message: Message) => {
+ ManagedChannelManager.handleMessage(message);
+ })
+
+ client.on('interactionCreate', async interaction => {
+ global.lastKnownInteraction = interaction;
+ if (!interaction.guild) {
+ // @ts-ignore
+ if (interaction.channel) { // @ts-ignore
+ interaction.channel.send({
+ embeds: [
+ new MessageEmbed()
+ .setDescription(":x: The bot can not be used in direct messages.")
+ ]
+ });
+ }
+ return;
+ }
+
+ global.processingStart = new Date();
+ if (interaction.isCommand()) {
+ new CommandInteractionManager(interaction);
+ }
+ });
+
+ client.login(token);
+ }
+} \ No newline at end of file
diff --git a/core/ErrorParser.ts b/core/ErrorParser.ts
new file mode 100644
index 0000000..954fcc5
--- /dev/null
+++ b/core/ErrorParser.ts
@@ -0,0 +1,67 @@
+export class ErrorParser {
+ public static parse(type: string, message: string): string {
+ let p1 = "Internal system failure.";
+
+ switch (type) {
+ case "Error":
+ p1 = "General failure.";
+ break;
+
+ case "InternalError":
+ p1 = "Internal engine failure.";
+ break;
+
+ case "RangeError":
+ p1 = "Invalid item range.";
+ break;
+
+ case "ReferenceError":
+ p1 = "Unable to reference element.";
+ break;
+
+ case "SyntaxError":
+ p1 = "Syntax failure.";
+ break;
+
+ case "TypeError":
+ p1 = "Inconsistent element type.";
+ break;
+
+ case "SongTooLongError":
+ p1 = "Song is too long.";
+ break;
+
+ case "URIError":
+ p1 = "Resource address parser error.";
+ break;
+
+ case "Warning":
+ p1 = "Non-critical error.";
+ break;
+ }
+
+ let p2 = message;
+
+ p2 = p2.replace(/Permission denied to access property (.*)/gm, "The system cannot access the $1 property.")
+ p2 = p2.replace(/(.*) is not defined/gm, "The system attempted to access the $1 variable while it is not defined at this point.")
+ p2 = p2.replace(/assignment to undeclared variable (.*)/gm, "The system attempted to assign a value to the $1 variable while it is not defined at this point.")
+ p2 = p2.replace(/assignment to undeclared variable (.*)/gm, "The system attempted to assign a value to the $1 variable while it is not defined at this point.")
+ p2 = p2.replace(/can't access lexical declaration (.*) before initialization/gm, "The system attempted to access the $1 variable while it has not been declared yet.")
+ p2 = p2.replace(/reference to undefined property (.*)/gm, "The system attempted to access the $1 property while it is not defined in that object.")
+ p2 = p2.replace(/reference to undefined property (.*)/gm, "The system attempted to access the $1 property while it is not defined in that object.")
+ p2 = p2.replace(/(.*) has no properties/gm, "The system attempted to access properties of $1 while it doesn't have any.")
+ p2 = p2.replace(/(.*) is \(not\) (.*)/gm, "The system attempted to access the $1 variable while it is not the same as $2.")
+ p2 = p2.replace(/(.*) is not a constructor/gm, "The system attempted to use $1 as a class constructor while it is not.")
+ p2 = p2.replace(/(.*) is not a function/gm, "The system attempted to use $1 as a function or method while it is not.")
+ p2 = p2.replace(/(.*) is not a non-null object/gm, "The system attempted to use $1 as an object while it is not null.")
+ p2 = p2.replace(/(.*) is read-only/gm, "The system attempted to change $1 while it is read-only.")
+ p2 = p2.replace(/(.*) is not iterable/gm, "The system attempted to count iterations of $1 while this is not possible.")
+ p2 = p2.replace(/(.*)\.prototype\.(.*) called on incompatible type/gm, "The system attempted to call prototype $2 of class $1 while it is not compatible.")
+ p2 = p2.replace(/can't access property (.*) of (.*)/gm, "The system attempted to access property $1 of $2 while that is not possible.")
+ p2 = p2.replace(/can't access property (.*) of (.*)/gm, "The system attempted to access property $1 of $2 while that is not possible.")
+ p2 = p2.replace(/can't assign to property (.*) on (.*): not an object/gm, "The system attempted to assign a value to property $1 of $2 while $2 is not an object.")
+ p2 = p2.replace(/can't define property (.*): (.*) is not extensible/gm, "The system attempted to define a $1 property on $2 while it is not extensible.")
+
+ return p1 + " " + p2;
+ }
+} \ No newline at end of file
diff --git a/core/InteractionManager.ts b/core/InteractionManager.ts
new file mode 100644
index 0000000..af2b62f
--- /dev/null
+++ b/core/InteractionManager.ts
@@ -0,0 +1,3 @@
+export class InteractionManager {
+
+} \ No newline at end of file
diff --git a/core/LogManager.ts b/core/LogManager.ts
new file mode 100644
index 0000000..916bc67
--- /dev/null
+++ b/core/LogManager.ts
@@ -0,0 +1,84 @@
+import chalk from 'chalk';
+import * as fs from 'fs';
+
+if (!fs.existsSync("logs")) fs.mkdirSync("logs");
+
+export class LogManager {
+ public static logID = new Date().toISOString();
+
+ /**
+ * @return void
+ * @param message - The message to display
+ * @description Shows a warning message
+ */
+ public static warn(message: string): void {
+ let messageParts = message.split(":");
+ let messagePrefix = messageParts[0];
+ messageParts.shift()
+
+ console.log(
+ chalk.gray("[" + new Date().toISOString() + "] ") +
+ chalk.yellow("[warn] ") +
+ (messageParts.length > 0 ? chalk.underline(messagePrefix) + ":" + messageParts.join(":") : messagePrefix)
+ );
+
+ fs.appendFileSync("logs/" + LogManager.logID + ".txt", "\n[" + new Date().toISOString() + "] [warn] " + message);
+ }
+
+ /**
+ * @return void
+ * @param message - The message to display
+ * @description Shows an information message
+ */
+ public static info(message: string): void {
+ let messageParts = message.split(":");
+ let messagePrefix = messageParts[0];
+ messageParts.shift()
+
+ console.log(
+ chalk.gray("[" + new Date().toISOString() + "] ") +
+ chalk.cyan("[info] ") +
+ (messageParts.length > 0 ? chalk.underline(messagePrefix) + ":" + messageParts.join(":") : messagePrefix)
+ );
+
+ fs.appendFileSync("logs/" + LogManager.logID + ".txt", "\n[" + new Date().toISOString() + "] [info] " + message);
+ }
+
+ /**
+ * @return void
+ * @param message - The message to display
+ * @description Shows a debugging message
+ */
+ public static verbose(message: string): void {
+ let messageParts = message.split(":");
+ let messagePrefix = messageParts[0];
+ messageParts.shift()
+
+ console.log(
+ chalk.gray("[" + new Date().toISOString() + "] ") +
+ chalk.green("[verb] ") +
+ (messageParts.length > 0 ? chalk.underline(messagePrefix) + ":" + messageParts.join(":") : messagePrefix)
+ );
+
+ fs.appendFileSync("logs/" + LogManager.logID + ".txt", "\n[" + new Date().toISOString() + "] [verb] " + message);
+ }
+
+ /**
+ * @return void
+ * @param message - The message to display
+ * @description Shows an error message
+ */
+ public static error(message: string): void {
+ let messageParts = message.split(":");
+ let messagePrefix = messageParts[0];
+ messageParts.shift()
+
+ console.log(
+ chalk.gray("[" + new Date().toISOString() + "] ") +
+ chalk.red("[crit] ") +
+ (messageParts.length > 0 ? chalk.underline(messagePrefix) + ":" + messageParts.join(":") : messagePrefix)
+ );
+
+ fs.appendFileSync("logs/" + LogManager.logID + ".txt", "\n[" + new Date().toISOString() + "] [crit] " + message);
+ }
+}
diff --git a/core/ManagedChannelManager.ts b/core/ManagedChannelManager.ts
new file mode 100644
index 0000000..538b345
--- /dev/null
+++ b/core/ManagedChannelManager.ts
@@ -0,0 +1,41 @@
+import {Message} from "discord.js";
+import {Welcome} from "./Welcome";
+import Fuse from "fuse.js";
+import {LogManager} from "./LogManager";
+
+export class ManagedChannelManager {
+ public static handleMessage(message: Message): void {
+ if (message.author.id === global.client.user.id) return;
+ let data = global.data;
+
+ let channels = data.managedChannels.map(i => i.channel);
+ if (channels.includes(message.channel.id)) {
+ let channel = data.managedChannels.find(i => i.channel === message.channel.id);
+ if (channel.user === message.author.id) {
+ let question = Welcome.questions[channel.question];
+
+ const fuse = new Fuse(question.response, {
+ includeScore: true
+ })
+
+ let results = fuse.search(message.content);
+
+ if (results.length === 0) {
+ message.react("❌");
+ return;
+ }
+
+ LogManager.verbose("Question \"" + question.question + "\": response \"" + message.content + "\", score " + results[0].score.toFixed(5));
+
+ if (results[0].score < 0.5) {
+ message.react("✅");
+ message.member.roles.add("996411960293859400");
+ message.channel.delete();
+ data.managedChannels.splice(data.managedChannels.indexOf(channel), 1);
+ } else {
+ message.react("❌");
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/core/PresenceManager.ts b/core/PresenceManager.ts
new file mode 100644
index 0000000..3e88dbc
--- /dev/null
+++ b/core/PresenceManager.ts
@@ -0,0 +1,7 @@
+import {Client} from "discord.js";
+
+export class PresenceManager {
+ public static start(client: Client): void {
+ client.user.setPresence({activities: [{name: 'with you!'}], status: 'online'});
+ }
+} \ No newline at end of file
diff --git a/core/SlashCommandsRefresher.ts b/core/SlashCommandsRefresher.ts
new file mode 100644
index 0000000..0503366
--- /dev/null
+++ b/core/SlashCommandsRefresher.ts
@@ -0,0 +1,35 @@
+import {LogManager} from "./LogManager";
+import {REST} from '@discordjs/rest';
+import {Routes} from 'discord-api-types/v9';
+import {CommandsLoader} from "./CommandsLoader";
+
+export class SlashCommandsRefresher {
+ public static async refresh(clientId: string, token: string) {
+ const rest = new REST({version: '9'}).setToken(token);
+ try {
+ const commandsLoader = new CommandsLoader();
+ LogManager.info('Started refreshing application (/) commands.');
+
+ await rest.put(
+ Routes.applicationCommands(clientId),
+ {body: []},
+ );
+
+ try {
+ await rest.put(
+ Routes.applicationGuildCommands(clientId, "996331009744318534"),
+ {body: commandsLoader.slashCommands()},
+ );
+ } catch (e) {
+ await rest.put(
+ Routes.applicationGuildCommands(clientId, "969994404184084560"),
+ {body: commandsLoader.slashCommands()},
+ );
+ }
+
+ LogManager.info('Successfully reloaded application (/) commands. Changes may take a while to appear on Discord.');
+ } catch (error) {
+ console.error(error);
+ }
+ }
+} \ No newline at end of file
diff --git a/core/Welcome.ts b/core/Welcome.ts
new file mode 100644
index 0000000..1a5e7c1
--- /dev/null
+++ b/core/Welcome.ts
@@ -0,0 +1,50 @@
+import {GuildMember} from "discord.js";
+import {LogManager} from "./LogManager";
+import * as fs from "fs";
+
+export class Welcome {
+ public static questions = JSON.parse(fs.readFileSync("./assets/questions.json", "utf8"));
+
+ public static welcome(member: GuildMember) {
+ let data = global.data;
+ LogManager.warn(member.user.tag + " joined the server");
+
+ let guild = member.guild;
+ guild.channels.create("welcome").then(channel => {
+ let thisChannel = {
+ channel: channel.id,
+ user: member.user.id,
+ question: Math.floor(Math.random() * Welcome.questions.length),
+ };
+ data.managedChannels.push(thisChannel);
+
+ channel.permissionOverwrites.create(guild.roles.everyone, {
+ VIEW_CHANNEL: false,
+ });
+
+ channel.permissionOverwrites.create(member.id, {
+ VIEW_CHANNEL: true,
+ });
+
+ let welcome = guild.channels.resolve("996412407217926224");
+ // @ts-ignore
+ welcome.permissionOverwrites.create(member.id, {
+ VIEW_CHANNEL: false,
+ });
+
+ channel.send("**Welcome to " + guild.name + ", <@" + member.id + ">!**\n\nBefore you can access the server, you need to read and agree to the <#996403284246016031>. Once done, send a reply to the following question:\n\n> " + Welcome.questions[thisChannel.question].question + "\n\nIf you reply correctly, you will be able to access the server. Contact the mods if you need help (managed channel ID: `" + thisChannel.channel + "`).\n\n:warning: Note that the bot might not accept your answer if it is not spelled properly. If it is not accepted, try again with a different phrasing. In case of doubt, the bot will direct you to a mod.");
+ });
+ }
+
+ public static unwelcome(member: GuildMember) {
+ let data = global.data;
+ LogManager.warn(member.user.tag + " left the server");
+
+ let users = data.managedChannels.map(i => i.user);
+ if (users.includes(member.id)) {
+ let channel = data.managedChannels.find(i => i.user === member.id);
+ member.guild.channels.resolve(channel.channel).delete();
+ data.managedChannels.splice(data.managedChannels.indexOf(channel), 1);
+ }
+ }
+} \ No newline at end of file
diff --git a/index.ts b/index.ts
new file mode 100644
index 0000000..0446559
--- /dev/null
+++ b/index.ts
@@ -0,0 +1,65 @@
+import * as fs from 'fs';
+import {LogManager} from "./core/LogManager";
+import {CoolerPony} from "./core/CoolerPony";
+
+global.data = {};
+let lastSaveData = "{}";
+
+if (fs.existsSync("./data/database.json")) {
+ global.data = JSON.parse(fs.readFileSync("./data/database.json").toString());
+ lastSaveData = fs.readFileSync("./data/database.json").toString();
+}
+
+setInterval(() => {
+ if (lastSaveData !== JSON.stringify(global.data)) {
+ fs.writeFileSync("./data/database.json", JSON.stringify(global.data));
+ lastSaveData = JSON.stringify(global.data);
+ }
+});
+
+if (!global.data.managedChannels) {
+ global.data.managedChannels = [];
+}
+
+global.version = (fs.existsSync("./.git/refs/heads/mane") ? fs.readFileSync("./.git/refs/heads/mane").toString().trim().substring(0, 8) : (fs.existsSync("../.git/refs/heads/mane") ? fs.readFileSync("../.git/refs/heads/mane").toString().trim().substring(0, 8) : (fs.existsSync("./version.txt") ? fs.readFileSync("./version.txt").toString().trim() : (fs.existsSync("../version.txt") ? fs.readFileSync("../version.txt").toString().trim() : "live"))));
+global.build = (fs.existsSync("./build.txt") ? fs.readFileSync("./build.txt").toString().trim() : (fs.existsSync("../build.txt") ? fs.readFileSync("../build.txt").toString().trim() : "dev"));
+
+function restart() {
+ if (fs.existsSync("./data/lastRestart") && new Date().getTime() - new Date(fs.readFileSync("./data/lastRestart").toString()).getTime() < 60000) {
+ LogManager.error("Restart cancelled: too close to another restart, try again in a few seconds.");
+ fs.rmSync("./RESTART", {recursive: true});
+ } else {
+ fs.rmSync("./RESTART", {recursive: true});
+ fs.writeFileSync("./data/lastRestart", new Date().toISOString());
+ LogManager.warn("Restart requested.");
+ process.exit(14);
+ }
+}
+
+async function restartManager() {
+ if (fs.existsSync("./RESTART-FORCE")) {
+ fs.rmSync("./RESTART-FORCE", {recursive: true});
+ LogManager.warn("Force restart requested.");
+ process.exit(14);
+ }
+
+ if (fs.existsSync("./RESTART")) {
+ restart();
+ }
+}
+
+setInterval(restartManager, 500);
+global.systemRoot = __dirname;
+
+if (!fs.existsSync("data")) fs.mkdirSync("data");
+
+let token = "";
+if (fs.existsSync("token.txt")) token = fs.readFileSync("token.txt").toString().trim();
+else if (fs.existsSync("../token.txt")) token = fs.readFileSync("../token.txt").toString().trim();
+else {
+ LogManager.error("Cannot find token in the expected places (" + __dirname + "/token.txt or " + __dirname.split("/").splice(__dirname.split("/").length - 1, 1).join("/") + "/token.txt");
+ process.exit(1);
+}
+
+LogManager.verbose("Starting Discord bot...");
+new CoolerPony(token); \ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..9116b24
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,724 @@
+{
+ "name": "cooler-pony",
+ "version": "0.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "cooler-pony",
+ "version": "0.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "@discordjs/builders": "^0.13.0",
+ "@discordjs/rest": "^0.4.1",
+ "axios": "^0.27.2",
+ "chalk": "^4.1.2",
+ "discord-api-types": "^0.32.1",
+ "discord.js": "^13.7.0",
+ "fuse.js": "^6.6.2"
+ },
+ "devDependencies": {
+ "@types/node": "^17.0.33",
+ "typescript": "^4.6.4"
+ }
+ },
+ "node_modules/@discordjs/builders": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.13.0.tgz",
+ "integrity": "sha512-4L9y26KRNNU8Y7J78SRUN1Uhava9D8jfit/YqEaKi8gQRc7PdqKqk2poybo6RXaiyt/BgKYPfcjxT7WvzGfYCA==",
+ "dependencies": {
+ "@sapphire/shapeshift": "^2.0.0",
+ "@sindresorhus/is": "^4.6.0",
+ "discord-api-types": "^0.31.1",
+ "fast-deep-equal": "^3.1.3",
+ "ts-mixer": "^6.0.1",
+ "tslib": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/@discordjs/builders/node_modules/discord-api-types": {
+ "version": "0.31.2",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.31.2.tgz",
+ "integrity": "sha512-gpzXTvFVg7AjKVVJFH0oJGC0q0tO34iJGSHZNz9u3aqLxlD6LfxEs9wWVVikJqn9gra940oUTaPFizCkRDcEiA=="
+ },
+ "node_modules/@discordjs/collection": {
+ "version": "0.7.0-dev.1651493036-4ba0f56",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0-dev.1651493036-4ba0f56.tgz",
+ "integrity": "sha512-/6eYweo3mprHqYO+DTM89mZNp1AdvZelMZGSeiCUtMadhsdjycR1cfA08fRtivDGaRnvmecDaKIEfig9mXaRxw==",
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/@discordjs/rest": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.4.1.tgz",
+ "integrity": "sha512-rtWy+AIfNlfjGkAgA2TJLASdqli07aTNQceVGT6RQQiQaEqV0nsfBO4WtDlDzk7PmO3w+InP3dpwEolJI5jz0A==",
+ "dependencies": {
+ "@discordjs/collection": "^0.7.0-dev",
+ "@sapphire/async-queue": "^1.3.1",
+ "@sapphire/snowflake": "^3.2.1",
+ "@types/node-fetch": "^2.6.1",
+ "discord-api-types": "^0.29.0",
+ "form-data": "^4.0.0",
+ "node-fetch": "^2.6.7",
+ "tslib": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/@discordjs/rest/node_modules/discord-api-types": {
+ "version": "0.29.0",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.29.0.tgz",
+ "integrity": "sha512-Ekq1ICNpOTVajXKZguNFrsDeTmam+ZeA38txsNLZnANdXUjU6QBPIZLUQTC6MzigFGb0Tt8vk4xLnXmzv0shNg=="
+ },
+ "node_modules/@sapphire/async-queue": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.3.1.tgz",
+ "integrity": "sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g==",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@sapphire/shapeshift": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-2.2.0.tgz",
+ "integrity": "sha512-UEnKgMlQyI0yY/q+lCMX0VJft9y86IsesgbIQj6e62FBYSaMVr+IaMNpi4z45Q14VnuMACbK0yrbHISNqgUYcQ==",
+ "engines": {
+ "node": ">=v15.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@sapphire/snowflake": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.2.2.tgz",
+ "integrity": "sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==",
+ "engines": {
+ "node": ">=v14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "17.0.33",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.33.tgz",
+ "integrity": "sha512-miWq2m2FiQZmaHfdZNcbpp9PuXg34W5JZ5CrJ/BaS70VuhoJENBEQybeiYSaPBRNq6KQGnjfEnc/F3PN++D+XQ=="
+ },
+ "node_modules/@types/node-fetch": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz",
+ "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==",
+ "dependencies": {
+ "@types/node": "*",
+ "form-data": "^3.0.0"
+ }
+ },
+ "node_modules/@types/node-fetch/node_modules/form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
+ "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "node_modules/axios": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+ "dependencies": {
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/discord-api-types": {
+ "version": "0.32.1",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.32.1.tgz",
+ "integrity": "sha512-/ewl0CPYT5xjOC+SJ7wADJKjtpZfiiUaYXOP/Ns54lnBcv4Xqa4iKSqRF/w1fjiUvWTYN9W8UuOiyCHtmu5fJw=="
+ },
+ "node_modules/discord.js": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.7.0.tgz",
+ "integrity": "sha512-iV/An3FEB/CiBGdjWHRtgskM4UuWPq5vjhjKsrQhdVU16dbKrBxA+eIV2HWA07B3tXUGM6eco1wkr42gxxV1BA==",
+ "dependencies": {
+ "@discordjs/builders": "^0.13.0",
+ "@discordjs/collection": "^0.6.0",
+ "@sapphire/async-queue": "^1.3.1",
+ "@types/node-fetch": "^2.6.1",
+ "@types/ws": "^8.5.3",
+ "discord-api-types": "^0.30.0",
+ "form-data": "^4.0.0",
+ "node-fetch": "^2.6.1",
+ "ws": "^8.6.0"
+ },
+ "engines": {
+ "node": ">=16.6.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/discord.js/node_modules/@discordjs/collection": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.6.0.tgz",
+ "integrity": "sha512-Ieaetb36l0nmAS5X9Upqk4W7euAO6FdXPxn3I8vBAKEcoIzEZI1mcVcPfCfagGJZSgBKpENnAnKkP4GAn+MV8w==",
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/discord.js/node_modules/discord-api-types": {
+ "version": "0.30.0",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.30.0.tgz",
+ "integrity": "sha512-wYst0jrT8EJs2tVlwUTQ2xT0oWMjUrRMpFTkNY3NMleWyQNHgWaKhqFfxdLPdC2im9IuR5EsxcEgjhf/npeftw=="
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
+ "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fuse.js": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz",
+ "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+ "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+ },
+ "node_modules/ts-mixer": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz",
+ "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg=="
+ },
+ "node_modules/tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
+ },
+ "node_modules/typescript": {
+ "version": "4.6.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
+ "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
+ "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ }
+ },
+ "dependencies": {
+ "@discordjs/builders": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.13.0.tgz",
+ "integrity": "sha512-4L9y26KRNNU8Y7J78SRUN1Uhava9D8jfit/YqEaKi8gQRc7PdqKqk2poybo6RXaiyt/BgKYPfcjxT7WvzGfYCA==",
+ "requires": {
+ "@sapphire/shapeshift": "^2.0.0",
+ "@sindresorhus/is": "^4.6.0",
+ "discord-api-types": "^0.31.1",
+ "fast-deep-equal": "^3.1.3",
+ "ts-mixer": "^6.0.1",
+ "tslib": "^2.3.1"
+ },
+ "dependencies": {
+ "discord-api-types": {
+ "version": "0.31.2",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.31.2.tgz",
+ "integrity": "sha512-gpzXTvFVg7AjKVVJFH0oJGC0q0tO34iJGSHZNz9u3aqLxlD6LfxEs9wWVVikJqn9gra940oUTaPFizCkRDcEiA=="
+ }
+ }
+ },
+ "@discordjs/collection": {
+ "version": "0.7.0-dev.1651493036-4ba0f56",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0-dev.1651493036-4ba0f56.tgz",
+ "integrity": "sha512-/6eYweo3mprHqYO+DTM89mZNp1AdvZelMZGSeiCUtMadhsdjycR1cfA08fRtivDGaRnvmecDaKIEfig9mXaRxw=="
+ },
+ "@discordjs/rest": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.4.1.tgz",
+ "integrity": "sha512-rtWy+AIfNlfjGkAgA2TJLASdqli07aTNQceVGT6RQQiQaEqV0nsfBO4WtDlDzk7PmO3w+InP3dpwEolJI5jz0A==",
+ "requires": {
+ "@discordjs/collection": "^0.7.0-dev",
+ "@sapphire/async-queue": "^1.3.1",
+ "@sapphire/snowflake": "^3.2.1",
+ "@types/node-fetch": "^2.6.1",
+ "discord-api-types": "^0.29.0",
+ "form-data": "^4.0.0",
+ "node-fetch": "^2.6.7",
+ "tslib": "^2.3.1"
+ },
+ "dependencies": {
+ "discord-api-types": {
+ "version": "0.29.0",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.29.0.tgz",
+ "integrity": "sha512-Ekq1ICNpOTVajXKZguNFrsDeTmam+ZeA38txsNLZnANdXUjU6QBPIZLUQTC6MzigFGb0Tt8vk4xLnXmzv0shNg=="
+ }
+ }
+ },
+ "@sapphire/async-queue": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.3.1.tgz",
+ "integrity": "sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g=="
+ },
+ "@sapphire/shapeshift": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-2.2.0.tgz",
+ "integrity": "sha512-UEnKgMlQyI0yY/q+lCMX0VJft9y86IsesgbIQj6e62FBYSaMVr+IaMNpi4z45Q14VnuMACbK0yrbHISNqgUYcQ=="
+ },
+ "@sapphire/snowflake": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.2.2.tgz",
+ "integrity": "sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ=="
+ },
+ "@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="
+ },
+ "@types/node": {
+ "version": "17.0.33",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.33.tgz",
+ "integrity": "sha512-miWq2m2FiQZmaHfdZNcbpp9PuXg34W5JZ5CrJ/BaS70VuhoJENBEQybeiYSaPBRNq6KQGnjfEnc/F3PN++D+XQ=="
+ },
+ "@types/node-fetch": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz",
+ "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==",
+ "requires": {
+ "@types/node": "*",
+ "form-data": "^3.0.0"
+ },
+ "dependencies": {
+ "form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ }
+ }
+ },
+ "@types/ws": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
+ "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "axios": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+ "requires": {
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+ },
+ "discord-api-types": {
+ "version": "0.32.1",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.32.1.tgz",
+ "integrity": "sha512-/ewl0CPYT5xjOC+SJ7wADJKjtpZfiiUaYXOP/Ns54lnBcv4Xqa4iKSqRF/w1fjiUvWTYN9W8UuOiyCHtmu5fJw=="
+ },
+ "discord.js": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.7.0.tgz",
+ "integrity": "sha512-iV/An3FEB/CiBGdjWHRtgskM4UuWPq5vjhjKsrQhdVU16dbKrBxA+eIV2HWA07B3tXUGM6eco1wkr42gxxV1BA==",
+ "requires": {
+ "@discordjs/builders": "^0.13.0",
+ "@discordjs/collection": "^0.6.0",
+ "@sapphire/async-queue": "^1.3.1",
+ "@types/node-fetch": "^2.6.1",
+ "@types/ws": "^8.5.3",
+ "discord-api-types": "^0.30.0",
+ "form-data": "^4.0.0",
+ "node-fetch": "^2.6.1",
+ "ws": "^8.6.0"
+ },
+ "dependencies": {
+ "@discordjs/collection": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.6.0.tgz",
+ "integrity": "sha512-Ieaetb36l0nmAS5X9Upqk4W7euAO6FdXPxn3I8vBAKEcoIzEZI1mcVcPfCfagGJZSgBKpENnAnKkP4GAn+MV8w=="
+ },
+ "discord-api-types": {
+ "version": "0.30.0",
+ "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.30.0.tgz",
+ "integrity": "sha512-wYst0jrT8EJs2tVlwUTQ2xT0oWMjUrRMpFTkNY3NMleWyQNHgWaKhqFfxdLPdC2im9IuR5EsxcEgjhf/npeftw=="
+ }
+ }
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "follow-redirects": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
+ "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ=="
+ },
+ "form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fuse.js": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz",
+ "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA=="
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "node-fetch": {
+ "version": "2.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+ "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+ },
+ "ts-mixer": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz",
+ "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg=="
+ },
+ "tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
+ },
+ "typescript": {
+ "version": "4.6.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
+ "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==",
+ "dev": true
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "ws": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
+ "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
+ "requires": {}
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..346ad40
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "cooler-pony",
+ "version": "0.0.0",
+ "description": "A general-purpose Discord bot",
+ "main": "./build/index.js",
+ "scripts": {
+ "start": "cd ./build && node ./index.js",
+ "build": "tsc -p tsconfig.json && cp -r assets build"
+ },
+ "author": "Minteck",
+ "license": "MIT",
+ "devDependencies": {
+ "@types/node": "^17.0.33",
+ "typescript": "^4.6.4"
+ },
+ "dependencies": {
+ "@discordjs/builders": "^0.13.0",
+ "@discordjs/rest": "^0.4.1",
+ "axios": "^0.27.2",
+ "chalk": "^4.1.2",
+ "discord-api-types": "^0.32.1",
+ "discord.js": "^13.7.0",
+ "fuse.js": "^6.6.2"
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..6387630
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es2021",
+ "sourceMap": true,
+ "esModuleInterop": true,
+ "outDir": "./build",
+ "skipLibCheck": true
+ },
+ "exclude": [
+ "node_modules"
+ ]
+}