action.json and pad.json
Every key of the two files Vovó reads from a folder or a zip, generated from Vovó's own code, so it always matches this version. Vovó checks each file as it comes in and refuses it with a sentence saying what is wrong; a key it does not know is passed over without a word, which an editor that reads the schema underlines.
A key is lower-case letters, digits and _, and a name may have - too, each 32 characters at most.
For an editor that completes the keys and underlines mistakes, name the schema at the top of the file, as the examples do. vovo dev writes action.schema.json beside an action's parts; Vovó's source has both in its api folder.
action.json
An action's manifest: what it is called, what it hears, what it asks for, and what it offers whoever runs the live. It sits beside index.html, style.css, script.js and files/, in a package or a folder.
{
"$schema": "./action.schema.json",
"name": "Raffle",
"id": "example.raffle",
"version": "1.2.0",
"api": 1,
"author": "A maker",
"site": "https://example.com/raffle",
"description": "!join in chat joins the wheel; the panel draws the winner.",
"license": "MIT",
"triggers": ["chat", "timer"],
"bindings": [
{ "event": "chat", "filter": [{ "field": "text", "op": "starts", "value": "!join" }] },
{ "event": "timer", "filter": [{ "field": "every", "op": "is", "value": 300 }] }
],
"permissions": ["chat.send"],
"controls": [
{ "key": "open", "label": "Entries open", "kind": "toggle" },
{ "key": "prize", "label": "Prize", "kind": "text" }
],
"settings": [
{ "key": "color", "label": "Wheel colour", "kind": "color", "default": "#840926" },
{ "key": "size", "label": "Size", "kind": "number", "min": 200, "max": 800, "step": 10, "unit": "px", "default": 420 },
{ "key": "side", "label": "Side", "kind": "choice", "options": ["Left", "Right"], "default": "Right" }
],
"panel": [
{ "kind": "control", "key": "open" },
{ "kind": "control", "key": "prize" },
{ "kind": "form", "name": "add", "label": "Put someone in", "button": "Put", "fields": [{ "key": "who", "label": "Name" }] },
{ "kind": "button", "name": "draw", "label": "Draw", "confirm": true },
{ "kind": "list", "key": "names", "label": "In the wheel", "empty": "Nobody yet", "buttons": [{ "name": "out", "label": "Take out", "confirm": true }] }
]
}
$schema(text): Where an editor finds this file's schema:./action.schema.json, whichvovo devwrites beside it. Vovó reads nothing in it.name(text; required): Its name, unique in the profile: an action brought in with the name of one already there offers to take its place, or comes in as "Raffle 2".id(text, one line, at most 64 characters): A name its maker gives it once and never changes: the maker's name and the action's with a dot between them, asexample.raffle, in lower-case letters, digits and-. A newer version brought in finds the action it updates by it, whatever that action is called by then; without one, by its name. A copy made in Vovó does not keep it.version(text, one line, at most 32 characters): Its own version, shown in the list of actions and, when a package takes an action's place, beside the one that action had, saying so when the package is the same or older. Numbers with dots between them are read as numbers (1.10.0is newer than1.9.2, and1.0.0-beta.1comes before1.0.0); written any other way, two versions are only the same or not.1.0.0without it.api(whole number, 1 or more; required): The version of the API it was written for:1. One newer than this Vovó's is refused, with what to do.triggers(list of events): The events it hears: the API's, or names of its own, whichvovo.emitandvovo emitset off. A name close to one of the API's is refused, with the one meant.control,settingsandpanelreach an action that declares controls, settings or a panel without being listed.bindings(list): The triggers' conditions.permissions(list of permissions): What it may ask of Vovó, shown to whoever brings it in. One Vovó does not know is refused, with the one meant.persistent(true or false): Whether it stays on screen (a chat, a goal bar) rather than showing when its events come, which is how the editor and the list of actions name it. It changes nothing of how it runs.operator(true or false): Only for whoever runs the live: it hears the pad, hotkeys, timers, its controls and what OBS and the computer say, never viewers, chat, rewards, webhooks or another action's event. A trigger that someone else could set off is refused.controls(list, at most 32): What the pad changes while it runs.settings(list, at most 32): What whoever uses it may change without its code.panel(list): What it shows whoever runs the live, in Panels.author(text, one line, at most 80 characters): Who made it.site(web address, at most 200 characters): Where to read more of it, or of whoever made it.description(text, one line, at most 300 characters): What it does, in a line, shown as it is brought in.license(text, one line, at most 100 characters): On what terms it is handed over, as its maker says them: a licence's name (MIT,CC-BY-4.0) or a line of one's own, shown as it is brought in and in the list of actions. The licence's whole text goes beside this file asLICENSE(orLICENSE.md,LICENSE.txt), 64 KB of text at most, and is read from the import's dialog before the action comes in. Vovó says what is written and holds nobody to it: it checks no key, no account and no purchase.
Conditions
Each trigger again, with the conditions that must all hold for it to fire the action, as the editor saves them. One for each name in triggers, in any order: when they do not match one for one, every condition is left out without a word and each trigger fires on everything.
event(event; required): One of the names intriggers.filter(list): Its conditions; none, and every such event fires it.
A condition
One of the event's fields compared with a value. Texts are compared whatever the case; a number written as text is read as a number.
field(text; required): The event's field, as the API reference names it:viewers,reward,text. Atimer's interval is the fieldevery(seconds, 5 or more), and ahotkey's combination the fieldkeys, each withis.op(one ofis,not,above,atleast,below,atmost,contains,starts; required): How they are compared:isandnotas they are,above,atleast,belowandatmostas numbers,containsandstartsas texts.value(text, number, or true or false; required): What the field is compared with.
Controls
What the pad changes while the action runs: each kept in vovo.state under its key, and told to the action as the control event, which it hears without listing it.
key(key; required): Wherevovo.statekeeps it; no two controls share one.label(text; required): What the pad calls it.kind(one oftoggle,choice,counter,text; required): On or off; one of its options; a whole number, one up or down at a time; or a line of text, 300 characters at most.options(list of texts): A choice's options, two or more.max(number): A counter's goal, shown beside its count.
Settings
What whoever uses the action may change without its code, in Customize: read as vovo.settings.<key>, as --vovo-<key> in its CSS and, for on or off and a choice, as data-vovo-<key> on its document. What is chosen stays with the action, and a package carries only the defaults. Eight of them at most are texts.
kind(one ofcolor,number,toggle,choice,text,file,font,camera; required): What it is, as the parts below say.key(key; required): How the action reads it; no two settings share one, and none isaccent.label(text, at most 60 characters; required): What Customize calls it.
Settings: color
A colour; Vovó's wine, #840926, unless the action says.
default(colour,#RRGGBB): As the action comes; without one, the kind's own.
Settings: number
A number from a least to a most, by steps; the least unless the action says.
min(number; required): The least.max(number; required): The most, above the least.step(number): How far each step goes, above 0; 1 without it.unit(one ofpx,%,s,ms,em,deg): The unit its CSS variable carries; without one, a plain number.default(number): As the action comes; without one, the kind's own.
Settings: toggle
On or off; off unless the action says.
default(true or false): As the action comes; without one, the kind's own.
Settings: choice
One of its options; the first unless the action says.
options(list of texts, from 2 to 12; required): The options, each once.default(text): As the action comes; without one, the kind's own.
Settings: text
A line of text; empty unless the action says.
default(text, one line, at most 200 characters): As the action comes; without one, the kind's own.
Settings: file
A picture, a sound or a video that whoever uses it chooses, 50 MB at most; as the action comes, one of its own files, by its path, or none.
accept(one ofimage,sound,video): What it may be: a picture unless the action says.default(a file's path, ornull): As the action comes; without one, the kind's own.
Settings: font
One of the computer's fonts, by its family; Segoe UI unless the action says.
default(text, one line, at most 64 characters): As the action comes; without one, the kind's own.
Settings: camera
One of the computer's cameras, which whoever uses the action chooses in Customize, shown where the action places it (vovo.camera.place, with the camera.show permission); four at most. It comes with none, so it has no default.
Panel
What the action shows whoever runs the live in Panels, in the side bar: pieces in order, each told by its kind, drawn by Vovó. What is done there reaches the action as the panel event, which it hears without listing it. A panel piece starts another panel, holding the pieces after it up to the next, and the pieces before the first show on every one; a tab splits a panel into sections the same way. The names of buttons, forms and a list's buttons are unique in the action. An action declares 24 panels at most, each with 40 pieces and 12 tabs at most.
kind(one ofpanel,tab,heading,note,control,button,form,list; required): What it is, as the parts below say.
Panel: panel
Another panel starts here, with an entry of its own in Panels.
label(text, at most 30 characters; required): Its name in Panels.icon(text, at most 32 characters): One of Vovó's icons by its name, or an emoji; without one, the icon of the action's first button on the pad.
Panel: tab
A section of the panel: the pieces after it, up to the next tab.
label(text, at most 30 characters; required): Its name.
Panel: heading
A title over the pieces after it, which share a card.
label(text, at most 60 characters; required): Its words.
Panel: note
A line saying something.
label(text, at most 60 characters; required): Its words.
Panel: control
One of the action's controls, moved here as on the pad.
key(key; required): The control's key.
Panel: button
A button.
name(name; required): What thepanelevent says was pressed.label(text, at most 60 characters; required): Its words.confirm(true or false): The first press asks, and the second does.
Panel: form
A few fields and a button: what is typed goes with the press, in the event's values. A form of one field is typed in the panel itself.
name(name; required): What thepanelevent says was sent.label(text, at most 60 characters; required): Its words.button(text): The button's words.fields(list, from 1 to 4; required): Its fields.
A form's fields
Each a line typed in the panel, sent with the form by its key.
key(name; required): Its key in the event'svalues, once in the form.label(text, at most 60 characters; required): What the field is called.
Panel: list
A list read from the action's state, a line for each item, which may be thousands; one with more than eight lines has a search of its own.
key(text; required): The key ofvovo.statethat holds it.path(text): Inside that, the keys to the list, joined by dots:round.names.label(text, at most 60 characters; required): Its name.show(text): For items that are objects: the field a line shows, or a line with{field}s filled from each item.empty(text): What it says while it holds nothing.buttons(list, at most 3): Buttons on each line: a press tells the action the line'sitemandindex.
A list's buttons
Buttons on each of the list's lines.
name(name; required): What thepanelevent says was pressed.label(text, at most 60 characters; required): Its words.confirm(true or false): The first press asks, and the second does.
pad.json
A pad's pack: a zip or a folder of sounds and videos, with pad.json at its root saying the groups and their buttons; without it, each folder is a group and each sound or video a button named after its file. Brought in again, it updates what it names and leaves the rest as it is.
{
"$schema": "./pad.schema.json",
"groups": [
{
"name": "Sounds",
"color": "#2F6FDB",
"buttons": [
{ "label": "Applause", "icon": "👏", "sound": "sounds/applause.mp3", "favorite": true, "hotkey": "F13" },
{ "label": "Drum roll", "sound": "sounds/drums.ogg", "volume": 130 },
{ "label": "Rain", "video": "videos/rain.mp4", "key": "luma", "loop": true }
]
},
{
"name": "Live",
"buttons": [
{ "label": "Raffle", "action": "Raffle" },
{ "label": "Entries", "action": "Raffle", "control": "open" },
{ "label": "Starting", "scene": "Starting soon" },
{ "label": "Camera", "scene": "Game", "source": "Camera" },
{ "label": "Blur", "source": "Camera", "filter": "Blur" },
{ "label": "One minute", "team": "One minute, then I'm back." }
]
}
]
}
$schema(text): Where an editor finds this file's schema, such as./pad.schema.jsonbeside it. Vovó reads nothing in it.groups(list; required): The groups, in order.
Groups
A group of the pad and its buttons, in order.
name(text, at most 60 characters; required): Its name: a group of the profile with this name, whatever the case, is updated.color(colour,#RRGGBB): Its colour; without it, a group already there keeps its own and a new one takes the next.buttons(list): Its buttons.
Buttons
A button and what it does: Vovó reads the first of these it finds, in this order: sound; video; action with control; action; source with filter, or scene with source; scene. A pack brings 2,000 buttons at most.
label(text, at most 60 characters; required): Its name on the pad: a button of its group with this name, whatever the case, is updated.icon(text): One of Vovó's icons by its name, or an emoji.sound(a file's path): A sound of the pack, by its path in it: MP3, OGG, WAV, M4A, WEBM or FLAC, 20 MB at most.video(a file's path): A video of the pack, by its path in it: MP4 or M4V, 200 MB at most; it plays over the whole stage.key(text): What of the video is taken away, as OBS's filters do:none,luma(black),chroma(green) orchroma:#RRGGBB.loop(true or false): The video plays over and over, until its button is pressed again.action(text): One of the profile's actions, by its name, set off as itsbuttonevent.control(key): Withaction: one of its controls, by its key, moved as its pad button moves it.value(text): Withcontrol, for a choice: the option it picks; without it, it goes round them.scene(text): An OBS scene, by its name, put on air.source(text): Withscene: a source in it, shown or hidden. Withfilter: the source the filter is on.filter(text): Withsource: one of its filters, turned on or off.team(text, at most 300 characters): A line it writes to the team's conversation as whoever runs the live, never on stream or in chat; a device's pad never has the button.favorite(true or false): In Favorites or not; without it, a button already there keeps what it has.hotkey(text): The combination that presses it from any program, as people write it:F13,Ctrl+Shift+1. No two buttons of the profile share one.volume(whole number, from 0 to 150): How loud a sound or a video plays, as a percentage of its file; without it, a button already there keeps its own.