Performance testing
diff --git a/src/utils/client.ts b/src/utils/client.ts
index a57c639..43cbe11 100644
--- a/src/utils/client.ts
+++ b/src/utils/client.ts
@@ -1,8 +1,8 @@
-import Discord, { Client, Interaction, AutocompleteInteraction } from 'discord.js';
+import Discord, { Client, Interaction, AutocompleteInteraction, GatewayIntentBits } from 'discord.js';
 import { Logger } from "../utils/log.js";
 import Memory from "../utils/memory.js";
 import type { VerifySchema } from "../reflex/verify.js";
-import { Guilds, History, ModNotes, Premium } from "../utils/database.js";
+import { Guilds, History, ModNotes, Premium, PerformanceTest } from "../utils/database.js";
 import EventScheduler from "../utils/eventScheduler.js";
 import type { RoleMenuSchema } from "../actions/roleMenu.js";
 import config from "../config/main.json" assert { type: "json" };
@@ -21,6 +21,7 @@
         notes: ModNotes;
         premium: Premium;
         eventScheduler: EventScheduler;
+        performanceTest: PerformanceTest;
     };
     commands: Record<string, {
         command: Discord.SlashCommandBuilder |
@@ -32,7 +33,13 @@
     }> = {};
 
     constructor(database: typeof NucleusClient.prototype.database) {
-        super({ intents: 32767 });
+        super({ intents: [
+            GatewayIntentBits.Guilds,
+            GatewayIntentBits.GuildMessages,
+            GatewayIntentBits.MessageContent,
+            GatewayIntentBits.GuildPresences,
+            GatewayIntentBits.GuildMembers
+        ]});
         this.database = database;
     }
 }
@@ -42,7 +49,8 @@
     history: new History(),
     notes: new ModNotes(),
     premium: new Premium(),
-    eventScheduler: new EventScheduler()
+    eventScheduler: new EventScheduler(),
+    performanceTest: new PerformanceTest()
 });
 
 export default client;
