Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×

Success/Fails Value from Chat

I'm trying to make a magical item with charges that requires a [[1d6cf<5cs>6]]6 roll into chat. On success, I want to be able to recover max charges in the resource and send a msg like 'Recharge success; , and at failures, send a message like 'Failed, try again'. I found that "Resting in style" gave me some ideas but I just haven't be able to get it to work out.  I am using the 5th Edition OGL by Roll20 Companion and their sheets as well.  Any help or suggestions would be very helpful, I'm pretty new at all this and I've been learning through troubleshooting, but I'm at my wits end on whether or not this is worth it to automate it. (function () { const getClassLevel = function (charId, className) { var s = getAttrByName(charId, 'class_display'); if (s) { var m = s.match(new RegExp(className + " (\\d+)")); if (m && m.length > 0 && m[1]) { return Number(m[1]); } } return 0; }; const checkResource = function (charId, attr, actions, suggestions, restType) { if (!attr || attr.get("current") === "" || attr.get("max") === "") { return; } var name = getAttrByName(charId, attr.get('name') + '_name'); if (!name) { return; } var lcname = normalize(name); const briefRest = function (charId) { var charName = getAttrByName(charId, 'character_name'); var ht = getAttr(charId, "repeating_resource_-MzZAYDrkvWqNuSDiz3E_resource_left"); if (!verifiedCurAndMax(charName, ht, 'Recharge')) { return; } var max_ht = Number(ht.get("max")); var cur_ht = Number(ht.get("current")); var actions = []; var suggestions = []; // Regain Recharge var msg = cur_ht == max_ht ? "You are already at Max Charges." : `&{template:default} {{name=**Recharge**}} {{Charge=[[1d6cf<5cs>6]]6}}`; rollresult = JSON.parse(msg.content) var total = rollresult.total if (1*total < 5){ sendChat("Recharge fail" + charName, msg)}; else if((1*total > 4) && {cur_ht < max_ht}) { ht.setWithWorker({ current: max_ht }); } // Notify player var points = actions.concat(suggestions); if (points.length) { msg += "<ul><li>" + points.join("</li><li>") + "</li></ul>"; } sendChat("Recharge Success " + charName, msg); };
1648908044
GiGs
Pro
Sheet Author
API Scripter
Ypu have a lot of syntax errors (missing closing brackets, using the wrong sort of brackets), and i've done my best to fix them. I havent touched the actual code procedure, though it looks like it might be a bit more complex and specific than needed - for example this line: var ht = getAttr(charId, "repeating_resource_-MzZAYDrkvWqNuSDiz3E_resource_left"); That's only going to one for one specific character. No other character will have the same row ID (this part:-MzZAYDrkvWqNuSDiz3E). Anyway, the syntax corrections (I think): ( function () {             const getClassLevel = function ( charId , className ) {                 var s = getAttrByName ( charId , 'class_display' );                 if ( s ) {                     var m = s . match ( new RegExp ( className + " ( \\ d+)" ));                     if ( m && m . length > 0 && m [ 1 ]) {                         return Number ( m [ 1 ]);                     }                 }                 return 0 ;             };             const checkResource = function ( charId , attr , actions , suggestions , restType ) {                 if (! attr || attr . get ( "current" ) === "" || attr . get ( "max" ) === "" ) {                     return ;                 }                 var name = getAttrByName ( charId , attr . get ( 'name' ) + '_name' );                 if (! name ) {                     return ;                 }                 var lcname = normalize ( name );                 const briefRest = function ( charId ) {                     var charName = getAttrByName ( charId , 'character_name' );                     var ht = getAttr ( charId , "repeating_resource_-MzZAYDrkvWqNuSDiz3E_resource_left" );                     if (! verifiedCurAndMax ( charName , ht , 'Recharge' )) {                         return ;                     }                     var max_ht = Number ( ht . get ( "max" ));                     var cur_ht = Number ( ht . get ( "current" ));                     var actions = [];                     var suggestions = [];                     // Regain Recharge                     var msg = cur_ht == max_ht ? "You are already at Max Charges." : `&{template:default} {{name=**Recharge**}} {{Charge=[[1d6cf<5cs>6]]6}}` ;                     rollresult = JSON . parse ( msg . content );                     var total = rollresult . total ;                     if ( 1 * total < 5 ) {                         sendChat ( "Recharge fail" + charName , msg );                     } else if (( 1 * total > 4 ) && (                             cur_ht < max_ht                         )) {                         ht . setWithWorker ({                             current : max_ht                         });                     }                     // Notify player                     var points = actions . concat ( suggestions );                     if ( points . length ) {                         msg += "<ul><li>" + points . join ( "</li><li>" ) + "</li></ul>" ;                     }                     sendChat ( "Recharge Success " + charName , msg );                 };             };         }); Also seeing const and var in the same function is weird.
1648910091

Edited 1648910273
I guess I should have posted the entire Frankenstein script before. To Clarify, I wanted to use RowID because each player has a magical item that had the same recharge rules. Thanks for updates, I have added them in github, but I still get some errors.&nbsp; <a href="https://gist.github.com/API-Help42/42b39e5f7cb253b5ce932e34a03dbde5" rel="nofollow">https://gist.github.com/API-Help42/42b39e5f7cb253b5ce932e34a03dbde5</a>
1648944523

Edited 1648946043
Oosh
Sheet Author
API Scripter
What exactly did you want the script to do, just from a non-code perspective? It looks like it's doing a bunch more than it needs to, as GiGs said. Is the idea that a player manually rolls the recharge from a macro, and the script reacts to that? Or you want the script to react to a short rest happening, and offer the recharge roll to the player as a button? And then react to the roll? The second option is more work, but for the first one you should be able to chop out a whoooooole heap of code. I'm thinking the steps should run roughly thusly: - player rolls to recharge an item from a macro. You can make this easier on the script by making sure it tells the script what it needs to know - script recognises roll template and keyword, grabs item name, something like {{rname=Recharge Item - &lt;itemname&gt; }} - &lt;itemname&gt; can, of course, come from an attribute reference if the player is going to leave it in the same place, like @{other_resource_name}. The alternative is to hard code the name, "Wand of Silly Walks", which means the player can move the item around and the script will find it wherever it is. - script grabs the inline roll from the same template. If there's going to be multiple rolls in the template, you'll need to label the roll somehow, like {{description=Recharge Roll: [[1d6cf&lt;5cs&gt;6]] &lt;whatever other text&gt;}}. If it's only going to be a single roll, it doesn't matter, it's always roll index 0. - script compares the inline roll to the success number (can also be coded into the roll template if it can change) - report success/failure to the player (or publicly if you prefer) and on a success, add 1 to the resource counter unless at max Is that about right or not what you had in mind? Also, with all the corrections GiGs suggested, it looks like you could benefit from using an IDE to write code in - it'll pick up most of those syntax errors for you.
Yea you're exactly right. That is what I'm looking for.&nbsp; I have very little experience with java so I have been clipping pieces from other scripts that I know work. How they work? I have some clues but mostly I have no idea. So there will be a lot of things I don't want or need but I'm a little scared to remove them (don't want to break it). I'll try the IDE thing you recommended as well for another script I'm working on (separate thing that I want to learn and try out myself). This was obviously too big of a project for me to start out on.
1649035205

Edited 1649036622
Oosh
Sheet Author
API Scripter
I think this one's manageable for you, just need to break it down a little bit. Other people might have other recommendations for using another IDE but I'm just using vsCode (I'm a beginner hobbyist though, not a Pro). Getting an IDE set up properly was more confusing for me than learning coding, to be honest. If you do grab vsCode, make sure you also install ESlint - the base linting for javascript is really sub-par, and ESlint really should be packaged with vsCode as standard. Anyway, on to your script. So there's some useful threads around the place, mostly with Aaron's beardy mug showing up somewhere. Here's a couple of threads from someone starting out on API scripting, you might find them helpful: <a href="https://app.roll20.net/forum/permalink/6605115/" rel="nofollow">https://app.roll20.net/forum/permalink/6605115/</a> <a href="https://app.roll20.net/forum/permalink/6584105/" rel="nofollow">https://app.roll20.net/forum/permalink/6584105/</a> One of them also has Aaron's API script boilerplate posted in it. This is an outdated one, but it'll suit our purposes. Unless you have something advanced in mind, you can pretty much always follow this structure: const NAME = (() =&gt; { // eslint-disable-line no-unused-vars &nbsp; const version = '0.1.0' ; &nbsp; const lastUpdate = 1531675536 ; &nbsp; const schemaVersion = 0.1 ; &nbsp; const checkInstall = () =&gt; &nbsp;{ &nbsp; &nbsp; &nbsp; log ( '-=&gt; NAME v' + version + ' &lt;=- &nbsp;[' +( new Date ( lastUpdate * 1000 ))+ ']' ); &nbsp; &nbsp; &nbsp; if ( ! state . hasOwnProperty ( 'NAME' ) || state . NAME . version !== schemaVersion ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log ( ` &nbsp;&gt; Updating Schema to v ${ schemaVersion } &lt;` ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state . NAME = { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; version : schemaVersion &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }; &nbsp; &nbsp; &nbsp; } &nbsp; }; &nbsp; const handleInput = ( msg ) =&gt; { &nbsp; &nbsp; &nbsp; if ( msg . type !== "api" ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return ; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; let args = msg . content . split ( /\s + / ); &nbsp; &nbsp; &nbsp; switch ( args [ 0 ]) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case '!NAME' : &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break ; &nbsp; &nbsp; &nbsp; } &nbsp; }; &nbsp; const registerEventHandlers = () =&gt; { &nbsp; &nbsp; &nbsp; on ( 'chat:message' , handleInput ); &nbsp; }; &nbsp; on ( 'ready' , () =&gt; { &nbsp; &nbsp; &nbsp; checkInstall (); &nbsp; &nbsp; &nbsp; registerEventHandlers (); &nbsp; }); &nbsp; return { &nbsp; &nbsp; &nbsp; // Public interface here &nbsp; }; &nbsp; })(); One of the threads above has a thorough breakdown on what each part of the script does, but basically: checkInstall() - This is for versioning your script, and for setting initial configuration on the {state} object. This is just an API-facing object which persists between sandbox restarts. The primary way of holding config info for scripts. We'll ignore this step for now. handleInput() - This is generally the primary event handler in a script, and reacts to a chat message being posted to chat. registerEventHandlers() - this is mostly for readability on larger scripts, an index of trigger points into your internal functions. Not strictly necessary, especially on smaller scripts. on('ready', ...) - this event is fired by the API when it finishes loading all the resources it needs. You can put code before this event, but unless you have a specific reason to, you generally want all your code to be behind this event. This is why Aaron has the registerEventHandlers() function gated behind the on('ready') ... it ensures none of the entry points to the internal functions are live until the API has finished loading. return {} - the return section here is where you can expose any internal functions of variables you want people to be able to access from the sandbox. With the revealing module pattern, the only thing visible from an outer context is NAME on the first line. If you wanted other scripts to be able to call the handleInput() function, you would need to put that inside the return (and you can optionally define a new reference for it), so return { accessHandleInput: handleInput() } would allow other scripts to invoke the handleInput() function by the reference NAME.accessHandleInput(). We won't need this for now. Fully understanding this pattern is pretty advanced - don't worry if it's not making a huge amount of sense. The important thing for now is the syntax on the first and last lines that wrap the boilerplate. Don't mess up any of those braces or parentheses, or it won't work. So, with that out of the way, a rough outline of your script might look like this... I did totally forget to ask which system you're using. But this is a 5e example of a recharge macro you might use, just with @{selected} calls for now. I've thrown the word 'rechargeBot' in between the template properties - this gives us a keyword to look for in the API script which isn't displayed in chat. It's preferable to just searching for the word 'recharge' since that could appear in other, unrelated chat: &amp;{template:npcaction} rechargeBot {{rname=Recharge Item: @{selected|character_name}}} {{description=Item: **@{selected|other_resource_name}** Roll: [[1d6cf&lt;5cs6]]}} And then an outline of the script: const rechargeBot = (() =&gt; { // eslint-disable-line no-unused-vars &nbsp; const version = '0.1.0' ; &nbsp; const getRechargeData = ( msg ) =&gt; { &nbsp; &nbsp; // This function grabs the player, character, item and roll details from the macro posted to chat; &nbsp; &nbsp; // Returns an object holding everything we need from the recharge macro &nbsp; &nbsp; // example output: &nbsp; &nbsp; // &nbsp;{ &nbsp; &nbsp; // &nbsp; &nbsp;characterName: 'bob', &nbsp; &nbsp; // &nbsp; &nbsp;characterId: '-sd8vh13hWsdgnoisdg', &nbsp; &nbsp; // &nbsp; &nbsp;playerId: '-sdlngs89yhtg342o8gh', &nbsp; &nbsp; // &nbsp; &nbsp;itemName: 'wand of silly walks', &nbsp; &nbsp; // &nbsp; &nbsp;rollIndex: 0 &nbsp; &nbsp; // &nbsp;} &nbsp; &nbsp; &nbsp; &nbsp; // If anything goes wrong it returns null; &nbsp; } &nbsp; const checkRechargeRoll = ( inlineRolls , rollIndex , targetNumber = 6 ) =&gt; { &nbsp; &nbsp; // This function takes the inline rolls from the posted chat message, and the index we supply it to check if &nbsp; &nbsp; // the recharge was successfull &nbsp; &nbsp; // We've supplied a default parameter here with targetNumber=6. That means you don't need to supply it, but have the &nbsp; &nbsp; // option to override it if you wish &nbsp; &nbsp; // has three returns: true (recharge successful), false (recharge unsuccessful), null (an error occurred) &nbsp; } &nbsp; const findResourceCount = ( resourceName ) =&gt; { &nbsp; &nbsp; // This function takes the resource name as input (e.g. 'wand of fireballs') and locates the required attributes &nbsp; &nbsp; // You *could* hard-code this to @{other_resource} or similar, but then the script can never handle a &nbsp; &nbsp; // character with two wands. Much better to be able to search by resource name, then we can find multiple &nbsp; &nbsp; // resources in other_resource, class_resource, or the repeating_resource section below &nbsp; &nbsp; // returns an object containing the resource attributes required. &nbsp; &nbsp; // the fastest way to set an attribute is with it's unique id, so we'll return that with the data &nbsp; &nbsp; // example return of a resource located at other_resource: &nbsp; &nbsp; // &nbsp; &nbsp; // &nbsp;{ &nbsp; &nbsp; // &nbsp; &nbsp;name: 'other_resource', &nbsp; &nbsp; // &nbsp; &nbsp;id: '-NADSFUHg8sdhq3oh98ghs', &nbsp; &nbsp; // &nbsp; &nbsp;current: 1, &nbsp; &nbsp; // &nbsp; &nbsp;max: 3 &nbsp; &nbsp; // &nbsp;} &nbsp; } &nbsp; const rechargeItem = ( resourceData ) =&gt; { &nbsp; &nbsp; // This function does some error checking, then recharges the item if valid &nbsp; &nbsp; // e.g. check if the current charges is a number, or if the item is already at max &nbsp; &nbsp; // instead of returning a value from this function, we'll send the result to chat &nbsp; &nbsp; // with the 'sendResponse()' function below. &nbsp; &nbsp; // That will be the end point of a full run of the script &nbsp; } &nbsp; const sendResponse = ( content , chatTarget ) =&gt; { &nbsp; &nbsp; // If no char target is supplied, post the message publicly &nbsp; &nbsp; const messageTarget = ( chatTarget ) ? `/w " ${ chatTarget } " ` : `` ; // this is a ternary statement - a shorthand if/else. &nbsp; &nbsp; sendChat ( 'rechargeBot' , ` ${ messageTarget } ${ content } ` ); // these ${myVar} are template literals, an easy way to insert variables into Strings &nbsp; &nbsp; // Template literals require using ` backticks ` around them. I highly recommend getting used to them, as you can freely use &nbsp; &nbsp; // other punctuation (like '' and "") inside the backticks without messing up the code. &nbsp; } &nbsp; const handleInput = ( msg ) =&gt; { &nbsp; &nbsp; log ( msg ); &nbsp; &nbsp; // When you're starting out, LOG EVERYTHING! Roll20 can do some unexpected things, and it'll take forever to &nbsp; &nbsp; // bug hunt if you don't have logs. For example, someone unfamiliar with Roll20 might assume our parameter &nbsp; &nbsp; // here 'msg', is a String. It isn't and trying to use a String method will cause a crash. &nbsp; &nbsp; // 'msg' has a bunch of useful keys on it, check the wiki 'api chat events' section for details &nbsp; &nbsp; &nbsp; if ( msg . type === "api" &amp;&amp; msg . content . indexOf ( 'rechargeBot' ) &gt; -1 ) { &nbsp; &nbsp; &nbsp; &nbsp; // Recharge macro output found! The script moves to the main functions... &nbsp; &nbsp; &nbsp; &nbsp; const rechargeData = getRechargeData ( msg ); &nbsp; &nbsp; &nbsp; &nbsp; log ( rechargeData ); &nbsp; &nbsp; &nbsp; &nbsp; if ( rechargeData == null ) { &nbsp; // Keep error checking as we move through the script to prevent crashes down the line. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return ; // (myVar == null) is a handy shortcut for checking if something is null OR undefined &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; // Check if the roll was successful... &nbsp; &nbsp; &nbsp; &nbsp; const successfulRoll = checkRechargeRoll ( msg . inlineRolls , rechargeData . rollIndex ); &nbsp; &nbsp; &nbsp; &nbsp; log ( successfulRoll ); &nbsp; &nbsp; &nbsp; &nbsp; if ( successfulRoll == null ) sendResponse ( 'An error occurred' , msg . who ); //msg.who holds the name of the person who posted the message &nbsp; &nbsp; &nbsp; &nbsp; else if ( successfulRoll === false ) sendResponse ( 'Roll failed' , msg . who ); &nbsp; &nbsp; &nbsp; &nbsp; else if ( successfulRoll === true ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Hooray! The player got a good outcome and the script gets to do more stuff! &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const itemCharges = findResourceCount ( rechargeData . itemName ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log ( itemCharges ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ( itemCharges == null ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendResponse ( `Couldn't find item in Resources: " ${ rechargeData . itemName } "` , msg . who ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rechargeItem ( itemCharges ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; // The script will silently end here if we don't find our trigger word, which is fine. &nbsp; &nbsp; &nbsp; // But you may want to put an 'else' branch in here anyway, just for peace of mind while you're learning &nbsp; &nbsp; &nbsp; else { &nbsp; &nbsp; &nbsp; &nbsp; // This will make it obvious that the script executed when you check the API log &nbsp; &nbsp; &nbsp; &nbsp; log ( `rechargeBot didn't find a recharge macro.` ); &nbsp; &nbsp; &nbsp; } &nbsp; }; &nbsp; on ( 'ready' , () =&gt; { &nbsp; &nbsp; &nbsp; on ( 'chat:message' , handleInput ); &nbsp; }); })(); There's a whole heap of comments in there, but hopefully that breaks down the task into some more understandable chunks. The functions are (mostly) written in reverse order - the entry point is down the bottom, the next function to run is handleInput(), which is second-to-bottom and so on. This isn't necessary in every instance due to the revealing module structure of the outer wrapper - you *can* put handleInput() above the other functions if you wish. But I'd recommend trying to stick to the normal order until you understand the pattern a bit better - and the general syntax is you need to declare a function earlier in the script than when you invoke it. One other thing about this example handleInput() - it's pretty heavily loaded. That's not a big concern for this script, as we're only watching for one specific input. However, if your script has a bunch of different inputs to react to, you would absolutely want to move almost all of that code out of handleInput, and into something like handleRechargeMacro(). This lets you keep your handleInput() function as a clean and readable index of the paths through your script: const handleInput = ( msg ) =&gt; { &nbsp; if ( msg . type === "api" ) { &nbsp; &nbsp; if ( msg . content . indexOf ( 'rechargeBot' ) &gt; - 1 ) handleRecharge (); &nbsp; &nbsp; else if ( msg . content . indexOf ( 'blahblah' ) &gt; - 1 ) handleBlahblah (); &nbsp; &nbsp; else if ( msg . content . indexOf ( 'spelllevel=' ) &gt; - 1 ) handleSpellcast (); &nbsp; } }
1649107028

Edited 1649167174
Oosh, thank you so much for the well thought out response. This has actually helped me better understand the other scripts I have as well. I'm going to keep working on those, but this saves me a whole lot of time. I think I would have developed a pretty bad habit had I not gone to the Forums.&nbsp; Unfortunately, I tried it out and received the problem people are talking about currently on the forum. This is most likely a very different problem then your script as your logic doesn't include any of these in the log. Ill try again when this is fixed. "SyntaxError: Unexpected token '.' at eval (&lt;anonymous&gt;) &nbsp; &nbsp;at messageHandler (evalmachine.&lt;anonymous&gt;:713:6) &nbsp; &nbsp; at process.&lt;anonymous&gt; (/home/node/d20-api-server/node_modules/tiny-worker/lib/worker.js:65:55) &nbsp; &nbsp; at process.emit (events.js:314:20) &nbsp; &nbsp; at emit (internal/child_process.js:877:12) &nbsp; &nbsp; at processTicksAndRejections (internal/process/task_queues.js:85:21)" Edit: &nbsp;So I tried to change some names, but then it wasn't working. So I went back and started from scratch, and rolled twice. It looks like in the log, its trying to find an attribute, but the attribute is changing each time I roll? I rolled a few more times, and yea a unique code each and every time? I inspected the sheet, and that's definitely not changing. The playerid isnt either. This is the default script and a slightly different macro roll&nbsp; 1d6cf&lt;4cs&gt;5 &nbsp;and was working before so I have no idea. Roll: $[[0]]}}","inlinerolls":[{"expression":"1d6cf&lt;4cs&gt;5","results":{"resultType":"sum","rolls":[{"dice":1,"mods":{"customCrit":[{"comp":"&gt;=","point":5}],"customFumble":[{"comp":"&lt;=","point":4}]},"results":[{"v":3}],"sides":6,"type":"R"}],"total":3,"type":"V"},"rollid": "-MztyM-ztEGFPPDWmgNJ" ,"signature":"941162c2efcbf58885feb0b88b8563500f378d879bf2852136a03f6888f35aed11f3f3428e55e82100eca131d218ce5e7b8f57ffc9067ea4f0f6cf4f8ae377c5"}],"playerid":"-MwT6kbq3-yZfyiKHGZm","rolltemplate":"npcaction","type":"general","who":"Frank O. (GM)"} "rechargeBot didn't find a recharge macro." Roll: $[[0]]}}","inlinerolls":[{"expression":"1d6cf&lt;4cs&gt;5","results":{"resultType":"sum","rolls":[{"dice":1,"mods":{"customCrit":[{"comp":"&gt;=","point":5}],"customFumble":[{"comp":"&lt;=","point":4}]},"results":[{"v":6}],"sides":6,"type":"R"}],"total":6,"type":"V"},"rollid": "-MztykT2pVg_CmWSvh7A" ,"signature":"65bfb8e93a30b52142f4bcfe0eb4be329bca5c3978194a75644fbf0973c2bdc68403245f97d9d233ca4743a086f815f6bde8e1d617a6f55d04447b4d1dc0f8d9"}],"playerid":"-MwT6kbq3-yZfyiKHGZm","rolltemplate":"npcaction","type":"general","who":"Frank O. (GM)"} "rechargeBot didn't find a recharge macro."
Ok after some digging around, it looks like I found some culprits. If we split the below logic into two separate, we get different errors. if (msg.type === "api" &amp;&amp; msg.content.indexOf('rechargeBot') &gt; -1) { This first logic: if (msg.type === "api") {&nbsp; Gives us "rechargeBot didn't find a recharge macro" This second logic: if (msg.content.indexOf('rechargeBot') &gt; -1)&nbsp; Gives us "undefined". So it looks like the script thinks that msg.type is not equal to the api or cant find it. If we ignore that for a moment, and focus on the "undefined" aspect, we find: if (rechargeData == null) . Since const rechargeData = getRechargeData(msg) &nbsp;and const getRechargeData = (msg) , I can only assume that&nbsp;there must be something wrong with the (msg) or its not drawing from there. Which made me think of the macro. I tried a variety of different things and even added !recharge bot; nothing changed.&nbsp; If we look at the logs, it very clearly IS looking at the msg from this input. const handleInput = (msg) &nbsp; const handleInput = (msg) =&gt; { &nbsp; &nbsp; log(msg); and the log is sending out all the information we need. So now I am seriously scratching my head. Can (msg) be both handleInput AND&nbsp; getRechargeData . I am on the right track? Or am I way off? Im trying to logic this out and my head is spinning.
1649208805

Edited 1649218610
Oosh
Sheet Author
API Scripter
Whoops! Sorry, I messed that up. So used to "!myScript --doThings" API calls that I automatically put that line in, even though we don't want it here. Sorry about that! We definitely do not want to check that the msg.type === 'api', because that only applies to messages that start with the ! control character (like a command line input for a script). You can completely remove it, or listen for another condition instead. For specific template output, either msg.inlinerolls or msg.rolltemplate are a couple of keys to help filter out unwanted messages. This isn't strictly necessary - the msg.content search is probably enough - but it does make the script a little more efficient. A simple check to replace the type/API one, would be to check that the message is using the roll template we're expecting: if (msg.rolltemplate === 'npcaction' &amp;&amp; msg.content.indexOf('rechargeBot') &gt; -1) { Sorry for wasting your time with that bogus check - I wasn't really thinking at the time of that example script actually running in the sandbox, otherwise I might have tested it! :) This should be a more useful way to get going. It's just a skeleton, but it should hopefully get as far as posting a chat message saying it can't find any recharge data: const rechargeBot = (() =&gt; { // eslint-disable-line no-unused-vars &nbsp; const scriptName = `rechargeBot` ; &nbsp; const version = '0.1.0' ; &nbsp; // Pull the required data from the 'recharge item' chat message &nbsp; const getRechargeData = () =&gt; { &nbsp; &nbsp; return null ; &nbsp; } &nbsp; // Check if the recharge roll was successful &nbsp; const checkRechargeRoll = () =&gt; { &nbsp; &nbsp; return null ; &nbsp; } &nbsp; // Grab the required attributes from the Resource section on the character sheet &nbsp; const findResourceCount = () =&gt; { &nbsp; &nbsp; return null ; &nbsp; } &nbsp; // Recharge the item &nbsp; const rechargeItem = () =&gt; { &nbsp; &nbsp; return null ; &nbsp; } &nbsp; // Main function, calls all of the above function and controls conditional logic &nbsp; const processRecharge = () =&gt; { &nbsp; &nbsp; const rechargeData = getRechargeData (); &nbsp; &nbsp; log ( rechargeData ); &nbsp; &nbsp; const rollSuccessful = checkRechargeRoll (); &nbsp; &nbsp; log ( rollSuccessful ) &nbsp; &nbsp; const itemCharges = findResourceCount (); &nbsp; &nbsp; log ( itemCharges ); &nbsp; &nbsp; const rechargeResult = rechargeItem (); &nbsp; &nbsp; log ( rechargeResult ); &nbsp; &nbsp; if ( rechargeData == null ) utils . sendResponse ( `An error occurred - no recharge data retrieved from chat message.` ) &nbsp; } &nbsp; // Watch chat for a trigger message &nbsp; const handleInput = ( msg ) =&gt; { &nbsp; &nbsp; log ( msg ); &nbsp; &nbsp; if ( msg . rolltemplate === 'npcaction' &amp;&amp; msg . content . indexOf ( 'rechargeBot' ) &gt; - 1 ) { &nbsp; &nbsp; &nbsp; utils . sendResponse ( `Recharge macro was detected.` ); &nbsp; &nbsp; &nbsp; processRecharge ( msg ); // ==&gt; Pass to main function &nbsp; &nbsp; } else { &nbsp; &nbsp; &nbsp; log ( `rechargeBot didn't find a recharge macro.` ); &nbsp; &nbsp; } &nbsp; }; &nbsp; // (Optional) utility or helper functions which aren't script-specific &nbsp; const utils = (() =&gt; { &nbsp; &nbsp; // A convenience wrapper for Roll20's sendChat() function &nbsp; &nbsp; // Removes the (GM) tag if present, as this doesn't work in sendChat &nbsp; &nbsp; // Whisper to 'chatTarget' if supplied, otherwise post public message &nbsp; &nbsp; const sendResponse = ( content , chatTarget ) =&gt; { &nbsp; &nbsp; &nbsp; chatTarget = chatTarget ? ` ${ chatTarget } ` . replace ( ` (GM)` , '' ) : null ; &nbsp; &nbsp; &nbsp; const messageTarget = ( chatTarget ) ? `/w " ${ chatTarget } " ` : `` ; &nbsp; &nbsp; &nbsp; sendChat ( scriptName , ` ${ messageTarget } ${ content } ` ); &nbsp; &nbsp; } &nbsp; &nbsp; return { &nbsp; &nbsp; &nbsp; sendResponse: sendResponse &nbsp; &nbsp; } &nbsp; })(); &nbsp; // Initialise script &nbsp; on ( 'ready' , () =&gt; { &nbsp; &nbsp; on ( 'chat:message' , handleInput ); &nbsp; &nbsp; log ( ` ${ scriptName } v ${ version } - test version initialised` ); &nbsp; }); })(); I've ripped out all that commenting, and put some chat responses in so there's less flicking between the API console and Campaign. Chat logging gets annoying very quickly, but will do the job for now :) This version should be a much better starting point - we can fill in the functions one by one, they're currently just placeholders. We'll move all that conditional logic out of handleInput() , and into its own function processRecharge() , since it's better practice. I've also moved the sendResponse() function into another Module Pattern construct. This could be called utils, helpers, or something similar, and is a good place to put helpful, reusable little functions that make your life easier, and aren't specifically for the script you're writing. As for the questions about msg , hopefully all that will become clearer as we move through the script filling in functions. But it essentially, the object we get from Roll20 looks like this: const msg = { &nbsp; content : " rechargeBot {{rname=Recharge Item: ws}} {{description=Item: **Sticks**&lt;br/&gt;Roll: $[[0]]}}" , &nbsp; inlinerolls : [{ &nbsp; &nbsp; expression : "1d6cf&lt;5cs6" , &nbsp; &nbsp; results : [ /* SNIP */ ], &nbsp; &nbsp; rollid : "-MzwWUx1UZNTQq7Talww" , &nbsp; &nbsp; signature : "a33908ad21f12c242d348afc7e2a11fb10c8cf639b0b7381d63f9500634a145de7893fcf344cb43c81ab56eb6f80b12f89c530cff14a8bf1ccc916db62883bc9" , &nbsp; &nbsp; tdseed : 5052019603303414000 &nbsp; }], &nbsp; playerid : "-MKCUiniZDG7msdzzvft" , &nbsp; rolltemplate : "npcaction" , &nbsp; type : "general" , &nbsp; who : "Oosh (GM)" } I've snipped out the results from the inlineroll, just to save some space. This chat message is the primary data the script will work with, and most API scripts will pass this around to different functions so they can grab the bits they need and do stuff with them. Roll20 passes this object to the event handler (which we've called handleInput() , but can be called anything... this is just Roll20 custom). Our event handler function then names the object whatever it likes - most scripts just call it 'msg' since it's short, traditional, but fairly clearly short for 'message'. If you want to be more descriptive, you could call it messageObject or something, to remind yourself that it's a data object and not a String. You'd simply change the function declaration: &nbsp; const handleInput = ( messageObject ) =&gt; { and then everywhere through that function where we refer to msg , would need to be changed to messageObject . The data we're referencing remains the same (a Roll20 chat message object, passed to us by Roll20's functions), we're just changing the variable name we use inside our function to refer to it. Hopefully that makes sense - the message data all come from Roll20, but the traditions in scripts of using 'handleInput()' and 'msg' are just naming traditions. Anyway, let me know how you go - if the above works better and progresses to the whispered chat message in the sidebar, we can move on to filling some functions in.
1649685794

Edited 1649697243
Finally found some free time this week and got to messing with the code.&nbsp;I got the messages to whisper, but I cant seem to get the heading to whisper.
1649704357
timmaugh
Roll20 Production Team
API Scripter
If by "heading" you mean the message in blue (in your image), I don't think that is being generated by your script. This gets into the difference between an API message and a normal message... but the long and short of it is that the message sent using the npcaction template is sent already, and is intended to hit the chat interface as a public announcement. Then, Oosh's example code, takes action if it detects that the npcaction template was used and the term rechargeBot was included. In that case, it sends the whispered output. But it is listening after-the-fact . The original message has already been sent. To change the initial message, you'd have to change the macro/ability that is generating that message so that it utilized a whispered output.