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 whisper the output of an API script to a specific player

i'm trying to find a way to whisper the output of a script to a selected player. I found this script (written by The Aaron) on an older forum post that displays the portrait from an character's bio page to the chat. This  works great when I want to display the image to everyone, but I'd like a way to whisper the output to a particular player character (or characters). Currently, with this script, I select a token, then type !Art (actually, click a macro button that contains that), then the portrait in the Bio page appears in chat. What I'd like to do is: Create a macro that prompts me to select the token of the character whose portrait I want to display, then, after that token is selected, ask me to select the tokens of the characters to which I want to whisper the output. If this can't be done in a macro, I could use help in editing the script to do this natively. (And by help, I mean  tell me exactly what I need to change since I don't know how to code 😁 ) Original MOD, by The Aaron: on('ready',()=>{ on('chat:message', (msg) => { if('api'===msg.type && /^!art(\b\s|$)/i.test(msg.content) ){ let c = [...(new Set((msg.selected || []) .map(o=>getObj('graphic',o._id)) .filter(g=>undefined !== g) .map(g=>getObj('character',g.get('represents'))) .filter(c=>undefined !== c) .map(c=>c.get('avatar')) .map(u=>`<div><img src="${u.replace(/\?.*$/,'')}"></div>`) ))] .join('') ; if(c){ sendChat(msg.who, c); } } }); });
1744915189

