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 .
×
Advertisement Create a free account

[Meta Script] Fetch - retrieve attributes, repeating attributes, abilities, or token properties

1618937496

Edited 1633480527
timmaugh
Pro
API Scripter
Fetch FILE LOCATION:   My Personal Repo or the 1-click installer META SCRIPT:  This script is a meta-script, and part of the ZeroFrame Meta Toolbox . It can be used on its own, or in conjunction with ZeroFrame and the other meta-toolbox scripts, to modify  other  scripts' API commands. ABSTRACT:  Fetch provides a standardized way to retrieve sheet or token items, including token items not previously exposed to the chat interface (like the current side of a multi-sided token), and repeating elements. In addition, Fetch improves on the standard Roll20 sheet retrieval syntax by allowing you to specify a default value if the item does not exist (instead of erroring to chat). Introduction Fetch uses a standardized syntax to retrieve sheet or token items. These syntax structures are replaced with the retrieved value in the command line before the message is handed off to the intended-recipient script. Syntax Highlights The basic syntax is to use an @ (attribute or token property), % (ability), or * (repeating attribute), followed by the identifying data and default value (if desired) enclosed in parentheses. @(selected.token_id) => token id of selected @(Bob.Strength) => attribute of character @(speaker.Strength) => attribute of current speaking character @(Bob.Strength.max) => max value @(Bob|Strength|max) => pipes or periods work @(Bob.Strength[0]) => default value if Strength doesn't exist @(Bob.Strength.max[0]) => default value if max value of Strength doesn't exist *(Bob.spells.[name="Glitter Storm" prepared].spelllevel) => repeating attribute from spells list where the name sub-attribute is "Glitter Storm" and the spell is prepared, returns the spelllevel sub-attribute *(Bob.spells.[name~Glitter prepared].spelllevel.name) => where the name sub-attribute includes the text "Glitter", return the full name of the spelllevel sub-attribute *(Bob.spells.[name~Storm name!~Glitter].spelllevel.rowid) => where the name sub-attribute includes "Storm" but not "Glitter", whether or not the spell is prepared; return the rowid *(Bob.spells.[name~Glitter prepared].spelllevel.row$) => where name includes "Glitter" and is prepared, return the row number (i.e., $1) *(Bob.spells.[name~Glitter prepared].spelllevel.name$) => where name includes "Glitter" and is prepared, return the name using the row number *(Bob.spells.[name~Glitter prepared].spelllevel.name[not found]) => including a default value Token Information Token information can be retrieved with the basic syntax: @(token.info) (for this and all future examples, periods and pipes are interchangeable; also, see the section on Default Values to see how to include a default) In the example, "token" can be replaced with either "selected" or the ID of a given token. At this point, token names are not supported. The information that can be returned about a token is far broader than what Roll20 natively supports. Also, Fetch includes different shorthand codes to more quickly reference the data (for instance, you can use "side" instead of "currentside" to get the currentside of a multi-sided token). Consult the following table to find the easiest reference for the item you wish to return. TOKEN PROPERTY USE THIS REFERENCE _cardid cardid cid _id token_id tid _pageid pageid pid _subtype subtype sub _type token_type adv_fow_view_distance adv_fow_view_distance aura1_color aura1_color aura1 aura1_radius aura1_radius radius1 aura1_square aura1_square square1 aura2_color aura2_color aura2 aura2_radius aura2_radius radius2 aura2_square aura2_square square2 bar_location bar_location bar_loc bar1_link bar1_link link1 bar1_max bar1_max max1 bar1_value bar1_value bar1 bar1_current bar2_link bar2_link link2 bar2_max bar2_max max2 bar2_value bar2_value bar2 bar2_current bar3_link bar3_link link3 bar3_max bar3_max max3 bar3_value bar3_value bar3 bar3_current bright_light_distance bright_light_distance compact_bar compact_bar controlledby token_controlledby token_cby currentSide currentside side curside dim_light_opacity dim_light_opacity directional_bright_light_center directional_bright_light_center directional_bright_light_total directional_bright_light_total directional_low_light_center directional_low_light_center directional_low_light_total directional_low_light_total emits_bright_light emits_bright_light emits_bright emits_low_light emits_low_light emits_low fliph fliph flipv flipv gmnotes gmnotes has_bright_light_vision has_bright_light_vision has_directional_bright_light has_directional_bright_light has_directional_low_light has_directional_low_light has_limit_field_of_night_vision has_limit_field_of_night_vision has_limit_field_of_vision has_limit_field_of_vision has_night_vision has_night_vision has_nv nv_has height height imgsrc imgsrc isdrawing isdrawing drawing lastmove lastmove layer layer left left light_angle light_angle light_dimradius light_dimradius light_hassight light_hassight light_losangle light_losangle light_multiplier light_multiplier light_otherplayers light_otherplayers light_radius light_radius light_sensitivity_multiplier light_sensitivity_multiplier light_sensitivity_mult limit_field_of_night_vision_center limit_field_of_night_vision_center limit_field_of_night_vision_total limit_field_of_night_vision_total limit_field_of_vision_center limit_field_of_vision_center limit_field_of_vision_total limit_field_of_vision_total low_light_distance low_light_distance name token_name night_vision_distance night_vision_distance nv_dist nv_distance night_vision_effect night_vision_effect nv_effect night_vision_tint night_vision_tint nv_tint playersedit_aura1 playersedit_aura1 playersedit_aura2 playersedit_aura2 playersedit_bar1 playersedit_bar1 playersedit_bar2 playersedit_bar2 playersedit_bar3 playersedit_bar3 playersedit_name playersedit_name represents represents reps rotation rotation showname showname showplayers_aura1 showplayers_aura1 showplayers_aura2 showplayers_aura2 showplayers_bar1 showplayers_bar1 showplayers_bar2 showplayers_bar2 showplayers_bar3 showplayers_bar3 showplayers_name showplayers_name sides sides statusmarkers statusmarkers markers tint_color tint_color tint top top width width Caveat: Asynchronous Properties Be aware, certain properties like a character's Bio, Notes, and GM Notes are meant to be accessed asynchronously. Although you can return data from these fields with Fetch, you will not get the information you are looking for (i.e., the text of the GM Notes). Instead, you will get the blob identifier from the database back-end. This is not a limitation of Fetch, but a rule imposed by Roll20 for the retrieval of data from these fields. The fields are nevertheless included in Fetch should this rule ever be lifted. Simple Character Information from Token You can use either "selected" or a token ID with this formation to ask for a piece of character information (a property or simple attribute), provided the identified token represents a character to query against. If a character can not be identified, Fetch will return either the specified default value (if one is included), or an empty string. Simple Character Information (Attribute, Ability, Property) In addition to the just mentioned method for returning simple character information using "selected" or a token ID, you can return information from the character using the character id, the character's name, or a near-approximation of the character's name (Fetch will attempt to find the closest match to the name provided). As with Roll20 standard syntax, use an @ for character attributes and a % for abilities. Also use the @ for character properties (listed below). @(Glitterbomb Gleek.InitiativeMod) Character Properties Certain properties belong to the Roll20 character object separate from the character sheet. These can be referenced using the same formation, and one of the references from the table, below. CHARACTER PROPERTY REFERENCES _defaulttoken defaulttoken _id character_id char_id _type character_type char_type archived archived avatar avatar controlledby character_controlledby char_cby character_cby char_controlledby inplayerjournals inplayerjournals name character_name char_name The same caveat applies regarding asynchronous properties for the character as it did for the tokens. These properties are included for completeness and against the possibility that Roll20 will change the restriction to retrieve them in only an asynchronous fashion. Repeating Items Repeating items are different from standard attributes in that they are groups of related sub-attributes. One sub-attribute typically serves as the name of the item as it would appear on the sheet, while others track various measurements and data about that element. To return a given piece of data from a given row element, you often have to supply various parameters which, taken together, let you arrive at a unique repeating item. The basic formation for retrieving information from a repeating item is: *(character.list.[pattern].returned_attr) Where  character  can be "selected" (referring to the selected token), "speaker" (see below for notes on using the current speaker), a token id, a character id, a character name, or a near-approximation of a character name. list   refers to the list housing the repeating elements. Examples might include "spells", "skills", "cantrips", etc. pattern  refers to the various sub-attributes which help you narrow down a unique item from the given list. See below for more information. returned_attr  refers to the actual sub-attribute whose value you wish to return. For instance, given a spell named "Fireball", perhaps you want to return the sub-attribute "spelllevel". Using the Pattern: Using Information from Sub-Attributes When we're talking about repeating attributes, their naming follows the pattern: repeating_list_rowid_sub_attribute The  list  might be "spells", and each item in the list will have a unique  rowid . All of the sub-attributes of that entry will share that  rowid  in their name, and then they will continue with their own, differentiated field name (i.e, "spelllevel", "spellpoints", "preparation_time", etc.). Sub-attributes are the only part of the name of a repeating element where you can have an underscore. NOTE:  If you have trouble identifying the list or the sub-attributes of a repeating item, I suggest you use the  XRay  script (part of the InsertArg package), to walk a character sheet and see how each element is constructed. Using these sub-attributes, you can narrow down which item from the list you want to return. For example, Bob the Slayer has a spell list on his character sheet, where every item on that list has sub-attributes of  name ,  spell_level , and  prepared . He has the spell “Disintegrating Blast” entered on that list at level 1, 2, and 3. The following call would return the  spell_level  of the prepared “Disintegrating Blast”: *(Bob the Slayer.spells.[name = "Disintegrating Blast" prepared].spell_level Patterns are like conditional tests of the sub-attributes of a repeating item. All of the conditions must pass validation for the pattern to detect a repeating element (in other words, they must all be true). Also, while each test can be binary (a = b) or unary (a), a unary test (like, “prepared”, just above) implicitly tests for a truthy value from the field beyond just whether it exists. This typically represents an option button or checkbox on a character sheet. Separate tests using a space. If there is space in the text provided as the right hand side of a comparison, enclose it in single quotes, double quotes, or tick marks. Valid binary tests for a sub-attribute are: TEST | DESCRIPTION --------+---------------------------- a = b | equals a != b | does not equal a > b | is greater than a >= b | is greater than or equal a < b | is less than a <= b | is less than or equal a ~ b | includes a !~ b | does not include See the Syntax Highlights at the top of this post for other examples of using patterns. Special Information About Repeating Items The default return from a repeating item is the value of the requested sub-attribute (if you request the spelllevel of a spell on the repeating list, spells, you will get the value of that attribute). There are, however, other things you can return, such as the row ID of the repeating element (the thing that makes the "Fireball" spell, and all of its sub-attributes, unique on the spells list), the row number of the element (i.e., its current position on the character sheet no matter the order it was originally added), or the name, or name variations using either of these options. To retrieve these other pieces of data, include a period or pipe (use the same character as you use throughout this Fetch structure), then one of the following keywords: KEYWORD   |  RETURNS                  |  EXAMPLE RETURN ---------+---------------------------------------+--------------------------------------------------- rowid | row id of repeating element | -M1234567890abcdef row$ | sheet position of repeating element | $1 name | name of sub-attribute using rowid | repeating_spells_-M1234567890abcdef_spelllevel name$ | name of sub-attribute using row$ | repeating_spells_$1_spelllevel In use, this would look like:  *(Bob the Fabulous.spells.[name="Fireball"].spelllevel.row$) Default Values If Fetch does not find the requested information (either because the source token or character could not be located or the requested item doesn't exist), it will return a zero-length string to the command line in that position. You can provide a default value for it to return, instead, by including the default as the last thing within the parentheses. Enclose the default value in brackets. Everything between the brackets will be considered the default value. @(Bob.UnderwaterMacrame[0]) The above would use "0" as the default value if the character "Bob" could not be found, or if Bob did not have an attribute named "UnderwaterMacrame". Default values can be extremely helpful in math operations, returning either a 0 or a 1 (or some other value, as necessary) to allow processing to continue. This math can be accomplished with either MathOps (if you have that script installed), or with a deferred inline roll with ZeroFrame (again, if you have that script installed). Using Speaker For character items (properties, attributes, repeating attributes, or abilities), you can use "speaker" in the same way that you would use "selected" for a token. In this case, "speaker" will attempt to utilize the character you were speaking as when you sent the message.
1618937506

