From 3aa24443d40882b01945d413224a9c181fdde349 Mon Sep 17 00:00:00 2001
From: dakedres <ramondolive@gmail.com>
Date: Sat, 20 May 2023 15:27:32 -0600
Subject: [PATCH] Add infinite ranges & macro options

---
 src/constants.js |  3 +-
 src/index.js     | 90 ++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 78 insertions(+), 15 deletions(-)

diff --git a/src/constants.js b/src/constants.js
index 91330e6..c15fd55 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -1,6 +1,7 @@
 const constants = {
   rollRegex: /^(\d+)?([dhl])(\d+)(\s*([+\-*x\/])\s*(\d+))?/,
-  descriptionRegex: /\s*(\d+(-\d+)?)?([^;\n]+)/g,
+  optionRollRegex: /^(\d+)?(([dhl])(\d+))?(\s*[+\-*x\/]\s*\d+)?/,
+  descriptionRegex: /\s*(\d+(-(\d+)?)?)?([^;\n]+)/g,
   macroNameRegex: /^[a-z0-9]+$/,
 
   commands: {
diff --git a/src/index.js b/src/index.js
index 52baf5f..63f9a1e 100644
--- a/src/index.js
+++ b/src/index.js
@@ -8,9 +8,37 @@ dotenv.config()
 const replies = new Map()
 const commands = new Map()
 const db = new ClassicLevel('./db')
+ 
+const parseRollInt = (value, defaultValue) =>
+  value ? parseInt(value) : defaultValue
 
-const parseRoll = content => {
-  let match = constants.rollRegex.exec(content.trim())
+const parseOptionRoll = expression => {
+  let match = constants.optionRollRegex.exec(expression.trim())
+
+  let [ 
+    count,
+    modeSize,
+    mode,
+    size,
+    operationModifier,
+    operation,
+    modifier
+  ] = match
+    .slice(1)
+    .map(v => v == undefined ? null : v) // Allow us to merge it easier
+
+  return {
+    count: parseRollInt(count),
+    mode,
+    size: parseRollInt(size),
+    operation,
+    modifier: parseRollInt(modifier),
+    descriptionConditions: pullDescription(expression, match)
+  }
+}
+
+const parseRoll = expression => {
+  let match = constants.rollRegex.exec(expression.trim())
 
   if(match == null)
     return
@@ -24,18 +52,22 @@ const parseRoll = content => {
     modifier
   ] = match.slice(1)
 
-  let description = content.slice(match[0].length)
-
   return {
-    count: count ? parseInt(count) : 1,
+    count: parseRollInt(count, 1),
     mode,
-    size: parseInt(size),
+    size: parseRollInt(size),
     operation,
-    modifier: modifier ? parseInt(modifier) : undefined,
-    descriptionConditions: description && parseDescription(description)
+    modifier: parseRollInt(modifier),
+    descriptionConditions: pullDescription(expression, match)
   }
 }
 
+const pullDescription = (expression, match) => {
+  let description = expression.slice(match[0].length)
+
+  return description && parseDescription(description)
+}
+
 const parseDescription = description => {
   let conditions = [],
       match
@@ -47,14 +79,25 @@ const parseDescription = description => {
 
     let [
       range,
+      upperRangeExpression,
       upperRange,
       content
     ] = match.slice(1)
 
+    let lower = range[0],
+        upper
+
+    if(!upperRange) {
+      // Allow "X-" ranges to represent anything higher than X
+      upper = upperRangeExpression ? Infinity : lower
+    } else {
+      upper = upperRange[0]
+    }
+
     conditions.push({
       range: range && {
-        lower: range[0],
-        upper: upperRange ? upperRange[1] : range[0]
+        lower,
+        upper
       },
       content: content.trim()
     })
@@ -69,10 +112,10 @@ const handleMessage = (message, respond) => {
   if(dice == undefined)
     return // No dice
 
-  handleDice(dice, respond)
+  rollDice(dice, respond)
 }
 
-const handleDice = (dice, respond) => {
+const rollDice = (dice, respond) => {
   if(dice.size > 255) {
     respond('That die is way too big... .-.')
     return
@@ -203,7 +246,14 @@ const registerMacroCommands = async guildId => {
   for await (let [ name, dice ] of macros.iterator() )
     commands.push({
       name,
-      description: elipsify("Roll " + dice.replaceAll('\n', ';'), 100)
+      description: elipsify("Roll " + dice.replaceAll('\n', ';'), 100),
+      options: [
+        {
+          name: "options",
+          description: "Dice, modifiers, or descriptions to apply over the macro",
+          type: 3
+        }
+      ]
     })
 
   await rest.put(
@@ -358,8 +408,20 @@ client.on('interactionCreate', async interaction => {
 
   if(roll) {
     let dice = parseRoll(roll)
+    let optionsRoll = interaction.options.get('options').value
+    
+    if(optionsRoll) {
+      let optionDice = parseOptionRoll(optionsRoll)
 
-    handleDice(dice, content => interaction.reply(content) )
+      for(let [ key, value ] of Object.entries(optionDice)) {
+        if(value)
+          dice[key] = value
+      }
+
+      console.log(dice)
+    }
+
+    rollDice(dice, content => interaction.reply(content) )
   }
 })