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 .
×
Create a free account

How long does it usually take for a PR review?

1613825075
kahn265
Pro
API Scripter
Subject says it all. When someone does a PR to get code into "one click install", what is the usual turnaround? 
1613825466
David M.
Pro
API Scripter
Usually within a day or two for approval, but the changes are only published to the one-click once a week on Tuesday.
1613825950
kahn265
Pro
API Scripter
David M. said: Usually within a day or two for approval, but the changes are only published to the one-click once a week on Tuesday. Cool. Exactly what I wanted to know!
1613829178
The Aaron
Roll20 Production Team
API Scripter
Which one is your pull request?  Inclusion in the 1-click isn't always the default behavior, you'll want to be sure and explicitly ask for it. 
1613829297
The Aaron
Roll20 Production Team
API Scripter
Actually I found it. You'll need to move your script under a subdirectory named for the version. I'll leave notes on the pull request.
1613842296

Edited 1613842365
kahn265
Pro
API Scripter
Thanks! I've made the change. If there is anything that I should look out for, I'd appreciate it. I'm new to Javascript (this would have been much easier if it was Java, C#, or even C++. :D ) edit: And yes, already working on next version - I just wanted to get an initial PR to see how this goes.
1613847917

Edited 1613848102
The Aaron
Roll20 Production Team
API Scripter
Coming from traditional c-style languages, there are a few things that might be useful to know: 1) Everything is an object, even literals: "foo".toUpperCase() "FOO" 3..toFixed(2) "3.00" (the extra . with the number is the radix point) 2) Everything has a truthy value: if(""){ log("true"); } else { log("false"); } &gt; false if("tacos"){ log("true"); } else { log("false"); } &gt; true if(log){ log("true"); } else { log("false"); } &gt; true 3) Logical operators return the compared object, not a boolean value: let foo = bar || baz || qux || 23; foo will get assigned the value of the first variable that is defined with a truthy value (not false, zero, etc), or 23. 4) Functions don't need a return statement, they will return the special value undefined if they come to the end of a function without a return: const getFoo = () =&gt; { &nbsp;&nbsp;if(isFoo){ return foo; } // implicit return undefined; } let bar = getFoo(); undefined is basically javascript's "this space intentionally left blank".&nbsp; Compare that to null (this space explicitly blank) and NaN (some expected math calculation returned a value that isn't a number). 5) Don't over use try/catch.&nbsp; They're kind of expensive and they break the flow of execution.&nbsp; try { tokenLinkedToNpcCharacterSheet = findObjs(chatMessage.selected[0])[0]; } catch (e) { speakAsGuidanceToGM("Linked Token has not been selected"); return; } Is better written as if(chatMessage.selected) { tokenLinkedToNpcCharacterSheet = findObjs(chatMessage.selected[0])[0]; if(! tokenLinkedToNpcCharacterSheet ){ speakAsGuidanceToGM("Linked Token has not been selected"); return; } /* ... */ 6) Prefer === and !== to == and !=.&nbsp; The short forms do automatic type cohesion, which often gets you into bizarre circumstances. if([] == '') { log("true"); } else { log("false"); } &gt; true if([] === '') { log("true"); } else { log("false"); } &gt; false 7) Read Javascript: The Good Parts by Douglas Crockford .&nbsp; It's an older book (Pre Javascript ES6), but it's a great grounding in understanding Javascript and writing it well. Bonus) Enjoy the humor of this video:&nbsp; <a href="https://www.destroyallsoftware.com/talks/wat" rel="nofollow">https://www.destroyallsoftware.com/talks/wat</a>
1613851111
kahn265
Pro
API Scripter
VERY helpful! 1. Yeah, same for Java. Excpt.... &nbsp;3..toFixed !?!??!?! 2. The concept of "truthy" and "falsy" hurts my brain. 3. OK, but hard to follow. 4. Undefined vs null - VERY helpful 5. For the "2.0" version I'm working on, I'll start removing try/catch blocks. 6.&nbsp;&nbsp; I have been working hard to eliminate the == and != in favor of the === and !== as I noted those details. 7. Thanks for the book recommend I'll start looking for it! Also, I hope you don't mind me borrowing your "guid" code for this project :)
1613852063
The Aaron
Roll20 Production Team
API Scripter
I don't mind at all. =D&nbsp; It's not my code, I just extracted it out of Roll20's code.&nbsp; =D&nbsp; I saw your "thanks Aaron" in the code. =D 1) Yup!!!! =D&nbsp; You might find it more natural as one of these: (3).toFixed(2) &gt; "3.00" (3.).toFixed(2) &gt; "3.00" (3.0).toFixed(2) &gt; "3.00" One implication I meant to mention was that you don't need to String(foo).toUpperCase() if foo is already a string.&nbsp; Sometimes I'll write something like `${foo}`.toUpperCase() if I'm not sure what type foo is and I need a string, but usually you just know.&nbsp;&nbsp; Speaking of "you just know", everything that comes from the UI will always be a string, even if your player typed "23" in the bar, that will be the string "23", not the number 23, so if you get into parsing attribute values and such, you'll want to get cozy with parseInt() and parseFloat(). 2) Once you get used to it, it's a pretty nice abstraction.&nbsp; (This coming from a hardcore c++ programmer =D) 3) here's an example of practical use: let who = (getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); expanded: let who = ( // try to get the object for this player getObj('player',msg.playerid) // if it fails to find one (returns undefined) || // return an object that implements the expected interface (Null Object Pattern FTW) {get:()=&gt;'API'} ) // call the get function to get the display name .get('_displayname'); It basically saves you a bunch of if/else branches or ternary operators when you need to check the results of something. Looks good, definitely ask more questions and feel free to loot whatever I've written. =D Cheers!&nbsp;&nbsp;
1613856669
kahn265
Pro
API Scripter
I saw discussion of a script someone had come up with to automatically upload a script to roll20? (I saw your solution at the time). Because I've overwritten my own work 3 times, I'm interested in coming up with something other than writing a Selenium script to do it.
1613857249
The Aaron
Roll20 Production Team
API Scripter
Lucian had a way of doing it for the Shaped Script back in the day, but I never pursued it.&nbsp; You'd probably need to write a browser extension, or at least look at replicating the network transactions when saving a script in the browser using cURL or similar.
1613857483
kahn265
Pro
API Scripter
The Aaron said: Lucian had a way of doing it for the Shaped Script back in the day, but I never pursued it.&nbsp; You'd probably need to write a browser extension, or at least look at replicating the network transactions when saving a script in the browser using cURL or similar. Yeah, I already started checking that out. I wasn't looking forward to doing the "Authentication Dance" - I do enough of that in my day job.
1613858576
The Aaron
Roll20 Production Team
API Scripter
=D&nbsp; I'd just export the cookies from chrome or firefox and use that.&nbsp; The authentication doesn't timeout often, so you wouldn't need to update it much I'd wager.
1613858607
The Aaron
Roll20 Production Team
API Scripter
And you could always do the authentication dance later that way. =D
1613861148