Edited 1626814486
timmaugh
Pro
API Scripter
Updates and Releases v1.0.0 - Initial release v.1.0.1 - Minor bug/typo fix v 1.0.2 - macro retrieval, resolving properties stored as IDs to their names v 1.0.3 - token properties by name v 1.0.4 - fixed to limit Fetch calls to tokens on the current page v 1.0.5 - new token properties exposed by Roll20 now supported v 1.0.6 - fixed rowid retrieval for repeating item v 1.0.7 - fixed a bug that could allow improper ordering/processing of metascripts with ZeroFrame installed
1618937513
timmaugh
Pro
API Scripter
Advanced Usage and Tricks
This is working perfectly so far where {& define} had stopped working =D I noticed {&simple} still works with this, too, which is very convenient.
1618971593

Edited 1618971883
timmaugh
Pro
API Scripter
Fantastic! I'm glad it's working for you! And, yes... {&simple} still works, it's just a part of the ZeroFrame script, now, so that it's detected after all looping is complete. Also, {& flat} is another syntax token that does the same thing, should that make more sense to some people. EDIT: the good thing about the Fetch script compared to the older version of definitions in APILogic is that now you're not limited to defining the term first so that it is detected. You can drop this construction anywhere in your command line and have it be detected, parsed, and processed. You can still use a define tag, of course... but you don't have to.
1619409676
timmaugh
Pro
API Scripter
Version 1.0.1 Released FIXED : Minor bug around retrieving a value from a token when specifying the token id (instead of "selected").
1620082298

