[Help] Just starting to learn the API - need help getting started

1502551471
I just need a little push to help me get started writing a script and I'm just having trouble finding just the few tidbits I need. All the examples, scripts and tutorials I'm finding online quickly get over my head without ever telling me clearly the few basic things I'm trying to do. Could someone write a simple script that does the following for me so I can see how these basic mechanisms work: Receive a command from the chat window to run the script (lets say I type "getfruit" in the chat window or "!getfruit" or however commands work) Roll a D6 (let's say it results in a value of 3) Use that D6 result as an index reference to an array with 6 values in it (fruit = ["apple","pear","peach","mango","pineapple","plum"] so a value of 3 on the dice results in an index lookup of fruit[2] assuming indexes go from 0-5 in this case) Return that item to the chat window. (it returns fruit[2] which is "peach") So, I type !getfruit and it rolls a 3 and returns "peach" in the chat window. What I've pieced together so far is: /* globals sendChat, randomInteger, _, on */ var getfruit = (function() { 'use strict'; if (msg.type === "api" && msg.content.indexOf("!getfruit") !== -1){ var fruit = ["apple","pear","peach","mango","pineapple","plum"]; sendChat('gm',fruit[randomInteger(6)-1]); } }); Obviously it doesn't work but in the hours I've searched I can't find the pieces I'm missing in an easy to understand tutorial or code example. Please help!
1502558023
The Aaron
Pro
API Scripter
You're not far off.  Here's how I'd write that: on('ready',()=>{     const fruit = ["apple","pear","peach","mango","pineapple","plum"];          on('chat:message',(msg)=>{         if('api'===msg.type && msg.context.match(/^!getfruit/)){             sendChat('Your Fruit',`Your fruit is: ${fruit[(randomInteger(6)-1)]}`);         }     }); }); What you're missing is that the API is  event driven .  You have to register functions to be called when particular events occur.  In the above, I'm registering for 2 events.  The first one is the 'ready' event, which happens once the API is fully spun up.  It's usually best to delay real work until 'ready' happens.  The second event is the 'chat:message' event, which occurs whenever someone types in the chat box and hits enter. BTW, the first parameter to sendChat() is "who's talking".  If you want a chat message to go to the GM or a particular person, you'd put "/w gm " in front the same way you'd do in chat.
1502585657

