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.