Edited 1620418341
timmaugh
Pro
API Scripter
Version 1.0.2 Released May 3, 2021 ADDED : Macro retrieval FIXED : Decoding of GM Notes field retrieval ADDED : New retrieval properties for the recognizable names of items stored as IDs (ie, getting the name of the character represented by the ID stored in the represents property. Update Notes Macro Retrieval Use the formation #(macroname) to get a macro's action text into your current command line. This allows for late retrieval of the macro, preventing Roll20 from running it before you are ready. For instance, you can do a roll query to retrieve only the macro you want: !?{What macro do you want to retrieve?|Tacos,#(Tacos)|Not Tacos,#(NotTacos)|Cake,#(Cake)|Death,#(Death)} This particular usage was discussed in this post . Name Retrieval Token Properties The following properties were added as ways to return the name of the item stored as an ID with the token. NEW PROPERTY REFERENCES page page_name the name of the page referenced by the token's pageid property bar1_name name1 The name of the attribute linked to bar1 bar2_name name2 The name of the attribute linked to bar2 bar3_name name3 The name of the attribute linked to bar3 represents_name reps_name The name of the character this token represents token_controlledby_names token_cby_names A comma separated list of the names of the players who are listed on the token as controlling players. Note: tokens that represent characters have their access list controlled at the character level (see new properties for characters, below) As an example, this would retrieve the name of the character represented by a token, and use the term "Unlinked" if the token is not set to represent anyone: @(selected.reps_name[Unlinked]) Character Properties The following properties were added as ways to return the name of the item stored as an ID with the character. NEW PROPERTY REFERENCES character_controlledby_name character_cby_name char_controlledby_name char_cby_name A comma separated list of the names of the players who are listed on the character as controlling players. As an example, this would retrieve the list of players who could control the character Bob: @(Bob.char_cby_name)
1620418658
timmaugh
Pro
API Scripter
Version 1.0.3 Released May 7, 2021 ADDED : Token data retrieval using token name Update Notes Token Name Pretty straightforward, this one. You can now use the token name for the handle to reference the token: @(Bob the Hirsuite.gmnotes)
1620744288
timmaugh
Pro
API Scripter
With this release, Fetch also gets submitted to the one-click repository, and should be available from there with the next merge. Version 1.0.4 Released May 11, 2021 FIXED: Token retrieval by name now properly limits return to the page you are on, allowing for multiple tokens across different maps to have the same name (representing the same character). Update Notes Retrieve by Token Name As mentioned above, if you have tokens of the same name across multiple maps, you were not guaranteed to get the token you intended when you queried using the @(Bob.bar1) construction. Since it is common to have tokens on multiple maps that represent the characters (and which will share the name of the character), Fetch will limit itself to only the tokens on the current page (the player page for players, or the last page for the GM) if you use the name-retrieval formation. Should you need specifically to refer to a token on another page for any reason, use the token ID of that token in place of the name. Fetching by token ID is unaffected by the above change.
There doesn't happen to be a way to use Fetch to get token marker info, is there? Like which markers (custom included) are on a token and/or any numbers on those markers?
1620794117
timmaugh
Pro
API Scripter
Sure... you can use either statusmarkers or just markers with the token. @(selected.markers) @(Heretic.markers) That will return the comma separated list of markers applied. Not sure about the numbers on the tokens, but if they're not returned I can look to add it!
I can't seem to get it to return anything. I tried all three of the following @(selected.markers) !@(selected.markers) !@(selected.markers)&{simple} The first just returns that exact text to chat, the other two return nothing. Anything I'm missing? And would this allow a way to check for specific markers? I was thinking of setting up something that uses APIL to check for the presence of, say, a marker that represents an armor penalty.
I was also brainstorming ways I could use the meta scripts to tweak a damage listener script I use with TokenMod. Like comparing damage types and other traits from a damage roll to weakness/resistance/immunity traits in the target token's character sheet. But a lot of the attributes involved have multiple values / key words in a single attribute, and I'm not sure there's a way to reliably split up multiple values in one attribute then compare them to multiple values in other attributes. Would appreciate any insight or suggestions you might have, though.
1620827894

Edited 1620835237
timmaugh
Pro
API Scripter
Remember that when you use a bracket & ampersand formation (sounds like some kind of mercantile store... Bracket & Ampersand founded 1861 ) Anyway, when you do that, it's {& ... }, so the last one of yours is close, but you reversed the ampersand's position: !@(selected.markers){&simple} =D As for looking for an armor penalty, if you were using the marker broken-shield as the indicator of that, you could use APILogic and Fetch: !{&if "@(selected.markers)" ~ "broken-shield"}Armor penalty detected.{&else}No armor penalty here.{&end}{&simple} You don't need the quotation marks around either side of the condition, but I included them in case there are weird rules I don't know about for spaces and/or special characters in the names of custom markers. Using the quotes gives you your best protection against breaking the parser. On your other question, about using the meta scripts for larger comparisons of mutli-value attributes... here's an example. If this isn't close enough to what you're trying to do, post back with more specifics... If you have an effect that should only go off if a specific attribute includes certain keywords but not others, you can code that with APIL... if the inclusion keyword cases are: smoke (but not) fire ether (but not) astral And the attribute you want to test is attackmedium... !{& define ([themedium] @(selected.attackmedium[none]))}{& if ("themedium" ~ smoke && "themedium" !~ fire) || ("themedium" ~ "ether" && "themedium" !~ astral)}I passed the test. I shall go into the west, and apply my damage.{&else}Where's my Dark Queen Galadriel?({&end}){&simple} That's the proof of concept. (Note the parentheses around the {&end} tag to keep it from becoming a roll query.) If you had a lot of different conditions you wanted to track, you could make use of a Mule, too (and at this point, ZeroFrame becomes necessary). Setup a Mule as a table of the various conditions you want to test for, giving them meaningful names: SpectralSmoke=("themedium" ~ smoke && "themedium" !~ fire) SpectralEther=("themedium" ~ "ether" && "themedium" !~ astral) Then put your combos in using Mule .get statements, so you would have in that same Mule: SmokeOrEther=get.SpectralSmoke/get || get.SpectralEther/get Then your proof of concept, above could become (if your Mule is named "AttackMediumTable"): !{& define ([themedium] @(selected.attackmedium[none]))}{\& if get.SmokeOrEther/get}I passed the test. I shall go into the west, and apply my damage.{&else}Where's my Dark Queen Galadriel?{&end}{&simple}{& mule AttackMediumTable} Note the 2 Muler statements (the .get and the {& mule...} ). Also note that you have to defer the IF block one time to allow for 2 passes at the Mule -- one to get the initial variable "SmokeOrEther", and then a second because that variable dropped new Mule constructions in your command line. That said, your IF block got simpler (and reusable in another macro). If you don't like having to hardcode the deferrals into the IF block separate from the Mule variable (requiring you to know how many deferrals you need to include), you could move the entire IF block to the Mule with the deferrals in place, so they are set once and then forgotten. Or you could have a SmokeAndEtherDef variable, and set it to be one backslash: SmokeAndEtherDef=\ ...then retrieve that variable to break up the IF block construction: {get.SmokeAndEtherDef/get& if ... If you wanted to get all kinds of crazy with the meta sauce, you could have an APIL definition that assigned the attack type to the macro. That would make this macro text copy & pastable to another macro where you'd only havee to change the attack type in ONE place. If the definition assigned "SmokeAndEther", you could set it up so that the .get construction was filled with the definition of what to get: !{{ {& define ([attacktype] SmokeOrEther)} {get.attacktypeDef/get& define ([themedium] @(selected.attackmedium[none]))} {get.attacktypeDef/get& if get.attacktype/get} I passed the test. I shall go into the west, and apply my damage. {get.attacktypeDef/get&else} Where's my Dark Queen Galadriel? ({get.attacktypeDef/get&end}) {&simple} {& mule AttackMediumTable} {&log} }} (I dropped it to multiple lines to make it more readable; this will make for a message with a lot of whitespace, but that doesn't matter as a proof.) Note that definitions only run if the APIL logic blocks are well-formed, and they only apply to the pass of the loop on which they run. Meaning, if you only escape your IF but not your ELSE, then APIL will find an ELSE without an IF (a malformed IF block), and *not* process the definition statement. That's why our first definition runs clean -- without deferral -- to get the attack type. Our second definition is as deferred as our IF block pieces (using the same retrieved variable), so that the definition resolves on the same pass as the logic. In the meantime, Muler has retrieved the conditions for the logic, so by the time APIL runs, it has the proper tests to run. This is deep, deep meta-fu. If it helps to see the changes in the command line, drop a {&log} on that line at the end and look at the output. If you have any questions, post back! EDIT: The nice thing about Muled variables that return part of your command line is that they come into the picture after the message has already hit the chat the first time and at least the meta syntax constructs are gone before the intended-recipient script sees them, so you don't have to do a lot of the HTML replacements you might otherwise have to do!
1620827966
timmaugh
Pro
API Scripter
Oh, the above requires the latest Muler version, just pushed out this morning. I detected an error in my testing to set this all up, so the fix is now avialable in version 1.0.5. (Also headed to the one-click with the next merge.) =D
timmaugh said: Anyway, when you do that, it's {& ... }, so the last one of yours is close, but you reversed the ampersand's position: !@(selected.markers){&simple} =D Oops! I had been typing out &{template} a lot yesterday and got mixed up. Thanks for the correction, I'm getting the token markers printed from that now, and it even shows if a marker has a number! ,bird-claw::5448,purple@1,three-leaves@3,biohazard::5446@5 So many possible uses for this =D I haven't gotten around to looking into Muler yet, so I'll start digging into that soon. I have everything I'm working on set up in my test game that you have access to. The damage listener script is there; it parses messages for damage rolls then spits out a chat menu with TokenMod commands. Those commands are what I'm interested in altering with the meta scripts. I also saved a handout with (hopefully) clearer examples of what kinds of attributes and values this would all be handling. You're welcome to check any of that out if you're interested, meanwhile I'm gonna take a crack at wrapping my head around Muler :)
1621113250
I've been testing Fetch out with mixed results.  May be I just need to get my mind around the sequencing of how the logic works and the syntax.  Here's a ScriptCards code example I'm trying to test:   !scriptcard {{ --#title|Fetch Test --#whisper|gm --+Selected Test 1|@(selected.token_controlledby[not found]) --+Selected Test 2|@(selected.character_controlledby[not found]) --+Selected Test 3|@(selected.token_controlledby_names[not found]) --+Selected Test 4|@(selected.character_controlledby_name[not found]) --/|Loop through all of the tokens in "alltokens" --~tokencnt|array;pagetokens;alltokens;@{selected|token_id} --~TokenId|array;getfirst;alltokens --&CharId|[*[&TokenId]:t-represents] --?[&TokenId] -eq ArrayError|ENDLOOP --:LOOPCHECK| --&CharId|[*[&TokenId]:t-represents] --/+Debug|[*[&TokenId]:t-name] --/|Skip targets that are not on the token layer or that don't represent creatures --?[*[&TokenId]:t-layer] -ne objects|CONTINUE --?"[*[&TokenId]:npc]" -eq 1|CONTINUE --?"[*[&TokenId]:t-represents]" -inc "-"|START --^CONTINUE| --:START| --+Token|[b][*[&TokenId]:character_name][/b] --+TokenId|[&TokenId] --+CharId|[&CharId] --+T1 Cont by|@([&TokenId].token_controlledby_names[not found]) --+T2 Cont by|@([&TokenId].character_controlledby_name[not found]) --+C1 Cont by|@([&CharId].character_controlledby_name[not found]) --:CONTINUE| --~TokenId|array;getnext;alltokens --?[&TokenId] -ne ArrayError|LOOPCHECK --:ENDLOOP| }} The code is looping through all the tokens on the page, and for those tied to a character, attempts to output who controls it.  As you can see, I got a couple of the test with  @(selected.character_controlledby_name[not found])  to work (Selected Test 4).  However, it doesn't like using the dyanamic token or characacter id's when I walk through tokens on the page.  Not sure if this is a limitation or my syntax.   Results: Selected Test 1 Selected Test 2  -MNpocfYt34s_UIHv-Fi,-MVqEDdmkgsCbCR6uxQS,-MNq5H5M8Lydk_2XcEgl Selected Test 3  not found Selected Test 4  Jokaryn, Dummy A., Shaper Token   Eliza Redburrow TokenId  -M_fRm7G2ESTnpWTYD43 CharId  -MUdcY7R5dKdJzzhlQTH T1 Cont by  not found T2 Cont by  not found C1 Cont by  not found Token   Shaper TokenId  -M_fRm98yXabxoVLUGe9 CharId  -MPKwr99wT9sWlOlwMRM T1 Cont by  not found T2 Cont by  not found C1 Cont by  not found Token   Kharg TokenId  -M_fRmChC4FGs9ZCfg6O CharId  -MNvJHCq3ftZglXJKRQk T1 Cont by  not found T2 Cont by  not found C1 Cont by  not found Token   Jokaryn TokenId  -M_fRmEUWSzIcRdgiO6j CharId  -MNpyES-Cd9Fk8TZg6h6 T1 Cont by  not found T2 Cont by  not found C1 Cont by  not found Token   Xaral Mind-drinker TokenId  -M_fRmGJSCJyM-PSCoXX CharId  -MNvIO7FFGVxHfZ69z2G T1 Cont by  not found T2 Cont by  not found C1 Cont by  not found
1621137739
timmaugh
Pro
API Scripter
First, I only just noticed that the token controlledby property shorthand to get the names of the controlling players is "names" while it is "name" for character. My apologies... that was an oversight, but to maintain compatibility, I will enable both forms for both token and character as soon as possible. Next, I think you're running up against an issue of the order things are done. When a script-calling message is sent from the chat (or API), it is picked up by the first script in the queue. That script either processes it or it declines. Either way, when it's done, the message goes on to the next script in the queue, and so on. Fetch (or ZeroFrame, if you have that installed), makes sure that it steps in front of other scripts like ScriptCards so that it gets the message first and basically acts like an extension of the interface... giving you more access to a wider variety of game/token/character data. It does its work, then it releases the message and the next script in the queue picks it up... Eventually that next script is ScriptCards. By the time the message lands with ScriptCards, Fetch's processing is done. So when Fetch gets the message, it sees you want to return token and character data, and it populates the command line in the correct position (your Test 1, Test 2, Test 3, and Test 4 lines). But those are for the selected token (if you have many chosen, it only works on the first selected token). It returns the data and gets out of the way. When ScriptCards takes over, it builds an array and runs its own loop... but by then Fetch is done. The only way to trigger the meta scripts again is if ScriptCards were to send a new chat message through with a subset of data. That's the sort of thing forselected can do... you tell ScriptCards to run forselected, and SelectManager (the script that answers the !forselected handle) sends a new message through for every token that is currently selected. With something like that, you can defer the resolution of the Fetch call using a character you declare as a part of the handle. That way the construction isn't recognized and parsed when it would only resolve the first selected token out of 10, but when forselected runs and it is the first selected token out of only 1 token selected: forselected(^) someapihandle @^(selected.token_controlledby) I might come up with a better way to do what you want to do (it's late, so this is the first thing that came to me), but you could use a forselected call to write the data you wanted to a Mule, then interrupt the ScriptCards macro (I understand you can tell it to stop and get a chat button that would restart it). That restarting would effectively be a "new" message which would trigger the meta scripts. In that portion of the ScriptCards macro, you read the Mule data with a meta construction, and use the new information to keep going. I will also look at a way to iterate, in-line, over the set of items you want to handle, but it would still be a matter of collecting the data while the meta scripts are working (before ScriptCards gets involved), and then letting ScriptCards use that data to construct the output you want.
1621141500

