A powerful and extendable Discord bot, with it's own module system :3 thevoid.cafe/projects/voidy

โœจ๐Ÿ“ Update Loaders, Registry Handler draft

+56 -10
+5 -3
docs/architecture/top-level.md
··· 23 23 The command loader walks a directory and stores data from any file exporting an object that follows the Command type structure. 24 24 25 25 ## Handlers 26 - Handlers are static classes, which receive exported data from Loaders, though not directly, as Loader data is usually fetched by a Registry, and the Registry invokes a Handler to get data into our queue system, more on that later. 26 + Handlers are static classes, which are invoked, usually on discord events or interactions. 27 27 28 - Each handler has an `invoke` method, which takes JSON data, though it must always use the common exported JSON structure provided by Loaders. 28 + Each handler has an `invoke` method, which is the one I mentioned calling above. 29 29 30 - Any data filtering or mapping is then run in the background by the invoked Handler, which ultimately pushes data to the queue, and triggers a "handler::postInvoke" event afterward. 30 + The handler then queries the core registry and all other registries afterward, to find a fitting execution target. 31 + 32 + It's important to note that only repositories marked as active are taken into account. 31 33 32 34 @Todo: document Handler lifecycle events 33 35
src/commands/ping.ts src/modules/booru/commands/ping.ts
+9
src/core/Handler.ts
··· 1 + interface IHandler<T extends object> { 2 + invoke: (data: T) => void 3 + } 4 + 5 + export class Handler<T extends object> implements IHandler<T> { 6 + public invoke(data: T) { 7 + console.log(data); 8 + } 9 + }
+1 -1
src/core/Loader.ts
··· 23 23 * Recursively collects data from a directory based on the path specificed in dataSource property. 24 24 */ 25 25 public async collect() { 26 - const glob = new Glob(`**.ts`); 26 + const glob = new Glob(`**/**.ts`); 27 27 const iterator = glob.scan(this.dataSource); 28 28 29 29 for await (const path of iterator) {
+12 -2
src/core/Registry.ts
··· 1 - import type { Module } from "../loaders/ModuleLoader" 1 + import { ModuleLoader, type Module } from "../loaders/ModuleLoader" 2 2 3 3 export interface IRegistry { 4 + dataSource: string 4 5 store: Module[] 5 6 6 7 collect: () => Promise<void> ··· 10 11 } 11 12 12 13 export class Registry implements IRegistry { 14 + public dataSource: string; 13 15 public store: Module[] = []; 14 16 15 - public async collect() { } 17 + public constructor(dataSource: string) { 18 + this.dataSource = dataSource; 19 + } 20 + 21 + // @Todo: finish this implementation 22 + public async collect() { 23 + const moduleLoader = await (new ModuleLoader(this.dataSource)).collect(); 24 + console.log(moduleLoader.getJSON()[0]?.exports); 25 + } 16 26 17 27 public async prepare() { } 18 28
+3 -1
src/core/VoidyClient.ts
··· 7 7 public constructor(options: ClientOptions) { 8 8 super(options); 9 9 10 - this.registries = [new Registry()]; 10 + this.registries = [ 11 + new Registry(`${process.cwd()}/src/modules`), 12 + ]; 11 13 } 12 14 13 15 public start(token: string) {
src/events/ready.ts src/modules/booru/events/ready.ts
+7 -2
src/index.ts
··· 13 13 client.start(Bun.env.BOT_TOKEN); 14 14 15 15 // @Todo: Remove after event and command loader implementation is complete 16 - const eventLoader = await new EventLoader(join(__dirname, "events")).collect(); 17 - console.log(eventLoader); 16 + // const eventLoader = await new EventLoader(join(__dirname, "events")).collect(); 17 + // console.log(eventLoader); 18 + 19 + // @Todo: Remove after Registries implementation is complete 20 + for (const registry of client.registries) { 21 + await registry.collect(); 22 + }
+3 -1
src/loaders/ModuleLoader.ts
··· 1 + import type { Handler } from "../core/Handler" 1 2 import { Loader } from "../core/Loader" 2 3 3 4 export interface ModuleExportsItem<T extends object> { 4 5 source: string 5 - loader: Loader<T> 6 + loader: typeof Loader<T> 7 + handler: Handler<T> 6 8 } 7 9 8 10 export interface Module {
+16
src/modules/booru/module.ts
··· 1 + import { EventLoader } from "../../loaders/EventLoader"; 2 + import type { Module } from "../../loaders/ModuleLoader"; 3 + 4 + export default { 5 + name: "booru-fetcher", 6 + description: "Fetch images and other content from tag-based imageboards, like danbooru.", 7 + author: "jokiller230", 8 + 9 + exports: [ 10 + { 11 + source: `${import.meta.dir}/events`, 12 + loader: EventLoader, 13 + handler: {} 14 + } 15 + ] 16 + } as Module;