Edited 1613861238
kahn265
Pro
API Scripter
I figured they'd only be good for a day or so? But then again, I'm used to working with ones that last less than an hour. Easiest thing would be a headless selenium script that did the login and then paste. I could probably add a CI/CD flow into IntelliJ for that. The most elegant would be to try to reproduce the auth flow to the microservice and then reproduce the http traffic.... but again.. that's too much like my day job as a Software Quality Engineer in Test.... I was just hoping for something I could borrow....
1613990240

Edited 1613992667
Nice, more pro developers. Maybe we should open a thread to share workflow best practices (just like the stupid tricks thread). Debug Logging To write a line to the log including timestamp, linenumbers and functionname the following fuction can be used. As - in roll20 own words - you need to do 'Caveman debugging', i use&nbsp; copious amounts of mylog calls. &nbsp; &nbsp; mylog = function(msg) { &nbsp; &nbsp; &nbsp; &nbsp; let d = new Date(); &nbsp; &nbsp; &nbsp; &nbsp; let lines = new Error().stack.split("\n"); &nbsp; &nbsp; &nbsp; &nbsp; log(d.toUTCString() + " " + lines[2].trim() + " " + msg); &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;
The right line number - local creation of apiscript.js Roll20 concatenates all API scripts into a single script apiscript.js in order of the Tabs of the campaign script page. Errors refer to this file and are thus only right for the first file in the script. Solution: upload one .js. Create a shell script or make file that creates the single script locally on your computer and upload that as your only script.&nbsp; When you have a local fetch of your fork of the roll20-api-scripts, this allows you to controll the inclusion of: - specific versions of API scripts, - your own code and - downloaded snippets. Below a simplified version of script, just a simple bash script. The concatenated apiscript has the current date in the name to version it and is compiled to make sure it's syntax is correct. #!/bin/bash # outfile="/&lt;yourfolderhere&gt;/apiscript/apiscript$(date +%Y%m%d).js" countfile="/&lt;yourfolderhere&gt;/apiscript/apiscript.count" function append { &nbsp; &nbsp; echo "// source: $1" &gt;&gt; $outfile &nbsp; &nbsp; echo "" &gt;&gt; $outfile &nbsp; &nbsp; cat&nbsp; "$1" &gt;&gt; $outfile &nbsp; &nbsp; echo "" &gt;&gt; $outfile &nbsp; &nbsp; wc -c "$1" &gt;&gt; $countfile &nbsp; &nbsp; wc -c $outfile &gt;&gt; $countfile } echo "// compiled on: " $(date +%Y%m%d) &gt; $outfile echo "// " &gt;&gt; $outfile echo "// reason for one large script is to get meaningful linenumbers from the api console/error messages" &gt;&gt; $outfile echo "on('ready',function() {" &gt;&gt; $outfile echo "&nbsp; &nbsp; 'use strict';" &gt;&gt; $outfile echo "" &gt;&gt; $outfile echo "&nbsp; &nbsp; let d = new Date();" &gt;&gt; $outfile echo "&nbsp; &nbsp; log(d.toUTCString() + ' all scripts compiled into one massive one on $(date +%Y%m%d)');"&nbsp; &gt;&gt; $outfile echo "});" &gt;&gt; $outfile echo "" &gt;&gt; $outfile rm -f $countfile append "/&lt;your local git fetch folder here&gt;/roll20-api-scripts/5th Edition OGL by Roll20 Companion/1.4.4/5th Edition OGL by Roll20 Companion.js" append "/&lt;your local git fetch folder here&gt;/roll20-api-scripts/TokenMod/0.8.63/TokenMod.js" append "/&lt;your local git fetch folder here&gt;/roll20-api-scripts/CombatMaster/2.40/CombatMaster.js" # append "/&lt;your local git fetch folder here&gt;/roll20-api-scripts/HealthColors/1.6.1/HealthColors.js" append "/&lt;folder of my patched version&gt;/HealthColors with log.js" append "/&lt;adapted version of Aaron gist&gt;/APIHeartBeat with log.js" append "/&lt;personal development&gt;/ArtResizer.js" wget -O dimmerswitch$(date +%Y%m%d).js <a href="https://gist.githubusercontent.com/Gritmonger/fba46cb467dac5880c71f5e8bc0e3106/raw/4dd9e37c1bba30eaa464bf7ec9062c93b2f6a6c7/dimmerswitch.js" rel="nofollow">https://gist.githubusercontent.com/Gritmonger/fba46cb467dac5880c71f5e8bc0e3106/raw/4dd9e37c1bba30eaa464bf7ec9062c93b2f6a6c7/dimmerswitch.js</a> append /home/martijn/Documenten/dungeons/roll20/macro/apiscript/dimmerswitch$(date +%Y%m%d).js echo 'next line should show 7' &gt;&gt; $countfile grep -c "source:" $outfile &gt;&gt; $countfile node -c $outfile
Storing the API log locally When an apiscript crashes, the message that is visible in the campaign api hardly ever proves useful to find the error. Especially when you open this page after the fact. I hate losing logs and so on the homeserver a VM is running with firefox open on the campaign script pages of selected games. In the developer console of the browerser execute the following snippet to download each API log line in a seperate file. Firebase.enableLogging(function(message) { &nbsp; let millis = Date.now() &nbsp; let prefix = "p:0: handleServerMessage d "; &nbsp; if( message.startsWith(prefix) ) { &nbsp; &nbsp; &nbsp; let eventStr = message.substring(prefix.length) &nbsp; &nbsp; &nbsp; let campaign = eventStr.split("-")[1]; &nbsp; &nbsp; &nbsp; if( campaign === 'notifiers/campaign') { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;campaign = eventStr.split("-")[2]; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; let time = 0 &nbsp; &nbsp; &nbsp; if(eventStr.split('"time":').length &gt; 1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;time = eventStr.split('"time":')[1].split(/,}/)[0] &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; console.log("[FIREBASE]", eventStr); &nbsp; &nbsp; &nbsp; let filename = "apilog-"+campaign+"-"+millis+"-"+time+".json"; &nbsp; &nbsp; &nbsp; var blob = new Blob([eventStr], {type: 'text/csv'}); &nbsp; &nbsp; &nbsp; var e&nbsp; &nbsp; = document.createEvent('MouseEvents'); &nbsp; &nbsp; &nbsp; var a&nbsp; &nbsp; = document.createElement('a'); &nbsp; &nbsp; &nbsp; a.download = filename; &nbsp; &nbsp; &nbsp; a.href = window.URL.createObjectURL(blob); &nbsp; &nbsp; &nbsp; a.dataset.downloadurl =&nbsp; ['text/csv', a.download, a.href].join(':'); &nbsp; &nbsp; &nbsp; e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); &nbsp; &nbsp; &nbsp; a.dispatchEvent(e); &nbsp; } }); As this approach creates a file for each message, an hourly crontab moves the downloaded json files into a zip zip -m apilog.zip apilog-*.json Above snippet includes two timestamps in the filename of each log event: the time of your computer and the server time. The fluctuation of the difference between these two timestamps gives (under certain condictions) an indication of the API server lag. Which by the way is good - as the analysis of a few 100k logged events shows. On a side note: i dare say that using firebase to roll dice would be much less laggy than the current diceroll via Cloudflare.
1614019081

Edited 1614019146
kahn265
Pro
API Scripter
Thank you for the warm welcome! I'll be reading your posts more closely when my next meeting starts..... :) I love the best practices thread idea! Martijn S. said: Nice, more pro developers. Maybe we should open a thread to share workflow best practices (just like the stupid tricks thread). Debug Logging To write a line to the log including timestamp, linenumbers and functionname the following fuction can be used. As - in roll20 own words - you need to do 'Caveman debugging', i use&nbsp; copious amounts of mylog calls. &nbsp; &nbsp; mylog = function(msg) { &nbsp; &nbsp; &nbsp; &nbsp; let d = new Date(); &nbsp; &nbsp; &nbsp; &nbsp; let lines = new Error().stack.split("\n"); &nbsp; &nbsp; &nbsp; &nbsp; log(d.toUTCString() + " " + lines[2].trim() + " " + msg); &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; Oh yeah, I learned that back when I was first learning Turbo Pascal let debugLog = function (text) { if ( debugMode ) { log(text); } }; the "debugMode" is a boolean flag I can turn on and off. I'm totally adding the date/line number though :)
1614020548
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Thanks for that list, Aaron. I came across #3 while looking through a script the other day, and that cleared up my puzzlement. And #4 would have saved me a half hour of head banging that same day, had I known. :D
1614021149
The Aaron
Roll20 Production Team
API Scripter
I like the idea of a best practices.&nbsp; I'd suggest it goes in the Wiki as it will be much easier to maintain across multiple people, and we can link to it and expand it easily, and not worry about the thread locking.
1614021877
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
"I know a guy..."
1614022102
The Aaron
Roll20 Production Team
API Scripter
I was thinking the same thing... =D
1614024595
kahn265
Pro
API Scripter
I was about to create a "Best Practices" in the wiki..... THEN I FOUND THIS and linked to it <a href="https://wiki.roll20.net/API:Best_Practices" rel="nofollow">https://wiki.roll20.net/API:Best_Practices</a>
1614028413
The Aaron
Roll20 Production Team
API Scripter
Interesting!&nbsp; That's a pretty old page (2014) and is a bit of blend of Best Practices (which I like) and Coding Standards (which I don't).
1614088987
kahn265
Pro
API Scripter
The Aaron said: Interesting!&nbsp; That's a pretty old page (2014) and is a bit of blend of Best Practices (which I like) and Coding Standards (which I don't). It's a wiki! We can change it :)
1614089536
The Aaron
Roll20 Production Team
API Scripter
Truth! =D Maybe break it into 3 top level categories: Javascript Things that are Javascript specific, like the truthy || &amp;&amp; etc, and closures Roll20 API Things that are Roll20 API specific, like dealing with events, setting objects, etc Some suggestions about how to organize your code.&nbsp; For the people that just really want to suggest their particular style... =D
1614114680
kahn265
Pro
API Scripter
WOO! MY PR IS MERGED! I'M A PERSON!!!!
1614114767
kahn265
Pro
API Scripter
The Aaron said: Truth! =D Maybe break it into 3 top level categories: Javascript Things that are Javascript specific, like the truthy || &amp;&amp; etc, and closures Roll20 API Things that are Roll20 API specific, like dealing with events, setting objects, etc Some suggestions about how to organize your code.&nbsp; For the people that just really want to suggest their particular style... =D If no one has touched it this evening, I'll see if I can start re-organizing. Funny, this is the exact type of work I hate doing at work - but here - well it's not Confluence....
1614270196

Edited 1614270210
i suggest a fourth category: Workflow/pipeline - where we can share (ie i can read) smart ways of uploading/debugging/whatever
1614272240
Andreas J.
Forum Champion
Sheet Author
Translator
kahn265 said: I was about to create a "Best Practices" in the wiki..... THEN I FOUND THIS and linked to it <a href="https://wiki.roll20.net/API:Best_Practices" rel="nofollow">https://wiki.roll20.net/API:Best_Practices</a> I've interlinked this page with some of the other sheetworker/javascript pages on the wiki. Would be great if someone finds inspiration to create more guide for API, in the same way that there is currently lot of stuff for character sheet creation pages there.
1614307674
kahn265
Pro
API Scripter
WOO! Version 1.0 of script is published!!!!!!!! :D I'm more pleased about this than I probably should be!