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

Alien RPG - Talking to Mother API script help

Although I titled this as an Alien script it is system agnostic, but the main questions I want to ask are,  1. when creating a handout, I can get the API to name them incrementally, "Mother - 001", "Mother - 002" etc. But is there a way to get the script to send up  a query box and name the handout what ever I type as a response? 2. I can get the the script to store the created handouts and produce a list of handouts created via the API. But is there a way to, again through queries, type a message into the query box, then have the script edit and save the selected handout repeatedly to simulate a computer / AI typing a response to a question? Thanks in advance. Aaron.
1721619918
timmaugh
Pro
API Scripter
Hi, Aaron... In answer to #1, yes, you can use a query to supply the name. Ultimately, it comes down to what kind of command line you're expecting when you get a command that should trigger your script. This is going to come down to parsing the line. For instance, will your line look like: !bestscriptever ?{What should the handout be named?} ...or will your command line look like: !bestscriptever --name|?{What should the handout be named?} You'd isolate the name from the command line in different ways in those cases, so it's good to have a clear idea of what you're going to need -- and also to pick something that lets you scale for future expansion, if you need to. In this case, based on your second question, it sounds like you'll have need to both create the handout, and then to update it with text. In that case, you might want different controls: !mother --handout|?{Name of handout} ...might just create the handout if it doesn't exist... but you might want to enter text at the same time: !mother --handout|?{Name of handout} --text|This text will be added ...this command line would create the handout if it didn't exist, but once it was created (or located), would append the text in the --text argument. !mother --text|This text will replace what is there --replace ...the above command line might be an option where you don't have to specify the handout by name (since that can get tedious *and* it's prone to human error). Instead, maybe the script records the last handout you appended to in this way, and without an explicit --handout argument, it goes back to use that same handout. Also, this command line demonstrates the possibility of a --replace argument... where the default would be to append whatever you supply in the --text argument, but by including a --replace argument you are wiping the handout contents like a screen-clear before entering the new verbiage. Lots of different options. Here are a couple of links that discuss parsing the command line and thinking about your options: Command Lines and Inline Rolls Command Line Examination As for your second question... yes, you can incrementally update the handout. You'll just be writing the handout contents over and over, so you just have to break your string down and create the proper delay... maybe a feature or 2 to mimic typing... like if there is a range of milliseconds between saving the next letter to the handout... maybe if the next character encountered was a space, there might be a 10% chance to pause for a longer time (like the person typing was gathering their thoughts... checking notes, etc.). To pull it off, you'd split your string into an array of characters, and then run an array.forEach() on it where you update the handout contents in the callback. If you need an example, post back.
This is a big help, thank you. I'll give it another go and see if I can get things working. Thanks again!
OK I am back. here is what I have so far: on('ready', function() { "use strict"; // Ensure the state is initialized if (!state.motherHandouts) { state.motherHandouts = { count: 0, handouts: [] }; } else { // Ensure the handouts array is properly initialized state.motherHandouts.handouts = state.motherHandouts.handouts || []; } // Logging function function logDebug(message) { log(`DEBUG: ${message}`); } // Function to create a new handout function createHandout(player, handoutName) { logDebug(`Creating handout with name: ${handoutName}`); state.motherHandouts.count += 1; let flaggedHandoutName = `Mother ${('000' + state.motherHandouts.count).slice(-3)} - ${handoutName}`; let handout = createObj('handout', { name: flaggedHandoutName, inplayerjournals: "all", controlledby: (playerIsGM(player.id) ? '' : player.id) }); // Ensure the handouts array is not undefined if (!state.motherHandouts.handouts) { state.motherHandouts.handouts = []; } state.motherHandouts.handouts.push({ id: handout.id, name: flaggedHandoutName }); logDebug(`Handout created with ID: ${handout.id}`); sendChat('Mother', `/w "${player.get('displayname')}" Created handout: [${flaggedHandoutName}](<a href="http://journal.roll20.net/handout/${handout.id})`" rel="nofollow">http://journal.roll20.net/handout/${handout.id})`</a>); } // Function to clear the state without removing handouts function clearAllHandouts(player) { logDebug(`Resetting state and clearing all stored handouts information.`); state.motherHandouts = { count: 0, handouts: [] }; sendChat('Mother', `/w "${player.get('displayname')}" State has been reset and all stored handouts information has been cleared from state.`); logDebug(`State reset and all stored handouts information cleared from state.`); } // Function to update the last handout with text, simulating typing function updateHandout(player, text) { logDebug(`Updating last handout with text: ${text}`); let handoutInfo = state.motherHandouts.handouts[state.motherHandouts.handouts.length - 1]; if (handoutInfo) { let handout = getObj('handout', handoutInfo.id); if (handout) { handout.get('notes', function(notes) { let currentNotes = notes || ''; let characters = text.split(''); let index = 0; let interval = setInterval(() =&gt; { if (index &lt; characters.length) { let delay = Math.floor(Math.random() * (2000 - 100 + 1)) + 100; currentNotes += characters[index++]; let styledNotes = `&lt;div style="background-color: #000000; color: #00FF00;"&gt;${currentNotes}&lt;/div&gt;`; handout.set('notes', styledNotes); if (index === characters.length) { clearInterval(interval); sendChat('Mother', `/w "${player.get('displayname')}" Updated handout: ${handout.get('name')}`); logDebug(`Handout updated with new text`); } } }, 100); }); } else { sendChat('Mother', `/w "${player.get('displayname')}" No handout found to update.`); logDebug(`No handout found to update`); } } else { sendChat('Mother', `/w "${player.get('displayname')}" No handout found to update.`); logDebug(`No handout found to update`); } } // Function to send the main menu function sendMainMenu(playerid) { logDebug(`Sending main menu to player ID: ${playerid}`); let menu = `&amp;{template:default} {{name=Mother Handouts Menu}}` + `{{[Create Handout](~!mother --name|?{What should the handout be named?})}}` + `{{[Clear All Handouts](~!mother --clear-All)}}` + `{{[Update Last Handout](~!mother --text|?{What text should be added to the last handout?})}}`; sendChat('Mother', `/w "${getObj('player', playerid).get('displayname')}" ${menu}`); logDebug(`Main menu sent`); } // Main command listener on('chat:message', function(msg) { if (msg.type === 'api') { let player = getObj('player', msg.playerid); if (!player) { logDebug(`No player found with ID: ${msg.playerid}`); return; } logDebug(`API message received: ${msg.content}`); if (msg.content.indexOf('!mother --name|') !== -1) { let handoutName = msg.content.replace('!mother --name|', '').trim(); logDebug(`Received command to create handout with name: ${handoutName}`); createHandout(player, handoutName); } else if (msg.content === '!mother --clear-All') { logDebug(`Received command to clear all handouts`); clearAllHandouts(player); } else if (msg.content.indexOf('!mother --text|') !== -1) { let text = msg.content.replace('!mother --text|', '').trim(); logDebug(`Received command to update handout with text: ${text}`); updateHandout(player, text); } else if (msg.content === '!mother --main') { logDebug(`Received command to send main menu`); sendMainMenu(msg.playerid); } } }); }); there are a few things I can't figure out. 1. when I use the button to make a new handout I get this message:&nbsp; No ability was found for %{!mother --name|?{What should the handout be named?} &nbsp;but I can still create the handout 2. when I edit the text within the handout I get this message:&nbsp; No ability was found for %{!mother --text|?{What text should be added to the last handout?} &nbsp;&nbsp;but I can still edit the text and it inputs with a random delay of up to 2000ms. 3. When I try to clear the state of all stored handouts and reset the incremental number back to 001 I get this message:&nbsp; TypeError: Cannot read properties of undefined (reading 'substring') &nbsp;&nbsp;and the state isn't reset.
1721686620
timmaugh
Pro
API Scripter
Hmm...&nbsp; So, the "No ability found..." errors is because you don't need the tilde when you use a chat button to fire a bangsy message. The tilde is a shortcut for the button syntax so that an ability doesn't fire too early (for instance, if the Roll20 parsers see the ability syntax in the button syntax, it would expand it), but you're not using an ability. The fix for this is to remove the tilde characters from the buttons your sendMainMenu() function creates. That said, I'm not sure what the state is doing for you...? If you use the an IIFE structure (like the Revealing Module Pattern ) you don't have to write this data to the state... it can be held in the script's closure and be ready from call to call. If you somehow just need to differentiate "active" Mother handouts from "inactive" handouts, you can use an element in the name structure (like ending the name with " - archived"). That would mean that on('ready') you could load the list of non-archived handouts, and that list would stay resident in memory until the next reboot of the sandbox... but since you're dealing with permanent objects in the game (handouts), there's no reason for you to duplicate pointers in the state. Also, your line parsing is a little rigid (requiring very specific ways of putting together the arguments, and very specific capitalization (like "clear-All"). I would suggest testing the content of the message to see if it starts with "!mother", and exiting if it doesn't: if (!/^!mother\s*/i.test(msg.content)) { &nbsp; return; } Then you can split the arguments on spaces + double hyphens, and drop the first element in the resulting array since that will be the "!mother" portion: let args = msg.content.split(/\s+--/g).slice(1); Now you can process each of them in turn -- maybe converting them to lowercase for comparison to known arguments (like "clear-all") -- and they can come in any order... or with any number of spaces between them and the adjacent line parts. Then you just have to know which of them you can combine (for instance, you want the effect to be that the current text of the handout gets cleared before the next bit of text gets entered... you would want a --replace as well as a --text). Handling things this way would, by default, allow you to process arguments left-to-right... though you could introduce logic to proceed in any other way. The important thing is that by handling things this way, your command line syntax gets a lot easier to use. =D If you need help with any of this, post back. There are lots of us who like to help. =D
Thank you. I am sure I'll be back. I have only really started trying to write API scripts for my games recently-ish, and only when I have the time. And each new script is a learning experience, so any and all advice is welcome. Thank you.