diff --git a/src/utils/commandRegistration/register.ts b/src/utils/commandRegistration/register.ts
index a4c57c4..d96ca90 100644
--- a/src/utils/commandRegistration/register.ts
+++ b/src/utils/commandRegistration/register.ts
@@ -16,7 +16,7 @@
 async function registerCommands() {
     const commands = [];
 
-    const files = fs.readdirSync(config.commandsFolder, { withFileTypes: true }).filter(
+    const files: fs.Dirent[] = fs.readdirSync(config.commandsFolder, { withFileTypes: true }).filter(
         file => !file.name.endsWith(".ts") && !file.name.endsWith(".map")
     );
     console.log(`Registering ${files.length} commands`)
@@ -25,10 +25,15 @@
         const last = i === files.length - 1 ? "└" : "├";
         if (file.isDirectory()) {
             console.log(`${last}─ ${colours.yellow}Loading subcommands of ${file.name}${colours.none}`)
-            commands.push((await import(`../../../${config.commandsFolder}/${file.name}/_meta.js`)).command);
+            const fetched = (await import(`../../../${config.commandsFolder}/${file.name}/_meta.js`)).command;
+            commands.push(fetched);
         } else if (file.name.endsWith(".js")) {
             console.log(`${last}─ ${colours.yellow}Loading command ${file.name}${colours.none}`)
             const fetched = (await import(`../../../${config.commandsFolder}/${file.name}`));
+            fetched.command.setDMPermission(fetched.allowedInDMs ?? false)
+            fetched.command.setNameLocalizations(fetched.nameLocalizations ?? {})
+            fetched.command.setDescriptionLocalizations(fetched.descriptionLocalizations ?? {})
+            if (fetched.nameLocalizations || fetched.descriptionLocalizations) console.log("AAAAA")
             commands.push(fetched.command);
             client.commands["commands/" + fetched.command.name] = fetched;
         }
@@ -97,6 +102,8 @@
             console.log(`${last}─ ${colours.yellow}Loading message context menu ${file.name}${colours.none}`)
             const context = (await import(`../../../${config.messageContextFolder}/${file.name}`));
             context.command.setType(ApplicationCommandType.Message);
+            context.command.setDMPermission(context.allowedInDMs ?? false)
+            context.command.setNameLocalizations(context.nameLocalizations ?? {})
             commands.push(context.command);
 
             client.commands["contextCommands/message/" + context.command.name] = context;
@@ -182,6 +189,7 @@
     callback(data);
 }
 
+
 export default async function register() {
     let commandList: ( Discord.SlashCommandBuilder | Discord.ContextMenuCommandBuilder )[] = [];
     commandList = commandList.concat(await registerCommands());
diff --git a/src/utils/commandRegistration/slashCommandBuilder.ts b/src/utils/commandRegistration/slashCommandBuilder.ts
index 76ecabe..b2927d6 100644
--- a/src/utils/commandRegistration/slashCommandBuilder.ts
+++ b/src/utils/commandRegistration/slashCommandBuilder.ts
@@ -13,7 +13,13 @@
 }
 
 
-export async function group(name: string, description: string, path: string) {
+export async function group(
+    name: string,
+    description: string,
+    path: string,
+    nameLocalizations?: Record<string, string>,
+    descriptionLocalizations?: Record<string, 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, "│  ")
@@ -22,6 +28,8 @@
         subcommandGroup
             .setName(name)
             .setDescription(description)
+        if (nameLocalizations) { subcommandGroup.setNameLocalizations(nameLocalizations) }
+        if (descriptionLocalizations) { subcommandGroup.setDescriptionLocalizations(descriptionLocalizations) }
 
         for (const subcommand of fetched.subcommands) {
             subcommandGroup.addSubcommand(subcommand.command);
@@ -31,7 +39,16 @@
     };
 }
 
-export async function command(name: string, description: string, path: string, commandString: string | undefined = undefined) {
+export async function command(
+    name: string,
+    description: string,
+    path: string,
+    commandString: string | undefined = undefined,
+    nameLocalizations?: Record<string, string>,
+    descriptionLocalizations?: Record<string, string>,
+    userPermissions?: Discord.PermissionsString[],
+    allowedInDMs?: boolean
+) {
     // 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);
@@ -39,6 +56,14 @@
     return (command: SlashCommandBuilder) => {
         command.setName(name)
         command.setDescription(description)
+        command.setNameLocalizations(nameLocalizations ?? {})
+        command.setDescriptionLocalizations(descriptionLocalizations ?? {})
+        command.setDMPermission(allowedInDMs ?? false)
+        if (userPermissions) {
+            const bitfield = new Discord.PermissionsBitField()
+                bitfield.add(userPermissions)
+            command.setDefaultMemberPermissions(bitfield.bitfield)
+        }
 
         for (const subcommand of fetched.subcommands) {
             let fetchedCommand;
diff --git a/src/utils/database.ts b/src/utils/database.ts
index b14c5c4..ba1d89d 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -149,6 +149,33 @@
     }
 }
 
+export class PerformanceTest {
+    performanceData: Collection<PerformanceDataSchema>;
+
+    constructor() {
+        this.performanceData = database.collection<PerformanceDataSchema>("performance");
+    }
+
+    async record(data: PerformanceDataSchema) {
+        data.timestamp = new Date();
+        await this.performanceData.insertOne(data);
+    }
+    async read() {
+        return await this.performanceData.find({}).toArray();
+    }
+}
+
+export interface PerformanceDataSchema {
+    timestamp?: Date;
+    discord: number;
+    databaseRead: number;
+    resources: {
+        cpu: number;
+        memory: number;
+        temperature: number;
+    }
+}
+
 export class ModNotes {
     modNotes: Collection<ModNoteSchema>;
 
@@ -205,7 +232,11 @@
         };
         invite: {
             enabled: boolean;
-            channels: string[];
+            allowed: {
+                channels: string[];
+                roles: string[];
+                users: string[];
+            };
         };
         pings: {
             mass: number;
diff --git a/src/utils/log.ts b/src/utils/log.ts
index b097798..7ab7903 100644
--- a/src/utils/log.ts
+++ b/src/utils/log.ts
@@ -25,7 +25,7 @@
         const delta = num2 - num1;
         return `${num1} -> ${num2} (${delta > 0 ? "+" : ""}${delta})`;
     },
-    entry(value: string | null, displayValue: string): { value: string | null; displayValue: string } {
+    entry(value: string | number | null, displayValue: string): { value: string | null; displayValue: string } {
         return { value: value, displayValue: displayValue };
     },
     renderChannel(channel: Discord.GuildChannel | Discord.ThreadChannel) {
@@ -44,16 +44,15 @@
     },
     async getAuditLog(guild: Discord.Guild, event: Discord.GuildAuditLogsResolvable): Promise<Discord.GuildAuditLogsEntry[]> {
         await wait(250);
-        const auditLog = await guild.fetchAuditLogs({ type: event });
-        return auditLog as unknown as Discord.GuildAuditLogsEntry[];
+        const auditLog = (await guild.fetchAuditLogs({ type: event })).entries.map(m => m)
+        return auditLog as Discord.GuildAuditLogsEntry[];
     },
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     async log(log: any): Promise<void> {
         const config = await client.database.guilds.read(log.hidden.guild);
         if (!config.logging.logs.enabled) return;
-        if (!(log.meta.calculateType === true)) {
-            if (!toHexArray(config.logging.logs.toLog).includes(log.meta.calculateType))
-                console.log("Not logging this type of event");
+        if (!toHexArray(config.logging.logs.toLog).includes(log.meta.calculateType)) {
+            console.log("Not logging this type of event");
             return;
         }
         if (config.logging.logs.channel) {
diff --git a/src/utils/logTranscripts.ts b/src/utils/logTranscripts.ts
new file mode 100644
index 0000000..d5ab0b2
--- /dev/null
+++ b/src/utils/logTranscripts.ts
@@ -0,0 +1,3 @@
+function JSONTranscriptFromMessageArray(messages: Discord.Message[]) {
+    
+}
\ No newline at end of file
diff --git a/src/utils/performanceTesting/record.ts b/src/utils/performanceTesting/record.ts
new file mode 100644
index 0000000..2d9524b
--- /dev/null
+++ b/src/utils/performanceTesting/record.ts
@@ -0,0 +1,47 @@
+import client from "../client.js";
+import { resourceUsage } from "process";
+import { spawn } from "child_process";
+import config from "../../config/main.json" assert { type: "json" };
+
+
+const discordPing = () => {
+    return client.ws.ping;
+}
+
+const databaseReadTime = async () => {
+    const guild = await client.guilds.fetch(config.managementGuildID);
+    const user = guild.ownerId;
+    const currentYear = new Date().getFullYear();
+    const start = Date.now();
+    client.database.history.read(guild.id, user, currentYear - 1);
+    const end = Date.now();
+    return end - start;
+}
+
+const resources = () => {
+    const current = resourceUsage();
+    const temperatureRaw = spawn("acpi", ["-t"])
+    let temperatureData: number = 0;
+    temperatureRaw.stdout.on("data", (data) => {
+        return temperatureData = data.toString().split(", ")[1].split(" ")[0];  // °C
+    })
+    return {
+        memory: current.sharedMemorySize,
+        cpu: current.userCPUTime + current.systemCPUTime,
+        temperature: temperatureData
+    }
+}
+
+const record = async () => {
+    const results = {
+        discord: discordPing(),
+        databaseRead: await databaseReadTime(),
+        resources: resources()
+    }
+    client.database.performanceTest.record(results)
+    setInterval(async () => {
+        record();
+    }, 10 * 1000);
+}
+
+export { record };
\ No newline at end of file