Vovó Help

Actions

An action is a piece of HTML, CSS and JavaScript that shows in the overlay. Each runs on its own, with no network and no access to passwords: what it needs from outside, it asks Vovó for.

Starting

"New action" makes a blank one or one from an example: alerts and chat, now playing, countdown, goal, raffle, death counter and others. The example becomes a copy of yours, to change freely.

In three steps

HTML alone: a <template> that shows when the event happens, with the event's fields in braces.

<template data-on="follow" data-seconds="5">
    <div class="alert">{user} followed!</div>
</template>

A little JavaScript: vovo.on("raid", (e) => { ... }) and vovo.show("name", e). And the whole API for the rest.

Triggers

An action only gets the events in its list of triggers, in the editor. A pad button is the button trigger.

Conditions

The sliders icon beside a trigger opens its conditions: the trigger fires only when all of them hold. A raid with 50 or more, a message starting with !join, the right reward.

Timer and hotkey

The Timer trigger goes off now and then: its every condition says how many seconds apart (without it, every minute). The Hotkey trigger fires on a key combination, its keys condition, with the game in front too.

Controls

In the editor's Controls card, the action declares what the pad can change while it runs: on or off, a choice between options, a counter (with a goal, if you like) or a text. Each is kept in vovo.state, under the key the dialog shows, and every change arrives as the control event, with no trigger needed. The Alerts and chat, Follower goal and Death counter examples show how.

vovo.on("control", () =>
{
    document.getElementById("chat").hidden = !vovo.state.chat;
});

Customize

An action can let whoever uses it change it without opening its code: its colour, the size of its text, its font, a side of the screen, a picture or a sound of your own. The palette beside its panel's name, or in its row of the list of actions, opens Customize: a line for each setting, with what it holds, its default when it differs, and the way back to it. A change shows at once on the scenes that show the action, without restarting it, and each is a version that the audit undoes. It is chosen at this computer only: a phone or another Vovó never sees it. The action's package carries the defaults, never your choices; one that takes the action's place keeps the choices it still has room for.

Whoever writes the action declares them in the editor's Customize card, or under settings in its action.json: each a kind (a colour, a number from a least to a most, on or off, a choice, a text, a picture, a sound or a video, a font), a name, a key and a default. The action reads them in vovo.settings, and hears the settings event when one changes; its CSS reads them as var(--vovo-key) (a number with its unit, such as 32px), and an on or off or a choice as an attribute of the page, for selectors:

.chat
{
    color: var(--vovo-color);
    font-size: var(--vovo-size);
    font-family: var(--vovo-font), sans-serif;
}
html[data-vovo-side="Right"] .chat
{
    right: 40px;
}

A setting may also be a camera: whoever uses the action chooses one of their computer's cameras in Customize, and the action shows it where it places it (vovo.camera.place, with the camera.show permission). An action comes with no camera and never learns which one was chosen, nor sees its pictures: vovo.settings says only whether one is chosen, as true or false. The "Camera" example is a camera in a frame.

Panels

What a button cannot hold (the names in a raffle, the goals with their numbers, a press that should ask twice) goes in the action's panel: panel in its manifest, shown in Panels, in the side bar (its icon at the window's right edge, there while some action has a panel). A panel is a list of pieces Vovó draws: heading and note, a control by its key, a button (with confirm, it asks "Sure?" first), a form with up to four fields, and a list read from vovo.state (its key, and a path inside it), each line with up to three buttons.

An action with much to offer splits it in two ways. { "kind": "panel", "label": "Raffle", "icon": "gift" } starts another panel: an entry of its own in Panels, with its name and its icon (one of Vovó's icons by name, or an emoji; without one, the icon of the action's first pad button). The pieces after it, up to the next, are its own, and those before the first show at the top of every one. Inside a panel, tab starts a section (up to twelve): the pieces after it, up to the next tab, show while it is chosen, and those before the first tab show on every one. So a raffle is one panel with a section for the draw, another for who is in and another for who won, rather than one long page. An action that declares no { "kind": "panel" } has one, named after it.

The head of Panels names the one in front; a press there lists them all, each saying whether an open scene shows it and whose it is, with a search once there are more than eight. Sections show as tabs under the head: those that fit, and the rest under "More". A heading opens a group: the pieces after it share a card, buttons that follow each other share a line, a list is a card of its own with its count, and a note goes between the cards. A form of one field is typed in the panel itself, Enter sending it, and so is a text control; a form of more fields opens a small window. A counter with a goal (max) shows how far it is.

A list may hold thousands of lines. One with more than eight has a search of its own, which stays in sight while the list scrolls. When it is the last piece of its section it shows whole; with pieces after it, it shows its first eight lines and the way to them all, so put a long list last, or in a section of its own.

What is done there arrives as the panel event, with no trigger needed: its name, a form's values, and a line's item and index. Names are the action's, whichever panel shows them. An action hears its panel only while an open scene shows it, in OBS or in the preview window: with none, the panel says so, its buttons wait faded and "Open preview" opens the scene's; its controls and lists still show, since Vovó keeps them. Only whoever runs the live uses it, here or on a device you allowed the panels, and nothing reaches the action during panic.

