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

Updating Journal Entries via Api

1605209200
Elgin
Pro
Marketplace Creator
I am trying to write a script that appends information to a handout that all players have access to. This way I can amend the log in chat while also announcing the amendment in chat. The problem is I get an error demanding a callback function when accessing the previous value of the log. Here is my initial code.             var handout = findObjs({type: 'handout', name: 'Journal'})[0];             var oldval = handout.get('notes'); This gives me an error saying I need a call back function. Ok, so heres your callback             var handout = findObjs({type: 'handout', name: 'Journal'})[0];             var oldval = handout.get('notes',function(oldval) {handout.set('notes',oldval + "\nBlah");}); Now that call back function gets called over and over again until the program crashes due to infinite loop. WTF? Any ideas on what I am doing wrong here?
1605209501
The Aaron
Roll20 Production Team
API Scripter
Do you have an on('change:handout',...) handler?  Apparently API changes to handouts trigger the handler event, so you have to guard against that.
1605209676
The Aaron
Roll20 Production Team
API Scripter
Here's a post where I have some sample code around how to avoid that:&nbsp; <a href="https://app.roll20.net/forum/permalink/9414716/" rel="nofollow">https://app.roll20.net/forum/permalink/9414716/</a> There's some discussion about it in the messages before it.
1605210577
Elgin
Pro
Marketplace Creator
No I don't have a handler for it
1605210649
The Aaron
Roll20 Production Team
API Scripter
Can you post your full code?
1605210782
The Aaron
Roll20 Production Team
API Scripter
Here's a script with similar functionality that might be helpful:&nbsp;<a href="https://app.roll20.net/forum/post/4121164/notelog-v0-dot-1-2-updated-with-inline-roll-expansion-and-multiline-message-adding/" rel="nofollow">https://app.roll20.net/forum/post/4121164/notelog-v0-dot-1-2-updated-with-inline-roll-expansion-and-multiline-message-adding/</a>
1605210906
The Aaron
Roll20 Production Team
API Scripter
It looks like I use this for setting: let nl = getNoteLog(); nl.get('notes', function(n){ if(!_.isNull(n)){ setTimeout(function(){ let text=n+'&lt;br&gt;'+longtext; nl.set('notes',text); },0); } }); It's been a while since I wrote that, so probably I had the same problem and fixed it by the setTimeout() call to defer it out of the current callstack.
1605211322
Elgin
Pro
Marketplace Creator
var JournalLog = (function() { 'use strict';&nbsp; /** * Handle chat messages */ var handleChatMessage = function(msg) { &nbsp; &nbsp; var cmdName = "!JournalLog"; &nbsp; &nbsp; var msgTxt = msg.content; &nbsp; &nbsp; var newturnorder = []; &nbsp; &nbsp; &nbsp; &nbsp; if ((msg.type === "api")&nbsp; &nbsp; &nbsp; &amp;&amp; (msgTxt.indexOf(cmdName) !== -1) &nbsp; &nbsp; &amp;&amp; playerIsGM(msg.playerid)) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var handout = findObjs({type: 'handout', name: 'Journal'})[0]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat('blah',(JSON.stringify (handout))); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //var handout = getObj("character", "-JMGkBaMgMWiQdNDwjjS"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp; character.get("bio", function(bio) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //&nbsp; &nbsp; log(bio); //do something with the character bio here. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //}); &nbsp; &nbsp; } &nbsp; &nbsp;&nbsp; };&nbsp; return { /** * Register Roll20 handlers */ registerAPI : function() { on('chat:message',handleChatMessage); } };&nbsp; }());&nbsp; on("ready", function() { &nbsp; &nbsp;&nbsp; 'use strict';&nbsp; JournalLog.registerAPI(); });
1605211415
Elgin
Pro
Marketplace Creator
I might just copy your code. Looks like it does exactly what I am looking to do
1605211542
The Aaron
Roll20 Production Team
API Scripter
It's in the 1-click if you just want the full script.
1605213117
Elgin
Pro
Marketplace Creator
Looks like it needs some adjusting. What is a good example piece of code for writing to the chat log? I am currently using &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat("Journal",(JSON.stringify (longtext))); But that sometimes issues warnings. Also would be nice to change the background color of the text and maybe add an image next to the name
1605214915
The Aaron
Roll20 Production Team
API Scripter
Whatever you pass to sendChat() is written to chat, including HTML.&nbsp; depending on what you've got, you might want to loop over and build a string containing your formatted list. Here's a pretty simple script that just shows all the characters that the selected tokens represent: on('ready',() =&gt; { on('chat:message',(msg) =&gt; { if('api' === msg.type &amp;&amp; /!show-rep\b/i.test(msg.content) ){ let who = getObj('player',msg.playerid).get('displayname'); if(msg.selected){ let data = []; msg.selected.forEach( s =&gt; { let t = getObj('graphic', s._id); if(t &amp;&amp; t.get('represents').length ){ let c = getObj('character', t.get('represents')); data.push( `&lt;div&gt;&lt;div&gt;&lt;img style="max-width: 3em; max-height: 3em; border: 1px solid #999; background-color: white;" src="${t.get('imgsrc')}"&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;${c.get('name')}&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;` ); } }); sendChat('',`/w "${who}" ${data.join('')}`); } else { sendChat('',`/w "${who}" &lt;b&gt;No tokens selected&lt;/b&gt;`); } } }); });
1605233246
Elgin
Pro
Marketplace Creator
What is the deal with the args variable in your code? args = msg . content . split ( / \s / ) ; switch ( args . shift ( ) ) { let text = n + '&lt;br&gt;' + bulletChar + ' ' + args . join ( ' ' ) ; Is there a way to send multiple arguments to a API script?
1605235916
timmaugh
Forum Champion
API Scripter
That args variable is just the way the script catches the args of the command line. You determine that when you build the script, since you are always working around what command syntax the user will have to supply. For the line you quote: args = msg.content.split(/\s/); ... that is going to use a Regex to split on every 'white space' character. That means that a command line like this: !scripthandle Bilbo dragon=1 armor=2 would, in the args object, be an array like this: ["!scripthandle", "Bilbo", "dragon=1", "armor=2"] So, when you see this line: switch(args.shift()) { ...two things are going on, there. First, the shift() function is popping the first element out of the array, and *returning* it at the same time. That means that the switch() structure receives the first element out of args (the api handle, "!scripthandle"), and args is left with ONLY the arguments that followed the handle. Args is now: ["Bilbo", "dragon=1", "armor=2"] So if you need to do further parsing to build other structures, you can. It's up to you what each argument means, what order they come in, and whether the order even matters. If you want to know what dragon Bilbo is facing, you would have to identify the dragon argument (maybe it's the second element after you eliminate the api handle -- args[1] -- or maybe it is the one that begins "dragon="). Once you had it, you could split it on the "=", and use the second half to look up some information about dragon #1. At any time you can join those arguments, so if you *didn't* mean to split them in the first place (you only wanted to get ride of the api handle and keep everything after, you would 1) split on white space, 2) shift() or slice(1) to get rid of the api handle, then 3) join args again using a space. // if msg.content = '!logger This is the message to save' args = msg.content.split(/\s/);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// split on white spaces switch(args.shift()) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// remove first element (api handle) from args and use it in switch comparison &nbsp; &nbsp; case '!logger':&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// catch where it equals !logger &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; let text = n + '&lt;br&gt;' + bulletChar + ' ' + args.join(' '); // rebuild the args into a single string, and add other characters &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; break; } Note: the above method of switching on the api handle would make sense in a script that had multiple api handles to answer. In other words, if you had a "logger" and a "loggerlast" and a "loggersession", you would want to catch any/all of them, and pursue the right code based on what the supplied handle was. For other scripts that have only 1 command path based on the api handle, it's often better to test the handle at the same time that you determine whether the script should catch the api call in the first place. That prevents unnecessary processing/overhead.
1605236012
The Aaron
Roll20 Production Team
API Scripter
msg.content contains the full text of what is supplied in the chat, with some substitutions for inline rolls and such.&nbsp; You can parse that string however you like.&nbsp; Generally, people split it on whitespace: let args = msg.content.split(/\s+/); so, if the command was: !do thing with stuff args would be: ['!do','thing',with','stuff'] Then you could do whatever you like with that.&nbsp; In the case of notelog, it pulls off the first entry with args.shift(), and then matches it to the command name with a switch/case statement.
1605236409
timmaugh
Forum Champion
API Scripter
Ha! Sorry for the low-key ninja. I even copied my post, backed out, and double checked you hadn't responded! Thought it was game night, so I'd lend a hand.
1605240509
The Aaron
Roll20 Production Team
API Scripter
Oh yeah, it is. =D&nbsp; No worries. That's why I gave the terse answer.&nbsp; Your's is definitely more thorough!