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