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 to call api from a second api

Hi All, I have not really got to grips with Javascript but can use php, html etc, I have just been lazy and used jquery. I am looking to combine tokenMods 'controlled by' and token swappers change swap tokens for a druid's wild shape. As Javascript is asynchronous it often takes steps from the macro in a different order than planned. The order I am aiming for is: 1) remove controlled by all, and remove display name from current wildshape form:  !token-mod --set controlledby| !token-mod --off showname 2) swap the token, using a drop down query: !swaptoken ?{Choose Form|Otarius|Ape|Baboon|Badger|Black Bear|Boar|Brown Bear|Cat|Deer|Dire Wolf|Elk|Giant Badger|Giant Hyena|Giant Lizard|Giant Rat|Giant Spider|Giant Weasel|Giant Wolf Spider|Hyena|Mastiff|Panther|Tiger} 3) change the controlled by, display name and showname !token-mod --set controlledby|all !token-mod --set name|"Otarius ?{Choose Form}" !token-mod --on showname I thought the best way would be to create an api script, that then called each of the three parts after a delay. Two problems that I see: 1) I am not sure if setTimeout is the best way, or if there is a way to call the next function when the previous reports that it is completed, 2) I have no idea how to call the functions from the other scripts. I was hoping someone would be able to talk me through it. Many thanks
1488750410
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Why do you need to do step 1? why not just swap the token and then set the name to whatever it's going to be? no need to swap the controlledby field at all. Also, as a note, the problem isn't that Javascript is asynchronous. It is that the API script parser parses your API scripts into one large script and then iterates through them sequentially, however, since we don't have control over what order the scripts get put into the single script there's no way to make sure that tokenmod's commands are parsed prior to !swaptoken.
1488750575
The Aaron
Pro
API Scripter
So, you'll run into a few problems here. 1)  ?{Roll Query} is meaningless to the API.  There is no user to prompt.  You'd have to do it a different way. 2) TokenMod doesn't expose any functions you can call from the API. Your best bet would be to have you script take some argument like: !JonScript ?{Choose Form|Otarius|Ape|Baboon|Badger|Black Bear|Boar|Brown Bear|Cat|Deer|Dire Wolf|Elk|Giant Badger|Giant Hyena|Giant Lizard|Giant Rat|Giant Spider|Giant Weasel|Giant Wolf Spider|Hyena|Mastiff|Panther|Tiger} Then have it take its argument and issue api commands: let arg = /* parse args */; sendChat('','!token-mod --set ....'); sendChat('','!swaptoken '+arg); or something like that.
As we play around the table and occasionally remotely depending on people's schedules all pc tokens, including the druid, are controlled by all. all wild shapes are set up as 'monsters' for want of a better term controlled by no-one as these are used as creatures the pcs can also face. When I 'release' the token, effectively change shape, the controlled by is maintained by the creature and when one is placed on the map, players can see it, even if it is in an area of darkness The idea is to remove the controlled by before the token changes. Hope that makes sense?
Thanks Aaron, i will give it a try, many thanks
Well, i have tried! I have the following: { log('command = '+command+' arg = '+arg); sendChat('','!token-mod --set controlledby|'); log('a'); sendChat('','!token-mod --off showname'); log('b'); sendChat('','!swaptoken '+arg); log('c !swaptoken '+arg); sendChat('','!token-mod --set controlledby|all'); log('d'); if (arg.startsWith('Otarius')) { log('e'); sendChat('','!token-mod --set name|"Otarius '+arg+'"') sendChat('','!token-mod --on showname') } else { log('f'); sendChat('','!token-mod --set name|"Otarius"') } log('g'); } The logs were my attempt at trouble shooting! It runs through to 'g; but also produces the following error: TypeError: Cannot read property 'get' of undefined TypeError: Cannot read property 'get' of undefined at handleInput (apiscript.js:16130:36) at eval (eval at (/home/node/d20-api-server/api.js:146:34), :65:16) at Object.publish (eval at (/home/node/d20-api-server/api.js:146:34), :70:8) at /home/node/d20-api-server/api.js:1510:12 at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:560 at hc (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:39:147) at Kd (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:546) at Id.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:93:489) at Ld.Mb (/home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:94:425) at /home/node/d20-api-server/node_modules/firebase/lib/firebase-node.js:111:461 I kniow it is going to be something simple but i am blowed if i can find it. Any help would be very much appreciated
I think your problem is here if (arg.startsWith('Otarius')) { A "Cannot read property 'get' of undefined" error typically means that you are trying to do something using a null variable. Try using this instead if (arg && arg.startsWith('Otarius')) {
1488809338

Edited 1488811146
Sadly not,   if i included all that i meant to show it would help: var command = msg.content.substring(1,9); var arg = msg.content.substring(10); var arg=arg.trim(); if (command !== 'jontoken') { return; } else { log('command = '+command+' arg = '+arg);  sendChat('','!token-mod --set controlledby|'); log('a'); sendChat('','!token-mod --off showname'); log('b'); sendChat('','!swaptoken '+arg); log('c !swaptoken '+arg); sendChat('','!token-mod --set controlledby|all'); log('d'); if (arg && arg.startsWith('Otarius')) {    log('e');   sendChat('','!token-mod --set name|"Otarius"') } else {  log('f');  sendChat('','!token-mod --set name|"Otarius '+arg+'"')  sendChat('','!token-mod --on showname') } log('g'); the line i have in bold, returns "command = jontoken arg = Otarius" in the log, so the arg is being defined
1488812170
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
I think your problem is actually in token-mod or swaptoken. I'm not really familiar with swaptoken, but I know that token-mod either requires a selected token (which is impossible when sending a message from the API) or a defined token_id to be passed as an argument. You may also be running into a problem where one or both scripts are assuming a valid playerid is passed as part of the message I'd, but is instead getting 'API' as the playerid which would return 'undefined' from the following: var player = getObj('player',MSG.playerid); //returns undefined for an API generated message var player name = player.get('name'l;//will throw your reported error when attempted with an API generated message The other indication that this is the case is that your logs log through 'g' which would not happen if the error was in you code since you don't have any asynchronous functions in that code other than sendChat, which simply means that your API generated chat messages aren't sent until your script is finished.
Thanks Scott, that would make sense, need to do some more digging then
1488813422
The Aaron
Pro
API Scripter
Yeah, was afraid that might happen.  My scripts mostly assume they are called by a player.  I need to update that logic... It's because help tries to get a player name from the non-existing player 'api'.  The real issue is that you aren't supplying any tokens for it to operate on so it tries to show the help. Try this: var command = msg.content.substring(1,9); var arg = msg.content.substring(10); var arg=arg.trim(); var ids=_.pluck(message.selected,'_id'); if (command !== 'jontoken') { return; } else { log('command = '+command+' arg = '+arg);  sendChat('','!token-mod --set controlledby| --ids '+ids.join(' ')); log('a'); sendChat('','!token-mod --off showname --ids '+ids.join(' ')); log('b'); sendChat('','!swaptoken '+arg); log('c !swaptoken '+arg); sendChat('','!token-mod --set controlledby|all --ids '+ids.join(' ')); log('d'); if (arg && arg.startsWith('Otarius')) {    log('e');   sendChat('','!token-mod --set name|"Otarius" --ids '+ids.join(' ')) } else {  log('f');  sendChat('','!token-mod --set name|"Otarius '+arg+'" --ids '+ids.join(' '))  sendChat('','!token-mod --on showname --ids '+ids.join(' ')) } log('g');
1488813891

Edited 1488814031
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Should also note that the problem can be in another script as well if you have any scripts installed that don't have adequate message filtering to prevent them from attempting to operate on messages that they aren't programmed for/don't have all of their required information; about 1/4-1/2 of my handleInput function in any script is devoted just to being able to handle a wide range of message types, filtering out invalid input, and, for scripts designed to accept API generated messages, special handling of API messages. Speaking of handleInput, that's another indication of what to look for, based on your error track, the script throwing the error is using Aaron's basic script architecture. Doesn't mean it is one of Aaron's scripts but it is from one of us scripters that uses the same setup. Edit: looks like Aaron's got it covered
Thanks Aaron, I tried it the clumsy way with the fixed id on the end of the tokenmod calls, yours as ever much cleaner.  it still fails, i suspect it is in the swaptoken script as that will require an id, i will have a play, i am determined to learn lol
1488815102
The Aaron
Pro
API Scripter
Ah.  Probably !swaptokens expects to have a selected token.  Looks like swap tokens relies on the player being a GM or being on the character in question.  In this case, the player doesn't exist, so isn't on the character. You know, if you have the image urls up front, you could just swap them with TokenMod: !token-mod --set imgsrc|"http://some/url/in/a/user/library" --ids <someId>
I thought about that, but i track HP etc using the new token.  i am looking at how i can hack about the tokenswap script
1488817350
The Aaron
Pro
API Scripter
Probably the easiest thing to do is add a new argument which takes a path which doesn't require a player to exist, or substitutes a player instead of the API.  Actually that might be the easiest.  if arg --useplayer, msg.playerid = useplayerid.
1488821224
Lithl
Pro
Sheet Author
API Scripter
Kyle G. said: I think your problem is here if (arg.startsWith('Otarius')) { A "Cannot read property 'get' of undefined" error typically means that you are trying to do something using a null variable. Try using this instead if (arg && arg.startsWith('Otarius')) { Specifically, it means you're trying to read the property named "get" (in this context, likely the function get of a Roll20 object) from a variable which is undefined (not null, although you'd get a similar error in that case). Guarding against a falsy value for a startsWith call (a string function) would not help with that error. Additionally, the error message states that the error is occurring in a function named handleInput, which as Aaron has pointed out, means it's occurring in his code. Aaron's script is attempting to read a property of a player object, when no player object has been found, because the command was sent by the API rather than a player.
Thanks Both,   it looks like there is now relativly easy way to accomplish what i need?
1488828321
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
It's easy to do, it just requires that the scripts you are triggering be setup to accept API input.
Thanks Scott,  will have a play
Having spent a week on this, I give up.  all I am doing is stopping other APIs running properly by messing with this.
1489340273
The Aaron
Pro
API Scripter
It's often frustrating trying to get scripts in the API to work together the way you want.  Perhaps it would be easier to make a modified version of TokenSwapper that also sets the controlled by the way you want.  I can help you with that, if you'd like.
It is Aaron, I am frustrated with myself lol, PHP not a problem, HTML fine, CSS can do, APIs just banging my head against a wall.  I may be easier to create a modified version.
Jon, I use a modified version of the MarchOrder macro to have some tokens 'follow' but to the same location.  I'd use this to drop a 'shapechanged' token [e.g. bear] on the map, and make it follow the character which shapechanged.  Then whenever the character is moved, the 'shapechanged' token moves as well.  As the token was dropped after the actual PC token, it will move on top of the other.  Then when the 'shapechange' ends simply drag the new token off - and it stops following. Is this solution any use to you?
thanks for the offer Jim, i think i am nearly there.  just need to tweak a bit more.
Ok, nearly got it, but thinking bigger!! possibly a silly question: how to i set it so that function in my script can be called from other scripts?