Story programming cheatsheet

Table of contents

JavaScript

export const onArrive = async (api) => {
  // do something
} 

export const onMessage = async (msg, api) => {
  // do something
} 

// move player to a different node on the same board
api.moveTo("node1")

// advanced: move player on a different board
api.moveTo("node1", {channelKey: "board2"})

// send a text message to player
api.sendText("hello")

// send an image, audio or video message to player
// (to get the media file key, go to Media tab, open ⋮ menu, copy key)
api.sendImage("f00ba420-0123-4567-89abcdef012356789")
api.sendAudio("e4770840-3c2e-4eeb-b59b-a0e15e14190b")
api.sendVideo("b94eb8f3-72ef-476d-ad61-64adc18204e7")

// images are normally scaled and cropped to fit ("cover" mode). to make sure that everything on the image is visible, you can switch to "contain" mode:
api.sendImage("f00ba420-0123-4567-89abcdef012356789", { objectFit: "contain" })

// image are by default zoomable (enlarge on click or tap). prevent zoom:
api.sendImage("f00ba420-0123-4567-89abcdef012356789", { zoomable: false })

// send a clickable link
api.sendLink("https://docs.interkit.app/")
api.sendLink("Docs", { url: "https://docs.interkit.app/" })

// send an image with a link
api.sendImage("f00ba420-0123-4567-89abcdef012356789", { url: "https://docs.interkit.app/" })

// add a label
api.sendText("hello", {label: "bot"})

// respond to text in onMessage
if(msg.payload.text == "foo") {
  // do something
}

// send a choice
api.sendChoice({
  a: "option a",
  b: "option b"
})

// respond to a choice
if(msg.payload.key == "a") {
  // do something
}

// send a system message
api.sendSystem("Someone entered the channel")

// send a system image
api.sendSystemImage("f00ba420-0123-4567-89abcdef012356789")
api.sendSystemImage("f00ba420-0123-4567-89abcdef012356789", {width: "200px"})
// place the system image on either side of the chat
api.sendSystemImage("f00ba420-0123-4567-89abcdef012356789", {placement: "me"})
api.sendSystemImage("f00ba420-0123-4567-89abcdef012356789", {placement: "other"})

// delays (this works for sendText, sendChoice, sendImage and moveTo)
api.sendText("hello", {delay: 10}) // send the message 10 seconds later
api.sendText("hello", {delay: {hours: 1, minutes: 30}}) // 1 hour, 30 minutes later
api.sendText("hello", {delay: {nextHour: 13}}) // the "next 13 o'clock", either later today, or tomorrow (in the server's timezone!)
api.sendText("hello", {delay: {nextHour: 13, randomHours: 1}}) // add between 0 and 60 minutes, randomly

// forward a message to other users currently in this node, uses user variable "name" as label
api.echo(msg)

// get a variable for this user
await api.getUserVar("name")

// set a variable for this user
await api.setUserVar("name", "alice")

// set or get a property about an element for this user, for example "discovered"
await api.setElementProperty("f00ba420-0123-4567-89abcdef012356789", "discovered", true)
await api.getElementProperty("f00ba420-0123-4567-89abcdef012356789", "discovered")

// set a user specific property on a channel (eg to hide a channel for specific user)
await api.setChannelProperty("board1", "unlisted", true)

// load rows from a sheet
await api.getRows("elements")

// add a row to a sheet
await api.addRow("elements", {title: "hello"})

// update a row
await api.updateRow("elements", "rowKey", {title: "bye"})

// hide the interface for sending messages (persists for each board)
api.setInterface({text: false})
api.setInterface({text: true}) // turn it back on 

// allow user to take pictures and send them into chat
api.setInterface({text: true, photo: true}) // text and photo entry
api.setInterface({text: false, photo: true}) // just photo entry

// set preferred camera (front or back)
api.setInterface({photo: true, cameraFacingMode: "environment"}) // or "user" for selfie mode

// interface can be also set per message
api.sendText('Give me a photo', { setInterface: { photo: true, text: false } })

// present the user with a button to send their location
api.requestLocation("Send Location", {cancel: "Cancel"}) // you can also leave the cancel option blank

// respond to location
if(msg.payload.type == "locationResponse") {
  // do something
  if(api.distance(msg.payload.location, {lat: 56, lng: 12}) < 100) {
    api.sendText("you're close!")
  }
}
if(msg.payload.type == "locationRequestCanceled") {
  api.sendText("ok")
}

// trigger an action on the client by clicking an image
api.sendImage("f00ba420-0123-4567-89abcdef012356789", {action: {trigger: "triggerName", payload: payloadObject}})

// custom css class on message
api.sendImage("f00ba420-0123-4567-89abcdef012356789", {customClass: "special"})

/* translation, multi-language (i18n, l10n) */

// access current language
api.sendText('your language: ' + api.userLang)
api.sendText('your language, index: ' + api.userLangIndex)

// set language
api.setLang('de', 1) // the second argument has to match the index (0-based) in the AppBase blockly field 

// use current language
if (api.userLang === 'en') ...
if (api.userLangIndex === 1) ...
let text1 = ['Deutsch', 'Englisch'][api.userLangIndex]
let text2 = {de: 'Deutsch', en: 'Englisch'}[api.userLang]

// use text localized to current user language
export const onMessage = async (msg, api, t) => {
  api.sendChoice({ a: t('Ja|Yes'), b: t('Nein|No') })
}

// the t helper function takes pipe-separated strings, arrays or objets:
api.sendText(t('Ja|Yes'))
api.sendText(t(['Ja', 'Yes']))
api.sendText(t({ de: 'Ja', en: 'Yes' })) // order-independant

// use sendTextT, sendChoiceT, sendSystemT shortcuts, equivalently
api.sendTextT('Tschüß|Bye')
api.sendChoiceT({ a: 'Ja|Yes', b: ['Nein', 'No'] })
api.sendSystemT('Chat verlassen|Left the chat')

Twine-ish syntax

`ignore this first line

Hi! This is a message.

An empty newline separates paragraphs = message.

[script]console.log('This tag lets you passthru JS code');
  console.log('It's good to indent the next line.');
  // and backticks are currently not supported here!

A Twine link, like [[nodeName]],
will become a choice with moveTo to nodeName.
We also support Twine aliases/renames:
[[Go Home|home]] [[Go Home->home]] [[home<-Go Home]]
...with i18n:
[[Nach Hause|To home|homeNode]]
[[homeNode<-Nach Hause|To home]] etc.
All other text in paragraphs with links,
like this sentence, will be ignored.

Twine paragraphs can be explicit, immediate moveTos:
[->To home]

Twine paragraphs (message+moveTos) can take options:
Hello world[{"delay":10}]
[->To home][{"delay":{"hours":1}]
Give me a photo[{"setInterface":{"photo":true}}]
`