Edited 1621142790
No worries - I suspected it was a sequencing issue.  I have a good appreciation for what Fetch can do now.  I'm going to think about use cases for Fetch's ability to utilize script card syntax stored in a gmnote.  I'm still very new to JavaScript, and learning how the API's work with Roll20's macro engine, but I'm starting to figure things out.   I'm hopeful that over time, Kurt works to expose more of Roll20's object model (players, handouts, campaign, decks, text, paths, ...)  as he extends ScriptCards which is basically a big wrapper function on JavaScript and the Roll20 API.   Between ScriptCards, TokenMod, ChatSetAttr, and your SelectManager/ZeroFrame tools, most people would never even need to jump into JavaScript/API coding side.   Update :  I just ran across your series of posts on programming JS (var vs. let vs. const and Flow).  There is some gold in that discussion. 
1621171120
timmaugh
Pro
API Scripter
I appreciate it, Will. I hope Fetch and the Meta Scripts can be of use! (heh... sounds like a band). And nearly everything I've learned about javascript started with this board and the help I got from others along the way, so there's definitely a good community and a ton of support for growing skills. =D
1623250226
Andreas J.
Forum Champion
Sheet Author
Translator
Guess you'll update this for the new API keys?
1623253270
timmaugh
Pro
API Scripter
Yep. Should be out today or tomorrow, with the one-click to follow next week!
1623274792
timmaugh
Pro
API Scripter
Version 1.0.5 Released June 9, 2021 ADDED:  New token properties exposed to the API have been added Update Notes New Token Properties The token properties listed in this post  are now available to Fetch. The available shorthand references have been added to the first post in this thread, and are also listed here: To Reference Token Property    | Use One of These References ===============================|========================================= bar_location | bar_loc, bar_location compact_bar | compact_bar light_sensitivity_multiplier | light_sensitivity_multiplier, light_sensitivity_mult night_vision_effect | night_vision_effect, nv_effect For example, to get the bar location for a selected token, you can use: @(selected.bar_location) ...or... @(selected.bar_loc)
Is there any way Fetch or another meta script could be used to get an array of the values for a specific subattribute in existing rows and add those values together? For example, getting the _qty of all rows in a repeating_items section in a way that they could be either added together or plugged into a {}k1 or {}d1 type of function.
1623706645