Edited 1502585895
Thank you very much! That script and your explanation helps tremendously. I have a few minor followup questions: What is the "()=>" part of the on ready event? Is that just a shorter replacement for "function()" that I see in most scripts? Why is it const instead of var? Do I have to import some sort of globals to utilize the randomInteger function? If not, is there a list somewhere of the available global functions we have access to?  Never mind this last one, I found it.
1502586864
Scott C.
Pro
API Scripter
Todd B. said: Thank you very much! That script and your explanation helps tremendously. I have a few minor followup questions: What is the "()=>" part of the on ready event? Is that just a shorter replacement for "function()" that I see in most scripts? Why is it const instead of var? Do I have to import some sort of globals to utilize the randomInteger function? If not, is there a list somewhere of the available global functions we have access to?  Never mind this last one, I found it. Yep, ()=> is a shorter form for function() {}, it also doesn't create a new scope (Aaron will have to explain this in more detail). I'd guess that Aaron used const because nothing is manipulating the fruit, so it doesn't need to be able to change via push, shift, splice, or any of the other array manipulation functions.
1502590203
Ok, so const is this language's version of an immutable object? If I want it to be mutable then I'd use var I assume.
1502590701
The Aaron
Pro
API Scripter
Yeah, basically. Modern JavaScript has 3 ways to declare a variable: var -- this is the old way, it creates a mutable variable at function scope which is hoisted to the top of that scope.  let -- this creates a mutable variable at block scope with no hoisting.  const -- this creates an immutable variable at block scope with no hoisting.  In this case, immutable means you can't reassign the variable, but doesn't mean the variable's contents won't change. So a const variable that points to an object will always point to that object, but the object's properties might be changed (so you might append more items to the array, but it will always be an array, and will always be THAT array, even if the contents of that array change.  Function scope means inside that function.  Block scope means inside the { } block.  Hoisting means that if you use var to create a variable on the last line of your function, it's definition is hoisted to the top of the function scope and is defined for the entire body of the function.  let and const were introduced as part of ES6 to address the fact that var behaves so strangely compared to just about any other c-style language. let and const pretty much behave as you would expect, they are defined in the tightest scope that contains them, from the line where they are declared on, and undefined before that point or in a scope wider than their own.  As Scott said, () => {} is a nice shorthand way of creating functions. For the most part, you can just treat them like the long form function() {}. The only real difference is that they only have a block scope and not a function scope. You're unlikely to ever run into the importance of that distinction. =D. You can read up on "fat arrow functions" if you're curious (or maybe entice Brian to give a discussion on it. ( you know you want to Brian! =D ) ). Anyway, common wisdom on writing modern JavaScript is to use const by default, and let if you must, and never use var. Also fat arrow functions are very much in vogue. =D
1502594148
Ah, ok. It makes sense to use const if you can change the data inside the variable, though I'm afraid a lot of everything else you said is a little over my head and makes me dizzy - I only know a bit of python, and I'm a novice at best at even that. I kind of get the gist of what you're talking about though, it's just going to be a while before I can wrap my head around it and write efficient scripts. By the way, playing around with the script above, I found that msg.context.match didn't work, but msg.content.match fixed it. I saw something somewhere about weighted die rolls or weighted table lookups or something, I'm trying to figure that out now
1502594305
The Aaron
Pro
API Scripter
Ah, sorry about that. Typo. :) Really, you can just use 'let' and everything will be fine. 
1502594374
The Aaron
Pro
API Scripter
If you're interested, I can get you some examples and more thorough explanations when I'm not on my phone. 
1502603053
I saw in several other scripts that the on-ready call is at the end of the script with just a call to a registerEventHandler function in the main function above it, which in turn calls a handleInput function which does the necessary chat stuff. I'm trying to plug in that methodology into my script but I am getting a "registerEventHandler is not a function" error and I'm not sure why. There may very well be other issues with the script that I'll run into but that one is currently stumping me. Here's where I'm at with my script so far: var gettreasure = (function() { const twodsix = (function() { return randomInteger(6)+randomInteger(6); }); const dtwenty = (function() { return randomInteger(20); }); const valuables = [ {low: 1, high: 10, result: ["all copper", "all silver", "in foreign currency", "in local currency", "in a sack", "in small pouches", "in a large pouch", "in a bowl", "in a box", "in an unlocked chest", "in a locked chest"]}, {low: 11, high: 13, result: ["Ruby", "Emerald", "Opal", "Aquamarine", "Topaz", "Garnet", "Amber", "Pearl", "Amethyst", "Diamond", "Sapphire"]}, {low: 14, high: 16, result: ["Circlet", "Pendant", "Badge", "Brooch", "Bracelet", "Ring", "Necklace", "Earring", "Clasp (Fibulae)", "Arm Ring", "Crown"]}, {low: 17, high: 18, result: ["Scabbard Loop", "Hilt Plate", "Fittings, Studs", "Buckle & Plate", "Strap Fitting", "Pommel Cap", "Horse Trapping", "Scabbard Trapping", "Seal", "Beads", "2d6 Mineral (ingots)"]}, {low: 19, high: 20, result: ["Comb", "Talisman", "Animal Statuette", "Bowl", "Goblet", "Tableware", "Jug", "Cup", "Lamp", "Candlestick", "Censer"]} ]; function _getValuables(roll) { return _.find(valuables, function (test) { return (roll >= test.low && roll <= test.high); }); } function registerEventHandlers() { on('chat:message', handleInput); } function handleInput(msg) { if('api'===msg.type && msg.content.match(/^!gettreasure/)) { const treasure = gettreasure; const row = twodsix; const col = dtwenty; const chart = treasure._getValuables(col); sendChat('/w gm Treasure:',`${chart[row-1]}`); } } return { registerEventHandlers: registerEventHandlers }; }); on('ready', function() { 'use strict'; gettreasure.registerEventHandlers(); });
1502603356
Also, how do you paste in code into these posts like you've done above?
1502609598

