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

[Script] ScriptBase - A basic framework for structuring your scripts

Hey y'all! I am a JavaScript dev, new to Roll20 script development. I have been learning a lot from looking at the source of some of the popular scripts. I created a small utility script that provides a light framework for script development. I'm calling it ScriptBase. Source code here . The link also contains a usage example. It doesn't do everything! The command parsing is not as advanced as some scripts like TokenMod. But, it has been serving me well for my personal scripts so I thought I would share it. I'm happy to help out if you use it and run into any problems. Also open to suggestions for improvements.
1591165192
The Aaron
Forum Champion
API Scripter
Interesting!
1591182514
GiGs
Pro
Sheet Author
API Scripter
Most of the script creators on roll20 are self-taught amateurs (like me!) who will have no idea how to use that without a bit more instruction and explanation.
Good call. I'll try to explain what's going on. You can put the ScriptBase.js file in your API scripts, and make sure any scripts you write that use it come after it in the list of scripts. There are two parts of the script. CommandParser This is a utility for interpreting chat messages as commands. Let's say you want to define a simple script that responds to the command `!greet` that says hello to you. It would look like this: parser = new CommandParser ( '!greet' ) .default((options, message) => { let greeting = "Hello!" sendChat("Greeter", greeting) }) Now, what if you want to pass your name as an argument? (ex: !greet Ian) parser = new CommandParser ( '!greet' ) .default((options, message) => { let name = options._[0] let greeting = "Hello!" if (name) { greeting = "Hello " + name + "!" } sendChat("Greeter", greeting) }) Nice! But what if we want the option to YELL!? (ex: !greet Ian --yell) parser = new CommandParser ( '!greet' ) .default((options, message) => { let name = options._[0] let greeting = "Hello!" if (name) { greeting = "Hello " + name + "!" } if (options.yell) { greeting = greeting.toUpperCase() } sendChat("Greeter", greeting) }) How about an option to specify who the greeting is from? (ex: !greet Ian --from=Xanathar) parser = new CommandParser ( '!greet' ) .default((options, message) => { let name = options._[0] let from = "Greeter" let greeting = "Hello!" if (name) { greeting = "Hello " + name + "!" } if (options.from) { from = options.from } if (options.yell) { greeting = greeting.toUpperCase() } sendChat(from, greeting) }) For the last part of CommandParser, let's add a subcommand "bye" that says farewell. (ex: !greet bye Ian) parser = new CommandParser ( '!greet' ) .default((options, message) => { let name = options._[0] let from = "Greeter" let greeting = "Hello!" if (name) { greeting = "Hello " + name + "!" } if (options.from) { from = options.from } if (options.yell) { greeting = greeting.toUpperCase() } sendChat(from, greeting) }) .command('bye', (options, message) => { let name = options._[0] let from = "Greeter" let greeting = "Goodbye!" if (name) { greeting = "Goodbye " + name + "!" } if (options.from) { from = options.from } if (options.yell) { greeting = greeting.toUpperCase() } sendChat(from, greeting) })
1591202962

Edited 1591557358
ScriptBase This is a base class that you can use to structure your script. Side Note: Most Roll20 scripts are wrapped in a function. This is a construct in JavaScript known as "IIFE" (Immediately Invoked Function Expression). This is an old concept used to create isolated "modules" of code. You can read more about IIFEs here . I am choosing to use JavaScript classes to isolate code instead. This is what enables the ScriptBase approach. The bare minimum script will look like this class _MyCoolScript extends ScriptBase ( { name: "MyCoolScript", version: "0.1.0", // This can be any string. I like to use semver versioning , but you can put whatever you want. }) { constructor() { super() // You MUST remember to call super() in the constructor! this. parser = new CommandParser('!cool-script') .default((options, message) => { sendChat(this.name, "I don't know what my favorite color is :(") }) } } // You must remember to instantiate your script at the bottom const MyCoolScript = new _MyCoolScript() If your script needs to keep track of state (data that persists, even between Roll20 sessions), ScriptBase will handle that for you! class _MyCoolScript extends ScriptBase ( { name: "MyCoolScript", version: "0.1.1", // Make sure that stateKey is unique for all of your scripts. If there are duplicates, you will have problems stateKey: "MY_COOL_SCRIPT", // Here you specify how the state should be at the start. It could be as simple as an empty object {} initialState: { favoriteColor: "blue" } }) { constructor() { super() // You MUST remember to call super() in the constructor! // If you need to add more event handlers , you can do that here as well: // on("ready", () => console.log("ready!")) // on("change:graphic", (obj) => console.log("Graphic changed!")) this.parser = new CommandParser('!cool-script') .default((options, message) => { // You can access the state via this.state sendChat(this.name, "My favorite color is " + this.state.favoriteColor) }) .command('set', this.setColor) .command('reset', this.resetColor) } setColor = (options, message) => { const newColor = options._[0]; if (!newColor) { throw new Error("Please specify a color!") } this.state.favoriteColor = newColor } resetColor = (options, message) => { // You can call this.resetState() to set the state back to its initial value this.resetState() } } const MyCoolScript = new _MyCoolScript()