/[sudobot]/branches/7.x/src/core/DynamicLoader.ts
ViewVC logotype

Annotation of /branches/7.x/src/core/DynamicLoader.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (hide annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 6770 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 rakinar2 577 import { Awaitable } from "discord.js";
2     import { Router } from "express";
3     import { lstat, readdir } from "node:fs/promises";
4     import path, { basename, dirname } from "node:path";
5     import Controller from "../api/Controller";
6     import { EventListenerInfo } from "../decorators/GatewayEventListener";
7     import { ClientEvents } from "../types/ClientEvents";
8     import { Class, DefaultExport } from "../types/Utils";
9     import { log, logInfo } from "../utils/logger";
10     import type Client from "./Client";
11     import Command from "./Command";
12     import EventListener from "./EventListener";
13     import Service from "./Service";
14    
15     class DynamicLoader extends Service {
16     protected readonly eventHandlers = new WeakMap<object, Record<keyof ClientEvents, Function[]>>();
17    
18     private async iterateDirectoryRecursively(root: string, rootArray?: string[]) {
19     const filesAndDirectories = await readdir(root);
20     const files: string[] = [];
21    
22     for (const file of filesAndDirectories) {
23     const filepath = path.resolve(root, file);
24     const stat = await lstat(filepath);
25    
26     if (stat.isDirectory()) {
27     await this.iterateDirectoryRecursively(filepath, rootArray ?? files);
28     continue;
29     }
30    
31     (rootArray ?? files).push(filepath);
32     }
33    
34     return files;
35     }
36    
37     async loadControllers(router: Router) {
38     const eventListenerFiles = await this.iterateDirectoryRecursively(path.resolve(__dirname, "../api/controllers"));
39    
40     for (const file of eventListenerFiles) {
41     if ((!file.endsWith(".ts") && !file.endsWith(".js")) || file.endsWith(".d.ts")) {
42     continue;
43     }
44    
45     await this.loadController(file, router);
46     }
47     }
48    
49     async loadController(filepath: string, router: Router) {
50     const { default: ControllerClass }: DefaultExport<Class<Controller, [Client]>> = await import(filepath);
51     const controller = new ControllerClass(this.client);
52     this.client.server.loadController(controller, ControllerClass, router);
53     logInfo("Loaded Controller: ", ControllerClass.name);
54     }
55    
56     async loadEvents() {
57     const eventListenerFiles = await this.iterateDirectoryRecursively(path.resolve(__dirname, "../events"));
58    
59     for (const file of eventListenerFiles) {
60     if ((!file.endsWith(".ts") && !file.endsWith(".js")) || file.endsWith(".d.ts")) {
61     continue;
62     }
63    
64     await this.loadEvent(file);
65     }
66     }
67    
68     async loadEvent(filepath: string) {
69     const { default: EventListenerClass }: DefaultExport<Class<EventListener, [Client]>> = await import(filepath);
70     const listener = new EventListenerClass(this.client);
71     this.client.addEventListener(listener.name, listener.execute.bind(listener));
72     logInfo("Loaded Event: ", listener.name);
73     }
74    
75     async loadServiceFromDirectory(servicesDirectory = path.resolve(__dirname, "../services")) {
76     const commandFiles = await this.iterateDirectoryRecursively(servicesDirectory);
77    
78     for (const file of commandFiles) {
79     if ((!file.endsWith(".ts") && !file.endsWith(".js")) || file.endsWith(".d.ts")) {
80     continue;
81     }
82    
83     await this.client.serviceManager.loadService(file);
84     }
85     }
86    
87     async loadCommands(
88     commandsDirectory = path.resolve(__dirname, "../commands"),
89     loadMetadata: boolean = true,
90     filter?: (path: string, name: string) => Awaitable<boolean>
91     ) {
92     const commandFiles = await this.iterateDirectoryRecursively(commandsDirectory);
93    
94     for (const file of commandFiles) {
95     if ((!file.endsWith(".ts") && !file.endsWith(".js")) || file.endsWith(".d.ts")) {
96     continue;
97     }
98    
99     if (filter && !(await filter(file, path.basename(file)))) {
100     continue;
101     }
102    
103     await this.loadCommand(file, loadMetadata);
104     }
105     }
106    
107     async loadCommand(filepath: string, loadMetadata = true) {
108     const { default: CommandClass }: DefaultExport<Class<Command, [Client]>> = await import(filepath);
109     const command = new CommandClass(this.client);
110     const previousCommand = this.client.commands.get(command.name);
111    
112     if (loadMetadata && previousCommand) {
113     await this.unloadEventsFromMetadata(previousCommand);
114     }
115    
116     command.group = basename(dirname(filepath));
117     this.client.commands.set(command.name, command);
118    
119     for (const alias of command.aliases) {
120     this.client.commands.set(alias, command);
121     }
122    
123     if (loadMetadata) {
124     await this.loadEventsFromMetadata(command);
125     }
126    
127     logInfo("Loaded Command: ", command.name);
128     }
129    
130     async loadEventsFromMetadata(object: object, accessConstructor = true) {
131     const finalObject = accessConstructor ? object.constructor : object;
132     const metadata =
133     Symbol.metadata in finalObject
134     ? (finalObject[Symbol.metadata] as { eventListeners?: EventListenerInfo[] })
135     : {
136     eventListeners: Reflect.getMetadata("event_listeners", (finalObject as any).prototype)
137     };
138    
139     const handlerData = this.eventHandlers.get(object) ?? ({} as Record<keyof ClientEvents, Function[]>);
140    
141     for (const listenerInfo of metadata.eventListeners ?? []) {
142     const callback = object[listenerInfo.methodName as unknown as keyof typeof object] as Function;
143     const handler = callback.bind(object);
144     handlerData[listenerInfo.event as keyof typeof handlerData] ??= [] as Function[];
145     handlerData[listenerInfo.event as keyof typeof handlerData].push(handler);
146    
147     this.client.addEventListener(listenerInfo.event as keyof ClientEvents, handler);
148     }
149    
150     this.eventHandlers.set(object, handlerData);
151    
152     if (metadata.eventListeners) {
153     log(`Registered ${metadata.eventListeners?.length ?? 0} event listeners`);
154     }
155     }
156    
157     async unloadEventsFromMetadata(object: object) {
158     const handlerData = this.eventHandlers.get(object) ?? ({} as Record<keyof ClientEvents, Function[]>);
159     let count = 0;
160    
161     for (const event in handlerData) {
162     for (const callback of handlerData[event as keyof typeof handlerData]) {
163     this.client.removeEventListener(
164     event as keyof ClientEvents,
165     callback as (...args: ClientEvents[keyof ClientEvents]) => any
166     );
167     }
168    
169     count += handlerData[event as keyof typeof handlerData].length;
170     }
171    
172     log(`Unloaded ${count} event listeners`);
173     }
174     }
175    
176     export default DynamicLoader;

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26