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

Reviving GMWhisper thread

1724244493

Edited 1724245440
I recently started looking at creating my own script and happened upon this thread,  Community Forums: I Wrote My First API Script: It Allows the GM to Whisper the Player's Controlling the Selected Tokens | Roll20: Online virtual tabletop . This is a cool script but I found an issue with it and I'm not sure how to fix it. The code that I have has been modified slightly, not much really but is here, on("ready", function() { on("chat:message", function(msg) { if (msg.type=="api" && msg.content.indexOf("!whisp ")==0) { let PlayerList = new Set(); (msg.selected||[]).forEach((obj) => { let token = getObj('graphic', obj._id); if (token) { let character = getObj('character', token.get('represents')); if (character) { const controlled = character.get('controlledby').split(","); controlled.forEach(c => PlayerList.add(c)); } } }); const TheMessage = msg.content.slice(7); if (PlayerList.has('all')) { sendChat('the voice in your head', TheMessage); } else { (PlayerList||[]).forEach((player) => { let PlayerVar = getObj("player", player) ; let PlayerName = PlayerVar.get("_displayname"); sendChat('the voice in your head', `/w "${PlayerName}" ${TheMessage}`); }); } }; }); }); The problem begins if I don't have a player token selected and send the whisper. I get an undefined error as follows. For reference, the error message generated was:  TypeError: Cannot read properties of undefined (reading 'get') TypeError: Cannot read properties of undefined (reading 'get') at apiscript.js:16110:47 at Set.forEach (<anonymous>) at apiscript.js:16108:34 at eval (eval at <anonymous> (/home/node/d20-api-server/api.js:169:1), <anonymous>:65:16) at Object.publish (eval at <anonymous> (/home/node/d20-api-server/api.js:169:1), <anonymous>:70:8) at /home/node/d20-api-server/api.js:1763: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) I need some help fixing this so that my sandbox doesn't crash in this event. Not sure where to go for help. Also, is there a way that I can set this up for my players to use? They can't choose other players tokens so this doesn't work for them. Thanks in advance.
1724251811
timmaugh
Pro
API Scripter
I am failing to produce that error. I have selected no token, selected a token that doesn't represent a character, selected a token that doesn't have a player listed in the controlledby but has "All Players"... all kinds of combinations... Can you be more clear about what steps you take? Also, to let your players use this without letting them control each other's tokens, you have a couple of options. You can change the code so that it checks for the presence of a token ID in the line. Where that token ID would have to be and what sort of syntax it would require to be detected would be something you'd have to decide. For instance, right now the script checks for the message starting with "!whisp " to see if it's an actionable message. If you also tested for a regex like: /^!whisp([^\s]+)/ ...then, if that test passed, you could test the contents of the group1 match to see if it is a token ID. If so, get that token, and off you go. In that case, you would be altering your command line slightly. Where, now, you probably have a command line like: !whisp ?{Enter message to whisper} For the version where you could supply a tokenID, you would probably use a line like: !whisp@{Target|Whisper To|token_id} ?{Enter message to whisper} So you'd basically have a separate command line for each option (whether a token was selected or targeted). A different alternative would be to use the Metascript Toolbox (available in the one-click install), and employ SelectManager to make it seem that someone has a token selected. This wouldn't require any coding change. You would just use a command line more like: !whisp ?{Enter message to whisper} {&select @{Target|Whisper To|token_id} That will make it seem, for the purposes of the Whisper script, like the sender of the message has the token selected.
timmaugh said: I am failing to produce that error. I have selected no token, selected a token that doesn't represent a character, selected a token that doesn't have a player listed in the controlledby but has "All Players"... all kinds of combinations... Can you be more clear about what steps you take? I just select an NPC and run the command. Because the NPC has no player assigned, it throws the error. This is the NPC,  Thanks for the help. I'll take a look at the suggestions for the players. 
1724291356
timmaugh
Pro
API Scripter
Sorry... had to step away for most of the day. What is happening is that the set of PlayerNames is not actually empty when isn't an entry in the controlledby list. The player lookup runs one time with a lookup value that will never resolve into a player, so your player object is "undefined" at the time you try to get the display name. The minimum change to get this to work would be just to filter the contents of the PlayerNames set before you use them. So change this part: } else { (PlayerList||[]).forEach((player) => { ...into this: } else { [...PlayerList] .filter(p => p.length) .forEach((player) => { And that should do it. Like I said, that's the minimum to make it work. You were dealing with the error because you didn't have either a check to make sure your player object had filled after you tried to retrieve it OR a default object in place in case it never resolved: let PlayerVar = (getObj("player", player) || { get: () => '' }); But, as it stands, the above change should get it up and running. Just pointing out the other defensive measures you can take in case there's some other unforeseen thing.
@timmaugh, Thank you for your input. This now works flawlessly.