require("dotenv").config(); const MariaDB = require('mariadb'); const fs = require('fs'); const express = require('express'); const path = require('path'); const log = require("./log") const app = express(); global.log = log; const Discord = require("discord.js") const client = new Discord.Client({ intents: ["Guilds", "DirectMessages", "MessageContent", "GuildMembers"] }) const pool = MariaDB.createPool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASS, database: process.env.DB_NAME, connectionLimit: 50 // Overkill but why not lol }); global.db_pool = pool; // For access in other files // Middleware to parse incoming JSON requests app.use(express.json()); // Middleware to log requests app.use((req, res, next) => { if (process.env.NODE_ENV === 'production') return next(); log.info('--- Incoming Request ---'); log.info(`Method: ${req.method}`); log.info(`URL: ${req.url}`); log.info('Headers:', req.headers); if (Object.keys(req.body).length > 0) { log.info('Body:', req.body); } log.info('------------------------'); next(); }); app.get("/health", async (req, res) => { const status = { webServer: 'OK', discordClient: 'OK', mariaDB: 'OK', errors: [] }; if (!client.isReady()) { status.discordClient = 'FAIL'; status.errors.push('Discord client is not ready.'); } try { const connection = await pool.getConnection(); connection.release(); } catch (error) { status.mariaDB = 'FAIL'; status.errors.push('MariaDB connection failed.'); } if (status.errors.length > 0) { status.status = 'FAIL'; } else { status.status = 'OK'; } res.json(status); }); global.discord_client = client client.on("ready", async () => { log.info(`Logged into Discord as ${client.user.displayName}`); const commands = require("./commands") // Command registration log.info("Registering commands...") await (async () => { try { const rest = new Discord.REST().setToken(client.token); //Global //await rest.put(Discord.Routes.applicationGuildCommands(client.user.id, process.env.ADMIN_GUILD), { body: [] }) log.info(`Registering global commands`); rest.put(Discord.Routes.applicationCommands(client.user.id), { body: commands.global }).then(() => { log.info("Global commands registered") }).catch((error) => { log.error(error) }); //Admin // rest.put(Discord.Routes.applicationGuildCommands(client.user.id, process.env.ADMIN_GUILD), { body: commands.admin }).then(() => { // log.info("Admin commands registered") // }).catch((error) => { // log.error(error) // }); } catch (error) { log.error(error) } })(); app.listen(port, () => { log.info(`Listening on ${port}`) }) }); client.on("interactionCreate", async (interaction) => { if (!interaction.isCommand()) return; const command = require(`./commands/${interaction.commandName}`); if (!command) return; try { await command.execute(interaction); } catch (error) { log.error(error.stack); await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true }); } }); global.dmHandlers = {} client.on("messageCreate", async (message) => { if (message.author.bot) return; if (!message.channel.isDMBased()) return; if (!global.dmHandlers[message.author.id]) { return; } global.dmHandlers[message.author.id](message); }); // Global error handling for unhandled promise rejections and uncaught exceptions process.on('unhandledRejection', (reason, promise) => { log.error(`Unhandled Rejection: ${reason}`); // Application specific logging, throwing an error, or other logic here }); process.on('uncaughtException', (error) => { log.error('Uncaught Exception thrown:', error); // Application specific logging, throwing an error, or other logic here }); const port = process.env.SERVER_PORT || 3000; require("./migrations")(pool) .finally(() => { // Load all route modules from the 'routes' folder const routesPath = path.join(__dirname, 'routes'); fs.readdirSync(routesPath).forEach((file) => { const route = require(path.join(routesPath, file)); const routeName = `/${file.replace('.js', '')}`; // Use filename as route base app.use(routeName, route); log.info(`Using ${routeName}`) }); client.login(process.env.DISCORD_TOKEN); });