Handler.ts iterated readdirSync(COMMANDS_PATH) without filtering, so any .d.ts declaration file, .js.map source map, or non-class module that landed in commands/ would crash startup with 'mod.default is not a constructor'. Restrict loading to .ts/.js (excluding .d.ts and maps) and skip modules without a default-exported constructor.
33 lines
1.3 KiB
TypeScript
33 lines
1.3 KiB
TypeScript
import { ChatInputCommandInteraction, Collection } from "discord.js";
|
|
import { readdirSync } from "node:fs";
|
|
import { Command } from "../types/Command";
|
|
import { COMMAND_PATH, COMMANDS_PATH } from "../utils/Config";
|
|
|
|
export class Handler {
|
|
public commands: Collection<string, Command> = new Collection();
|
|
public coolDown: Map<string, number> = new Map();
|
|
|
|
public constructor() {
|
|
const commandFiles = readdirSync(COMMANDS_PATH).filter((f) => {
|
|
// ts-node로 dev 실행 시 .ts, 빌드 후엔 .js. 둘 다 허용하고
|
|
// 선언 파일(.d.ts)/소스맵(.js.map)은 제외한다.
|
|
if (f.endsWith(".d.ts")) return false;
|
|
if (f.endsWith(".js.map") || f.endsWith(".ts.map")) return false;
|
|
return f.endsWith(".ts") || f.endsWith(".js");
|
|
});
|
|
for (const commandFile of commandFiles) {
|
|
const mod = require(COMMAND_PATH(commandFile));
|
|
if (typeof mod.default !== "function") continue; // not a command module
|
|
const command = new mod.default() as Command;
|
|
this.commands.set(command.metaData.name, command);
|
|
}
|
|
}
|
|
|
|
public runCommand(interaction: ChatInputCommandInteraction) {
|
|
const commandName = interaction.commandName;
|
|
const command = this.commands.get(commandName);
|
|
|
|
if (!command) return;
|
|
if (command.slashRun) command.slashRun(interaction);
|
|
}
|
|
} |