Edited 1623715546
timmaugh
Pro
API Scripter
Fetch can't do that -- yet. That functionality resides in InsertArg and I intend to port it over to Fetch as soon as possible. The beauty of the meta-script approach is that it happens in the current run line. InsertArg, being an earlier generation, relied on constructing the command line first, then expecting you to run it. So you could run IA to get the list (a one-off process)... or you could have your usage-glow be running IA to update your command line every time (which you then run). You're looking to use the getattrs{{}}  getrepeating{{}} function of IA , but honestly the easiest way to build it is to Xray a character and drill down on their Items repeating list. Once you get an element pulled up, you should see a table of data (a row per sub-attribute of each element). Use the "Build" button to get a cross-section of the repeating list around that attribute, then use your up arrow to see the syntax. If I have time later tonight, I will do it myself and post an example. EDIT: Corrected the above to the actual function you would want to use... but I'm having trouble getting it to resolve properly. I'll have to dig into that to see if there is something that changed... maybe the character sheet updates? Anyway, once it's working, the basic syntax should be something like: !ia --whisper --show#getrepeating{{!!c#getme{{}} !!s#items !!d#, !!sfxa#qty}} That would give you the qty sub-attributes from the items list for the current speaker ( getme{{}} ), separated by a comma (the d argument). You could separate them by a plus sign by replacing that , with a +. I'll see if I can figure out what is going wrong, there.
1623778053
timmaugh
Pro
API Scripter
OK, I found the error and sent a new version of InsertArg to the 1-click (along with a couple of other fixes I'd been meaning to make for a while). With that new version you should be able to do what you want, Persephone. I will see about promoting that functionality to true meta-script speed as a part of Fetch so it won't have to be a 2-step process.
1623895159
I'm trying to understand the interactions of deferral characters. !forselected(^) @^{tracker|@(selected|token_name)} I'm quite sure I'm doing something wrong here, but I have no idea what it is. The objective is to interrupt the @{} notation in favor of Fetch to retrieve the name of the token, and *then* allow Roll20 to parse @{tracker|<token name>}.
1623899863

Edited 1623899972
timmaugh said: That would give you the qty sub-attributes from the items list for the current speaker ( getme{{}} ), separated by a comma (the d argument). You could separate them by a plus sign by replacing that , with a +. I'll see if I can figure out what is going wrong, there. Okay, that sounds like it's getting close! I did realize it's a bit more complicated than how I initially explained it. The main reason I was looking into this is because I thought I might be able to work around an ongoing sheetworker bug. I have API buttons set up in inventory items so their quantities can be adjusted, but the sheetworker bug causes the total inventory capacity to inflate when doing this (so reducing one item's qty might increase the total bulk from 6 to 14). So my idea was what if the API commands for adjusting one item's quantity also could take each inventory's row and sum up each (bulk * qty) to force change the total bulk, overriding the inflation with the correct total. Also, not exactly related but involves Fetch: I was trying to set up a macro that would use ChatSetAttr, APIL, and Fetch to take the info from a row in one repeating section, and create a new row in a different repeating section on the same sheet, but I'm failing to remember how exactly {& define} works now. I tried the original method that used APIL only then remembered you took that out when you made Fetch. So here's where I'm at so far: !{& define ([featattr] *(compendium.feat-skill.[feat_skill~"?{Feat}"].feat_skill.rowid)) }The attr is featattr{&simple} with the intent that the defined term will be plugged into the middle of direct attr calls in place of the row id, like @{compendium|repeating_feat-skill_featattr_feat_skill}. But the command above only ever returns 'The attr is '. I feel like once again I'm missing something quite obvious, and would be grateful for your guidance!
1623904227
timmaugh
Pro
API Scripter
Colin C. said: I'm trying to understand the interactions of deferral characters. !forselected(^) @^{tracker|@(selected|token_name)} I'm quite sure I'm doing something wrong here, but I have no idea what it is. The objective is to interrupt the @{} notation in favor of Fetch to retrieve the name of the token, and *then* allow Roll20 to parse @{tracker|<token name>}. Unfortunately, the @{target|...} construction is in the same class of Roll20 parsable objects that requires the message to have come from a user when the parsing happens -- like @{target} statements and ?{roll|queries}. That means Roll20 won't parse what you're sending when you defer it that way, because by the time you un-escape it, it's part of the forselected message -- which was sent by the API. The good news is that this is a very retrievable piece of game information, and I can add this to Fetch to get with a @(tracker...) construct. I'll see if I can knock that out. Other things to remember... 1) forselected is intending to iterate an API call over your selected tokens, so you would need to turn that into basic output again if you want to see the result in chat. You can do that with a deferred SIMPLE construct: {^&simple} 2) forselected will send the command line for each token you have selected, but it occurs after meta-script processing hits the message (forselected is a part of SelectManager that is registered as a normal, non-meta script, giving the meta toolbox time to work on the message). That means by the time forselected sends the above command line, the Fetch construction has been processed, and the contents of the token name inserted into the command line. If you have 10 tokens selected, you will get 10 outputs of the same character's initiative value in the tracker. This won't be noticeable if you have only one token selected, of course. If you want to defer the resolution of the token name Fetch until you are in the forselected-spawned messages (one for each token) so that you get a different token name for each individual token, you can use either a) the same deferral character you declare as part of forselected, b) use at(selected...) instead of @(selected...) , or c) you can use the ++ version of forselected and @{selected|token_name}. 3) Lastly, remember that if you're using forselected in ScriptCards, SC has ^ built in as a default deferral character. You can change it, but if you don't, SC will remove it before forselected gets a chance to. That might be what you want, or it might not, but it is something you should definitely be aware of.
1623938692
timmaugh
Pro
API Scripter
Persephone said: Also, not exactly related but involves Fetch: I was trying to set up a macro that would use ChatSetAttr, APIL, and Fetch to take the info from a row in one repeating section, and create a new row in a different repeating section on the same sheet, but I'm failing to remember how exactly {& define} works now. I tried the original method that used APIL only then remembered you took that out when you made Fetch. So here's where I'm at so far: !{& define ([featattr] *(compendium.feat-skill.[feat_skill~"?{Feat}"].feat_skill.rowid)) }The attr is featattr{&simple} with the intent that the defined term will be plugged into the middle of direct attr calls in place of the row id, like @{compendium|repeating_feat-skill_featattr_feat_skill}. But the command above only ever returns 'The attr is '. I feel like once again I'm missing something quite obvious, and would be grateful for your guidance! I think you have the right of it, but again you've found a minor bug where somewhere along the way the rowid retrieval got "unplugged" from your valid returns. I have released a new version (1.0.6) which you can get from my repo until it makes it in the 1-click next week. With this fix, you can once again get the rowid of a repeating element as you would expect. That said... in my testing  you should be getting the default return if it doesn't recognize "rowid". In other words, you should be getting the contents of the "feat_skill" sub-attributer. So why aren't you getting anything? Let me break down what your line is doing, and you can tell me if that's not what it should be doing... The ! and {&simple} work together to initiate the process, then dump out to a simple chat message. Leaving them off, you have: {& define ([featattr] *(compendium.feat-skill.[feat_skill~"?{Feat}"].feat_skill.rowid)) }The attr is featattr Definitions follow the pattern of: {& define ([term] definition) ([term2] definition2)} So you are defining the term featattr to be a Fetch construction for a repeating item. Once filled, it will be subbed-in, downline. All of that looks good, I think. The Fetch construction is going to ask for input to fill the "?{Feat}" roll query. Once you provide that, Fetch will look on a character named "compendium" in the repeating list "feat-skill" for an entry where the sub-attribute "feat_skill" includes the text you provide to the roll query, and it will return the rowid of the element (using the feat_skill sub-attribute, but at this point any sub-attribute will work since they all have the same rowid). If all of that sounds like what you're trying to do, then I would suggest some troubleshooting checks: -- obviously update to Fetch v1.0.6 -- the Fetch construction "includes" test is case-sensitive, so if you were looking for "Actual Cannibal" and you entered "actual", you wouldn't find it -- use the updated XRay (part of the pending release, available to 1-click next week but in my personal repo now) to walk the character sheet and make sure that you have the correct sub-attribute field names and the right contents to test for -- simplify the statement down to just the Fetch + the {& simple} construct (ie, remove the definition), and use a default value as an "empty": !*(compendium.feat-skill.[feat_skill~"?{Feat}"].feat_skill.rowid[not found]){&simple} In that case, if you see "not found" in the chat, you know the command processed, you just haven't provided the right data to find the row you're looking for. Once you get the proper Fetch construction, plug it back into the definition, and it should work. If you can't get it once you get the new Fetch, let me know and I'll jump into your test game to see if I can figure out what is going on. =D
timmaugh said: The Fetch construction is going to ask for input to fill the "?{Feat}" roll query. Once you provide that, Fetch will look on a character named "compendium" in the repeating list "feat-skill" for an entry where the sub-attribute "feat_skill" includes the text you provide to the roll query, and it will return the rowid of the element (using the feat_skill sub-attribute, but at this point any sub-attribute will work since they all have the same rowid). If all of that sounds like what you're trying to do, then I would suggest some troubleshooting checks: -- obviously update to Fetch v1.0.6 -- the Fetch construction "includes" test is case-sensitive, so if you were looking for "Actual Cannibal" and you entered "actual", you wouldn't find it That's exactly what I'm attempting, and those steps were just what I needed! I somehow missed the recent updates and was still on v1.0.4, and for some reason I was assuming only the "equals" test is case-sensitive. Also, it cracks me up whenever I see you reference ACSL xD But I've finished the macro, and it's working flawlessly! !setattr --name compendium {& define ([featattr] *(compendium.feat-skill.[feat_skill~"?{Feat (case sensitive)}"].feat_skill.rowid[not found])) } --repeating_feat-general_-CREATE_feat_general|%repeating_feat-skill_featattr_feat_skill% (%repeating_feat-skill_featattr_feat_skill_skill%) --repeating_feat-general_-CREATE_feat_general_level|%repeating_feat-skill_featattr_feat_skill_level% --repeating_feat-general_-CREATE_feat_general_traits|%repeating_feat-skill_featattr_feat_skill_traits% --repeating_feat-general_-CREATE_feat_general_prerequisites|%repeating_feat-skill_featattr_feat_skill_prerequisites% --repeating_feat-general_-CREATE_feat_general_action|%repeating_feat-skill_featattr_feat_skill_action% --repeating_feat-general_-CREATE_feat_general_trigger|%repeating_feat-skill_featattr_feat_skill_trigger% --repeating_feat-general_-CREATE_feat_general_requirements|%repeating_feat-skill_featattr_feat_skill_requirements% --repeating_feat-general_-CREATE_feat_general_frequency|%repeating_feat-skill_featattr_feat_skill_frequency% --repeating_feat-general_-CREATE_feat_general_benefits|%repeating_feat-skill_featattr_feat_skill_benefits% --repeating_feat-general_-CREATE_feat_general_special|%repeating_feat-skill_featattr_feat_skill_special% --repeating_feat-general_-CREATE_feat_general_notes|%repeating_feat-skill_featattr_feat_skill_notes% --fb-content Feat copied.
1624030754
timmaugh
Pro
API Scripter
Excellent! Persephone said: Also, it cracks me up whenever I see you reference ACSL xD hee hee!
1624115127
timmaugh
Pro
API Scripter
Version 1.0.6 Released June 17, 2021 FIXED:  The special return of 'rowid' for a repeating item was previously broken; using it (as Persephone did, just above), should get the ID associated with the entire set of sub-attributes associated with a single "entry" in a repeating list.
1626480117
Hey Tim, it is I again! I'm having some trouble with the interaction of Fetch, ScriptCards and the deferral character.  What I have is some amount of starships on the map, each having a attribute with their pilot's name.  I'm trying to get that pilot's name, use Fetch to get the id for that character, then use that ID in a loop that rolls a piloting skill roll (for initiative) in a card that has the starship tokens selected.  So I have a variable that gets the pilots name and I am trying to construct the Fetch call in ScriptCards to get the appropriate ID for each pilot throughout the loop. Here is what I have so far:  !scriptcard {{ --#emoteState|Hidden --~PageTokens|array;selectedtokens;Starships --~CurrentStarship|array;getfirst;Starships --:InititiveLoop| --#sourceToken|[&CurrentStarship] --&PilotName|[*S:pilot] --&PilotID|@([&PilotName].character_id){^&simple} --~PilotID|string;before;{;[&PilotID] --=PilotInitiative|[*[&PilotID]:piloting] [Skill] + 1d20 [Base] --&Init|+[&PilotName] rolls a [$PilotInitiative] for turn order from [*S:t-name].[br][br] --~|turnorder;replacetoken;[&CurrentStarship];[$PilotInitiative] --~CurrentStarship|array;getnext;Starships --?[&CurrentStarship] -eq ArrayError|InititiveDone|InititiveLoop| --:InititiveDone| --+|[&Init] }} It seems to be deferring correctly, but isn't returning a character id. Do I have the deferral character in the right place? Is what I'm trying to do not possible? Thanks!
1626495497

Edited 1626608956
timmaugh
Pro
API Scripter
Hey, Erik... The deferral character "hides" a syntax structure until the line it is hidden within is sent through the chat interface again... and the {&simple} structure would make sure that the line it is in gets dumped out to the chat interface (instead of being an API message). What I See Going Wrong ...so *if* the line: --&PilotID|@([&PilotName].character_id){&^simple} ...generates a new message, then ScriptCards would remove the ^ and let the {&simple} structure be detected by the new message (you're not using a metascript deferral character). I don't think SC is actually dispatching a new message, but if it were, I don't think you want the {&simple} structure at all -- you want to retain the value. You'd want to defer the Fetch construction: --&PilotID|@^([&PilotName].character_id) But, again, I don't think SC is actually dispatching a new message in this line. Instead, Fetch runs before the SC loop ever starts, and it looks for a character with an actual name of "[&PilotName]". It doesn't find one, of course. What I Think You Could Do, Instead Assumption 1: You have starships. Those starships have attributes. Therefore they are backed by character sheets. Assumption 2: Those starships have attributes to track the Pilot's name. That means you probably have an existing way to change the Pilot's name as would be attached to each starship. Assumption 3: It looks like you select the starships before you run this (SC is building an array from them). If all that is true, then... Solution: Use forselected (from SelectManager) to run a ChatSetAttr command, deferring the Fetch construction, and write the Pilot's character_id to the starship character. If this happens before the SC loop runs, you can retrieve both the PilotName and PilotID variable at the same time, in the same way. You can either run this forselected command before the SC command,  or you can embed the whole thing in a Plugger EVAL statement in the SC command to have it process prior to releasing the message to ScriptCards: {& eval}!forselected(^) setattr --sel --pilotID|@^(selected.character_id){& /eval} ...which, if you're keeping track, is Plugger running SelectManager's forselected construct running a ChatSetAttr to utilize a Fetch retrieval... ALL WITHIN a ScriptCard command line. Pretty cool. It uses a metascript deferral character (declared as a part of the forselected handle), and defers the Fetch construction until each individual command is sent. You're guaranteed that the character ID you retrieve will be as up-to-date as it would be from your ScriptCard loop, because we're using the same key (the pilot name) to get it on a per-starship basis. Then your ScriptCard line for the PilotName variable should be (I think): --&PilotID|[*S:pilotID] I believe that should work. =D EDIT: I had the deferral character declared in the wrong place (attached to the EVAL tag rather than the forselected handle)
1626581860
This worked perfectly!  Thanks, Tim!
1626609048
timmaugh
Pro
API Scripter
Excellent! Glad to help!
1626814445

Edited 1633522968
timmaugh
Pro
API Scripter
Version 1.0.7 Released July 20, 2021 FIXED : Ordering bug Update Notes There was a bug in the way this script ordered itself with ZeroFrame that would potentially allow it to run prematurely. The fix is available at my personal repo or it will come with the 1-click merge (probably tomorrow). Thread Closed => New Thread Opened With this thread timing out from inactivity, I've had to open a new thread. If you are reading this and you need to know more or see even more recent updates to the script, keep reading at this link .