Edited 1744936487
timmaugh
Pro
API Scripter
It's absolutely doable...  In the code, above, the script allows you to select multiple tokens. It constructs the message of all of the art (for all of the character-representing-tokens put together), and sends it to the original sender of the message. You're discussing a case where you would then be asked to select the tokens from which you would derive your recipients (instead of using the sender of the original message). That  would require a two-step/two-message approach. Message 1 would load up the art from the selected tokens and store it somewhere in the script. Message 2, with new selected tokens, would determine the recipient players, combine it with the art, and send the message. This script uses that 2-step process:  Step 1 Run !getart It will prompt you to select the recipient tokens and provide a button Step 2 Click the button. That's it. const ShowArt = (() => { let art; const getGameGMs = () => { return findObjs({ type: 'player' }).filter(p => playerIsGM(p.id)); } const getPlayersFromToken = (t) => { if (!t) { return; } let cby = !t.get('represents').length ? t.get('controlledby') : (getObj('character', t.get('represents')) || { get: () => '' }).get('controlledby'); if (!cby.length) { cby = getGameGMs().map(gm => gm.id).join(','); } let cbyArray = cby.split(/\s*,\s*/g); return cbyArray.map(c => c.toLowerCase()).includes('all') ? findObjs({ type: 'player' }) : cbyArray.map(p => getObj('player', p)); }; on('ready', () => { on('chat:message', (msg) => { if ('api' === msg.type && /^!getart(\b\s|$)/i.test(msg.content)) { art = [...(new Set((msg.selected || []) .map(o => getObj('graphic', o._id)) .filter(g => undefined !== g) .map(g => getObj('character', g.get('represents'))) .filter(c => undefined !== c) .map(c => c.get('avatar')) .map(u => `<div><img src="${u.replace(/\?.*$/, '')}"></div>`) ))].join(''); if (art.length) { sendChat('ShowArt', `/w ${msg.who} <div>Art gathered. Now select the recipients to show then press this button [Show Art](!
!showart).</div>`); } } else if ('api' === msg.type && /^!showart(\b\s|$)/i.test(msg.content)) { if (!art.length) { return; } // could put message here to report no art had been previously selected let recipientObj = (msg.selected || []) .map(o => getObj('graphic', o._id)) .filter(g => undefined !== g) .map(getPlayersFromToken) .filter(a => undefined !== a) .reduce((m, a) => [...m, ...a], []) .reduce((m, p) => { m[p.id] = p; return m; }, {}); let recipients = Object.keys(recipientObj).map(k => recipientObj[k].get('displayname')); if (recipients.length) { recipients.forEach(p => { sendChat('ShowArt', `/w ${p} ${art}`); }) } } }); }); })();
1744932347

Edited 1744932466
Thanks for the answer, but of course, i have issues :) I copied the script and added it to my MOD library, then restarted the Experimental sandbox (took forever to spin up). I then selected a token that had an attached character sheet and ran !getart. The script apparently ran because I was then prompted to select the tokens that would receive the whisper: NADA. To output at all. When I checked the tab that was open to the game's MOD page, I saw this: These are the MODs that I have loaded:
1744936184
timmaugh
Pro
API Scripter
I see it. Untested edge case. Give me a minute to fix it.
1744936600
timmaugh
Pro
API Scripter
OK, I updated the code, above. Basically, if there is no "controlledby" list for the token/character, it will send the art to the game GMs. I just wasn't being careful that in that case I was accessing the player objects when I got the GMs rather than their IDs (which is what constitutes the list of the "controlledby" property). Anyway, I corrected that and tested.
1744985721

Edited 1744985789
I updated the code, and no longer get the error in the sandbox; Yay! However... I hate to pick nits about a gift (a gift that is greatly appreciated!), but I've noticed another quible: I get on a map page that includes the player ribbon. I then select a token with an attached character sheet and run !getart , which gives me the opportunity to select which player controlled tokens get to see it. I select those tokens. In the chat, I see the art only if I as GM have explicit control of the tokens selected . (IOW, "Rick GM" is listed in the "Can be edited and controlled by" section of the character sheet permissions.) I thought that all whispers to a character were visible to the GM of a game? I don't usually add myself to the "Edited By" field of character sheets since I've read that it can cause problems (though I can't remember what, exactly, those problems were). Edit: Wait ... Looking at your code (as a person who doesn't know how to code), do I see that the whispers are being sent to the player  rather than the player's character ? That would explain it. If that's the case, I'll just test the script with one of my players logged in, and if it works, all good. Thanks again!
1744999583

Edited 1744999612
timmaugh
Pro
API Scripter
Yes to all. While you generally see your own whispers to other people, you won't with API-generated whispers. And while you might have seen the whisper had I sent it to a character (and you had controlling rights over the character, explicitly), I didn't want to have to build the check (and the message reporting a failure) if you selected a non-character-representing-token. Instead, I just built the list of players to send to off of the controlledby property of the character (if the token represents a character) or the token (if it does not). I guess it wouldn't be hard to add the GMs to that list... so that no matter who else you were whispering to, the GMs would also always receive that same message... Let me know if you want that. But, yes... as it is written now, if your players log in and you send the message to them, they will see it. I tested this in my own game having my account open in a Chrome browser and one of my test/dummy accounts in the same game but connected from a Firefox browser. I was able to send whispers from a token that my dummy account had control over, and the dummy account saw the result.
1745001839

Edited 1745001996
OK, now I'm clear(er) abouf how it works, and this will certainly work for what I want to do. I trust my players to tell me whether or not they got what I intended them to see :)  Thanks again! The point of this ability is for me to be able to show one PC (say, the rogue scouting ahead) the NPC that they spot around the corner without displaying it to the rest of the party since it's not in their line of sight. I've been doing this the long way by recording am image's url and creating a custom macro for that instance, but that gets cumbersome. In my D&D game, I use a few non-WOTC bestiaries, so the players often meet things that they can't immediately recognize. When this happens, the first PC's player to spot the beastie gets to try and describe it.
This script works beautifully! It does exactly what I wanted. This is so useful that I wonder if you'd consider adding this one, as well as the !art script that I posted at the start (or a combination of the two) to the 1-click.  timmaugh said: Yes to all. While you generally see your own whispers to other people, you won't with API-generated whispers. And while you might have seen the whisper had I sent it to a character (and you had controlling rights over the character, explicitly), I didn't want to have to build the check (and the message reporting a failure) if you selected a non-character-representing-token. Instead, I just built the list of players to send to off of the controlledby property of the character (if the token represents a character) or the token (if it does not). I guess it wouldn't be hard to add the GMs to that list... so that no matter who else you were whispering to, the GMs would also always receive that same message... Let me know if you want that. But, yes... as it is written now, if your players log in and you send the message to them, they will see it. I tested this in my own game having my account open in a Chrome browser and one of my test/dummy accounts in the same game but connected from a Firefox browser. I was able to send whispers from a token that my dummy account had control over, and the dummy account saw the result.
1745160739
timmaugh
Pro
API Scripter
Appreciate the kind words. Submitting it to the 1-click is definitely a possibility... though I would probably combine the original intent of the script (show the art to the person who sent the message) with the new usage... basically rolling the original sender of the message into the recipient list (that would get around the fact that you, as GM, aren't seeing the messages the script is sending to your players, too). Also, I'd probably give the messages a bit more presentation quality using my existing message template library script. Also, I could probably add a new/second button to the "art has been collected" message... the new button would collect the players in your game and arrange them as a query. The button would be a "Send by Query..." and let you pick a single player to show the art to, quickly. Also, I'd want to put the error-line-number disambiguation component on there, for later troubleshooting purposes. So... yeah. Doable. I'd just want to tweak it a bit to get it above the "scriptlet" category (in my brain) to a full-blown, 1-click-worthy script. =D