edit - or use Scriptcards :) Sure, so the inline rolls in a chat message are stored in the message object which is what triggers the API's on('chat:message') listener, and we're passing that to the rest of the script as the parameter (msg). More information on the chat message event here , you'll need to click the 'expand' button to see the example inlinerolls object. It's an array of data, essentially one for each roll as the parser breaks it up, though each roll can be split into sub rolls (like the [[1d6 + 2d6]] example above) which further complicates things. You can deal with these subrolls with another level of iteration, but if you're not planning on using rolls like that there's not much point adding the complexity. What this part does: let atkRoll = msg.inlinerolls.find(r => r.expression.match(/(\dd6)/i)); if (!atkRoll) {sendChat('Roller', `Couldn't find valid Xd6 attack roll`); return} let atkDice = atkRoll.results.rolls[0].results .filter(res=>!res.d) .map(res=>res.v) .sort((a,b) => b-a ==> b+a); The first line is finding the first roll at the top level of the array with an expression matching <digit>d6. If you have multiple d6 rolls and the attack roll is not the first one, this is going to grab the wrong roll and would need to be refined a bit. The next line is just a check to make sure we don't get errors further down if atkRoll is empty. The following part will make more sense if you check out the example roll object. Another good way to check it out is to add a log line in there: on('ready', () => { on('chat:message', (msg) => { if (msg.inlinerolls && msg.content.match(/weapon: (high|mid|low)/i)) { log(msg.inlinesrolls); ... This will throw the rolls into the API log each time the script is triggered. Unfortunately it's not formatted at all in the API log - you can either copy & paste it into an IDE, or open your browser console with F12, and paste it into the command console.log(...paste...), then it'll be formatted as an array of objects with drop-down arrows to explore. So those last few lines are first grabbing the rolls from the way the roll data is structured with results.rolls[0].results - rolls[0] is grabbing the first (zero-indexed) subroll from the array, and the second "results" is yet another array of the individual die results of that subroll. This is an example of what that might look like: "results": [ {"v": 6}, {"v": 5}, {"v": 6}, {"v": 3, "d": true} ] So the filter function removes any object which has a key called 'd' and has a truthy value. This is how a dropped die from "4d6dl1" is stored, so it'll filter those out. We should then have an array like this: "results": [{"v": 6}, {"v": 5}, {"v": 6}] The map function grabs the value of the 'v' key from the object - this is just cleaning up the data so we've got a simple array of numbers instead of key/value pairs. So now we have this: [6,5,6] The sort function then sorts an array depending on the compare function provided. I've used shorthand syntax there, but it's a function with 2 parameters which sorts two values depending on whether the function returns a negative, positive or zero result. That (a,b) => b-a is really function(a,b) {return b - a} , which means if a is bigger than b , sort a first - descending order. For a normal ascending order, you'd use a - b . So now we have [6,6,5] - provided there are 3 dice in there, we know that index 0 is the highest (or equal highest, the sort function does nothing for a zero-result), index 1 is the middle and index 2 is the lowest (or, again, equal). So if you want the extreme version in there, there's a few ways to go about that. The dmgIndex line is another syntactic shortcut called a ternary, it's basically a bunch of nested if() statements, and probably not ideal for understanding what's going on. It'll make more sense as traditional if statements: let damage; if (/extreme/i.test(weaponType)) damage = atkDice[0] + atkDice[1]; else if (/high/i.test(weaponType)) damage = atkDice[0]; else if (/mid/i.test(weaponType)) damage = atkDice[1]; else if (/low/i.test(weaponType)) damage = atkDice[2]; else {sendChat('Roller', `Unexpected weapon damage category!`); return} So there's a couple of issues with what you've tried above - firstly there's an extra | pipe in the regular expression at the top which is going to let everything containing "weapon: " through, as it's included an empty string as a match option, and a string is made up of an infinite number of empty strings. I won't go into regular expressions, but here's a link if you're interested. They're very useful for searching for triggers, as you can make them case insensitive and be as specific or vague as you like in how or where the characters appear in the input. The addition to the sort() function isn't legal either, the => fat arrow syntax is a way of declaring a function, but you can't add ==> one of these on the end. In terms of doing more things with the number once you've extracted it: absolutely. You can easily do whatever you want inside the script, but since this is tabletop and not a video game, you generally want to make it visible to the players. You're free to throw the numbers back to the chat parser via the sendChat() function, as all the Javascript variables will be replaced by numbers/strings/whatever you have stored in there, before Roll20 sees it again. Here's an updated version with the 'extreme' tag, a fancied up output (with some totally made up damage rules as an example), and relaxed the weapon search term to it doesn't need a colon or space after it, just any of the categories appearing somewhere after the word 'weapon' in the same chat message will do: on('ready', () => { on('chat:message', (msg) => { if (msg.inlinerolls && msg.content.match(/weapon.*(extreme|high|mid|low)/i)) { let weaponType = msg.content.match(/weapon.*(extreme|high|mid|low)/i)[1]; let atkRoll = msg.inlinerolls.find(r => r.expression.match(/(\dd6)/i)); if (!atkRoll) {sendChat('Roller', `Couldn't find valid Xd6 attack roll`); return} let atkDice = atkRoll.results.rolls[0].results .filter(res=>!res.d) .map(res=>res.v) .sort((a,b) => b-a); log(atkDice); if (atkDice.length < 3) {sendChat('Roller', `A minimum of 3 dice must be rolled/kept!`); return} let damage; if (/extreme/i.test(weaponType)) damage = atkDice[0] + atkDice[1]; else if (/high/i.test(weaponType)) damage = atkDice[0]; else if (/mid/i.test(weaponType)) damage = atkDice[1]; else if (/low/i.test(weaponType)) damage = atkDice[2]; else {sendChat('Roller', `Unexpected weapon damage category!`); return} sendChat('Roller', `&{template:default} {{name=Damage Roll}} {{Damage=Double damage (crit) + Bless<br>[[2*${damage}[${weaponType}]+1d4[Bless]]]!}}`); } }); });