import { REST, Routes, APIApplicationCommand } from "discord.js"; import fs from "node:fs"; import path from "node:path"; import logger from "./src/utils/logger"; // Use default import now import dotenv from "dotenv"; // --- Setup --- dotenv.config(); // Load .env variables // Log presence of required env vars (optional, but helpful for debugging) // logger.info(`CLIENT_ID: ${process.env.CLIENT_ID ? 'Present' : 'MISSING!'}`); // logger.info(`DISCORD_TOKEN: ${process.env.DISCORD_TOKEN ? 'Present' : 'MISSING!'}`); // --- Configuration --- const clientId = process.env.CLIENT_ID; const token = process.env.DISCORD_TOKEN; if (!clientId || !token) { logger.error("Missing CLIENT_ID or DISCORD_TOKEN in .env file for command deployment!"); process.exit(1); } const commands: Omit[] = []; // Type the commands array more accurately // Grab all the command files from the commands directory const commandsPath = path.join(__dirname, "src", "commands"); // Read .ts files now const commandFiles = fs.readdirSync(commandsPath).filter((file: string) => file.endsWith(".ts")); // Add string type // Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment logger.info(`Started loading ${commandFiles.length} application (/) commands for deployment.`); const loadCommandsForDeployment = async () => { for (const file of commandFiles) { const filePath = path.join(commandsPath, file); try { // Use dynamic import const commandModule = await import(filePath); // Assuming commands export default or have a 'default' property const command = commandModule.default || commandModule; if ( command && typeof command === "object" && "data" in command && typeof command.data.toJSON === "function" ) { // We push the JSON representation which matches the API structure commands.push(command.data.toJSON()); logger.info(`Loaded command for deployment: ${command.data.name}`); } else { logger.warn( `[WARNING] The command at ${filePath} is missing a required "data" property with a "toJSON" method.`, ); } } catch (error: unknown) { // Type error as unknown const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Error loading command at ${filePath} for deployment: ${errorMessage}`, error); } } }; // Construct and prepare an instance of the REST module const rest = new REST({ version: "10" }).setToken(token); // Define the deployment function const deployCommands = async () => { try { await loadCommandsForDeployment(); // Wait for commands to be loaded if (commands.length === 0) { logger.warn("No commands loaded for deployment. Exiting."); return; } logger.info(`Started refreshing ${commands.length} application (/) commands.`); // The put method is used to fully refresh all commands const guildId = process.env.GUILD_ID; let data: any; // Type appropriately if possible, depends on discord.js version if (guildId) { // Deploying to a specific guild (faster for testing) logger.info(`Deploying commands to guild: ${guildId}`); data = await rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands }); logger.info( `Successfully reloaded ${data.length} application (/) commands in guild ${guildId}.`, ); } else { // Deploying globally (can take up to an hour) logger.info("Deploying commands globally..."); data = await rest.put(Routes.applicationCommands(clientId), { body: commands }); logger.info(`Successfully reloaded ${data.length} global application (/) commands.`); } } catch (error: unknown) { // Type error as unknown const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Failed during command deployment: ${errorMessage}`, error); } }; // Execute the deployment deployCommands(); // Note: The old wipe logic is removed as PUT overwrites existing commands. // If you specifically need to wipe commands first for some reason, // you can add separate PUT requests with an empty body before deploying.