diff --git a/package.json b/package.json index 5dac49fb..d92cf105 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "heptagram", - "version": "5.0.1", + "version": "5.1.0", "description": "The open-source multipurpose discord bot with the goal to be the single needed bot for any server.", "main": "./prod/src/main.js", "scripts": { @@ -21,11 +21,11 @@ "homepage": "https://github.com/Heptagram-Bot/Heptagram#readme", "dependencies": { "@pm2/io": "^5.0.0", - "@sapphire/time-utilities": "^1.7.4", "@types/chai": "^4.3.1", "@types/mocha": "^9.1.1", "axios": "^0.27.2", "chai": "^4.3.6", + "chalk": "^5.0.1", "cors": "^2.8.5", "dayjs": "^1.11.3", "discord.js": "^14.0.2", diff --git a/src/events/clientEvents/ready.ts b/src/events/clientEvents/ready.ts index 0226efed..5056bcc9 100644 --- a/src/events/clientEvents/ready.ts +++ b/src/events/clientEvents/ready.ts @@ -1,7 +1,7 @@ import { EmbedBuilder } from "discord.js"; import { Heptagram } from "../../interfaces/Heptagram"; -import { heptagramLogHandler } from "../../modules/heptagramLogHandler"; +import * as logger from "../../mo../../modules/heptagramLogger"; /** * Sends a notification to the debug hook when Heptagram has connected to @@ -10,7 +10,7 @@ import { heptagramLogHandler } from "../../modules/heptagramLogHandler"; * @param {Heptagram} Heptagram's Client instance. */ export const ready = async (Heptagram: Heptagram): Promise => { - heptagramLogHandler.log("info", "Fetching reaction role data..."); + logger.info("Fetching reaction role data..."); const readyEmbed = new EmbedBuilder(); readyEmbed.setTitle( "<:status_online:951855000605298708> Heptagram is online <:status_online:951855000605298708>" @@ -26,8 +26,8 @@ export const ready = async (Heptagram: Heptagram): Promise => { }); await Heptagram.debugHook.send({ embeds: [readyEmbed] }); - heptagramLogHandler.log("info", "Discord ready!"); + logger.info("Discord ready!"); - heptagramLogHandler.log("info", "Loaded PM2 counts!"); + logger.info("Loaded PM2 counts!"); Heptagram.pm2.metrics.events.mark(); }; diff --git a/src/index.ts b/src/index.ts index 95810982..b1b7f4e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ import { Heptagram } from "./interfaces/Heptagram"; import { loadCommands } from "./modules/commands/loadCommands"; import { registerCommands } from "./modules/commands/owner/registerCommands"; import { heptagramErrorHandler } from "./modules/heptagramErrorHandler"; -import { heptagramLogHandler } from "./modules/heptagramLogHandler"; +import * as logger from "./modules/heptagramLogger"; import { loadPM2 } from "./modules/loadPM2"; import { validateEnv } from "./utils/validateEnv"; import { validateNode } from "./utils/validateNode"; @@ -18,14 +18,14 @@ import { validateNode } from "./utils/validateNode"; * call the necessary helpers to prepare Heptagram, and then log in to Discord. */ void (async () => { - heptagramLogHandler.log("info", "Starting bot..."); + logger.info("Starting bot..."); const validatedNode = await validateNode(); if (!validatedNode.valid) { - heptagramLogHandler.log("error", validatedNode.message); + logger.error(validatedNode.message); process.exit(1); } else { - heptagramLogHandler.log("info", validatedNode.message); + logger.info(validatedNode.message); } const Heptagram = new Client({ @@ -34,22 +34,22 @@ void (async () => { allowedMentions: { parse: ["users", "roles"], repliedUser: true }, }) as Heptagram; - heptagramLogHandler.log("info", "Validating environment variables..."); + logger.info("Validating environment variables..."); const validatedEnvironment = await validateEnv(Heptagram); if (!validatedEnvironment.valid) { - heptagramLogHandler.log("error", `${validatedEnvironment.message}`); + logger.error(validatedEnvironment.message); return; } else { - heptagramLogHandler.log("info", "Environment variables validated."); + logger.info("Environment variables validated."); } - heptagramLogHandler.log("info", "Loading PM2..."); + logger.info("Loading PM2..."); const loadedPM2 = await loadPM2(Heptagram); if (!loadedPM2) { - heptagramLogHandler.log("error", "Unable to load Grafana metrics"); + logger.error("Unable to load Grafana metrics"); return; } else { - heptagramLogHandler.log("info", "PM2 loaded."); + logger.info("PM2 loaded."); } Heptagram.debugHook = new WebhookClient({ url: Heptagram.configs.whUrl }); @@ -57,7 +57,7 @@ void (async () => { /* This catches when the process is about to exit and destroys the discord.js client in order to allow for a graceful shutdown. */ process.on("exit", () => { - heptagramLogHandler.log("info", "Shutting down gracefully..."); + logger.info("Shutting down gracefully..."); Heptagram.destroy(); }); @@ -67,52 +67,54 @@ and destroys the discord.js client in order to allow for a graceful shutdown. */ */ process.on("unhandledRejection", async (error: Error) => { await heptagramErrorHandler(Heptagram, "Unhandled Rejection Error", error); - await heptagramLogHandler.log("error", error); + await logger.error(`${error}`); }); process.on("uncaughtException", async (error) => { await heptagramErrorHandler(Heptagram, "Uncaught Exception Error", error); - await heptagramLogHandler.log("error", error); + await logger.error(`${error}`); }); - heptagramLogHandler.log("info", "Importing commands..."); + logger.info("Importing commands..."); const commands = await loadCommands(Heptagram); // eslint-disable-next-line require-atomic-updates Heptagram.commands = commands; if (!commands.length) { - heptagramLogHandler.log("error", "failed to import commands."); + logger.error("failed to import commands."); return; } if (process.env.NODE_ENV !== "production") { - heptagramLogHandler.log("info", "Registering commands in development..."); + logger.info("Registering commands in development..."); const success = await registerCommands(Heptagram); if (!success) { - heptagramLogHandler.log("error", "failed to register commands."); + logger.error("failed to register commands."); return; } } - heptagramLogHandler.log("info", "Connecting to database..."); + logger.info("Connecting to database..."); const databaseConnection = await connectDatabase(Heptagram); if (!databaseConnection) { - heptagramLogHandler.log("error", "failed to connect to database."); + logger.error("failed to connect to database."); return; } else { - heptagramLogHandler.log("info", "Database connected."); + logger.info("Database connected."); } - heptagramLogHandler.log("info", "Attaching event listeners..."); + logger.info("Attaching event listeners..."); handleEvents(Heptagram); - heptagramLogHandler.log("info", "Connecting to Discord..."); + logger.info("Connecting to Discord..."); await Heptagram.login(Heptagram.configs.token); - heptagramLogHandler.log("info", "Setting activity..."); + logger.info("Setting activity..."); Heptagram.user?.setActivity({ name: `over ${Heptagram.guilds.cache.size} guilds`, type: ActivityType.Watching, }); + + logger.ready("Heptagram is now running."); })(); export default Heptagram; diff --git a/src/modules/commands/owner/reboot.ts b/src/modules/commands/owner/reboot.ts index e5d427d6..7b694bfb 100644 --- a/src/modules/commands/owner/reboot.ts +++ b/src/modules/commands/owner/reboot.ts @@ -3,7 +3,7 @@ import moment from "moment"; import { Heptagram } from "../../../interfaces/Heptagram"; import { heptagramErrorHandler } from "../../heptagramErrorHandler"; -import { heptagramLogHandler } from "../../heptagramLogHandler"; +import * as logger from "../../heptagramLogger"; /**s * Reloads and restarts the bot. @@ -42,10 +42,7 @@ export const reboot = async (Heptagram: Heptagram, message: Message) => { }) .then(async (collected) => { if (collected.first()?.content.toLowerCase() === "yes") { - heptagramLogHandler.log( - `info`, - `${message.author.username} has rebooted the bot.` - ); + logger.info(`${message.author.username} has rebooted the bot.`); (await Heptagram.debugHook.send({ embeds: [embed] })) && message.reply({ embeds: [embed] }).then(() => { process.exit(); diff --git a/src/modules/commands/owner/registerCommands.ts b/src/modules/commands/owner/registerCommands.ts index 7a6d0830..e824a13c 100644 --- a/src/modules/commands/owner/registerCommands.ts +++ b/src/modules/commands/owner/registerCommands.ts @@ -7,7 +7,7 @@ import { import { Heptagram } from "../../../interfaces/Heptagram"; import { heptagramErrorHandler } from "../../heptagramErrorHandler"; -import { heptagramLogHandler } from "../../heptagramLogHandler"; +import * as logger from "../../heptagramLogger"; /** * Takes both the commands and contexts, parses the `data` properties as needed, @@ -40,13 +40,13 @@ export const registerCommands = async ( }); if (process.env.NODE_ENV === "production") { - heptagramLogHandler.log("info", "registering commands globally!"); + logger.info("registering commands globally!"); await rest.put(Routes.applicationCommands(Heptagram.configs.id), { body: commandData, }); } else { - heptagramLogHandler.log("info", "registering to home guild only"); + logger.info("registering to home guild only"); await rest.put( Routes.applicationGuildCommands( Heptagram.configs.id, diff --git a/src/modules/heptagramErrorHandler.ts b/src/modules/heptagramErrorHandler.ts index c555797d..b7078102 100644 --- a/src/modules/heptagramErrorHandler.ts +++ b/src/modules/heptagramErrorHandler.ts @@ -8,10 +8,9 @@ import { Types } from "mongoose"; import { version as tsVersion } from "typescript"; import { Heptagram } from "../interfaces/Heptagram"; +import * as logger from "../modules/heptagramLogger"; import { customSubstring } from "../utils/customSubstring"; -import { heptagramLogHandler } from "./heptagramLogHandler"; - /** * Takes the error object generated within the code, logs the * information in the console. Then, generates an error ID, builds an error embed, and sends @@ -37,12 +36,10 @@ export const heptagramErrorHandler = async ( Heptagram.pm2.metrics.errors.mark(); } const error = err as Error; - heptagramLogHandler.log("error", `There was an error in the ${context}:`); - heptagramLogHandler.log( - "error", + logger.error(`There was an error in the ${context}:`); + logger.error( JSON.stringify({ errorMessage: error.message, errorStack: error.stack }) ); - const errorId = new Types.ObjectId(); const errorEmbed = new EmbedBuilder(); errorEmbed.setTitle( diff --git a/src/modules/heptagramLogHandler.ts b/src/modules/heptagramLogHandler.ts deleted file mode 100644 index f1683057..00000000 --- a/src/modules/heptagramLogHandler.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createLogger, format, transports, config } from "winston"; - -const { combine, timestamp, colorize, printf } = format; - -/** - * Standard log handler, using winston to wrap and format - * messages. Call with `logHandler.log(level, message)`. - * - * @param {string} level - The log level to use. - * @param {string} message - The message to log. - */ -export const heptagramLogHandler = createLogger({ - levels: config.npm.levels, - level: "silly", - transports: [new transports.Console()], - format: combine( - timestamp({ - format: "YYYY-MM-DD HH:mm:ss", - }), - colorize(), - printf((info) => `${info.level}: ${[info.timestamp]}: ${info.message}`) - ), - exitOnError: false, -}); diff --git a/src/modules/heptagramLogger.ts b/src/modules/heptagramLogger.ts new file mode 100644 index 00000000..1c0cf135 --- /dev/null +++ b/src/modules/heptagramLogger.ts @@ -0,0 +1,75 @@ +import chalk from "chalk"; +import moment from "moment"; + +export const log = ( + content: string, + type: + | "log" + | "info" + | "warn" + | "error" + | "debug" + | "load" + | "ready" + | "mongo" + | "heptagram" = "log" +) => { + const timestamp = `${chalk.white(`[${moment().format("DD-MM-YY H:m:s")}]`)}`; + + switch (type) { + case "log": + return console.log(`${chalk.grey("[LOG]")} ${timestamp} ${content}`); + case "info": + return console.log( + `${chalk.blueBright("[INFO]")} ${timestamp} ${chalk.blueBright( + content + )}` + ); + case "warn": + return console.log( + `${chalk.yellow("[WARN]")} ${timestamp} ${chalk.yellow(content)} ` + ); + case "error": + return console.log( + `${chalk.red("[ERROR]")} ${timestamp} ${chalk.red(content)} ` + ); + case "debug": + return console.log( + `${chalk.blueBright("[DEBUG]")} ${timestamp} ${chalk.magenta( + content + )} ` + ); + case "load": { + return console.log( + `${timestamp} ${chalk.magenta(type.toUpperCase())} ${content} ` + ); + } + case "ready": + return console.log( + `${chalk.green("[READY]")} ${timestamp} ${chalk.green(content)}` + ); + case "mongo": + return console.log( + `${chalk.hex("#f1421a")("[MONGO]")} ${timestamp} ${chalk.hex("#f1421a")( + content + )}` + ); + case "heptagram": + return console.log( + `${chalk.hex("#FFF800")("[HEPTAGRAM]")} ${timestamp} ${chalk.hex( + "#FFF800" + )(`${content}`)}` + ); + default: + throw new TypeError("Logger type not correct."); + } +}; + +export const info = (content: string) => log(content, "info"); +export const warn = (content: string) => log(content, "warn"); +export const error = (content: string) => log(content, "error"); +export const debug = (content: string) => log(content, "debug"); +export const load = (content: string) => log(content, "load"); +export const ready = (content: string) => log(content, "ready"); +export const mongo = (content: string) => log(content, "mongo"); +export const heptagram = (content: string) => log(content, "heptagram"); diff --git a/src/modules/loadPM2.ts b/src/modules/loadPM2.ts index 81249c3b..7a46f2b2 100644 --- a/src/modules/loadPM2.ts +++ b/src/modules/loadPM2.ts @@ -2,7 +2,7 @@ import io from "@pm2/io"; import { Heptagram } from "../interfaces/Heptagram"; -import { heptagramLogHandler } from "./heptagramLogHandler"; +import * as logger from "./modules/heptagramLogHandler"; /** * Module to load the PM2 config and attach it to Heptagram. @@ -25,7 +25,7 @@ export const loadPM2 = (Heptagram: Heptagram): boolean => { io.init(); return true; } catch (err) { - heptagramLogHandler.log("error", err); + heptagramLogger.log("error", err); return false; } }; diff --git a/src/utils/randomNumber.ts b/src/utils/randomNumber.ts new file mode 100644 index 00000000..3bbc58a4 --- /dev/null +++ b/src/utils/randomNumber.ts @@ -0,0 +1,10 @@ +/** + * gets a random number between min and max + * + * @param {number} min + * @param {number} max + * @returns {number} A human-readable format of the number of seconds. + */ +export const randomNumber = (min: number, max: number): number => { + return Math.floor(Math.random() * (max - min)) + min; +}; diff --git a/src/utils/validateEnv.ts b/src/utils/validateEnv.ts index a813eed6..cefb2e4d 100644 --- a/src/utils/validateEnv.ts +++ b/src/utils/validateEnv.ts @@ -1,7 +1,7 @@ import * as child from "child_process"; import { Heptagram } from "../interfaces/Heptagram"; -import { heptagramLogHandler } from "../modules/heptagramLogHandler"; +import * as logger from "../modules/heptagramLogger"; /** * Validates that all expected environment variables are set with *some* value. @@ -101,7 +101,7 @@ export const validateEnv = ( return { valid: true, message: "Environment variables validated!" }; } catch (err) { - heptagramLogHandler.log("error", err); + logger.error(`${err}`); return { valid: false, message: "Unknown error when validating environment", diff --git a/src/utils/validateNode.ts b/src/utils/validateNode.ts index 94208ad3..68401cc3 100644 --- a/src/utils/validateNode.ts +++ b/src/utils/validateNode.ts @@ -1,4 +1,4 @@ -import { heptagramLogHandler } from "../modules/heptagramLogHandler"; +import * as logger from "../modules/heptagramLogger"; /** * Validates some important things that the bot needs to be able to run. @@ -18,7 +18,7 @@ export const validateNode = (): { valid: boolean; message: string } => { return { valid: true, message: "Node ENV ok!" }; } catch (err) { - heptagramLogHandler.log("error", err); + logger.error(`${err}`); return { valid: false, message: "Unknown error when validating environment", diff --git a/yarn.lock b/yarn.lock index 33d3fbb8..7be00bc2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -312,22 +312,6 @@ __metadata: languageName: node linkType: hard -"@sapphire/time-utilities@npm:^1.7.4": - version: 1.7.6 - resolution: "@sapphire/time-utilities@npm:1.7.6" - dependencies: - "@sapphire/utilities": ^3.9.2 - checksum: eadcdf2cbadcc15fa36d7d3b4719acb05f30ed0611f3f8bcfd47d8037221239168f190e79b2c05942cc66a66cbbf3c7d313eda15e9a2a3cff81cf25cec42f3ce - languageName: node - linkType: hard - -"@sapphire/utilities@npm:^3.9.2": - version: 3.9.2 - resolution: "@sapphire/utilities@npm:3.9.2" - checksum: eeef33b03b802e931ada752b8c335cc8bde4017f8029b577b0247e3f3d2b57bf5f1936b2c8c29aef5e2c6975d9b162363e4c5c434c4fead0bba74253d067b4d9 - languageName: node - linkType: hard - "@sindresorhus/is@npm:^0.14.0": version: 0.14.0 resolution: "@sindresorhus/is@npm:0.14.0" @@ -943,7 +927,7 @@ __metadata: languageName: node linkType: hard -"all-contributors-cli@npm:^6.20.0": +"all-contributors-cli@npm:6.20.0": version: 6.20.0 resolution: "all-contributors-cli@npm:6.20.0" dependencies: @@ -1514,6 +1498,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.0.1": + version: 5.0.1 + resolution: "chalk@npm:5.0.1" + checksum: 7b45300372b908f0471fbf7389ce2f5de8d85bb949026fd51a1b95b10d0ed32c7ed5aab36dd5e9d2bf3191867909b4404cef75c5f4d2d1daeeacd301dd280b76 + languageName: node + linkType: hard + "character-entities-html4@npm:^2.0.0": version: 2.1.0 resolution: "character-entities-html4@npm:2.1.0" @@ -3492,7 +3483,6 @@ __metadata: resolution: "heptagram@workspace:." dependencies: "@pm2/io": ^5.0.0 - "@sapphire/time-utilities": ^1.7.4 "@types/chai": ^4.3.1 "@types/cors": 2.8.12 "@types/express": 4.17.13 @@ -3501,9 +3491,10 @@ __metadata: "@typescript-eslint/eslint-plugin": 5.33.1 "@typescript-eslint/parser": 5.33.1 alex: 10.0.0 - all-contributors-cli: ^6.20.0 + all-contributors-cli: 6.20.0 axios: ^0.27.2 chai: ^4.3.6 + chalk: ^5.0.1 cors: ^2.8.5 dayjs: ^1.11.3 discord.js: ^14.0.2