Writing in chat

With the chat.send permission, vovo.chat.send("...") writes in the live's chat as you. Vovó keeps the pace (20 lines in 30 seconds) and writes once what two pages showing the action ask for; two presses of a button are two lines. The Chat reminder example shows how. With { as: "persona" } the line goes through the persona's own Twitch account instead (Integrations, under Twitch): what the persona says that is not one of its answers, such as a line someone types for it. While that account is not connected, nothing is written, and nothing as you in its place.

Vovó tells one page's call from another's by the event the action was handling when it called. A call made later, after an await or in a timer, says which event it answers with { event: e }, e being what your listener was given: vovo.chat.send("Welcome!", { event: e }). The same goes for vovo.emit, vovo.pad.play, vovo.webhook.send, vovo.draw, vovo.media.play and OBS's calls. Without it, two pages would write the line twice, and a moderator's link would wait in the bell.

The microphone, the screen and how you move

An action can follow how loud your microphone is (audio.level), the screen's colour (screen.color) and how you move in a game (input.move): how far the camera turned, where you walk and whether you jump, from the mouse, W, A, S and D or the arrows, and a controller, so a scene sways with the camera. The "Screen glow" and "Camera sway" examples show how. Vovó reads each only while an open overlay shows an action waiting for it, and the status bar shows its icon in its middle for as long as it does, naming the actions in its tooltip. Of the keyboard it reads the movement keys alone, as a direction, never which key, and not while Vovó's own window is in front, where what you type would sway the scene; the mouse and a controller still move it then, unless "Pause the movement while Vovó is in front" is on (Settings, General). The sound never leaves the computer, only how loud it is.

Clips and videos

With the media.play permission, vovo.media.play(link) shows a Twitch clip, a Twitch video or a YouTube video in its official player, where vovo.media.place(element) put it, with its sound. An action only the operator sets off plays it at once; any other, which someone in chat may have set off, waits in the bell until you (or a device allowed to approve) let it through. The player cannot tell when a clip ends: give { seconds: 60 }, or call vovo.media.stop(). The Clips on a TV example takes !clip and a link from moderators.

Your moderators are the people you trust with the chat: in Integrations, under Twitch, "Moderators' links play at once" lets a link one of them (or you) pastes go on air without waiting, when the action shows it because of their message. Off unless you turn it on; everyone else's link still waits.

Voice calls

With the call permission, an action can put you on the phone with an AI model, on air: you talk at the microphone, it answers aloud, and the live hears both. vovo.call.start({ instructions, voice }) opens the call with your OpenAI key (Integrations, AI); instructions is who the model is and how it talks. The call opens silent, so the action can ring a telephone first; vovo.call.answer() opens the microphone and has the model speak first, vovo.call.tell() tells it something during the call, and vovo.call.end() hangs up. call.started, call.answered, call.text (what is said, written down) and call.ended say what happens.

What the model says is heard as it is said, with nobody reading it first, so a call starts only from something you did by hand: a pad button, a hotkey, a control or the action's panel. Chat, rewards and webhooks start none. OpenAI bills it by the second from its opening, so each call has a ceiling (five minutes at most). While one is on, a red telephone shows in the status bar: press it, or panic, to hang up at once. Use headphones, or the model hears itself from your speakers.

Webhooks

With the webhook permission, vovo.webhook.send("name", { ... }) asks Vovó to post the JSON to a webhook set up in Settings, Webhooks: a Discord channel's, an automation, a tool on your network. The address stays in this computer's vault, out of backups and packages, and the action knows only the name. The Announce on Discord example shows how.

Inbound webhooks

The other way round, a service on the internet tells Vovó: in Settings, Webhooks, create an inbound webhook, copy its address and paste it in the service (a donation page, a store, a script). Each call reaches actions as the webhook event, with hook (the name) and data (what was sent). Use the webhook trigger with the condition that hook is the name. Each webhook has an address of its own: give it only to the service that calls it. The relay keeps a call sealed for this computer for a day at most, so one made while Vovó is closed arrives when it opens. What arrives is someone else's text: actions show it as text only.

From a program on this computer

A program on this computer can tell an action what it knows: a script that reads a game's log, a tool that keeps a tournament's score, a macro. The action listens to an event with a name of its own, among the triggers in its action.json (as in a folder vovo dev follows):

"triggers": ["score"]

and hears it as any other:

vovo.on("score", (e) =>
{
    document.getElementById("home").textContent = e.home;
    document.getElementById("away").textContent = e.away;
});

The program tells it with vovo emit, the name and the fields:

vovo emit score home=2 away=1

A number, true and false arrive as they are, anything else as text. Lists and objects go whole as JSON, which is how a program calls it:

import json, subprocess
subprocess.run(["vovo", "emit", "score", "--fields", json.dumps({"home": 2, "away": 1})])

It says which actions heard it, and fails (exit code 1, with the reason) when none would: no action has that name among its triggers, their conditions do not hold, or no open page shows them. It goes where an action's own vovo.emit would, never during panic, 30 in 10 seconds at most, and the audit says the command line set it off. An action only for the operator does not hear it. What a program sends is that program's text: the action shows it as text only. To pretend that an event of the API happened, such as a follow, use vovo simulate.

