Many changes, including: command registration works (now actually calls the command functions), fixed emoji embeds, started fixing some commands, database no longer uses proxy
diff --git a/src/commands/nucleus/_meta.ts b/src/commands/nucleus/_meta.ts
index 751feca..d66b5d2 100644
--- a/src/commands/nucleus/_meta.ts
+++ b/src/commands/nucleus/_meta.ts
@@ -3,6 +3,6 @@
const name = "nucleus";
const description = "Commands relating to Nucleus itself";
-const subcommand = await command(name, description, `settings/logs`)
+const subcommand = await command(name, description, `nucleus`)
export { name, description, subcommand as command };
diff --git a/src/commands/verify.ts b/src/commands/verify.ts
index bf6a306..b9556a6 100644
--- a/src/commands/verify.ts
+++ b/src/commands/verify.ts
@@ -5,7 +5,6 @@
const command = new SlashCommandBuilder().setName("verify").setDescription("Get verified in the server");
const callback = async (interaction: CommandInteraction): Promise<void> => {
- interaction.reply("boo")
verify(interaction);
};
diff --git a/src/utils/client.ts b/src/utils/client.ts
index 6aa9b43..61cf3bf 100644
--- a/src/utils/client.ts
+++ b/src/utils/client.ts
@@ -22,14 +22,14 @@
premium: Premium;
eventScheduler: EventScheduler;
};
- // commands: Record<string, {
- // command: Discord.SlashCommandBuilder |
- // ((builder: Discord.SlashCommandBuilder) => Discord.SlashCommandBuilder) |
- // Discord.SlashCommandSubcommandBuilder | ((builder: Discord.SlashCommandSubcommandBuilder) => Discord.SlashCommandSubcommandBuilder) | Discord.SlashCommandSubcommandGroupBuilder | ((builder: Discord.SlashCommandSubcommandGroupBuilder) => Discord.SlashCommandSubcommandGroupBuilder),
- // callback: (interaction: Interaction) => Promise<void>,
- // check: (interaction: Interaction) => Promise<boolean> | boolean
- // }> = {};
- commands: Discord.Collection<string, [Function, Function]> = new Discord.Collection();
+ commands: Record<string, {
+ command: Discord.SlashCommandBuilder |
+ ((builder: Discord.SlashCommandBuilder) => Discord.SlashCommandBuilder) |
+ Discord.SlashCommandSubcommandBuilder | ((builder: Discord.SlashCommandSubcommandBuilder) => Discord.SlashCommandSubcommandBuilder) | Discord.SlashCommandSubcommandGroupBuilder | ((builder: Discord.SlashCommandSubcommandGroupBuilder) => Discord.SlashCommandSubcommandGroupBuilder),
+ callback: (interaction: Interaction) => Promise<void>,
+ check: (interaction: Interaction) => Promise<boolean> | boolean
+ }> = {};
+ // commands: Discord.Collection<string, [Function, Function]> = new Discord.Collection();
constructor(database: typeof NucleusClient.prototype.database) {
super({ intents: 32767 });
diff --git a/src/utils/commandRegistration/getFilesInFolder.ts b/src/utils/commandRegistration/getFilesInFolder.ts
index d8a1298..875e0b0 100644
--- a/src/utils/commandRegistration/getFilesInFolder.ts
+++ b/src/utils/commandRegistration/getFilesInFolder.ts
@@ -12,11 +12,11 @@
try {
if (file.isDirectory()) {
// Get the _meta.ts file
- subcommandGroups.push((await import(`../../../${path}/${file.name}/_meta.js`)).command);
+ subcommandGroups.push(await import(`../../../${path}/${file.name}/_meta.js`));
} else if (file.name.endsWith(".js")) {
// If its a file
console.log(`│ ${indent}├─ Loading subcommand ${file.name}`)
- subcommands.push((await import(`../../../${path}/${file.name}`)).command);
+ subcommands.push(await import(`../../../${path}/${file.name}`));
}
} catch (e) {
console.error(`│ ${indent}│ └─ Error loading ${file.name}: ${e}`);
diff --git a/src/utils/commandRegistration/register.ts b/src/utils/commandRegistration/register.ts
index a734921..1b55496 100644
--- a/src/utils/commandRegistration/register.ts
+++ b/src/utils/commandRegistration/register.ts
@@ -2,6 +2,7 @@
import config from "../../config/main.json" assert { type: "json" };
import client from "../client.js";
import fs from "fs";
+import Discord from "discord.js";
const colours = {
@@ -29,7 +30,7 @@
console.log(`${last}─ ${colours.yellow}Loading command ${file.name}${colours.none}`)
const fetched = (await import(`../../../${config.commandsFolder}/${file.name}`));
commands.push(fetched.command);
- client.commands.set(fetched.command.name, [fetched.check, fetched.callback]);
+ client.commands["commands/" + fetched.command.name] = fetched;
}
i++;
console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.green}Loaded ${file.name} [${i} / ${files.length}]${colours.none}`)
@@ -47,12 +48,13 @@
console.log(`Processed ${commands.length} commands, registering...`)
+ const updateCommands = process.argv.includes("--update-commands");
if (developmentMode) {
const guild = await client.guilds.fetch(config.developmentGuildID);
- guild.commands.set(processed);
+ if (updateCommands) guild.commands.set(processed);
console.log(`Commands registered in ${guild.name}`)
} else {
- client.application!.commands.set(processed);
+ if (updateCommands) client.application!.commands.set(processed);
console.log(`Commands registered globally`)
}
@@ -86,50 +88,30 @@
async function registerCommandHandler() {
client.on("interactionCreate", async (interaction: Interaction) => {
- if (!interaction.isCommand()) return;
+ if (!interaction.isChatInputCommand()) return;
const commandName = interaction.commandName;
const subcommandGroupName = interaction.options.getSubcommandGroup(false);
const subcommandName = interaction.options.getSubcommand(false);
- let fullCommandName = commandName + (subcommandGroupName ? ` ${subcommandGroupName}` : "") + (subcommandName ? ` ${subcommandName}` : "");
+ const fullCommandName = "commands/" + commandName + (subcommandGroupName ? `/${subcommandGroupName}` : "") + (subcommandName ? `/${subcommandName}` : "");
- const command = this.commands.get(fullCommandName);
- if (!command) return;
+ const command = client.commands[fullCommandName];
+ const callback = command?.callback;
+ const check = command?.check;
- const sendErrorMessage = async (error: Error) => {
- if (this.listenerCount("commandError")) {
- return this.emit("commandError", interaction, error);
+ if (!callback) return;
+ if (check) {
+ let result;
+ try {
+ result = await check(interaction);
+ } catch (e) {
+ console.log(e);
+ result = false;
}
- let method = (!interaction.deferred && !interaction.replied) ? interaction.reply.bind(interaction) : interaction.followUp.bind(interaction);
- await method({
- embeds: [
- new Embed()
- .setColor(0xff0000)
- .setTitle("I couldn't run that command")
- .setDescription(error.message ?? error.toString())
- ]
- , ephemeral: true});
+ if (!result) return;
}
-
- try {
- let hasPermission = await command.check(interaction);
-
- if (!hasPermission) {
- sendErrorMessage(new CheckFailedError("You don't have permission to run this command"));
- return;
- }
- } catch (error) {
- sendErrorMessage(error);
- return;
- }
- try {
- await command.callback(interaction);
- } catch (error) {
- this._error(error);
- sendErrorMessage(error);
- return;
- }
+ callback(interaction);
});
}
diff --git a/src/utils/commandRegistration/slashCommandBuilder.ts b/src/utils/commandRegistration/slashCommandBuilder.ts
index c7ac55f..76ecabe 100644
--- a/src/utils/commandRegistration/slashCommandBuilder.ts
+++ b/src/utils/commandRegistration/slashCommandBuilder.ts
@@ -2,6 +2,8 @@
import type { SlashCommandBuilder } from "discord.js";
import config from "../../config/main.json" assert { type: "json" };
import getSubcommandsInFolder from "./getFilesInFolder.js";
+import client from "../client.js";
+import Discord from "discord.js";
const colours = {
@@ -12,6 +14,7 @@
export async function group(name: string, description: string, path: string) {
+ // If the name of the command does not match the path (e.g. attachment.ts has /attachments), use commandString
console.log(`│ ├─ Loading group ${name}`)
const fetched = await getSubcommandsInFolder(config.commandsFolder + "/" + path, "│ ")
console.log(`│ │ └─ ${fetched.errors ? colours.red : colours.green}Loaded ${fetched.subcommands.length} subcommands for ${name} (${fetched.errors} failed)${colours.none}`)
@@ -21,14 +24,16 @@
.setDescription(description)
for (const subcommand of fetched.subcommands) {
- subcommandGroup.addSubcommand(subcommand);
+ subcommandGroup.addSubcommand(subcommand.command);
};
return subcommandGroup;
};
}
-export async function command(name: string, description: string, path: string) {
+export async function command(name: string, description: string, path: string, commandString: string | undefined = undefined) {
+ // If the name of the command does not match the path (e.g. attachment.ts has /attachments), use commandString
+ commandString = "commands/" + (commandString ?? path);
const fetched = await getSubcommandsInFolder(config.commandsFolder + "/" + path);
console.log(`│ ├─ ${fetched.errors ? colours.red : colours.green}Loaded ${fetched.subcommands.length} subcommands and ${fetched.subcommandGroups.length} subcommand groups for ${name} (${fetched.errors} failed)${colours.none}`)
return (command: SlashCommandBuilder) => {
@@ -36,10 +41,17 @@
command.setDescription(description)
for (const subcommand of fetched.subcommands) {
- command.addSubcommand(subcommand);
+ let fetchedCommand;
+ if (subcommand.command instanceof Function) {
+ fetchedCommand = subcommand.command(new Discord.SlashCommandSubcommandBuilder());
+ } else {
+ fetchedCommand = subcommand.command;
+ }
+ client.commands[commandString! + "/" + fetchedCommand.name] = subcommand
+ command.addSubcommand(fetchedCommand);
}
for (const group of fetched.subcommandGroups) {
- command.addSubcommandGroup(group);
+ command.addSubcommandGroup(group.command);
};
return command;
};
diff --git a/src/utils/database.ts b/src/utils/database.ts
index 2c299d0..2ae6af9 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -1,30 +1,11 @@
import type Discord from "discord.js";
import { Collection, MongoClient } from "mongodb";
-// @ts-expect-error
-import structuredClone from "@ungap/structured-clone";
import config from "../config/main.json" assert { type: "json" };
const mongoClient = new MongoClient(config.mongoUrl);
await mongoClient.connect();
const database = mongoClient.db("Nucleus");
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export const Entry = (data: any) => {
- data = data ?? {};
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- data.getKey = (key: any) => data[key];
- return {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- get(target: Record<string, any>, prop: string, receiver: any) {
- let dataToReturn = data[prop];
- if (dataToReturn === null) return Reflect.get(target, prop, receiver);
- if (typeof dataToReturn === "object" && !Array.isArray(dataToReturn))
- dataToReturn = new Proxy(Reflect.get(target, prop, receiver), Entry(dataToReturn));
- return dataToReturn ?? Reflect.get(target, prop, receiver);
- }
- };
-};
-
export class Guilds {
guilds: Collection<GuildConfig>;
defaultData: GuildConfig | null;
@@ -42,7 +23,7 @@
async read(guild: string): Promise<GuildConfig> {
const entry = await this.guilds.findOne({ id: guild });
- return new Proxy(structuredClone(this.defaultData), Entry(entry)) as unknown as GuildConfig;
+ return Object.assign({}, this.defaultData, entry);
}
async write(guild: string, set: object | null, unset: string[] | string = []) {
@@ -263,6 +244,7 @@
};
};
verify: {
+ enabled: boolean;
role: string | null;
};
tickets: {
diff --git a/src/utils/defaultEmbeds.ts b/src/utils/defaultEmbeds.ts
index 2200a5e..2334504 100644
--- a/src/utils/defaultEmbeds.ts
+++ b/src/utils/defaultEmbeds.ts
@@ -1,5 +1,6 @@
import EmojiEmbed from "./generateEmojiEmbed.js";
+import getEmojiByName from "./getEmojiByName.js";
export const LoadingEmbed = [
- new EmojiEmbed().setTitle("Loading").setDescription("One moment...").setStatus("Danger").setEmoji("NUCLEUS.LOADING")
+ new EmojiEmbed().setDescription(`${getEmojiByName("NUCLEUS.LOADING")} One moment...`).setStatus("Danger")
];
diff --git a/src/utils/generateEmojiEmbed.ts b/src/utils/generateEmojiEmbed.ts
index a7926df..e1b481a 100644
--- a/src/utils/generateEmojiEmbed.ts
+++ b/src/utils/generateEmojiEmbed.ts
@@ -1,4 +1,4 @@
-import { EmbedBuilder } from "discord.js";
+import { EmbedBuilder } from "@discordjs/builders";
import getEmojiByName from "./getEmojiByName.js";
const colors = {
@@ -11,19 +11,19 @@
_title = "";
_emoji: string | null = null;
- // @ts-expect-error
- // This *is* meant to be an accessor rather than a property
- override get title() {
- if (!this._emoji) return this._title;
- return `${getEmojiByName(this._emoji)} ${this._title}`;
+ _generateTitle() {
+ if (this._emoji) { return `${getEmojiByName(this._emoji)} ${this._title}`; }
+ return this._title;
}
override setTitle(title: string) {
this._title = title;
+ super.setTitle(this._generateTitle());
return this;
}
setEmoji(emoji: string) {
this._emoji = emoji;
+ super.setTitle(this._generateTitle());
return this;
}
setStatus(color: "Danger" | "Warning" | "Success") {
@@ -32,4 +32,5 @@
}
}
+
export default EmojiEmbed;
diff --git a/src/utils/getEmojiByName.ts b/src/utils/getEmojiByName.ts
index f953a4f..3fa2b53 100644
--- a/src/utils/getEmojiByName.ts
+++ b/src/utils/getEmojiByName.ts
@@ -4,7 +4,8 @@
[key: string]: string | EmojisIndex | EmojisIndex[];
}
-function getEmojiByName(name: string, format?: string): string {
+function getEmojiByName(name: string | null, format?: string): string {
+ if (!name) return "";
const parts = name.split(".");
let id: string | EmojisIndex | EmojisIndex[] | undefined = emojis;
for (const part of parts) {
@@ -25,7 +26,7 @@
return id.toString();
}
if (id === undefined) {
- return "<a:_:946346549271732234>";
+ return "";
} else if (id.toString().startsWith("a")) {
return `<a:_:${id.toString().slice(1, id.toString().length)}>`;
}