diff --git a/README.md b/README.md index 1c8561a..891d5fc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # The Artificer - A Dice Rolling Discord Bot -Version 1.4.2 - 2021/02/14 +Version 1.4.3 - 2021/03/21 The Artificer is a Discord bot that specializes in rolling dice. The bot utilizes the compact [Roll20 formatting](https://artificer.eanm.dev/roll20) for ease of use and will correctly perform any needed math on the roll (limited to basic algebra). diff --git a/config.example.ts b/config.example.ts index bb70595..b269dd2 100644 --- a/config.example.ts +++ b/config.example.ts @@ -1,6 +1,6 @@ export const config = { "name": "The Artificer", // Name of the bot - "version": "1.4.2", // Version of the bot + "version": "1.4.3", // Version of the bot "token": "the_bot_token", // Discord API Token for this bot "localtoken": "local_testing_token", // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token" "prefix": "[[", // Prefix for all commands diff --git a/mod.ts b/mod.ts index 2ba9367..79b1666 100644 --- a/mod.ts +++ b/mod.ts @@ -52,6 +52,7 @@ startBot({ // Interval to rotate the status text every 30 seconds to show off more commands setInterval(() => { + utils.log(LT.LOG, "Changing bot status"); try { // Wrapped in try-catch due to hard crash possible editBotsStatus(StatusTypes.Online, intervals.getRandomStatus(cache), ActivityType.Game); @@ -61,7 +62,10 @@ startBot({ }, 30000); // Interval to update bot list stats every 24 hours - LOCALMODE ? utils.log(LT.INFO, "updateListStatistics not running") : setInterval(() => intervals.updateListStatistics(botID, cache.guilds.size), 86400000); + LOCALMODE ? utils.log(LT.INFO, "updateListStatistics not running") : setInterval(() => { + utils.log(LT.LOG, "Updating all bot lists statistics"); + intervals.updateListStatistics(botID, cache.guilds.size); + }, 86400000); // setTimeout added to make sure the startup message does not error out setTimeout(() => { @@ -73,11 +77,13 @@ startBot({ }, 1000); }, guildCreate: (guild: Guild) => { + utils.log(LT.LOG, `Handling joining guild ${JSON.stringify(guild)}`); sendMessage(config.logChannel, `New guild joined: ${guild.name} (id: ${guild.id}). This guild has ${guild.memberCount} members!`).catch(e => { utils.log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); }); }, guildDelete: (guild: Guild) => { + utils.log(LT.LOG, `Handling leaving guild ${JSON.stringify(guild)}`); sendMessage(config.logChannel, `I have been removed from: ${guild.name} (id: ${guild.id}).`).catch(e => { utils.log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); }); @@ -86,12 +92,14 @@ startBot({ messageCreate: async (message: Message) => { // Ignore all other bots if (message.author.bot) return; - + // Ignore all messages that are not commands if (message.content.indexOf(config.prefix) !== 0) return; + + utils.log(LT.LOG, `Handling message ${JSON.stringify(message)}`); // Split into standard command + args format - const args = message.content.slice(config.prefix.length).trim().split(/ +/g); + const args = message.content.slice(config.prefix.length).trim().split(/[ \n]+/g); const command = args.shift()?.toLowerCase(); // All commands below here @@ -126,7 +134,7 @@ startBot({ }); } - // [[rollhelp or [[rh or [[hr + // [[rollhelp or [[rh or [[hr or [[?? // Help command specifically for the roll command else if (command === "rollhelp" || command === "rh" || command === "hr" || command === "??" || command?.startsWith("xdy")) { // Light telemetry to see how many times a command is being run @@ -384,6 +392,7 @@ startBot({ // Check if any of the args are command flags and pull those out into the modifiers object for (let i = 0; i < args.length; i++) { + utils.log(LT.LOG, `Checking ${command + args.join(" ")} for command modifiers ${i}`); switch (args[i].toLowerCase()) { case "-nd": modifiers.noDetails = true; @@ -414,6 +423,7 @@ startBot({ // -gm is a little more complex, as we must get all of the GMs that need to be DMd while (((i + 1) < args.length) && args[i + 1].startsWith("<@")) { + utils.log(LT.LOG, `Finding all GMs, checking args ${JSON.stringify(args)}`); // Keep looping thru the rest of the args until one does not start with the discord mention code modifiers.gms.push(args[i + 1].replace(/[!]/g, "")); args.splice((i + 1), 1); @@ -437,7 +447,7 @@ startBot({ case "-o": args.splice(i, 1); - if (args[i].toLowerCase() !== "d" && args[i].toLowerCase() !== "a") { + if (args[i].toLowerCase()[0] !== "d" && args[i].toLowerCase()[0] !== "a") { // If -o is on and asc or desc was not specified, error out m.edit("Error: Must specifiy a or d to order the rolls ascending or descending"); @@ -450,7 +460,7 @@ startBot({ return; } - modifiers.order = args[i].toLowerCase(); + modifiers.order = args[i].toLowerCase()[0]; args.splice(i, 1); i--; @@ -509,16 +519,27 @@ startBot({ // And message the full details to each of the GMs, alerting roller of every GM that could not be messaged modifiers.gms.forEach(async e => { + utils.log(LT.LOG, `Messaging GM ${e}`); // If its too big, collapse it into a .txt file and send that instead. const b = await new Blob([returnText as BlobPart], { "type": "text" }); - // Update return text - returnText = "<@" + message.author.id + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nFull details have been attached to this messaged as a `.txt` file for verification purposes."; + if (b.size > 8388290) { + // Update return text + returnText = "<@" + message.author.id + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nFull details could not be attached to this messaged as a `.txt` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one."; - // Attempt to DM the GMs and send a warning if it could not DM a GM - await sendDirectMessage(e.substr(2, (e.length - 3)), { "content": returnText, "file": { "blob": b, "name": "rollDetails.txt" } }).catch(() => { - utils.sendIndirectMessage(message, "WARNING: " + e + " could not be messaged. If this issue persists, make sure direct messages are allowed from this server.", sendMessage, sendDirectMessage); - }); + // Attempt to DM the GMs and send a warning if it could not DM a GM + await sendDirectMessage(e.substr(2, (e.length - 3)), returnText).catch(() => { + utils.sendIndirectMessage(message, "WARNING: " + e + " could not be messaged. If this issue persists, make sure direct messages are allowed from this server.", sendMessage, sendDirectMessage); + }); + } else { + // Update return text + returnText = "<@" + message.author.id + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nFull details have been attached to this messaged as a `.txt` file for verification purposes."; + + // Attempt to DM the GMs and send a warning if it could not DM a GM + await sendDirectMessage(e.substr(2, (e.length - 3)), { "content": returnText, "file": { "blob": b, "name": "rollDetails.txt" } }).catch(() => { + utils.sendIndirectMessage(message, "WARNING: " + e + " could not be messaged. If this issue persists, make sure direct messages are allowed from this server.", sendMessage, sendDirectMessage); + }); + } }); // Finally send the text @@ -536,13 +557,21 @@ startBot({ // If its too big, collapse it into a .txt file and send that instead. const b = await new Blob([returnText as BlobPart], { "type": "text" }); - // Update return text - returnText = "<@" + message.author.id + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a `.txt` file for verification purposes."; + if (b.size > 8388290) { + // Update return text + returnText = "<@" + message.author.id + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a `.txt` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one."; - // Remove the original message to send new one with attachment - m.delete(); - - await utils.sendIndirectMessage(message, { "content": returnText, "file": { "blob": b, "name": "rollDetails.txt" } }, sendMessage, sendDirectMessage); + // Send the results + m.edit(returnText); + } else { + // Update return text + returnText = "<@" + message.author.id + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a `.txt` file for verification purposes."; + + // Remove the original message to send new one with attachment + m.delete(); + + await utils.sendIndirectMessage(message, { "content": returnText, "file": { "blob": b, "name": "rollDetails.txt" } }, sendMessage, sendDirectMessage); + } } else { // Finally send the text m.edit(returnText); @@ -565,6 +594,7 @@ startBot({ else { // Start looping thru the possible emojis config.emojis.some((emoji: EmojiConf) => { + utils.log(LT.LOG, `Checking if command was emoji ${emoji}`); // If a match gets found if (emoji.aliases.indexOf(command || "") > -1) { // Light telemetry to see how many times a command is being run diff --git a/src/api.ts b/src/api.ts index 87c143e..090f045 100644 --- a/src/api.ts +++ b/src/api.ts @@ -29,7 +29,7 @@ import config from "../config.ts"; // start initializes and runs the entire API for the bot const start = async (dbClient: Client, cache: CacheData, sendMessage: (c: string, m: (string | MessageContent)) => Promise, sendDirectMessage: (c: string, m: (string | MessageContent)) => Promise): Promise => { const server = serve({ hostname: "0.0.0.0", port: config.api.port }); - utils.log(LT.LOG, `HTTP api running at: http://localhost:${config.api.port}/`); + utils.log(LT.INFO, `HTTP api running at: http://localhost:${config.api.port}/`); // rateLimitTime holds all users with the last time they started a rate limit timer const rateLimitTime = new Map(); @@ -38,6 +38,7 @@ const start = async (dbClient: Client, cache: CacheData, sendMessage: (c: string // Catching every request made to the server for await (const request of server) { + utils.log(LT.LOG, `Handling request: ${JSON.stringify(request)}`); // Check if user is authenticated to be using this API let authenticated = false; let rateLimited = false; @@ -90,6 +91,7 @@ const start = async (dbClient: Client, cache: CacheData, sendMessage: (c: string const query = new Map(); if (tempQ !== undefined) { tempQ.split("&").forEach(e => { + utils.log(LT.LOG, `Breaking down request query: ${request} ${e}`); const [option, params] = e.split("="); query.set(option.toLowerCase(), params); }); @@ -279,6 +281,7 @@ const start = async (dbClient: Client, cache: CacheData, sendMessage: (c: string // Make a new return line to be sent to the roller let normalText = apiPrefix + "<@" + query.get("user") + ">" + returnmsg.line1 + "\nResults have been messaged to the following GMs: "; gms.forEach(e => { + utils.log(LT.LOG, `Appending GM ${e} to roll text`); normalText += "<@" + e + "> "; }); @@ -295,16 +298,24 @@ const start = async (dbClient: Client, cache: CacheData, sendMessage: (c: string }); } + const newMessage: MessageContent = {}; + // If its too big, collapse it into a .txt file and send that instead. + const b = await new Blob([returnText as BlobPart], { "type": "text" }); + + if (b.size > 8388290) { + // Update return text + newMessage.content = apiPrefix + "<@" + query.get("user") + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a `.txt` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one."; + } else { + // Update return text + newMessage.content = apiPrefix + "<@" + query.get("user") + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nFull details have been attached to this messaged as a `.txt` file for verification purposes."; + newMessage.file = { "blob": b, "name": "rollDetails.txt" }; + } + // And message the full details to each of the GMs, alerting roller of every GM that could not be messaged gms.forEach(async e => { - // If its too big, collapse it into a .txt file and send that instead. - const b = await new Blob([returnText as BlobPart], { "type": "text" }); - - // Update return text - returnText = apiPrefix + "<@" + query.get("user") + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nFull details have been attached to this messaged as a `.txt` file for verification purposes."; - + utils.log(LT.LOG, `Messaging GM ${e} roll results`); // Attempt to DM the GMs and send a warning if it could not DM a GM - await sendDirectMessage(e, { "content": returnText, "file": { "blob": b, "name": "rollDetails.txt" } }).catch(async () => { + await sendDirectMessage(e, newMessage).catch(async () => { const failedSend = "WARNING: <@" + e + "> could not be messaged. If this issue persists, make sure direct messages are allowed from this server." // Send the return message as a DM or normal message depening on if the channel is set if ((query.get("channel") || "").length > 0) { @@ -342,12 +353,14 @@ const start = async (dbClient: Client, cache: CacheData, sendMessage: (c: string // If its too big, collapse it into a .txt file and send that instead. const b = await new Blob([returnText as BlobPart], { "type": "text" }); - // Update return text - returnText = "<@" + query.get("user") + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a `.txt` file for verification purposes."; - - // Set info into the newMessage - newMessage.content = returnText; - newMessage.file = { "blob": b, "name": "rollDetails.txt" }; + if (b.size > 8388290) { + // Update return text + newMessage.content = apiPrefix + "<@" + query.get("user") + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a `.txt` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one."; + } else { + // Update return text + newMessage.content = apiPrefix + "<@" + query.get("user") + ">" + returnmsg.line1 + "\n" + returnmsg.line2 + "\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a `.txt` file for verification purposes."; + newMessage.file = { "blob": b, "name": "rollDetails.txt" }; + } } // Send the return message as a DM or normal message depening on if the channel is set @@ -672,6 +685,7 @@ const start = async (dbClient: Client, cache: CacheData, sendMessage: (c: string const query = new Map(); if (tempQ !== undefined) { tempQ.split("&").forEach(e => { + utils.log(LT.LOG, `Parsing request query #2 ${request} ${e}`); const [option, params] = e.split("="); query.set(option.toLowerCase(), params); }); diff --git a/src/intervals.ts b/src/intervals.ts index ad83819..569fb69 100644 --- a/src/intervals.ts +++ b/src/intervals.ts @@ -39,6 +39,7 @@ const getRandomStatus = (cache: CacheData): string => { // Sends the current server count to all bot list sites we are listed on const updateListStatistics = (botID: string, serverCount: number): void => { config.botLists.forEach(async e => { + utils.log(LT.LOG, `Updating statistics for ${JSON.stringify(e)}`) if (e.enabled) { const tempHeaders = new Headers(); tempHeaders.append(e.headers[0].header, e.headers[0].value); @@ -49,7 +50,7 @@ const updateListStatistics = (botID: string, serverCount: number): void => { "headers": tempHeaders, "body": JSON.stringify(e.body).replace('"?{server_count}"', serverCount.toString()) // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number }); - utils.log(LT.LOG, `${JSON.stringify(response)}`); + utils.log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`); } }); }; diff --git a/src/solver.ts b/src/solver.ts index c50236d..e6052f7 100644 --- a/src/solver.ts +++ b/src/solver.ts @@ -61,6 +61,7 @@ const compareOrigidx = (a: RollSet, b: RollSet): number => { const escapeCharacters = (str: string, esc: string): string => { // Loop thru each esc char one at a time for (let i = 0; i < esc.length; i++) { + utils.log(LT.LOG, `Escaping character ${esc[i]} | ${str}, ${esc}`); // Create a new regex to look for that char that needs replaced and escape it const temprgx = new RegExp(`[${esc[i]}]`, "g"); str = str.replace(temprgx, ("\\" + esc[i])); @@ -162,6 +163,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol // Loop until all remaining args are parsed while (remains.length > 0) { + utils.log(LT.LOG, `Handling roll ${rollStr} | Parsing remains ${remains}`); // Find the next number in the remains to be able to cut out the rule name let afterSepIdx = remains.search(/\d/); if (afterSepIdx < 0) { @@ -217,6 +219,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol // Configure CritScore for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.critScore.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { + utils.log(LT.LOG, `Handling roll ${rollStr} | Parsing cs> ${i}`); rollConf.critScore.range.push(i); } break; @@ -224,6 +227,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol // Configure CritScore for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.critScore.on = true; for (let i = 0; i <= tNum; i++) { + utils.log(LT.LOG, `Handling roll ${rollStr} | Parsing cs< ${i}`); rollConf.critScore.range.push(i); } break; @@ -237,6 +241,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol // Configure CritFail for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.critFail.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { + utils.log(LT.LOG, `Handling roll ${rollStr} | Parsing cf> ${i}`); rollConf.critFail.range.push(i); } break; @@ -244,6 +249,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol // Configure CritFail for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.critFail.on = true; for (let i = 0; i <= tNum; i++) { + utils.log(LT.LOG, `Handling roll ${rollStr} | Parsing cf< ${i}`); rollConf.critFail.range.push(i); } break; @@ -271,6 +277,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol // Since only one drop or keep option can be active, count how many are active to throw the right error let dkdkCnt = 0; [rollConf.drop.on, rollConf.keep.on, rollConf.dropHigh.on, rollConf.keepLow.on].forEach(e => { + utils.log(LT.LOG, `Handling roll ${rollStr} | Checking if drop/keep is on ${e}`); if (e) { dkdkCnt++; } @@ -335,6 +342,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol // Initial rolling, not handling reroll or exploding here for (let i = 0; i < rollConf.dieCount; i++) { + utils.log(LT.LOG, `Handling roll ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`); // If loopCount gets too high, stop trying to calculate infinity if (loopCount > MAXLOOPS) { throw new Error("MaxLoopsExceeded"); @@ -344,6 +352,8 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol const rolling = JSON.parse(JSON.stringify(templateRoll)); // If maximiseRoll is on, set the roll to the dieSize, else if nominalRoll is on, set the roll to the average roll of dieSize, else generate a new random roll rolling.roll = maximiseRoll ? rollConf.dieSize : (nominalRoll ? ((rollConf.dieSize / 2) + 0.5) : genRoll(rollConf.dieSize)); + // Set origidx of roll + rolling.origidx = i; // If critScore arg is on, check if the roll should be a crit, if its off, check if the roll matches the die size if (rollConf.critScore.on && rollConf.critScore.range.indexOf(rolling.roll) >= 0) { @@ -366,6 +376,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol // If needed, handle rerolling and exploding dice now if (rollConf.reroll.on || rollConf.exploding) { for (let i = 0; i < rollSet.length; i++) { + utils.log(LT.LOG, `Handling roll ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`); // If loopCount gets too high, stop trying to calculate infinity if (loopCount > MAXLOOPS) { throw new Error("MaxLoopsExceeded"); @@ -432,6 +443,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol let rerollCount = 0; if (rollConf.reroll.on) { for (let i = 0; i < rollSet.length; i++) { + utils.log(LT.LOG, `Handling roll ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[i])}`); rollSet[i].origidx = i; if (rollSet[i].rerolled) { @@ -480,6 +492,7 @@ const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): Rol // Now its time to drop all dice needed let i = 0; while (dropCount > 0 && i < rollSet.length) { + utils.log(LT.LOG, `Handling roll ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`); // Skip all rolls that were rerolled if (!rollSet[i].rerolled) { rollSet[i].dropped = true; @@ -508,6 +521,7 @@ const formatRoll = (rollConf: string, maximiseRoll: boolean, nominalRoll: boolea // Loop thru all parts of the roll to document everything that was done to create the total roll tempRollSet.forEach(e => { + utils.log(LT.LOG, `Formatting roll ${rollConf} | ${JSON.stringify(e)}`); let preFormat = ""; let postFormat = ""; @@ -573,6 +587,7 @@ const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: boolean // Evaluate all parenthesis while (conf.indexOf("(") > -1) { + utils.log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`); // Get first open parenthesis const openParen = conf.indexOf("("); let closeParen = -1; @@ -580,6 +595,7 @@ const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: boolean // Using nextParen to count the opening/closing parens, find the matching paren to openParen above for (let i = openParen; i < conf.length; i++) { + utils.log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for matching ) openIdx: ${openParen} checking: ${i}`); // If we hit an open, add one (this includes the openParen we start with), if we hit a close, subtract one if (conf[i] === "(") { nextParen++; @@ -622,8 +638,10 @@ const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: boolean // Evaluate all EMMDAS by looping thru each teir of operators (exponential is the higehest teir, addition/subtraction the lowest) const allCurOps = [["^"], ["*", "/", "%"], ["+", "-"]]; allCurOps.forEach(curOps => { + utils.log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`); // Iterate thru all operators/operands in the conf for (let i = 0; i < conf.length; i++) { + utils.log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)} | Checking ${JSON.stringify(conf[i])}`); // Check if the current index is in the active teir of operators if (curOps.indexOf(conf[i].toString()) > -1) { // Grab the operands from before and after the operator @@ -754,6 +772,7 @@ const parseRoll = (fullCmd: string, localPrefix: string, localPostfix: string, m // Loop thru all roll/math ops for (let i = 0; i < sepRolls.length; i++) { + utils.log(LT.LOG, `Parsing roll ${fullCmd} | Working ${sepRolls[i]}`); // Split the current iteration on the command postfix to separate the operation to be parsed and the text formatting after the opertaion const [tempConf, tempFormat] = sepRolls[i].split(localPostfix); @@ -763,6 +782,7 @@ const parseRoll = (fullCmd: string, localPrefix: string, localPostfix: string, m // Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens let parenCnt = 0; mathConf.forEach(e => { + utils.log(LT.LOG, `Parsing roll ${fullCmd} | Checking parenthesis balance ${e}`); if (e === "(") { parenCnt++; } else if (e === ")") { @@ -777,6 +797,7 @@ const parseRoll = (fullCmd: string, localPrefix: string, localPostfix: string, m // Evaluate all rolls into stepSolve format and all numbers into floats for (let i = 0; i < mathConf.length; i++) { + utils.log(LT.LOG, `Parsing roll ${fullCmd} | Evaluating rolls into mathable items ${JSON.stringify(mathConf[i])}`); if (mathConf[i].toString().length === 0) { // If its an empty string, get it out of here mathConf.splice(i, 1); @@ -841,8 +862,9 @@ const parseRoll = (fullCmd: string, localPrefix: string, localPostfix: string, m fullCmd = fullCmd.substr(0, (fullCmd.length - 1)); } - // Escape any | chars in fullCmd to prevent spoilers from acting up + // Escape any | and ` chars in fullCmd to prevent spoilers and code blocks from acting up fullCmd = escapeCharacters(fullCmd, "|"); + fullCmd = fullCmd.replace(/`/g, ""); let line1 = ""; let line2 = ""; @@ -850,27 +872,28 @@ const parseRoll = (fullCmd: string, localPrefix: string, localPostfix: string, m // If maximiseRoll or nominalRoll are on, mark the output as such, else use default formatting if (maximiseRoll) { - line1 = " requested the theoretical maximum of: `[[" + fullCmd + "`"; + line1 = ` requested the theoretical maximum of: \`${localPrefix}${fullCmd}\``; line2 = "Theoretical Maximum Results: "; } else if (nominalRoll) { - line1 = " requested the theoretical nominal of: `[[" + fullCmd + "`"; + line1 = ` requested the theoretical nominal of: \`${localPrefix}${fullCmd}\``; line2 = "Theoretical Nominal Results: "; } else if (order === "a") { - line1 = " requested the following rolls to be ordered from least to greatest: `[[" + fullCmd + "`"; + line1 = ` requested the following rolls to be ordered from least to greatest: \`${localPrefix}${fullCmd}\``; line2 = "Results: "; tempReturnData.sort(compareTotalRolls); } else if (order === "d") { - line1 = " requested the following rolls to be ordered from greatest to least: `[[" + fullCmd + "`"; + line1 = ` requested the following rolls to be ordered from greatest to least: \`${localPrefix}${fullCmd}\``; line2 = "Results: "; tempReturnData.sort(compareTotalRolls); tempReturnData.reverse(); } else { - line1 = " rolled: `[[" + fullCmd + "`"; + line1 = ` rolled: \`${localPrefix}${fullCmd}\``; line2 = "Results: "; } // Fill out all of the details and results now tempReturnData.forEach(e => { + utils.log(LT.LOG, `Parsing roll ${fullCmd} | Making return text ${JSON.stringify(e)}`); let preFormat = ""; let postFormat = ""; diff --git a/src/utils.ts b/src/utils.ts index abd7c99..363b860 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -12,6 +12,7 @@ import { nanoid } from "../deps.ts"; +import { DEBUG } from "../flags.ts"; import { LogTypes } from "./utils.enums.ts"; // Constant initialized at runtime for consistent file names @@ -19,36 +20,6 @@ let startDate: string; let logFolder: string; let initialized = false; -// split2k(longMessage) returns shortMessage[] -// split2k takes a long string in and cuts it into shorter strings to be sent in Discord -const split2k = (chunk: string): string[] => { - // Replace any malformed newline characters - chunk = chunk.replace(/\\n/g, "\n"); - const bites = []; - - // While there is more characters than allowed to be sent in discord - while (chunk.length > 2000) { - // Take 2001 chars to see if word magically ends on char 2000 - let bite = chunk.substr(0, 2001); - const lastI = bite.lastIndexOf(" "); - if (lastI < 2000) { - // If there is a final word before the 2000 split point, split right after that word - bite = bite.substr(0, lastI); - } else { - // Else cut exactly 2000 characters - bite = bite.substr(0, 2000); - } - - // Push and remove the bite taken out of the chunk - bites.push(bite); - chunk = chunk.slice(bite.length); - } - // Push leftovers into bites - bites.push(chunk); - - return bites; -}; - // ask(prompt) returns string // ask prompts the user at command line for message const ask = async (question: string, stdin = Deno.stdin, stdout = Deno.stdout): Promise => { @@ -101,13 +72,9 @@ const cmdPrompt = async (logChannel: string, botName: string, sendMessage: (c: s const channelID = args.shift() || ""; const message = args.join(" "); - // Utilize the split2k function to ensure a message over 2000 chars is not sent - const messages = split2k(message); - for (let i = 0; i < messages.length; i++) { - sendMessage(channelID, messages[i]).catch(reason => { - console.error(reason); - }); - } + sendMessage(channelID, message).catch(reason => { + console.error(reason); + }); } catch (e) { console.error(e); @@ -119,13 +86,9 @@ const cmdPrompt = async (logChannel: string, botName: string, sendMessage: (c: s else if (command === "ml") { const message = args.join(" "); - // Utilize the split2k function to ensure a message over 2000 chars is not sent - const messages = split2k(message); - for (let i = 0; i < messages.length; i++) { - sendMessage(logChannel, messages[i]).catch(reason => { - console.error(reason); - }); - } + sendMessage(logChannel, message).catch(reason => { + console.error(reason); + }); } // help or h @@ -183,10 +146,13 @@ const initLog = (name: string): void => { // Handles sending messages to console.log and sending a copy of the log to a file for review on crashes const log = async (level: LogTypes, message: string, error = new Error()): Promise => { const msgId = await nanoid(10); - const formattedMsg = `${new Date().toISOString()} | ${msgId} | ${level} | ${message}`; + const formattedMsg = `${new Date().toISOString()} | ${msgId} | ${level.padEnd(5)} | ${message}`; const traceMsg = `${error.stack}` // Default functionality of logging to console - console[level](formattedMsg); + if (level !== LogTypes.LOG || DEBUG) { + console[level](formattedMsg); + } + // Logging to files for permanent info if (initialized) { await Deno.writeTextFile(`./${logFolder}/${level}/${startDate}.log`, `${formattedMsg}\n`, {append: true}); @@ -195,4 +161,4 @@ const log = async (level: LogTypes, message: string, error = new Error()): Promi } }; -export default { split2k, cmdPrompt, sendIndirectMessage, initLog, log }; +export default { cmdPrompt, sendIndirectMessage, initLog, log }; diff --git a/www/api/index.html b/www/api/index.html index 73b16f4..b5c50a0 100644 --- a/www/api/index.html +++ b/www/api/index.html @@ -122,7 +122,7 @@ Built by Ean Milligan diff --git a/www/home/index.html b/www/home/index.html index c42322f..eb482b7 100644 --- a/www/home/index.html +++ b/www/home/index.html @@ -94,7 +94,7 @@ Built by Ean Milligan