diff --git a/.env.example b/.env.example index bf64ba3..bb3d7c2 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,8 @@ # Copy this file to `.env` before editing -# TODO: Base URL of the Stoat API to connect to. +# Base URL of the Stoat API to connect to. # Defaults to https://api.stoat.chat/0.8 -STOAT_API_URL= +# STOAT_API_URL= # Your bot account's token. BOT_TOKEN= diff --git a/main.js b/main.js index 2680772..13e4e86 100644 --- a/main.js +++ b/main.js @@ -263,10 +263,6 @@ class AutoDice { return await this.sendHelp(message); } - if (content.includes(".")) { - return await message.reply("⚠️ **Invalid Input:** Decimals are not supported. Please use whole numbers only."); - } - const args = content.split(/\s+/); const trigger = args[0].toLowerCase(); @@ -295,8 +291,14 @@ class AutoDice { } // Extract dice formula + optional label - const { formula, label } = this.extractFormula(args.join(" ")); - if (!formula) return; + const joined = args.join(" "); + const { formula, label } = this.extractFormula(joined); + if (!formula) { + if (/d[\df%]/i.test(joined)) { + return await message.reply("⚠️ **Invalid Input:** That dice expression couldn't be parsed. Dice counts and sides must be whole numbers."); + } + return; + } await this.processRoll(message, formula, isDMRequest, label); } @@ -359,11 +361,13 @@ class AutoDice { output += `**Repeating ${count} times:** \`${formula}\`\n`; for (let i = 0; i < count; i++) { const res = this.calculateDice(formula); - output += `${i + 1}. **${res.total}** \t${res.breakdown}\n`; + output += res.total === "Error" ? `${i + 1}. ⚠️ **Error:** Invalid expression\n` : `${i + 1}. **${res.total}** \t${res.breakdown}\n`; } } else { const res = this.calculateDice(input); - if (label) { + if (res.total === "Error") { + output = "⚠️ **Invalid Input:** That dice expression couldn't be parsed. Dice counts and sides must be whole numbers."; + } else if (label) { output += `**${res.total}** — ${res.breakdown}`; } else { output += `🎲 **${res.total}**\n${res.breakdown}`; @@ -388,15 +392,15 @@ class AutoDice { isDiceString(str) { if (!/d[\df%]/i.test(str)) return false; - const withoutDice = str.replace(/(\d+)?d(\d+|[fF%])([a-z!<>=\d]*)/gi, ""); - return /^[0-9+\-*/()<>\s!]*$/i.test(withoutDice); + const withoutDice = str.replace(/(?=\d]*)/gi, ""); + return /^[0-9.+\-*/()<>\s!]*$/i.test(withoutDice); } calculateDice(expression) { // Expand shorthands - expression = expression.replace(/\bd%\b/gi, "d100"); + expression = expression.replace(/d%/gi, "d100"); - const diceRegex = /(\d+)?d(\d+|[fF%])([a-z!<>=\d]*)/gi; + const diceRegex = /(?=\d]*)/gi; const rolls = []; const evalString = expression.replace(diceRegex, (match, countStr, sidesStr, mods) => { @@ -457,6 +461,7 @@ class AutoDice { let total = 0; try { + if (/[a-z]/i.test(evalString)) throw new Error("invalid dice expression"); const sanitized = evalString.replace(/[^0-9+\-*/().\s]/g, ""); if (!sanitized) throw new Error("empty expression"); total = new Function(`return ${sanitized}`)(); @@ -483,7 +488,7 @@ class AutoDice { } // Success counting (>N) - const successMatch = modStr.match(/>(\d+)/); + const successMatch = modStr.match(/(?(\d+)/); if (successMatch) { const threshold = parseInt(successMatch[1]); results.forEach((d) => { @@ -494,7 +499,7 @@ class AutoDice { } // Fail counting ( { @@ -542,7 +547,7 @@ class AutoDice { Visit [the website]() for more usage information and [the AutoMod server](https://stt.gg/automod) for help. - **Basic Rolls:** \`@AutoDice [number]d[sides]\` (eg \`10d6\`). Use \`d%\` for percentile or \`dF\` for Fate dice. -- **Math:** Supports \`+\`, \`-\`, \`*\`, \`/\`. (eg \`1d20 + 5\`). +- **Math:** Supports \`+\`, \`-\`, \`*\`, \`/\`, and decimals. (eg \`1d20 + 5\` or \`d540*0.7\`). - **Advantage/Disadvantage:** Shorthand \`adv\` or \`dis\` (expands to \`2d20kh1\` / \`2d20kl1\`). - **Sorting:** Suffix \`sa\` (ascending) or \`sd\` (descending). (eg \`10d6sd\`). - **Keep/Drop:** \`kh[n]\` / \`kl[n]\` to keep highest/lowest, or \`dh[n]\` / \`dl[n]\` to drop them. (eg \`4d6dl1\`).