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 .
×

Two Issues with Creating a Macro Chat Template in an API/Mod

Issue 1&nbsp; I am relearning API/Mods. Because I'm adding more conditional features for a character's dexterity and sheet notes, I need to translate this template from a macro script: into a javascript API/Mod.&nbsp; What I've found via Google does not seem to work, especially the API equivalent of selected character colour. This link, <a href="https://help.roll20.net/hc/en-us/articles/360037256754-API-Chat#API:Chat-chat:message" rel="nofollow">https://help.roll20.net/hc/en-us/articles/360037256754-API-Chat#API:Chat-chat:message</a> &nbsp;refers to JSON.Parse, about which I know nothing about so I find the examples not helpful. Is there anything out there to help someone who is relearning API/Mods? Issue 2 My other issue is how do I make the output, or checkoutput, from sendChat("System", checkoutput) into something that I can put into the template? A snippet of my API which works very well with respect to sendChat output is as follows: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Tim - dealing with output if (checktype === "dex") { //sendChat("System", "checktype command is for dexterity"); //sendChat("System", "Saving Throw Bonus: " + getAttrByName(character.id,"armorbonus")); checkresult = getAttrByName(character.id,"armorbonus"); if (checkresult == "0") { checkoutput = "No possible dexterity bonus/penalty."; } else if (parseFloat(checkresult) &lt; 0) { checkoutput = "**Dexterity**: Check with DM if your " + checkresult + " bonus applies."; } sendChat("System", checkoutput); return; } else if (checktype === "st") { //sendChat("System", "dexcheck checktype is saving throw notes"); //sendChat("System", "Saving Throw Notes: " + getAttrByName(character.id,"savingthrow_notes")); checkresult = getAttrByName(character.id,"savingthrow_notes"); if (checkresult == "") { checkoutput = "**Saving Throw Notes**: N/A"; } else { checkoutput = "**Saving Throw Notes**: " + checkresult; } sendChat("System", checkoutput); return; } else if (checktype === "mr") { //sendChat("System", "dexcheck checktype is magic resistance notes"); //sendChat("System", "Magic Resistannce Notes: " + getAttrByName(character.id,"magic_resistance")); checkresult = getAttrByName(character.id,"magic_resistance") if (checkresult == "") { checkoutput = "**Magic Resistance Notes**: N/A"; } else { checkoutput = "**Magic Resistance Notes**: " + checkresult; } sendChat("System", checkoutput); return; } else { //sendChat("System", "After writing **!dexcheck** you must add a space and include: **dex** for dexterity bonus; **st** for saving throw notes; or **mr** for magic resistance notes."); checkresult = ""; sendChat("System", checkoutput); return; } }
1778530049
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Hi Tim! Without knowing more specifics, the gist is that you can send styled html through sendchat. All styling must be done inline, as API scripts do not have a mechanism to handle classes. Avoid real returns in your html code, as they will be interpreted as separate messages and break the output. Use &lt;br&gt; or &lt;p&gt;. When styling, avoid pure black or white, as these can be co-opted by dark mode/light mode. Use #111111 and #eeeeee for example. I typically handle all styling in a css object with a different style defined by each property in the variable. Here's one: &nbsp; &nbsp; const CSS = { &nbsp; &nbsp; &nbsp; &nbsp; container: "background:#1f1f1f;border:2px solid #444;border-radius:8px;padding:10px;color:#eee;font-size:13px;", &nbsp; &nbsp; &nbsp; &nbsp; header: "font-size:16px;font-weight:bold;margin-bottom:8px;color:#fff;", &nbsp; &nbsp; &nbsp; &nbsp; section: "margin-top:8px;", &nbsp; &nbsp; &nbsp; &nbsp; sectionTitle: "font-weight:bold;color:#ffd700;margin-bottom:4px;", &nbsp; &nbsp; &nbsp; &nbsp; item: "margin-left:10px;margin-bottom:4px;", &nbsp; &nbsp; &nbsp; &nbsp; link: "display:inline-block;background:#2d6cdf;color:#fff;padding:2px 6px;margin:2px 2px 0 0;border-radius:4px;text-decoration:none;font-size:11px;font-weight:bold;border:1px solid #1f4ea3;", &nbsp; &nbsp; &nbsp; &nbsp; linkArchived: "display:inline-block;background:#555;color:#ccc;padding:2px 6px;margin:2px 2px 0 0;border-radius:4px;text-decoration:none;font-size:11px;font-weight:bold;border:1px solid #333;", &nbsp; &nbsp; &nbsp; &nbsp; count: "color:#aaa;", &nbsp; &nbsp; &nbsp; &nbsp; none: "color:#8fbc8f;font-weight:bold;" &nbsp; &nbsp; }; Then, when formatting output, you can call the style, like so: &lt;div style="${CSS.sectionTitle}"&gt;Character Sheets&lt;/div&gt; This keeps all of your css nicely centralized and editable. On small scripts, you may not need this much detail, but it does make the script easier to read than: &lt;div style="font-weight:bold;color:#ffd700;margin-bottom:4px;"&gt;Character Sheets&lt;/div&gt;` Also, check out the help center section on sendChat .&nbsp; If you want to dissect an existing script, you can use a tool like ChatGPT or Claude to analyze the parts and walk you through it, explaining what each bit does, and drilling down on anything potentially confusing. Be sure to specify that you are analyzing a Roll20 mod script, and beware AI hallucinations! :)
keithcurtis said: Hi Tim! Without knowing more specifics, the gist is that you can send styled html through sendchat. All styling must be done inline, as API scripts do not have a mechanism to handle classes. Avoid real returns in your html code, as they will be interpreted as separate messages and break the output. Use &lt;br&gt; or &lt;p&gt;. I really appreciate it Keith. The actual specifics are in <a href="https://app.roll20.net/forum/post/12741850/recreating-a-char-box-used-in-an-api-slash-mod" rel="nofollow">https://app.roll20.net/forum/post/12741850/recreating-a-char-box-used-in-an-api-slash-mod</a> &nbsp;which is this same question in a more convoluted way - I reposted here because I thought maybe I had put too much into it. :) But I think I can work with what you've given me here, for not. Thanks so much.
keithcurtis said: Then, when formatting output, you can call the style, like so: &lt;div style="${CSS.sectionTitle}"&gt;Character Sheets&lt;/div&gt; Hi Keith, so I set up your CSS constant at the beginning of my API, soon after other variabls are set: if (msg.type === "api" &amp;&amp; chatcommand.indexOf("!test") === 0) { if (!msg.selected) { sendChat("System", "---------"); sendChat("System", "Please select a token first."); return; } // Map through selected items to find character objects let characters = msg.selected .map(obj =&gt; getObj('graphic', obj._id)) // Get the token object .filter(token =&gt; token &amp;&amp; token.get('represents') !== "") // Check if it represents a character .map(token =&gt; getObj('character', token.get('represents'))); // Get the character object const CSS = { container: "background:#1f1f1f;border:2px solid #444;border-radius:8px;padding:10px;color:#eee;font-size:13px;", header: "font-size:16px;font-weight:bold;margin-bottom:8px;color:#fff;", section: "margin-top:8px;", sectionTitle: "font-weight:bold;color:#ffd700;margin-bottom:4px;", item: "margin-left:10px;margin-bottom:4px;", link: "display:inline-block;background:#2d6cdf;color:#fff;padding:2px 6px;margin:2px 2px 0 0;border-radius:4px;text-decoration:none;font-size:11px;font-weight:bold;border:1px solid #1f4ea3;", linkArchived: "display:inline-block;background:#555;color:#ccc;padding:2px 6px;margin:2px 2px 0 0;border-radius:4px;text-decoration:none;font-size:11px;font-weight:bold;border:1px solid #333;", count: "color:#aaa;", none: "color:#8fbc8f;font-weight:bold;" }; That all compiles nicely when I save the script and the script executes as expected outputting the sendChat I alredy have with it. When I'm not sure about is how to deal with the parts of your statement here.&nbsp; When I include a sendChat using what you suggested: &lt;div style="${CSS.sectionTitle}"&gt;Character Sheets&lt;/div&gt; I include it here: sendchat("System", &lt;div style="${CSS.sectionTitle}"&gt;Character Sheets&lt;/div&gt;) But that seems to make the script gag. I've tried it again with "Character Sheets" in quotes, but it still dies. Do I need to add more &lt;div styles&gt; with other parts? I'm looking at the code from TradingPost1E from&nbsp; ROTTO . It's a little different, I've not played with this: const panel = (title, body) =&gt; '&lt;div style="background:#2a2118;border:1px solid #5b4632;border-radius:10px;padding:10px;max-width:760px;color:#f7f1e6;box-shadow:0 2px 6px rgba(0,0,0,0.35);"&gt;' + '&lt;div style="font-size:22px;font-weight:bold;text-align:center;margin:0 0 10px 0;font-family:Georgia,serif;color:#fff8ec;line-height:1.15;word-break:break-word;"&gt;' + esc(title) + '&lt;/div&gt;' + '&lt;div style="background:#efe3cf;color:#1b1712;padding:12px;border-radius:6px;"&gt;' + body + '&lt;/div&gt;' + '&lt;/div&gt;'; There's a title and a body to input. He later uses sendToPlayer(playerid, panel(title, makeBody(lines))); and sendToGM(panel(title, makeBody(lines)));&nbsp; I had tried sendChat which your example. I don't know the above two commands well. So, anyway, I'm just wondering about how to output these "templates"?
1778551124
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
${CSS.sectionTitle} is something called a template literal. It allows you to easily use variables in the middle of a string. In JavaScript, code containing template literals is enclosed in backticks. You could write that line two ways. Constructing a string: sendChat("System", '&lt;div style="' + CSS.sectionTitle + '"&gt;Character Sheets&lt;/div&gt;'); Using template literals: sendChat("System", `&lt;div style="${CSS.sectionTitle}"&gt;Character Sheets&lt;/div&gt;`); Note where I have used single quotes (to allow you to enclose a double quote):&nbsp; ' and where I have used a backtick:&nbsp; ` Once you get used to template literals, they are much easier to handle, as you don't have to make sure you nest single and double quotes properly. Either line will resolve to: &lt;div style="font-weight:bold;color:#ffd700;margin-bottom:4px;"&gt;Character Sheets&lt;/div&gt; Finally your line had one other problem. sendChat("System", &lt;div style="${CSS.sectionTitle}"&gt;Character Sheets&lt;/div&gt;) That fails because HTML must still be inside a JavaScript string. JavaScript does not automatically treat raw HTML as a string value. The template literal (or quoted string) is what turns it into text that sendChat() can send.
1778551469
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
To your other question, without delving too deeply into the other program this is a constant containing a function: const panel = (title, body) =&gt; '&lt;div style="background:#2a2118;border:1px solid #5b4632;border-radius:10px;padding:10px;max-width:760px;color:#f7f1e6;box-shadow:0 2px 6px rgba(0,0,0,0.35);"&gt;' + '&lt;div style="font-size:22px;font-weight:bold;text-align:center;margin:0 0 10px 0;font-family:Georgia,serif;color:#fff8ec;line-height:1.15;word-break:break-word;"&gt;' + esc(title) + '&lt;/div&gt;' + '&lt;div style="background:#efe3cf;color:#1b1712;padding:12px;border-radius:6px;"&gt;' + body + '&lt;/div&gt;' + '&lt;/div&gt;'; Elsewhere in your script you can call it like: panel ("Saving Throws","Saving throws are important rolls that can save your life"); That passes those two strings into the function as title and body. The function then inserts them into the HTML it builds and returns the finished formatted string.
keithcurtis said: ${CSS.sectionTitle} is something called a template literal. It allows you to easily use variables in the middle of a string. In JavaScript, code containing template literals is enclosed in backticks. You could write that line two ways. Keith, Can I use your const CSS as you've written above to create a chat box type of thing? I've played around with a little of it: sendChat("System", "player|characters[0]" + '&lt;div style="' + CSS.header + '"&gt; Well hello &lt;/div&gt;&lt;div style="' + CSS.sectionTitle + '"&gt;' + character.get("name") + ' &lt;i&gt;Saving Throws&lt;/i&gt;' +'&lt;/div&gt;&lt;div style="' + CSS.item + '"&gt;Info&lt;/div&gt;'); I did change the #ffd700 color in sectionTitle to #001FE9. But with the above I just get individual lines in the chat tab (which I've changed to dark mode because the CSS.header prints in a light white): I'm not sure how to tie your Const CSS into a chatbox result. I figure I can add text/calculated variables/buttons as needed following your example with Character Sheet, I'm just not sure how to make the chatbox out of it.&nbsp; Sorry to be a Pain.&nbsp;
1778649434
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Wrap the whole output in a containing div. You are sending a series of individual divs. Each div is a block display by default, and they come&nbsp; out as individual stacked items. If you want them each on their own line, this is fine. If you want them inline, use &lt;span&gt; or add display:inline-block ; to your div style. I'm not sure what&nbsp;player|characters[0] is supposed to be. Currently, it's just a text string, though characters[0] looks like you are pulling the member of an array. Basically, wrap the whole output in a div using the style CSS.container.
keithcurtis said: Wrap the whole output in a containing div. You are sending a series of individual divs. Each div is a block display by default, and they come&nbsp; out as individual stacked items. If you want them each on their own line, this is fine. If you want them inline, use &lt;span&gt; or add display:inline-block ; to your div style. I'm not sure what&nbsp;player|characters[0] is supposed to be. Currently, it's just a text string, though characters[0] looks like you are pulling the member of an array. Basically, wrap the whole output in a div using the style CSS.container. Yes, indeed, characters[0] is a separate array. Thanks, Keith, but I can't get things to work at all. I'll have to come up with other solutions.
You originally talked about: I would like to replace: "Ask DM: does dexterity count" with !dexcheck dex; "Saving Throw Notes" and any results with !dexcheck st; and "Magic Resistance Notes" and any results with !dexcheck mr. To have everything in a single message, you want to avoid three macro calls, as they will each appear as their own message. If the information is to be supplied, you should have three parameters, and the IF statements should have their own IF statement instead of IF ELSE IF to handle all three together. Or, if detected in the API and not a parameter, each one can be its own function that generates a section of the desired output in a single message. Also, scanning your code, the handling of the dex value only deals with zero and negative numbers - do you not require anything to happen to positive numbers?
1778717037

Edited 1778717591
MidNiteShadow7 said: You originally talked about: I would like to replace: "Ask DM: does dexterity count" with !dexcheck dex; "Saving Throw Notes" and any results with !dexcheck st; and "Magic Resistance Notes" and any results with !dexcheck mr. To have everything in a single message, you want to avoid three macro calls, as they will each appear as their own message. If the information is to be supplied, you should have three parameters, and the IF statements should have their own IF statement instead of IF ELSE IF to handle all three together. Or, if detected in the API and not a parameter, each one can be its own function that generates a section of the desired output in a single message. Also, scanning your code, the handling of the dex value only deals with zero and negative numbers - do you not require anything to happen to positive numbers? Thank you for having a look.&nbsp; My initial big thing was being able to break things up for 3 conditions.&nbsp;I understand your approach to the Ifs and it makes perfect sense considering I want to have all three result popping up in the chat anyway. Thanks. :) Ack! you're right, I forgot to deal with positive numbers, eek, I meant to do that too, thanks for catching that. Thanks.
Tim M said: Thank you for having a look.&nbsp; Finally got this working as a single output. Keith's&nbsp; Now to figure out how to stuff it into an API chatbox with the name as a header. Or if I still can't figure that out - I'll just let it go to chat.