diff --git a/src/events/interactionCreate/handlers/chatInputCommand/index.ts b/src/events/interactionCreate/handlers/chatInputCommand/index.ts index 78ca532..0f24744 100644 --- a/src/events/interactionCreate/handlers/chatInputCommand/index.ts +++ b/src/events/interactionCreate/handlers/chatInputCommand/index.ts @@ -16,7 +16,7 @@ export default async (interaction: ChatInputCommandInteraction) => { const { client, commandName } = interaction; const currentCommand = client.commands.get(commandName); - if (!currentCommand) throw new Error(`Unknown command ${commandName}`); + if (!currentCommand) throw new Error("Command unavailable"); await currentCommand.execute(interaction).catch((error: Error) => { const buttons = new ActionRowBuilder().addComponents( diff --git a/src/events/ready/index.ts b/src/events/ready/index.ts index 72cc060..2c68a73 100644 --- a/src/events/ready/index.ts +++ b/src/events/ready/index.ts @@ -12,7 +12,11 @@ export const options: IEventOptions = { // Execute the event export const execute = async (client: Client) => { - logger.info("Discord's API client is ready!"); + if (!client.user) throw new Error("Client user unavailable"); + + logger.info({ + message: `Connected to Discord!`, + }); updatePresence(client); await deployCommands(client); diff --git a/src/handlers/command/index.ts b/src/handlers/command/index.ts index 3d9c083..88a5311 100644 --- a/src/handlers/command/index.ts +++ b/src/handlers/command/index.ts @@ -6,32 +6,30 @@ import logger from "../../middlewares/logger"; // Register the commands. export const register = async (client: Client) => { - logger.info("🔧 Started command management"); + await checkDirectory("commands").then(async (commandNames) => { + for await (const commandName of commandNames) { + const commandProfiler = logger.startTimer(); - const commandNames = await checkDirectory("commands"); - if (!commandNames) return logger.warn("No available commands found"); + await import(`../../commands/${commandName}`) + .then((command: ICommand) => { + client.commands.set(command.builder.name, command); - const totalCommands = commandNames.length; - let loadedCommands = 0; + commandProfiler.done({ + commandName, + message: `Registered command '${commandName}'`, + level: "debug", + }); - logger.info(`🔧 Loading ${totalCommands} commands`); - - // Import an command. - const importCommand = async (name: string) => { - const command: ICommand = await import(`../../commands/${name}`); - - client.commands.set(command.builder.name, command); - return loadedCommands++; - }; - - for await (const commandName of commandNames) { - await importCommand(commandName).then(() => { - return logger.verbose(`🔧 Loaded command "${commandName}"`); - }); - - if (loadedCommands === totalCommands) { - return logger.info("🔧 All commands loaded"); + return command; + }) + .catch((error) => { + commandProfiler.done({ + message: `Failed to register command '${commandName}'`, + commandName, + error, + level: "error", + }); + }); } - } - return true; + }); }; diff --git a/src/handlers/database/index.ts b/src/handlers/database/index.ts index dff0994..614ca5e 100644 --- a/src/handlers/database/index.ts +++ b/src/handlers/database/index.ts @@ -14,6 +14,12 @@ prisma.$use(async (params, next) => { `Query ${params.model}.${params.action} took ${after - before}ms` ); + if (after - before >= 50) { + logger.warn( + `Query ${params.model}.${params.action} took ${after - before}ms` + ); + } + return result; }); diff --git a/src/handlers/deferReply/index.ts b/src/handlers/deferReply/index.ts index 1029879..92f1fb2 100644 --- a/src/handlers/deferReply/index.ts +++ b/src/handlers/deferReply/index.ts @@ -1,5 +1,5 @@ import { BaseInteraction, EmbedBuilder } from "discord.js"; -import getEmbedConfig from "../../helpers/getEmbedData"; +import getEmbedData from "../../helpers/getEmbedData"; export default async (interaction: BaseInteraction, ephemeral: boolean) => { if (!interaction.isRepliable()) @@ -9,7 +9,7 @@ export default async (interaction: BaseInteraction, ephemeral: boolean) => { ephemeral, }); - const embedConfig = await getEmbedConfig(interaction.guild); + const embedConfig = await getEmbedData(interaction.guild); await interaction.editReply({ embeds: [ diff --git a/src/handlers/deployCommands/index.ts b/src/handlers/deployCommands/index.ts index 94feecf..e8ebd58 100644 --- a/src/handlers/deployCommands/index.ts +++ b/src/handlers/deployCommands/index.ts @@ -1,39 +1,29 @@ import { Client, RESTPostAPIApplicationCommandsJSONBody } from "discord.js"; -import { ICommand } from "../../interfaces/Command"; import logger from "../../middlewares/logger"; export default async (client: Client) => { - // 1. Destructure the client. const { application } = client; if (!application) throw new Error("No application found"); - // 2. Log that we are starting the command management. - logger.info("🔧 Started command deployment"); + const builders: Array = []; - // 3. Get the commands. - const commands: Array = []; - client.commands.forEach((command: ICommand) => { - commands.push(command.builder.toJSON()); - - logger.verbose(`🔧 Loaded command "${command.builder.name}"`); + client.commands.forEach((command) => { + builders.push(command.builder.toJSON()); }); - // 4. Set the commands. - await application.commands.set(commands).then(() => { - logger.info("🔧 Deployed commands globally"); + await application.commands.set(builders).then(() => { + logger.info({ builders, message: "Registered commands to users!" }); }); - // 5. Tell the user that development mode is enabled. if (process.env.NODE_ENV === "development") { - logger.info("🔧 Development mode enabled"); - await application.commands - .set(commands, process.env.DISCORD_GUILD_ID) + .set(builders, process.env.DISCORD_GUILD_ID) .then(() => { - logger.info(`🔧 Deployed commands to guild`); + logger.info({ + builders, + devGuildId: process.env.DISCORD_GUILD_ID, + message: "Registered commands to development guild!", + }); }); } - - // 6. Log that we are done with the command management. - logger.info("🔧 Finished command deployment"); }; diff --git a/src/handlers/event/index.ts b/src/handlers/event/index.ts index 2048cd4..49d42e7 100644 --- a/src/handlers/event/index.ts +++ b/src/handlers/event/index.ts @@ -6,49 +6,51 @@ import logger from "../../middlewares/logger"; // Registers all available events. export const register = async (client: Client) => { - logger.info("📡 Started event management"); + const profiler = logger.startTimer(); - const eventNames = await checkDirectory("events"); - if (!eventNames) return logger.warn("No available events found"); + await checkDirectory("events").then(async (eventNames) => { + const totalEvents = eventNames.length; + let loadedEvents = 0; - const totalEvents = eventNames.length; - let loadedEvents = 0; + // Import an event. + const importEvent = async (name: string) => { + await import(`../../events/${name}`).then((event: IEvent) => { + // Create a new event execute function. + const eventExecutor = async (...args: Promise[]) => { + await event.execute(...args); + }; - logger.info(`📡 Loading ${totalEvents} events`); + switch (event.options.type) { + case "once": + client.once(name, eventExecutor); + break; - // Import an event. - const importEvent = async (name: string) => { - const event: IEvent = await import(`../../events/${name}`); + case "on": + client.on(name, eventExecutor); + break; + default: + throw new Error(`Unknown event type`); + } - // Create a new event execute function. - const eventExecutor = async (...args: Promise[]) => { - await event.execute(...args); + logger.debug({ + eventName: name, + type: event.options.type, + message: `Listening to event '${name}'`, + }); + return loadedEvents++; + }); }; - switch (event.options.type) { - case "once": - client.once(name, eventExecutor); - break; + for await (const eventName of eventNames) { + await importEvent(eventName); - case "on": - client.on(name, eventExecutor); - break; - default: - throw new Error(`📡 Invalid event type for event: ${name}`); + if (loadedEvents === totalEvents) { + return profiler.done({ + message: "Successfully listening to all events!", + }); + } } - return loadedEvents++; - }; - - for await (const eventName of eventNames) { - await importEvent(eventName).then(() => { - return logger.verbose(`📡 Loaded event "${eventName}"`); - }); - - if (loadedEvents === totalEvents) { - return logger.info("📡 All events loaded"); - } - } - - return true; + return true; + }); }; diff --git a/src/handlers/schedule/index.ts b/src/handlers/schedule/index.ts index de32418..acc6ddc 100644 --- a/src/handlers/schedule/index.ts +++ b/src/handlers/schedule/index.ts @@ -6,19 +6,25 @@ import logger from "../../middlewares/logger"; // Start all jobs that are in the schedules directory export const start = async (client: Client) => { - logger.info("⏰ Started job management"); - const jobNames = await checkDirectory("schedules"); if (!jobNames) return logger.warn("⏰ No available jobs found"); return await Promise.all( jobNames.map(async (jobName) => { - const job: IJob = await import(`../../schedules/${jobName}`); - - return schedule.scheduleJob(job.options.schedule, async () => { - logger.verbose(`⏰ Performed the job "${jobName}"`); - await job.execute(client); - }); + await import(`../../schedules/${jobName}`) + .then((job: IJob) => { + return schedule.scheduleJob(job.options.schedule, async () => { + logger.verbose(`⏰ Performed the job "${jobName}"`); + await job.execute(client); + }); + }) + .catch((error) => { + logger.warn({ + jobName, + message: `Failed to start job ${jobName}`, + error, + }); + }); }) ); }; diff --git a/src/handlers/updatePresence/index.ts b/src/handlers/updatePresence/index.ts index 9fdb486..ba26751 100644 --- a/src/handlers/updatePresence/index.ts +++ b/src/handlers/updatePresence/index.ts @@ -1,5 +1,5 @@ // Dependencies -import { ActivityType, Client } from "discord.js"; +import { ActivitiesOptions, ActivityType, Client } from "discord.js"; import logger from "../../middlewares/logger"; // Function @@ -12,18 +12,27 @@ export default (client: Client) => { const memberCount = guilds.cache.reduce((a, g) => a + g.memberCount, 0); const guildCount = guilds.cache.size; + const activities: ActivitiesOptions[] = [ + { + name: `${guildCount} guilds`, + type: ActivityType.Watching, + }, + { + name: `${memberCount} members`, + type: ActivityType.Watching, + }, + ]; + + const activity = activities[Math.floor(Math.random() * activities.length)]; + // 3. Set the presence. - user.setPresence({ - activities: [ - { - name: `${guildCount} guilds | ${memberCount} members`, - type: ActivityType.Watching, - }, - ], - }); + user.setActivity(activity); // 4. Log the presence. - return logger.info( - `👀 Presence set to "${guildCount} guilds | ${memberCount} members"` - ); + return logger.debug({ + guildCount, + memberCount, + message: `Presence updated`, + activity, + }); }; diff --git a/src/helpers/checkDirectory/index.ts b/src/helpers/checkDirectory/index.ts index 5160e63..6454b9a 100644 --- a/src/helpers/checkDirectory/index.ts +++ b/src/helpers/checkDirectory/index.ts @@ -1,7 +1,16 @@ import fs from "fs"; +import logger from "../../middlewares/logger"; const fsPromises = fs.promises; export default async (path: string) => { - const result = await fsPromises.readdir(`${__dirname}/../../${path}`); - return result; + const directoryPath = `${process.cwd()}/dist/${path}`; + + return await fsPromises.readdir(directoryPath).then((result) => { + logger.debug({ + message: `Checked directory ${path}`, + directoryPath, + result, + }); + return result; + }); }; diff --git a/src/middlewares/logger/index.ts b/src/middlewares/logger/index.ts index a91f01d..17baeee 100644 --- a/src/middlewares/logger/index.ts +++ b/src/middlewares/logger/index.ts @@ -1,17 +1,17 @@ import winston from "winston"; import "winston-daily-rotate-file"; -const { combine, timestamp, printf, errors, colorize, align, json } = +const { combine, timestamp, json, errors, colorize, align, printf } = winston.format; - -export default winston.createLogger({ +const logger = winston.createLogger({ level: process.env.LOG_LEVEL || "info", + format: combine(errors({ stack: true }), timestamp(), json()), transports: [ new winston.transports.DailyRotateFile({ filename: "logs/combined-%DATE%.log", datePattern: "YYYY-MM-DD", maxFiles: "14d", - format: combine(timestamp(), json()), + format: combine(errors({ stack: true }), timestamp(), json()), }), new winston.transports.Console({ format: combine( @@ -21,8 +21,13 @@ export default winston.createLogger({ format: "YYYY-MM-DD HH:MM:ss", }), align(), - printf((info) => `[${info.timestamp}] ${info.level}: ${info.message}`) + printf( + (info) => + `[${info.timestamp}] ${info.level}: ${info.stack || info.message}` + ) ), }), ], }); + +export default logger;