Edited 1502633054
The Aaron
Pro
API Scripter
Yeah, that's how I like to organize my larger scripts. It's called the  Revealing Module Pattern .  What you're missing is the IIFE ("Iffy" -- Immediately Invoked Function Expression). You've fallen prey to a bit of cargo cult programming with all that wrapping functions in () =D. You only need to wrap the outer one (and even then it's more about convention). The basic layout is this: const module = (function(){ /* create things in function scope */ return { something: funcScopeThing }; })(); // note that this is executing module.something(); so module gets assigned the result of executing the anonymous function, an object with the property something.  This returned-out-of-function-scope thing is called a Closure. The thing returned (aliased as module.something) retains access to the things created in the function scope. It's a way to have an internal private implementation in a language that is generally public everything.  Code formatting is available in the paragraph symbol at the top left. 
1502634806

Edited 1502717743
The Aaron
Pro
API Scripter
Minor correction, it's a lexical scope for 'this' that fat arrow functions lack. They have a function scope. They can't be used with new to create prototypical inheritance objects like functions can, and they don't get the arguments variatic variadic magic object on execution. (But you can use the rest operator to get the same thing without the weird semantics)
1502685829

Edited 1502685891
I kind of understand what you're saying about IIFE, and that certainly pushed me in the right direction to get my script working (as of a few minutes ago at any rate - now to work out how to build the rest of it) , , , but you're going over my head again. ;)   There's a lot of programming terminology that I really just don't have a good grasp on what it all exactly means. Scope - I kinda have the gist of what this means, but no idea what you mean by "lexical scope." Fat Arrow Functions - No idea yet what that is, and haven't had time to try looking it up yet. Inheritance Objects - I have a vague notion of what this means, I don't know what prototypical inheritance objects means. Variatic Magic Object - No idea what this is. Rest Operator - I don't know what that is yet, or the whats or whys it is used You don't need to explain all this stuff, you've already been a tremendous help and I don't want to waste your time with newbie questions. I will probably eventually get to the point of understanding it all. My next goal is understanding the formatting for if-then, for and while loops, as well as manipulating arrays as I'm going to be using them quite a bit for this script.
1502717516

Edited 1502717563
The Aaron
Pro
API Scripter
Scope is the part of the program where something is accessible: // global scope function test(){ // function scope if(true){ // block scope } } Declaring a variable with var is hoisted to the current function or global scope, meaning this: function test(){ if(true){ var hoistedFunctionScope = 42; } } is treated like this: function test(){ var hoistedFunctionScope; if(true){ hoistedFunctionScope = 42; } } It can lead to issues, particularly with the looping constructs.  Generally it only causes problems with sloppy code, but const and let prevent the problems because they aren't hoisted and have block scope: function test(){ if(true){ let blockScoped = 42; } } Fat Arrow Functions is just what they call functions declared with the => like this: const square = (x) => x * x; as opposed to traditional function declarations like these: function square(x){ return x * x ; } const square = function(x){ return x * x ; } Inheritance Objects -- I worded that poorly.  I should have said "they can't be used to create new objects by passing them as the argument to the new operator."  Javascript uses a somewhat bizarre method for object-oriented inheritance called prototypical inheritance.  You can mostly ignore it but the gist of it is "you create copies of a prototype object.  Changes to that prototype object at runtime are inherited by all the copies."  To create new objects, you pass the prototype function to the new operator: const Proto=function(){ this.thing=3; } let obj = new Proto(); Fat Arrow Functions can't be used like that because they don't have a this object and the wiring that goes along with it.  It's probably not important to understand that as in modern Javascript you're creating classes and making instances of those for all your object-oriented needs. Variadic Magic Object -- to support writing a function that takes a variable number of arguments, javascript has a magic object that functions get named arguments : const sum = function(){ let tot=0; for(let i = 0; i<arguments.length; ++i){ tot+=arguments[i]; } return tot; }; const total = sum(1,2,3,4,5); // total is 15 Fat Arrow Functions don't get that arguments object.  The arguments object is almost an Array, but not quite, which can be confusing.  However, in modern Javascript, you have the Rest Operator which can accomplish the same thing (this might not be available on the API yet): const sum = ( ...args )=>args.reduce((m,a)=>m+a,0); The rest operator has the benefit that it actually produces an Array object, so everything works as expected.  You can also combine it with other named arguments: const something = (named1, named2, ...theRest) => /* some code */; It has a counterpart operator called the Spread Operator , which spreads all the properties or values of an object or array into the context of another object or array (also possibly not in the API yet): const mergeObjs = (obj1, obj2) => ({ ...obj1, ...obj2}); The nice thing is, you probably don't need to understand any of that to get some great benefit from the API and even to write some really useful scripts.  But if it interests you, I don't mind droning on about it. =D  Talking about it reinforces my understanding and if I got anything wrong, Brian will likely correct me. =D
1502723783
Jakob
Pro
Sheet Author
API Scripter
Small addition to The Aaron's explanation: all the things which have been stated as possibly not being in the API are, in fact, in the API, it has been updated to Node v7.x a few months ago.
1502725320
The Aaron
Pro
API Scripter
SWEET!  Thanks for verifying that Jakob.  I looooove Rest and Spread.  Using the crap out of them in work code... =D
1502730733
Brian
Pro
Sheet Author
API Scripter
Scott C. said: I'd guess that Aaron used const because nothing is manipulating the fruit, so it doesn't need to be able to change via push, shift, splice, or any of the other array manipulation functions. const only prevents reassignment. It doesn't make the object immutable. If you have a const array, you can still push, shift, splice, etc. as you please. In order to prevent manipulation, you need Object.freeze(myObj). const constantArr = []; constantArr.push(5); console.log(constantArr); // [5] constantArr = [5, 6]; // TypeError: Assignment to constant variable let frozenArr = Object.freeze([]); frozenArr.push(5); // TypeError: Cannot add property 0, object is not extensible frozenArr = [5, 6]; console.log(frozenArr); // [5, 6] const constantFrozenArr = Object.freeze([]); constantFrozenArr.push(5); // TypeError: Cannot add property 0, object is not extensible constantFrozenArr = [5, 6]; // TypeError: Assignment to constant variable There is also Object.seal, which behaves similarly, except that freeze also makes existing properties immutable, while seal does not.
1502731416