Preview and Simulate

The editor shows the action live as you type, and the Simulate buttons fire the triggers without waiting for the real thing and move the controls as the pad's button would. Nothing in the preview reaches the live. The icon in the corner of the code or the preview gives it the whole window; the same icon, or Esc, puts it back beside the other.

Screen readers

Screen readers cannot read the code editor. The Plain text button, above the code, puts a text box in its place that Narrator, NVDA and JAWS read whole, by line, word and character. It starts on when Windows says a screen reader runs; Tab moves on, so indent with spaces.

Files

In the editor's Files tab, add images, sounds, videos and fonts. The action uses them by name: <img src="logo.png">. To pass one of its own sounds or videos through Web Audio (a compressor, a meter), ask for it with crossOrigin = "anonymous" before giving its name: an action runs apart from everything, and to it even its own files come from elsewhere, which Web Audio otherwise hears as silence.

Permissions

What the action may ask of Vovó is under Behaviour: writing in chat, calling a webhook, showing the shared screen, showing a camera, showing sources and turning filters on in OBS. Switched off, the action cannot.

OBS from an action

With the obs.toggle permission, vovo.obs.source("Scene", "Source", true) shows a source in an OBS scene (false hides it; without it, it switches), and vovo.obs.filter("Source", "Filter", true) does the same to a filter. The names are OBS's, whatever the case and the accents; one OBS does not have is said in the Console, with the one meant. That is all an action reaches in OBS: it cannot stop the stream or read its key.

Only for the operator

Under Behaviour, "Only for whoever runs the live" makes the action set off only by the pad, hotkeys, timers and its controls: never by viewers, chat, rewards, webhooks or another action's event. Use it for what nobody watching should be able to do, such as clearing the chat or switching scenes.

Versions

Every save keeps a version. "History" brings any of them back.

Packages

The export icon, in the list of actions, writes the action to a .zip with its code and its files, to hand to someone. Under Behaviour, its author, site and a line on what it does go with it, so whoever brings it in knows what it is and where it came from. "Import action" shows what the package brings and what it asks for before keeping it, and opens the action in the editor: read it before using it. A package with the name of an action you have is most likely that action in a newer version, so it offers to take its place: the action keeps its scenes, buttons and state, and the version it had stays in its history. In a terminal, vovo export and vovo import do the same, and vovo import --replace takes the place of the action of its name. A package made for others can say an id under Behaviour, its maker's name and its own with a dot between them (example.raffle). Its next version then finds the action it updates even after you renamed it, and two makers' actions of one name never take each other's place; vovo import --replace goes by it too. Either way, the import says which version takes the place of which, and says so when the file is the same version or an older one, and what you chose in Customize stays wherever the new version still has that setting. A package can say under which licence it is handed over: a licence's name or a line of your own under Behaviour, and the licence's whole text, taken there from a file or kept as LICENSE beside action.json in a folder. Whoever brings the action in sees the line, and reads the text from the import's dialog before it comes in. Vovó says what you wrote and holds nobody to it: it checks no key, no account and no purchase, and selling an action happens outside Vovó. The copy icon, beside it, duplicates the action and opens the copy in the editor, to start a variation from one that works: its code, files and triggers come along; its state, scenes and buttons do not, and a hotkey trigger comes without its combination, so one key does not set off both.

In your own editor

An action can be written in the editor you prefer, or by a build of your own: keep its parts in a folder as a package holds them (action.json, index.html, style.css, script.js and files/) and, in a terminal, run vovo dev with the folder. Vovó opens its editor on the action of that name, making it from the folder when the profile has none, and shows each change saved in the folder in the preview at once; Simulate works as always. Like a change typed here, it reaches the live only when you Save. vovo dev stop, or Stop following at the top of the editor, lets go of the folder.

vovo new <folder> starts that folder for you, blank or from one of the examples (vovo new --list names them, and --from goal takes one), and vovo check <folder> says everything that is wrong with a folder or a package at once, making and opening nothing: each problem with its file, and what Vovó would take and pass over, such as a key it has no use for. Both answer with Vovó closed too. vovo export <action> <folder> writes an action you already have as such a folder.

vovo dev also writes vovo.d.ts in the folder, the API as this Vovó has it, and a jsconfig.json when there is none, so an editor such as VS Code completes vovo., the events' names in vovo.on(" and each event's fields. Neither is part of the action, and a package never carries them; under version control, keep them or ignore them as you like. To have a wrong field underlined, add "checkJs": true to the jsconfig's compilerOptions.

It writes action.schema.json there too, no part of the action either: with "$schema": "./action.schema.json" at the top of action.json, the editor completes its keys, offers the events and the permissions by name, and underlines what Vovó would refuse.

The preview opens in your browser too, at the address vovo dev prints or from the arrow in the preview's corner: the same draft, run by the same vovo as on air, reloaded at each change, with Simulate reaching it, and with the browser's own developer tools (its console, breakpoints, the elements).

To show on the live

The action has to be in a scene, and the scene in OBS.