···1+// Core
2+export * from "./core/Loader";
3+export * from "./core/ModuleManager";
4+export * from "./core/VoidyClient";
5+6+// Types
7+export * from "./core/types/Button";
8+export * from "./core/types/Command";
9+export * from "./core/types/Event";
10+export * from "./core/types/Module";
11+export * from "./core/types/Resource";
12+13+// Handlers
14+export * from "./handlers/ButtonHandler";
15+export * from "./handlers/CommandHandler";
16+17+// Loaders
18+export * from "./loaders/ButtonLoader";
19+export * from "./loaders/CommandLoader";
20+export * from "./loaders/EventLoader";
21+export * from "./loaders/ModuleLoader";
+16
packages/framework/src/loaders/ButtonLoader.ts
···0000000000000000
···1+//===============================================
2+// Imports
3+//===============================================
4+import type { Button } from "../core/types/Button";
5+import { Loader } from "../core/Loader";
6+7+//===============================================
8+// ButtonLoader Implementation
9+//===============================================
10+export class ButtonLoader extends Loader<Button> {
11+ public id = "button";
12+ public async validate(data: Partial<Button>) {
13+ if (!data.id || !data.execute) return null;
14+ return data as Button;
15+ }
16+}
+16
packages/framework/src/loaders/CommandLoader.ts
···0000000000000000
···1+//===============================================
2+// Imports
3+//===============================================
4+import type { Command } from "../core/types/Command";
5+import { Loader } from "../core/Loader";
6+7+//===============================================
8+// CommandLoader Implementation
9+//===============================================
10+export class CommandLoader extends Loader<Command> {
11+ public id = "command";
12+ public async validate(data: Partial<Command>) {
13+ if (!data.id || !data.data || !data.execute) return null;
14+ return data as Command;
15+ }
16+}
+16
packages/framework/src/loaders/EventLoader.ts
···0000000000000000
···1+//===============================================
2+// Imports
3+//===============================================
4+import type { Event } from "../core/types/Event";
5+import { Loader } from "../core/Loader";
6+7+//===============================================
8+// EventLoader Implemenation
9+//===============================================
10+export class EventLoader extends Loader<Event> {
11+ public id = "event";
12+ public async validate(data: Partial<Event>) {
13+ if (!data.id || !data.name || !data.execute) return null;
14+ return data as Event;
15+ }
16+}
+23
packages/framework/src/loaders/ModuleLoader.ts
···00000000000000000000000
···1+//===============================================
2+// Imports
3+//===============================================
4+import type { Module } from "../core/types/Module";
5+import { Loader } from "../core/Loader"
6+7+//===============================================
8+// ModuleLoader Implementation
9+//===============================================
10+export class ModuleLoader extends Loader<Module> {
11+ public id = "module";
12+ public async validate(data: Partial<Module>) {
13+ if (
14+ !data.id ||
15+ !data.name ||
16+ !data.description ||
17+ !data.author ||
18+ !data.exports
19+ ) return null;
20+21+ return data as Module;
22+ }
23+}
···0001import { Glob } from "bun";
23-interface ILoader<T> {
0004 id: string
5 cache: T[]
6 source: string
···10 getJSON: () => T[]
11}
1213-export class Loader<T extends object> implements ILoader<T> {
14- public id = "loader";
00015 public cache: T[] = [];
16- public source;
1718 public constructor(source: string) {
19 if (!source) throw new Error("Class of type Loader was initialized without the *required* source parameter.");
···57 /**
58 * Validates a singular element during data collection, and returns whatever should be written to the cache.
59 */
60- public async validate(data: Partial<T>): Promise<T | null> {
61- return null;
62- }
6364 /**
65 * Returns the JSON-ified contents of the loader cache
···1+//===============================================
2+// Imports
3+//===============================================
4import { Glob } from "bun";
56+//===============================================
7+// Loader Definition
8+//===============================================
9+interface ILoader<T extends object> {
10 id: string
11 cache: T[]
12 source: string
···16 getJSON: () => T[]
17}
1819+//===============================================
20+// Loader Implementation
21+//===============================================
22+export abstract class Loader<T extends object> implements ILoader<T> {
23+ public abstract id: string;
24 public cache: T[] = [];
25+ public source: string;
2627 public constructor(source: string) {
28 if (!source) throw new Error("Class of type Loader was initialized without the *required* source parameter.");
···66 /**
67 * Validates a singular element during data collection, and returns whatever should be written to the cache.
68 */
69+ public abstract validate(data: Partial<T>): Promise<T | null>;
007071 /**
72 * Returns the JSON-ified contents of the loader cache
-83
src/core/Registry.ts
···1-import { ButtonLoader, type Button } from "../loaders/ButtonLoader"
2-import { CommandLoader, type Command } from "../loaders/CommandLoader"
3-import { EventLoader, type Event } from "../loaders/EventLoader"
4-import { ModuleLoader, type Module } from "../loaders/ModuleLoader"
5-6-export enum RegistryCacheKey {
7- Events = "events",
8- Modules = "modules",
9- Buttons = "buttons",
10- Commands = "commands",
11-}
12-13-export type RegistryCache = {
14- events: Event[],
15- modules: Module[],
16- buttons: Button[],
17- commands: Command[],
18- [x: string]: object[],
19-};
20-21-export interface IRegistry {
22- id: string
23- active: boolean
24- dataSource: string
25-26- cache: RegistryCache;
27-28- collectModules: () => Promise<void>
29- processModules: () => Promise<void>
30-31- activate: () => Promise<void>
32- deactivate: () => Promise<void>
33-}
34-35-export class Registry implements IRegistry {
36- public id: string;
37- public active = false;
38- public dataSource: string;
39-40- // Initialize cache stores
41- public cache: RegistryCache = {
42- events: [],
43- modules: [],
44- buttons: [],
45- commands: [],
46- }
47-48- public constructor(id: string, dataSource: string) {
49- this.id = id;
50- this.dataSource = dataSource;
51- }
52-53- /** Collect modules from specified dataSource directory */
54- public async collectModules() {
55- // Collect modules and bundle their JSON contents into an array.
56- const moduleLoader = new ModuleLoader(this.dataSource);
57- const modules = (await moduleLoader.collect()).getJSON();
58-59- // Merge all modules into the store.
60- this.cache.modules = modules;
61- }
62-63- /** Process exports of all collected modules */
64- public async processModules() {
65- for (const module of this.cache.modules) {
66- for (const item of module.exports) {
67- const loader = new item.loader(item.source);
68- await loader.collect();
69-70- // Mape loader output to correct cache key
71- this.cache[`${loader.id}s`] = [...loader.getJSON()];
72- }
73- }
74- }
75-76- public async activate() {
77- this.active = true;
78- }
79-80- public async deactivate() {
81- this.active = false;
82- }
83-}
···1import { MessageFlags, SlashCommandBuilder } from "discord.js";
2-import type { Command } from "../../../loaders/CommandLoader";
34export default {
05 data: new SlashCommandBuilder()
6 .setName("ping")
7 .setDescription("View the websocket ping between Discord and the Bot."),
···1import { MessageFlags, SlashCommandBuilder } from "discord.js";
2+import type { Command } from "voidy-framework";
34export default {
5+ id: "ping",
6 data: new SlashCommandBuilder()
7 .setName("ping")
8 .setDescription("View the websocket ping between Discord and the Bot."),
···1-import type { Event } from "../../../loaders/EventLoader";
2-import type { VoidyClient } from "../../../core/VoidyClient";
3-import { ButtonHandler } from "../../../handlers/ButtonHandler";
4import { Events, MessageFlags, type Interaction } from "discord.js";
5-import { ChatInputCommandHandler } from "../../../handlers/CommandHandler";
0000067export default {
08 name: Events.InteractionCreate,
9 execute: async (client: VoidyClient, interaction: Interaction) => {
10 if (interaction.isChatInputCommand() && interaction.isCommand()) {
11- // Filter the client command cache to locate the invoked command
12- const payload = client.registryManager.getCache().commands.filter(commands => commands.data.name === interaction.commandName)[0];
00000000001314- if (!payload) return interaction.reply({
15 content: `Sorry, but the command ${interaction.commandName} could not be located in my command cache >:3`,
16 flags: [MessageFlags.Ephemeral]
17 });
1819- ChatInputCommandHandler.invoke(interaction, payload, client);
20 } else if (interaction.isButton()) {
21 // Filter the client button cache to locate the invoked button
22- const payload = client.registryManager.getCache().buttons.filter(buttons => buttons.id === interaction.customId)[0];
2324- if (!payload) return interaction.reply({
25 content: `Sorry, but the button ${interaction.customId} could not be located in my button cache >:3`,
26 flags: [MessageFlags.Ephemeral]
27 });
2829- ButtonHandler.invoke(interaction, payload, client);
30 } else {
31 let dmChannel = interaction.user.dmChannel;
32
···0001import { Events, MessageFlags, type Interaction } from "discord.js";
2+import {
3+ ChatInputCommandHandler,
4+ ButtonHandler,
5+ type VoidyClient,
6+ type Event
7+} from "voidy-framework";
89export default {
10+ id: "interactionCreate",
11 name: Events.InteractionCreate,
12 execute: async (client: VoidyClient, interaction: Interaction) => {
13 if (interaction.isChatInputCommand() && interaction.isCommand()) {
14+ // Set the top-level command name
15+ let commandId = interaction.commandName;
16+17+ // Try to get a subgroup first
18+ const subgroup = interaction.options.getSubcommandGroup(false);
19+ if (subgroup) commandId += `.${subgroup}`;
20+21+ // Then subcommand (or subcommand in a group)
22+ const subcommand = interaction.options.getSubcommand(false);
23+ if (subcommand) commandId += `.${subcommand}`;
24+25+ const command = client.commands.get(commandId);
2627+ if (!command) return interaction.reply({
28 content: `Sorry, but the command ${interaction.commandName} could not be located in my command cache >:3`,
29 flags: [MessageFlags.Ephemeral]
30 });
3132+ ChatInputCommandHandler.invoke(interaction, command, client);
33 } else if (interaction.isButton()) {
34 // Filter the client button cache to locate the invoked button
35+ const button = client.buttons.get(interaction.customId);
3637+ if (!button) return interaction.reply({
38 content: `Sorry, but the button ${interaction.customId} could not be located in my button cache >:3`,
39 flags: [MessageFlags.Ephemeral]
40 });
4142+ ButtonHandler.invoke(interaction, button, client);
43 } else {
44 let dmChannel = interaction.user.dmChannel;
45