Edited 1502731432
The Aaron
Pro
API Scripter
Probably I should start using a different term than immutable for const.  The variable is immutable, though the value is not, but that's probably too fine a distinction.  Similar to the difference between a const pointer and regular pointer in C++. =D
1502772316
Thanks!  I understood a lot of that because of your excellent explanation. Understanding and knowing when best to use them is a whole other ball of wax. I'm not sure why I'd ever prevent manipulation of a variable unless there was some sort of security concern or it's more memory efficient for instance. Now I'm trying to figure out how best to import the information my script needs from the !message. I tried to do: const mymessage = msg.content; and then const mymessage = msg.content(); But neither one worked - it complained about content not being a function. I then did: const mymessage = msg.content.replace("!script ",""); Which did give me basically what I needed and can work for my purposes, but what function after msg.content do I use to just pass in the message into the variable as a string?  I feel like it shouldn't be that hard to find but I can't find any list of the available functions.
1502773012
The Aaron
Pro
API Scripter
msg.content is a string. I like to use: let cmds=msg.content.split(/\s+/); that makes cmds an array of all the strings in the command, split by white space. You can then use cmds.shift() to remove the command (and possibly switch on it). 
1502775710
The Aaron
Pro
API Scripter
Most of using const and Object.freeze() and the like are more about preventing accidental  errors than malicious ones. It lets the interpreter tell you when you typed the wrong name. Additionally, it can make things faster in a few ways. The interpreter is free to do more optimization behind the scenes if it knows you won't be changing a variable. But you can take advantage as well. If you know a variable can't be reassigned and you know you've assigned it a value, you can avoid the if checks to verify it. 
1502913087
Brian
Pro
Sheet Author
API Scripter
The Aaron said: msg.content is a string. I like to use: let cmds=msg.content.split(/\s+/); that makes cmds an array of all the strings in the command, split by white space. You can then use cmds.shift() to remove the command (and possibly switch on it).  [shameless plug] I'm a fan of  splitArgs : let args = msg.content.splitArgs(); let command = args.shift().substring(1); The splitArgs script will let you handle quoted arguments (eg, !mycommand foo "bar baz"  will treat bar baz as one argument, instead of having the arguments "bar  and baz" ). You can also pass a different argument delimiter to the function, either a string or regular expression (/\s/g is used by default). It also works with single quotes ( !mycommand foo 'bar baz'  is the same as the above example), and doesn't get confused by a single level of nested quotes of the opposite type ( !mycommand "this arg 'has nested' quotes"  results in the single argument this arg 'has nested' quotes ). splitArgs is also available as a one-click install. [/shameless plug]
1503118741
Alright! I finally got my entire script working beautifully thanks to all the help but now I've run into another wall. How do I execute an api script from a macro? This is the function (and the data I'm passing it) which I'm trying to call within a macro and which draws upon attributes of the creature in question: !gettreasure @{selected|token_name}|@{Level}|@{Ind Treasure Dice}|@{Ind Treasure Odds}|@{Lair Treasure Dice}|@{Lair Treasure Odds} The result would look like this in the chat: !gettreasure Skeleton, Decrepit|1|1|100000|1|200000 Trying to spit out the command into the chat doesn't work (it just doesn't spit anything out) and I only found one post in the forums that mentions it isn't possible without some sort of interception script. If that's the case, that seems like a pretty bit oversight in functionality. How would I go about getting this to work?