@lumiastream/lumia-types 3.9.2 → 3.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activity.types.js +1 -1
- package/dist/custom-code/custom-actions.md +625 -4
- package/dist/custom-code/helper-functions.md +9 -5
- package/dist/custom-overlays/custom-overlays-cheatsheet.md +21 -6
- package/dist/custom-overlays/custom-overlays-documentation.md +35 -3
- package/dist/custom-overlays/custom-overlays.d.ts +21 -3
- package/dist/custom-overlays/gpt-instructions.md +1 -1
- package/dist/custom-overlays.d.ts +21 -3
- package/dist/esm/activity.types.js +1 -1
- package/package.json +1 -1
package/dist/activity.types.js
CHANGED
|
@@ -685,7 +685,7 @@ exports.LumiaAlertFriendlyValues = {
|
|
|
685
685
|
[LumiaAlertValues.YOUTUBE_SUBSCRIBER]: 'Youtube Subscriber',
|
|
686
686
|
[LumiaAlertValues.YOUTUBE_SUPERCHAT]: 'Youtube Superchat',
|
|
687
687
|
[LumiaAlertValues.YOUTUBE_SUPERSTICKER]: 'Youtube Supersticker',
|
|
688
|
-
[LumiaAlertValues.YOUTUBE_GIFTS]: 'Youtube Gifts',
|
|
688
|
+
[LumiaAlertValues.YOUTUBE_GIFTS]: 'Youtube Gifts (Jewels)',
|
|
689
689
|
[LumiaAlertValues.YOUTUBE_SESSION_GIFTS]: 'Youtube Session Gifts',
|
|
690
690
|
[LumiaAlertValues.YOUTUBE_ENTRANCE]: 'Youtube Entrance',
|
|
691
691
|
[LumiaAlertValues.YOUTUBE_LIKE]: 'Youtube Like',
|
|
@@ -25,7 +25,7 @@ Let's get started:
|
|
|
25
25
|
**System bases** (built into Lumia):
|
|
26
26
|
`delay, lumia, overlay, api, commandRunner, inputEvents`
|
|
27
27
|
|
|
28
|
-
> Note: `lumia` and `overlay
|
|
28
|
+
> Note: the canonical system bases are `lumia` and `overlay`. The older spellings `lumiaActions` and `overlayActions` still run but are **deprecated** — prefer `lumia` / `overlay`. The input base is `inputEvents` (plural).
|
|
29
29
|
|
|
30
30
|
**Integration bases** — every connected integration is also a valid base. These include (and more are added over time):
|
|
31
31
|
`twitch, youtube, facebook, tiktok, kick, discord, obs, slobs, meld, spotify, youtubemusic, nowplaying, vlc, voicemod, streamerbot, vtubestudio, midi, osc, artnet, mqtt, serial, websocket, broadlink, hue, lifx, nanoleaf, govee, wled, wiz, tplink, tuya, yeelight, elgato, streamdeck, touchportal, loupedeck, homeassistant, switchbot` plus any installed plugin (use the plugin's id as the base).
|
|
@@ -36,7 +36,27 @@ Let's get started:
|
|
|
36
36
|
|
|
37
37
|
`variables`: allows you to send in different variables for each action. But do note that the variables that are already on the command/alert will also be spread on to this variables object. Variables are not required
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
To pause between actions, insert a dedicated **delay step**: an entry whose `type` is `"delay"` with the milliseconds in `delay` (e.g. `{ type: "delay", delay: 1000 }`). It can carry any `base` and runs in order with the rest of the list. (An inline `delay` on a non-delay action is **not** applied by the runner — use a delay step.)
|
|
40
|
+
|
|
41
|
+
#### Where actions live: the unified `actions` lane
|
|
42
|
+
|
|
43
|
+
Inside a Lumia **command** or **alert**, every action — whatever its `base` (a `lumia` system action, an `overlay` action, a `twitch` action, an integration, a plugin) — lives together in one ordered list on the command:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"actions": {
|
|
48
|
+
"before": [{ "base": "lumia", "type": "tts", "value": { "message": "Hi" } }],
|
|
49
|
+
"after": [{ "base": "twitch", "type": "clip", "value": { "title": "Clutch!", "duration": 30 } }],
|
|
50
|
+
"waitForActions": true
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- **`before`** runs before the command/alert's main effect (its light / overlay reaction); **`after`** runs after it.
|
|
56
|
+
- **`waitForActions`** (optional): when `true`, each action is awaited so the list runs in strict order before the command continues; when omitted or `false`, the list fires without waiting.
|
|
57
|
+
- Every entry is one action object of the shape `{ base, type, value?, delay?, args? }` — the same object you pass to the custom-code `actions()` helper. `delay` holds the milliseconds for a `type: "delay"` step; `args` carries the extra payload used by imported Streamer.bot actions.
|
|
58
|
+
|
|
59
|
+
This unified `actions` lane is the current model, and the one the AI action creator and the Streamer.bot / Mix It Up importers target. It **replaces** the older per-source command fields (`lumiaActions`, `overlayActions`, `api`, `commandRunner`, `inputEvents`) and the per-integration lanes (`command.<integration>.before/after` — e.g. `command.wavelink`, `command.streamfog`, whose entries have no `base`), all of which are **deprecated** — within the unified lane the source is simply each entry's `base`.
|
|
40
60
|
|
|
41
61
|
### Passing an array of actions
|
|
42
62
|
|
|
@@ -63,9 +83,11 @@ These are the built-in `type` values for each system base.
|
|
|
63
83
|
|
|
64
84
|
**`base: "commandRunner"`** — `app/file, shell command, delay`
|
|
65
85
|
|
|
66
|
-
**`base: "inputEvents"`** — `keyboard, mouse, delay`
|
|
86
|
+
**`base: "inputEvents"`** — `keyboard, mouse, delay`. The key/mouse data is **flat on the action**, not under `value`:
|
|
87
|
+
> - `keyboard`: `{ base: "inputEvents", type: "keyboard", keyboardValue: { value: "ctrl+j", valueType: "combination" } }` — `valueType` is `combination` (a hotkey like `ctrl+shift+f5`) or `input` (type the literal `value` as text); optional `longPress`, `cpm`.
|
|
88
|
+
> - `mouse`: `{ base: "inputEvents", type: "mouse", mouseValue: { x: 0, y: 100, clickEvent: "left", moveType: "set" } }` — `clickEvent` is `left` / `right` / `none`; optional `x1`, `y1`, `doubleClick`, `mouseSpeed`.
|
|
67
89
|
|
|
68
|
-
|
|
90
|
+
**delay step** — an entry with `type: "delay"` pauses the list; put the milliseconds in `delay` (e.g. `{ type: "delay", delay: 1000 }`). `duration` is accepted in place of `delay`, and the step runs under any `base`.
|
|
69
91
|
|
|
70
92
|
> Tip: most of the `lumia` and `overlay` actions already have dedicated helper functions (`tts`, `chatbot`, `overlaySetTextContent`, etc.) in `helper-functions.md`. Reach for `actions()` mainly when you need an integration action that does not have a helper yet.
|
|
71
93
|
|
|
@@ -95,6 +117,242 @@ async function() {
|
|
|
95
117
|
}
|
|
96
118
|
```
|
|
97
119
|
|
|
120
|
+
### Engagement & system `lumia` actions
|
|
121
|
+
|
|
122
|
+
These `lumia` actions drive Lumia's engagement features (raffles, tournaments, viewer queue, song requests) and system controls. None of them has a dedicated helper, so call them through `actions()`. The action shape is `{ base: "lumia", type: "<type>", value: { ... } }` — the fields in the tables below go **inside the inner `value` object**. Text fields accept template tokens like `{{username}}`.
|
|
123
|
+
|
|
124
|
+
#### Enable / disable commands, alerts, folders & automations
|
|
125
|
+
|
|
126
|
+
`on: true` enables, `on: false` disables.
|
|
127
|
+
|
|
128
|
+
| type | `value` | targets |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| `setCommand` | `{ value: "<command name>", on: true }` | a chat command |
|
|
131
|
+
| `setChatbotCommand` | `{ value: "<command name>", on: true }` | a chatbot command |
|
|
132
|
+
| `setTwitchPointsCommand` | `{ value: "<command name>", on: true }` | a Twitch channel-points reward command |
|
|
133
|
+
| `setTwitchExtensionCommand` | `{ value: "<command name>", on: true }` | a Twitch extension command |
|
|
134
|
+
| `setKickPointsCommand` | `{ value: "<command name>", on: true }` | a Kick points command |
|
|
135
|
+
| `setChatMatchCommand` | `{ value: "<chat-match id>", on: true }` | a chat-match command (by id) |
|
|
136
|
+
| `setAlert` | `{ value: "<alert key>", on: true }` | an alert (its `pathKey`) |
|
|
137
|
+
| `setAlertVariation` | `{ value: "<alert key>", variation: "<variation id>", on: true }` | one variation of an alert |
|
|
138
|
+
| `setFolder` | `{ value: "<folder id>", on: true }` | a command/alert folder |
|
|
139
|
+
| `setAutomation` | `{ value: "<automation name>", on: true }` | an automation/timer (matched by name) |
|
|
140
|
+
| `setVoicecommands` | `{ on: true }` | the voice-commands input as a whole |
|
|
141
|
+
| `setFuzeAudioSensitivity` | `{ value: 50 }` | Fuze audio sensitivity (number) |
|
|
142
|
+
|
|
143
|
+
#### Variables, local storage & files
|
|
144
|
+
|
|
145
|
+
| type | `value` | notes |
|
|
146
|
+
| --- | --- | --- |
|
|
147
|
+
| `appendToVariable` | `{ value: "<variable name>", message: "<item>", unique: true }` | append an item to a list variable; `unique` skips it if already present |
|
|
148
|
+
| `unappendFromVariable` | `{ value: "<variable name>", message: "<item>" }` | remove an item from a list variable |
|
|
149
|
+
| `saveLocal` | `{ value: "<key>", message: "<value>" }` | persist a value (same store as the `{{save_local}}` / `{{load_local}}` chat functions) |
|
|
150
|
+
| `writeToFile` | `{ value: "<file path>", message: "<content>", on: true }` | write text to a file; `on: true` appends instead of overwriting |
|
|
151
|
+
|
|
152
|
+
> For plain variables prefer the `setVariable` / `getVariable` / `deleteVariable` helpers in `helper-functions.md`.
|
|
153
|
+
|
|
154
|
+
#### Userlevels & restrictions
|
|
155
|
+
|
|
156
|
+
| type | `value` | notes |
|
|
157
|
+
| --- | --- | --- |
|
|
158
|
+
| `addToUserlevel` | `{ value: "<userlevel id>", message: "<username>" }` | add a user to a userlevel |
|
|
159
|
+
| `removeFromUserlevel` | `{ value: "<userlevel id>", message: "<username>" }` | remove a user from a userlevel |
|
|
160
|
+
| `addToRestrictionsList` | `{ message: "<username>" }` | restrict a user (no `value`) |
|
|
161
|
+
| `removeFromRestrictionsList` | `{ message: "<username>" }` | unrestrict a user |
|
|
162
|
+
|
|
163
|
+
#### Raffles
|
|
164
|
+
|
|
165
|
+
| type | `value` | notes |
|
|
166
|
+
| --- | --- | --- |
|
|
167
|
+
| `raffleStart` | `{ title: "<title>", preset: "<raffle id>", auto_end: true, ends_after: 5 }` | all optional; `preset` seeds from a saved raffle, `ends_after` is in **minutes** (default 30), `auto_end` turns on the timer |
|
|
168
|
+
| `raffleEntry` | `{ value: "<username>" }` | needs a started raffle |
|
|
169
|
+
| `raffleRemoveEntry` | `{ value: "<username>" }` | |
|
|
170
|
+
| `raffleStop` | `{}` | stop accepting entries |
|
|
171
|
+
| `raffleGetWinner` | `{}` | pick a winner (raffle must be stopped first) |
|
|
172
|
+
| `raffleEnd` | `{}` | end the raffle |
|
|
173
|
+
|
|
174
|
+
#### Tournaments
|
|
175
|
+
|
|
176
|
+
| type | `value` | notes |
|
|
177
|
+
| --- | --- | --- |
|
|
178
|
+
| `tournamentStart` | `{ preset: "<tournament id>" }` | start from a preset, or a new tournament if omitted |
|
|
179
|
+
| `tournamentEntry` | `{ message: "<username>", value: "<avatar url>", extra: "<team>", platform: "twitch" }` | `message` is the username; needs a running tournament with signups open |
|
|
180
|
+
| `tournamentRemoveEntry` | `{ message: "<username>", platform: "twitch" }` | |
|
|
181
|
+
| `tournamentUpdatePoints` | `{ message: "<username>", points: "+100" }` | `points` uses the modifier syntax (`+ - * / =`); defaults to `=` (set) when no modifier |
|
|
182
|
+
| `tournamentEnd` | `{}` | |
|
|
183
|
+
|
|
184
|
+
#### Viewer queue
|
|
185
|
+
|
|
186
|
+
| type | `value` | notes |
|
|
187
|
+
| --- | --- | --- |
|
|
188
|
+
| `viewerQueueEntry` | `{ value: "<username>" }` | needs a started queue |
|
|
189
|
+
| `viewerQueueLeave` | `{ value: "<username>" }` | |
|
|
190
|
+
| `viewerQueuePickPlayer` | `{ value: 1, mode: "single" }` | pick N players; `mode` is `single`, `bulkfirst`, or `bulkrandom` (default `single`, 1 player) |
|
|
191
|
+
| `viewerQueuePlayPause` | `{ on: true }` | `true` plays, `false` pauses |
|
|
192
|
+
| `viewerQueueEndQueue` | `{}` | |
|
|
193
|
+
|
|
194
|
+
#### Song requests
|
|
195
|
+
|
|
196
|
+
| type | `value` | notes |
|
|
197
|
+
| --- | --- | --- |
|
|
198
|
+
| `addSongRequest` | `{ value: "<search term or url>", requesterUsername: "{{username}}", skipApproval: true }` | `skipApproval` (or `forceApprove`) bypasses the approval queue |
|
|
199
|
+
| `skipSongRequest` | `{}` | |
|
|
200
|
+
| `clearSongRequestQueue` | `{}` | |
|
|
201
|
+
|
|
202
|
+
#### Queue, cooldowns & session (no `value` needed)
|
|
203
|
+
|
|
204
|
+
| type | what it does |
|
|
205
|
+
| --- | --- |
|
|
206
|
+
| `pauseQueue` / `resumeQueue` | pause / resume the action queue |
|
|
207
|
+
| `removeCurrentQueueItem` | drop the item currently running |
|
|
208
|
+
| `runLastQueueItem` | re-run the last completed queue item |
|
|
209
|
+
| `clearQueue` | empty the queue |
|
|
210
|
+
| `clearCooldowns` | clear all command cooldowns |
|
|
211
|
+
| `resetSession` | reset session variables (session counts) |
|
|
212
|
+
| `cleanAll` | clear queue + cooldowns and send lights to default |
|
|
213
|
+
| `backToDefault` | send lights back to their default |
|
|
214
|
+
| `refreshSettings` | hard-refresh Lumia's settings |
|
|
215
|
+
| `replayLastEventListEvent` | replay the most recent event-list event |
|
|
216
|
+
|
|
217
|
+
Call these with no inner value, e.g. `await actions([{ base: "lumia", type: "clearQueue" }]);`
|
|
218
|
+
|
|
219
|
+
#### Other command calls & webhooks
|
|
220
|
+
|
|
221
|
+
| type | `value` | notes |
|
|
222
|
+
| --- | --- | --- |
|
|
223
|
+
| `callRandomCommand` | `{ options: ["cmdA", "cmdB", "cmdC"], addToEndOfQueue: false }` | run one of the listed commands at random |
|
|
224
|
+
| `toggleStreamMode` | `{}` | flip stream mode on/off (use `setStreamMode { on }` to set it explicitly) |
|
|
225
|
+
| `sendToWebhook` | `{ value: "<url>", message: "<json string>" }` | `message` must be valid JSON — it is parsed and POSTed as the request body |
|
|
226
|
+
| `sendToDiscordWebhook` | `{ value: "<discord webhook url>", message: "<text>" }` | posts text to a Discord webhook |
|
|
227
|
+
| `sendToDiscordWithMediaWebhook` | `{ value: "<discord webhook url>", message: "<text>", file: "<local file path>" }` | posts text + an uploaded file |
|
|
228
|
+
| `sendToPrinter` | `{ value: "<IP:port or usb://vendor:product>", type: "image", message: "<image path>" }` | `type` is `image` (default) or `text`; for `text`, `message` is the text to print |
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
async function() {
|
|
232
|
+
// Start a 5-minute raffle, then enter the running viewer
|
|
233
|
+
await actions([
|
|
234
|
+
{ base: "lumia", type: "raffleStart", value: { title: "Giveaway", auto_end: true, ends_after: 5 } },
|
|
235
|
+
{ base: "lumia", type: "raffleEntry", value: { value: "{{username}}" } },
|
|
236
|
+
]);
|
|
237
|
+
|
|
238
|
+
// Disable a command and clear the queue
|
|
239
|
+
await actions([
|
|
240
|
+
{ base: "lumia", type: "setCommand", value: { value: "!hug", on: false } },
|
|
241
|
+
{ base: "lumia", type: "clearQueue" },
|
|
242
|
+
]);
|
|
243
|
+
done();
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Points, loyalty & currency actions
|
|
248
|
+
|
|
249
|
+
Several `lumia` actions change a point value, and they all share the same **modifier** syntax on the amount you pass in:
|
|
250
|
+
|
|
251
|
+
| You pass | Result |
|
|
252
|
+
| --- | --- |
|
|
253
|
+
| `+100` | add 100 |
|
|
254
|
+
| `-100` | subtract 100 |
|
|
255
|
+
| `*2` | multiply by 2 |
|
|
256
|
+
| `/2` | divide by 2 |
|
|
257
|
+
| `=300` | set the exact value to 300 |
|
|
258
|
+
|
|
259
|
+
A bare number with **no** modifier falls back to each action's own default (noted per action below). The same `+ - * / =` modifiers also work on the `updateCounter` and `updateVariable` actions.
|
|
260
|
+
|
|
261
|
+
#### `setUserLoyaltyPoint` — change a viewer's loyalty balance
|
|
262
|
+
|
|
263
|
+
| Field | Meaning |
|
|
264
|
+
| --- | --- |
|
|
265
|
+
| `value.value` | the amount (with optional modifier) |
|
|
266
|
+
| `value.message` | the username to target — defaults to the command's own `{{username}}` |
|
|
267
|
+
| `value.platform` | optional; defaults to the event's platform, then `twitch` |
|
|
268
|
+
|
|
269
|
+
**Default modifier: `+`** — a bare number *adds* points.
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
async function() {
|
|
273
|
+
// Give the viewer who triggered this 100 points
|
|
274
|
+
await actions([{ base: "lumia", type: "setUserLoyaltyPoint", value: { value: "100", message: "{{username}}" } }]);
|
|
275
|
+
|
|
276
|
+
// Take 50 points from a specific user on YouTube
|
|
277
|
+
await actions([{ base: "lumia", type: "setUserLoyaltyPoint", value: { value: "-50", message: "someviewer", platform: "youtube" } }]);
|
|
278
|
+
|
|
279
|
+
// Set an exact balance of 500
|
|
280
|
+
await actions([{ base: "lumia", type: "setUserLoyaltyPoint", value: { value: "=500", message: "{{username}}" } }]);
|
|
281
|
+
done();
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
#### `setLoyaltyPointValue` — change the loyalty-points **cost** of one of your commands
|
|
286
|
+
|
|
287
|
+
| Field | Meaning |
|
|
288
|
+
| --- | --- |
|
|
289
|
+
| `value.value` | the command name whose cost you are changing |
|
|
290
|
+
| `value.points` | the new cost (with optional modifier) |
|
|
291
|
+
|
|
292
|
+
**Default modifier: `=`** — a bare number *sets* the cost.
|
|
293
|
+
|
|
294
|
+
```js
|
|
295
|
+
async function() {
|
|
296
|
+
await actions([{ base: "lumia", type: "setLoyaltyPointValue", value: { value: "myredeem", points: "250" } }]); // cost becomes 250
|
|
297
|
+
await actions([{ base: "lumia", type: "setLoyaltyPointValue", value: { value: "myredeem", points: "+50" } }]); // cost goes up by 50
|
|
298
|
+
done();
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
#### `setTwitchPointValue` / `setTwitchExtensionBitsValue` — change a Twitch reward's cost
|
|
303
|
+
|
|
304
|
+
Both take the same `{ value: "<commandName>", points: "<amount>" }` shape as `setLoyaltyPointValue` and also **default to `=`**. `setTwitchPointValue` updates a channel-points reward's cost; `setTwitchExtensionBitsValue` updates a Twitch-extension command's Bits cost.
|
|
305
|
+
|
|
306
|
+
```js
|
|
307
|
+
async function() {
|
|
308
|
+
await actions([{ base: "lumia", type: "setTwitchPointValue", value: { value: "myreward", points: "1000" } }]);
|
|
309
|
+
await actions([{ base: "lumia", type: "setTwitchExtensionBitsValue", value: { value: "myextcommand", points: "100" } }]);
|
|
310
|
+
done();
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Overlay actions without a helper
|
|
315
|
+
|
|
316
|
+
These `base: "overlay"` actions don't have a dedicated helper yet. Their inner `value` targets a layer with `layer` (add `overlay: "<overlay name>"` to disambiguate which overlay the layer belongs to). `content` fields accept template tokens.
|
|
317
|
+
|
|
318
|
+
#### Spin wheel
|
|
319
|
+
|
|
320
|
+
| type | `value` | notes |
|
|
321
|
+
| --- | --- | --- |
|
|
322
|
+
| `spinwheelAddItem` | `{ layer: "<layer>", content: "<item title>", image: "<image url>" }` | adds an item (max 60); `image` is optional |
|
|
323
|
+
| `spinwheelRemoveItem` | `{ layer: "<layer>", content: "<item title>" }` | removes a matching item |
|
|
324
|
+
| `spinwheelReset` | `{ layer: "<layer>", removeItems: true }` | `removeItems: true` wipes all items; otherwise it just resets the wheel |
|
|
325
|
+
|
|
326
|
+
#### Polls / trigger games
|
|
327
|
+
|
|
328
|
+
| type | `value` | notes |
|
|
329
|
+
| --- | --- | --- |
|
|
330
|
+
| `pollStart` | `{ layer: "<layer>" }` | start the poll |
|
|
331
|
+
| `pollTrigger` | `{ layer: "<layer>" }` | trigger using the caller's username/message |
|
|
332
|
+
| `pollAddItem` | `{ layer: "<layer>", content: "<option title>", trigger: "<keyword>" }` | `trigger` defaults to the next option number |
|
|
333
|
+
| `pollRemoveItem` | `{ layer: "<layer>", content: "<option title>" }` | |
|
|
334
|
+
| `pollResetVotes` | `{ layer: "<layer>" }` | |
|
|
335
|
+
| `pollSetTimer` | `{ layer: "<layer>", content: "30" }` | `content` is the timer duration in seconds |
|
|
336
|
+
|
|
337
|
+
#### Game overlays, content & screenshots
|
|
338
|
+
|
|
339
|
+
| type | `value` | notes |
|
|
340
|
+
| --- | --- | --- |
|
|
341
|
+
| `sendGameTrigger` | `{ layer: "<layer>", content: "<value>" }` | targets the player from the caller's context |
|
|
342
|
+
| `sendGameUpdate` | `{ layer: "<layer>", content: "<value>" }` | |
|
|
343
|
+
| `setContent` | `{ layer: "<layer>", content: "<text>" }` | same as the `overlaySetTextContent` helper |
|
|
344
|
+
| `alertEvent` | `{ value: "<alert id>", variation: "<variation>", duration: 5000 }` | fire an overlay alert event; `duration` in ms |
|
|
345
|
+
| `takeScreenshot` | `{ value: "<overlay id>", format: "png", conversion: "original" }` | `format` is `png` or `jpeg`; `conversion` is `original`, `thermal`, `grayscale`, `invert`, or `binary`; resolves to the saved image path |
|
|
346
|
+
|
|
347
|
+
#### HUD
|
|
348
|
+
|
|
349
|
+
| type | `value` | notes |
|
|
350
|
+
| --- | --- | --- |
|
|
351
|
+
| `hudToggle` | `{}` | toggle the HUD |
|
|
352
|
+
| `hudOverlayChange` | `{ value: "<overlay>" }` | switch the HUD overlay |
|
|
353
|
+
| `hudVolumeSet` | `{ volume: 50 }` | set HUD volume |
|
|
354
|
+
| `hudOpacitySet` | `{ volume: 80 }` | set HUD opacity — note the value goes in the `volume` field |
|
|
355
|
+
|
|
98
356
|
### OBS (v5) actions
|
|
99
357
|
|
|
100
358
|
OBS has a dedicated helper, so you do **not** need `actions()` for it: pass any OBS websocket v5 request to `sendRawObsJson({ "request-type": "...", ...params })` (see `helper-functions.md`). The `request-type` and fields are exactly the same ones Lumia's OBS action editor uses. Common ones:
|
|
@@ -132,4 +390,367 @@ async function() {
|
|
|
132
390
|
|
|
133
391
|
Other available request-types include `SetCurrentProfile`, `SetCurrentSceneCollection`, `CreateInput`, `RemoveInput`, `SetSceneItemTransform`, `SetSceneItemBlendMode`, `SetInputAudioMonitorType`, `TriggerHotkeyByName`, `SetStudioModeEnabled`, `SendStreamCaption`, and the `Start/Stop/Toggle` variants for recording, streaming, replay buffer and virtual cam — the same list as the OBS action editor.
|
|
134
392
|
|
|
393
|
+
### Integration actions
|
|
394
|
+
|
|
395
|
+
Every connected integration is also a valid `base` (`spotify`, `discord`, `vtubestudio`, `midi`, `osc`, and the rest listed at the top). An integration action only runs if that integration is **connected and enabled** — otherwise it is silently skipped. The exact `type` and `value` vary per integration; the common ones are below. To find anything not listed, set the action up once in the normal action editor and read off its fields.
|
|
396
|
+
|
|
397
|
+
**Wrapper styles differ per integration** — each subsection states which it uses:
|
|
398
|
+
|
|
399
|
+
- **`value` object** — `{ base, type, value: { ... } }`: Discord, VTube Studio, Twitch, YouTube, Kick, TikTok, Meld, SwitchBot, Wave Link, Camera Hub, StreamFog.
|
|
400
|
+
- **scalar `value`** — `{ base, type, value: "..." }`: Spotify, VLC, YouTube Music, Voicemod, Streamer.bot.
|
|
401
|
+
- **flat fields** — `{ base, type, ...fields }` with no `value` wrapper: MIDI, OSC, OBS, MQTT, WebSocket, Serial, Art-Net, Broadlink (SLOBS is a special case, noted below).
|
|
402
|
+
|
|
403
|
+
Every integration also supports a delay step: `{ base: "<integration>", type: "delay", delay: <ms> }`. String fields generally resolve `{{template}}` tokens (Art-Net is the exception).
|
|
404
|
+
|
|
405
|
+
> **Lights** (Hue, LIFX, Govee, Nanoleaf, WLED, Elgato key lights, etc.) have **no** action `base` — control them with the `sendColor` helper (see `helper-functions.md`), not through `actions()`.
|
|
406
|
+
|
|
407
|
+
#### Spotify (`base: "spotify"`)
|
|
408
|
+
|
|
409
|
+
Spotify actions take a single scalar `value` (not an object). Playback control needs an active Spotify device.
|
|
410
|
+
|
|
411
|
+
| type | `value` | notes |
|
|
412
|
+
| --- | --- | --- |
|
|
413
|
+
| `setPlayState` | `"play"` / `"pause"` / `"toggle"` | |
|
|
414
|
+
| `skipToNext` / `skipToPrevious` | — | no value needed |
|
|
415
|
+
| `setVolume` | `0`–`100` | percent |
|
|
416
|
+
| `seek` | `<milliseconds>` | |
|
|
417
|
+
| `setShuffle` | `"true"` / `"false"` / `"toggle"` | |
|
|
418
|
+
| `setRepeat` | `"track"` / `"context"` / `"off"` | |
|
|
419
|
+
| `searchAndPlay` | `"<query or track url>"` | queues the match then skips to it |
|
|
420
|
+
| `searchAndPlayImmediately` | `"<query or track url>"` | plays it right away |
|
|
421
|
+
| `searchAndAddToQueue` | `"<query or track url>"` | adds to the queue |
|
|
422
|
+
| `searchAndAddToPlaylist` | `"<query or track url>"` + top-level `target: "<playlist id>"` | |
|
|
423
|
+
|
|
424
|
+
```js
|
|
425
|
+
async function() {
|
|
426
|
+
await actions([{ base: "spotify", type: "setVolume", value: 40 }]);
|
|
427
|
+
await actions([{ base: "spotify", type: "searchAndAddToQueue", value: "never gonna give you up" }]);
|
|
428
|
+
await actions([{ base: "spotify", type: "searchAndAddToPlaylist", value: "yesterday", target: "37i9dQZF1DXcBWIGoYBM5M" }]);
|
|
429
|
+
done();
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
#### Discord (`base: "discord"`)
|
|
434
|
+
|
|
435
|
+
Discord actions take a `value` object and post through your connected Discord.
|
|
436
|
+
|
|
437
|
+
| type | `value` | notes |
|
|
438
|
+
| --- | --- | --- |
|
|
439
|
+
| `sendMessage` | `{ value: "<message>", target: "<channel id>", file: "<local path>", embeds: "<json string>", on: false }` | the message text is `value.value`; `target` defaults to your primary channel; `on: true` sends it as TTS; `embeds` is a JSON-array string |
|
|
440
|
+
| `addMemberRole` | `{ value: "<username>", target: "<role id>" }` | |
|
|
441
|
+
| `removeMemberRole` | `{ value: "<username>", target: "<role id>" }` | |
|
|
442
|
+
| `kickMember` | `{ value: "<username>" }` | |
|
|
443
|
+
|
|
444
|
+
```js
|
|
445
|
+
async function() {
|
|
446
|
+
await actions([{ base: "discord", type: "sendMessage", value: { value: "Stream is live! {{twitch_channel_title}}" } }]);
|
|
447
|
+
done();
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
#### VTube Studio (`base: "vtubestudio"`)
|
|
452
|
+
|
|
453
|
+
VTube Studio actions take a `value` object; the identifier (model / hotkey / expression) goes in the inner `value.value`.
|
|
454
|
+
|
|
455
|
+
| type | `value` | notes |
|
|
456
|
+
| --- | --- | --- |
|
|
457
|
+
| `loadModel` | `{ value: "<model name or id>" }` | |
|
|
458
|
+
| `hotkeyTrigger` | `{ value: "<hotkey id>" }` | |
|
|
459
|
+
| `expressionTrigger` | `{ value: "<expression name>", on: true }` | `on` activates / deactivates |
|
|
460
|
+
| `moveModel` | `{ positionX: "0", positionY: "0.5", rotation: "0", timeInSeconds: 1, valuesAreRelativeToModel: false }` | position/rotation are numbers or numeric strings |
|
|
461
|
+
| `resizeModel` | `{ size: "-22.5", timeInSeconds: 1, valuesAreRelativeToModel: false }` | `size` ranges -100 (smallest) to 100 (biggest); applied through MoveModelRequest |
|
|
462
|
+
|
|
463
|
+
```js
|
|
464
|
+
async function() {
|
|
465
|
+
await actions([{ base: "vtubestudio", type: "hotkeyTrigger", value: { value: "MyHotkey" } }]);
|
|
466
|
+
done();
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
#### MIDI (`base: "midi"`) and OSC (`base: "osc"`)
|
|
471
|
+
|
|
472
|
+
MIDI and OSC read their fields **flat on the action object** — there is no `value` wrapper.
|
|
473
|
+
|
|
474
|
+
```js
|
|
475
|
+
async function() {
|
|
476
|
+
// MIDI: send a note (channel 1-16, note name like "C3", velocity 0-127)
|
|
477
|
+
await actions([{ base: "midi", type: "note-on", port: 0, channel: 1, note: "C3", velocity: 127 }]);
|
|
478
|
+
|
|
479
|
+
// OSC: send a typed message. type is "s" (string), "f" (float), or "i" (int).
|
|
480
|
+
await actions([{ base: "osc", type: "f", name: "/track/1/volume", sendPort: "9000", value: 0.8 }]);
|
|
481
|
+
done();
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
> MIDI device targeting: `port` (the device's port number) is the simplest selector and is required. On a multi-device setup — where ports can shift between sessions — also pass the optional `deviceId` and/or `deviceName`; Lumia resolves the target by `deviceId`, then `deviceName`, then `port`. `type` is `note-on`, `note-off`, or `delay` (a `delay` entry just waits its `delay` ms).
|
|
486
|
+
|
|
487
|
+
#### Twitch (`base: "twitch"`)
|
|
488
|
+
|
|
489
|
+
Twitch actions take a `value` object. Most string inputs — usernames, message ids, comma-separated option lists — go in `value.message`. Each action needs the matching Twitch permission to be granted on your connection.
|
|
490
|
+
|
|
491
|
+
Stream & engagement:
|
|
492
|
+
|
|
493
|
+
| type | `value` | notes |
|
|
494
|
+
| --- | --- | --- |
|
|
495
|
+
| `changeStreamTitle` | `{ message: "<title>" }` | |
|
|
496
|
+
| `changeCurrentCategory` | `{ message: "<game/category>" }` | matched by name |
|
|
497
|
+
| `clip` | `{ title: "<title>", duration: 30 }` | `duration` 5–60s; sets `{{twitch_last_clip_url}}` |
|
|
498
|
+
| `createStreamMarker` | `{ message: "<description>" }` | |
|
|
499
|
+
| `runCommercial` | `{ duration: 60 }` | one of 30/60/90/120/150/180 |
|
|
500
|
+
| `changeUserColor` | `{ color: "blue" }` | named color (Prime/Turbo can use hex) |
|
|
501
|
+
| `shoutout` | `{ message: "<username>" }` | runs your shoutout command |
|
|
502
|
+
| `raid` | `{ message: "<channel>" }` | |
|
|
503
|
+
| `sendAnnouncement` | `{ message: "<text>", color: "primary" }` | color: primary/blue/green/orange/purple |
|
|
504
|
+
| `createPoll` | `{ title: "<title>", message: "opt1,opt2", duration: 60 }` | options comma-separated; `duration` seconds |
|
|
505
|
+
| `endPoll` | `{ reason: "ARCHIVED" }` | `ARCHIVED` or `TERMINATED` |
|
|
506
|
+
| `createPrediction` | `{ title: "<title>", message: "outcome1,outcome2", duration: 60 }` | |
|
|
507
|
+
| `endPrediction` | `{ reason: "RESOLVED", message: "<winning outcome>" }` | `RESOLVED`/`CANCELED`/`LOCKED`; `message` is the winning outcome text |
|
|
508
|
+
| `updateRedemptionStatus` | `{ status: "FULFILLED" }` | `FULFILLED` or `CANCELED`; applies to pending redemptions |
|
|
509
|
+
|
|
510
|
+
Chat modes (`on: true` enables):
|
|
511
|
+
|
|
512
|
+
| type | `value` |
|
|
513
|
+
| --- | --- |
|
|
514
|
+
| `setSubscriberMode` / `setEmotesMode` / `setUniqueChatMode` | `{ on: true }` |
|
|
515
|
+
| `setFollowMode` | `{ on: true, duration: 0 }` (`duration` in **minutes**) |
|
|
516
|
+
| `setSlowMode` | `{ on: true, duration: 30 }` (`duration` in **seconds**) |
|
|
517
|
+
| `clearChat` | `{}` |
|
|
518
|
+
|
|
519
|
+
Moderation:
|
|
520
|
+
|
|
521
|
+
| type | `value` | notes |
|
|
522
|
+
| --- | --- | --- |
|
|
523
|
+
| `banUser` | `{ message: "<username>", reason: "" }` | |
|
|
524
|
+
| `unbanUser` | `{ message: "<username>" }` | |
|
|
525
|
+
| `timeoutUser` | `{ message: "<username>", duration: 600, reason: "" }` | `duration` in seconds |
|
|
526
|
+
| `deleteMessage` | `{ message: "<message id>" }` | |
|
|
527
|
+
| `pinMessage` | `{ message: "<text>", duration: 0 }` | sends then pins; `duration` 0 = no expiry, else 30–1800s |
|
|
528
|
+
| `unpinMessage` | `{}` | |
|
|
529
|
+
| `addModerator` / `removeModerator` / `addVip` / `removeVip` | `{ message: "<username>" }` | |
|
|
530
|
+
| `warnUser` | `{ message: "<username>", reason: "" }` | |
|
|
531
|
+
| `blockUser` / `unblockUser` | `{ message: "<username>" }` | |
|
|
532
|
+
| `addBlockedWord` / `removeBlockedWord` | `{ message: "<word>" }` | |
|
|
533
|
+
| `shieldModeOn` / `shieldModeOff` | `{}` | |
|
|
534
|
+
| `setSuspiciousUserStatus` | `{ message: "<username>", status: "ACTIVE_MONITORING" }` | `ACTIVE_MONITORING` or `RESTRICTED` |
|
|
535
|
+
| `removeSuspiciousUserStatus` | `{ message: "<username>" }` | |
|
|
536
|
+
|
|
537
|
+
```js
|
|
538
|
+
async function() {
|
|
539
|
+
await actions([{ base: "twitch", type: "clip", value: { title: "Clutch!", duration: 30 } }]);
|
|
540
|
+
await actions([{ base: "twitch", type: "sendAnnouncement", value: { message: "Raid incoming!", color: "purple" } }]);
|
|
541
|
+
done();
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
#### YouTube (`base: "youtube"`)
|
|
546
|
+
|
|
547
|
+
`value` object. Usernames go in `value.message`.
|
|
548
|
+
|
|
549
|
+
| type | `value` | notes |
|
|
550
|
+
| --- | --- | --- |
|
|
551
|
+
| `changeStreamTitle` | `{ title: "<title>", message: "<description>" }` | note: `message` is the **description** |
|
|
552
|
+
| `banUser` | `{ message: "<username>" }` | permanent |
|
|
553
|
+
| `unbanUser` | `{ message: "<username>" }` | |
|
|
554
|
+
| `timeoutUser` | `{ message: "<username>", duration: 300 }` | `duration` in seconds |
|
|
555
|
+
| `deleteMessage` | `{ message: "<message id>" }` | |
|
|
556
|
+
| `shoutout` | `{ message: "<username>" }` | |
|
|
557
|
+
|
|
558
|
+
#### Kick (`base: "kick"`)
|
|
559
|
+
|
|
560
|
+
`value` object. Usernames and the stream title both go in `value.message`. No poll or send-message action (chat goes through the chatbot).
|
|
561
|
+
|
|
562
|
+
| type | `value` | notes |
|
|
563
|
+
| --- | --- | --- |
|
|
564
|
+
| `changeStreamTitle` | `{ message: "<title>" }` | |
|
|
565
|
+
| `changeCurrentCategory` | `{ message: "<category>" }` | matched by name |
|
|
566
|
+
| `shoutout` | `{ message: "<username>" }` | |
|
|
567
|
+
| `banUser` | `{ message: "<username>", reason: "" }` | |
|
|
568
|
+
| `unbanUser` | `{ message: "<username>" }` | |
|
|
569
|
+
| `timeoutUser` | `{ message: "<username>", duration: 600, reason: "" }` | `duration` in seconds |
|
|
570
|
+
|
|
571
|
+
#### TikTok (`base: "tiktok"`)
|
|
572
|
+
|
|
573
|
+
`value` object. TikTok actions are **video upload only** (everything else is inbound events).
|
|
574
|
+
|
|
575
|
+
| type | `value` | notes |
|
|
576
|
+
| --- | --- | --- |
|
|
577
|
+
| `uploadDraftVideo` | `{ media: "<local file path>" }` | uploads to your TikTok inbox to finish/publish manually |
|
|
578
|
+
| `uploadAndPublishVideo` | `{ media: "<local file path>", title: "<title>", privacy_level: "PUBLIC_TO_EVERYONE", disable_comment: false, disable_duet: false, disable_stitch: false }` | `privacy_level`: `PUBLIC_TO_EVERYONE` / `MUTUAL_FOLLOW_FRIENDS` / `SELF_ONLY` |
|
|
579
|
+
|
|
580
|
+
#### OBS (`base: "obs"`)
|
|
581
|
+
|
|
582
|
+
For OBS, prefer the **`sendRawObsJson`** helper documented above — the `actions()` OBS path uses the same OBS-websocket-v5 `request-type` values and fields. Reach for `base: "obs"` only to batch OBS in one ordered `actions([...])` array with other bases, or to use Lumia's convenience source types.
|
|
583
|
+
|
|
584
|
+
OBS fields are **flat** on the action object (the type key is `request-type`, hyphenated):
|
|
585
|
+
|
|
586
|
+
```js
|
|
587
|
+
async function() {
|
|
588
|
+
await actions([
|
|
589
|
+
{ base: "obs", "request-type": "SetCurrentProgramScene", sceneName: "Starting Soon" },
|
|
590
|
+
{ base: "obs", "request-type": "SetInputMute", inputName: "Mic/Aux", inputMuted: true },
|
|
591
|
+
]);
|
|
592
|
+
done();
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
Common direct request-types (flat fields, verified against real setups):
|
|
597
|
+
|
|
598
|
+
| `request-type` | fields |
|
|
599
|
+
| --- | --- |
|
|
600
|
+
| `SetCurrentProgramScene` | `{ sceneName }` |
|
|
601
|
+
| `SetSceneItemEnabled` | `{ sceneName, inputName, sceneItemEnabled }` — Lumia resolves `inputName` → the scene-item id |
|
|
602
|
+
| `SetSourceFilterEnabled` | `{ sourceName, filterName, filterEnabled }` |
|
|
603
|
+
| `SaveSourceScreenshot` | `{ sourceName, imageFormat, imageFilePath }` |
|
|
604
|
+
| `StartStream` / `StopStream` / `StartRecord` / `StopRecord` | no extra fields |
|
|
605
|
+
|
|
606
|
+
Convenience source types let you set a source's content without hand-building `inputSettings`, but they only work if you **also** pass `lumiaOrigin: "SetInputSettings"` — Lumia rewrites the request into a real `SetInputSettings` call using it. Without `lumiaOrigin` the alias is sent verbatim and OBS rejects it. The aliases: `SetSourceText` (`{ inputName, text }`), `SetGenericUrlSource` / `SetSourceUrl` (`{ inputName, url }`), `SetGenericFileSource` / `SetImageFileSource` / `SetMediaFileSource` (`{ inputName, file }`):
|
|
607
|
+
|
|
608
|
+
```js
|
|
609
|
+
{ base: "obs", "request-type": "SetMediaFileSource", lumiaOrigin: "SetInputSettings", inputName: "Replay", file: "{{get_latest_file_from_folder=G:/Replays}}" }
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
For custom code it's usually simpler to skip the alias and call `sendRawObsJson({ "request-type": "SetInputSettings", inputName: "Replay", inputSettings: { local_file: "..." } })` directly. All other request-types match the OBS v5 API exactly.
|
|
613
|
+
|
|
614
|
+
#### Streamlabs Desktop (`base: "slobs"`)
|
|
615
|
+
|
|
616
|
+
SLOBS uses JSON-RPC, so an action carries both a `type` (for delay routing) and an RPC `method` + `params`. Only a few types are exposed:
|
|
617
|
+
|
|
618
|
+
| type | shape | notes |
|
|
619
|
+
| --- | --- | --- |
|
|
620
|
+
| `SetCurrentScene` | `{ base: "slobs", type: "SetCurrentScene", method: "makeSceneActive", params: { resource: "ScenesService", args: ["<sceneId>"] } }` | switch scene |
|
|
621
|
+
| `SetCurrentSceneCollection` | `{ ..., type: "SetCurrentSceneCollection", method: "load", params: { resource: "SceneCollectionsService", args: ["<collectionId>"] } }` | |
|
|
622
|
+
| `SetSourceRender` | `{ ..., type: "SetSourceRender", method: "setVisibility", params: { resource: "<sceneItemId>", args: [true] } }` | scene-item visibility |
|
|
623
|
+
|
|
624
|
+
#### Meld Studio (`base: "meld"`)
|
|
625
|
+
|
|
626
|
+
`value` object; scene/layer/track/effect targets accept the friendly name or id.
|
|
627
|
+
|
|
628
|
+
| type | `value` | notes |
|
|
629
|
+
| --- | --- | --- |
|
|
630
|
+
| `SetScene` | `{ value: "<scene>" }` | show a scene |
|
|
631
|
+
| `SetStagedScene` | `{ value: "<scene>" }` | set the staged (preview) scene |
|
|
632
|
+
| `ShowStagedScene` | `{}` | promote staged to live |
|
|
633
|
+
| `SetLayerVisibility` | `{ value: "<layer>", on: true }` | |
|
|
634
|
+
| `SetLayerEffect` | `{ parent: "<layer>", value: "<effect>", on: true }` | |
|
|
635
|
+
| `SetAudioTrackMute` | `{ value: "<track>", on: true }` | `on: true` = muted |
|
|
636
|
+
| `StartRecord` / `StopRecord` / `StartStream` / `StopStream` | `{}` | |
|
|
637
|
+
| `Clip` / `Screenshot` | `{}` | |
|
|
638
|
+
| `SendCommand` | `{ value: "meld.<command>" }` | raw command, e.g. `meld.toggleStreamingAction` |
|
|
639
|
+
|
|
640
|
+
#### VLC (`base: "vlc"`)
|
|
641
|
+
|
|
642
|
+
Scalar `value`.
|
|
643
|
+
|
|
644
|
+
| type | `value` | notes |
|
|
645
|
+
| --- | --- | --- |
|
|
646
|
+
| `setPlayState` | `"play"` / `"pause"` / `"toggle"` | |
|
|
647
|
+
| `playlistNext` / `playlistPrevious` | — | |
|
|
648
|
+
| `setVolume` | `0`–`125` | percent |
|
|
649
|
+
| `seek` | `"+30"` / `"-30"` / `"50%"` / `"120"` | VLC seek syntax |
|
|
650
|
+
| `setShuffle` / `setRepeat` / `setLoop` | `"true"` / `"false"` / `"toggle"` | |
|
|
651
|
+
| `addToQueueAndPlay` | `"<uri or path>"` | |
|
|
652
|
+
| `addToQueue` | `"<uri or path>"` | |
|
|
653
|
+
| `playlistEmpty` | — | clears the playlist |
|
|
654
|
+
|
|
655
|
+
#### YouTube Music (`base: "youtubemusic"`)
|
|
656
|
+
|
|
657
|
+
Scalar `value`; most types take no value.
|
|
658
|
+
|
|
659
|
+
| type | `value` | notes |
|
|
660
|
+
| --- | --- | --- |
|
|
661
|
+
| `setPlayState` | `"play"` / `"pause"` / `"toggle"` | |
|
|
662
|
+
| `skipToNext` / `skipToPrevious` | — | |
|
|
663
|
+
| `toggleShuffle` / `toggleRepeat` | — | |
|
|
664
|
+
| `thumbsUp` / `thumbsDown` | — | |
|
|
665
|
+
| `setVolumeUp` / `setVolumeDown` | — | |
|
|
666
|
+
| `searchAndPlayImmediately` | `"<url, videoId, or playlistId>"` | YTMDesktop app only |
|
|
667
|
+
|
|
668
|
+
#### Voicemod (`base: "voicemod"`)
|
|
669
|
+
|
|
670
|
+
Scalar `value` (voice/sound name) for the lookups; the on/off toggles take no value.
|
|
671
|
+
|
|
672
|
+
| type | `value` | notes |
|
|
673
|
+
| --- | --- | --- |
|
|
674
|
+
| `voice` | `"<voice name>"` | load a specific voice |
|
|
675
|
+
| `random_voice` / `random_voice_favorite` | — | random voice (favorites only for the latter) |
|
|
676
|
+
| `sound` / `soundboard` | `"<sound name>"` | play a meme sound |
|
|
677
|
+
| `random_sound` / `random_soundboard` | — | random sound |
|
|
678
|
+
| `stop_all_sounds` | — | |
|
|
679
|
+
| `on` / `off` / `on_off` | — | voice changer on / off / toggle |
|
|
680
|
+
| `hear_my_voice_on` / `hear_my_voice_off` / `hear_my_voice_on_off` | — | |
|
|
681
|
+
| `background_on_off` / `mic_on_off` | — | toggle background effects / mute mic |
|
|
682
|
+
|
|
683
|
+
#### Streamer.bot (`base: "streamerbot"`)
|
|
684
|
+
|
|
685
|
+
Scalar `value` is the action name; `args` is a flat top-level JSON string.
|
|
686
|
+
|
|
687
|
+
```js
|
|
688
|
+
async function() {
|
|
689
|
+
await actions([{ base: "streamerbot", type: "action", value: "My SB Action", args: '{"user":"{{username}}","amount":5}' }]);
|
|
690
|
+
done();
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
#### Protocols — MQTT, WebSocket, Serial, Art-Net, Broadlink
|
|
695
|
+
|
|
696
|
+
These read their fields **flat** on the action object (no `value` wrapper).
|
|
697
|
+
|
|
698
|
+
```js
|
|
699
|
+
async function() {
|
|
700
|
+
// MQTT: publish to a topic. host is the broker URL — mqtt://<host>:<port>
|
|
701
|
+
await actions([{ base: "mqtt", type: "send", host: "mqtt://192.168.1.50:1883", topic: "home/light", value: "ON" }]);
|
|
702
|
+
|
|
703
|
+
// WebSocket: send to one configured connection by its id
|
|
704
|
+
await actions([{ base: "websocket", type: "data", device: "<websocketId>", value: "hello" }]);
|
|
705
|
+
|
|
706
|
+
// Serial: write raw data to a port (COM3 on Windows, /dev/tty.* on macOS)
|
|
707
|
+
await actions([{ base: "serial", type: "write", port: "COM3", value: "L1\n" }]);
|
|
708
|
+
|
|
709
|
+
// Art-Net: set DMX channels on a universe (channel 1-512, value 0-255). universe is a string
|
|
710
|
+
await actions([{ base: "artnet", type: "artnet", universe: "0", values: [{ channel: 1, value: 255 }, { channel: 2, value: 128 }] }]);
|
|
711
|
+
|
|
712
|
+
// Broadlink: send a learned IR/RF code by its library id to a device
|
|
713
|
+
await actions([{ base: "broadlink", type: "ir", device: "<deviceId>", value: "<codeId>" }]);
|
|
714
|
+
done();
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
#### Devices — SwitchBot, Wave Link, Camera Hub, StreamFog
|
|
719
|
+
|
|
720
|
+
All take a `value` object.
|
|
721
|
+
|
|
722
|
+
| base | type | `value` | notes |
|
|
723
|
+
| --- | --- | --- | --- |
|
|
724
|
+
| `switchbot` | `turnOn` / `turnOff` / `toggle` | `{ deviceId: "<id>" }` | |
|
|
725
|
+
| `wavelink` | `FILTER_SET` | `{ filterId: "<uuid>", isEnabled: true, inputId: "Wave Link Mic In 1" }` | enable/disable an input's filter; `filterId: "ALL_EFFECTS"` targets every effect on that input |
|
|
726
|
+
| `wavelink` | `SET_INPUT_VOLUME` | `{ inputId: "<id>", volume: 50, mixerID: "com.elgato.mix.local" }` | volume 0–100; mixer `local`/`stream`/`all` |
|
|
727
|
+
| `wavelink` | `MUTE_INPUT` | `{ inputId: "<id>", muted: true, mixerID: "com.elgato.mix.local" }` | |
|
|
728
|
+
| `wavelink` | `SET_OUTPUT_VOLUME` / `MUTE_OUTPUT` | `{ volume: 50, mixerID: "com.elgato.mix.local" }` | |
|
|
729
|
+
| `wavelink3` | same types as `wavelink` | same shapes | Wave Link 3.0 uses base `wavelink3`; some legacy filter/mic types are unsupported on v3 |
|
|
730
|
+
| `camerahub` | `SET_WEBCAM_PROPERTY` | `{ propertyID: "Brightness", propertyValue: 60 }` | all camerahub types share one property-bag `value` (`deviceID, propertyID, propertyValue, intensity, isEnabled, …`) — set only the fields the type uses. Also `SELECT_DEVICE`, `SET_NVIDIA_VIDEO_EFFECT`, `SET_LUT_EFFECT` (`intensity`), `SET_PROMPTER_PROPERTY` |
|
|
731
|
+
| `streamfog` | `activateLens` | `{ lensId: "<id or name>", duration: 10 }` | `lensId: "random"` picks one; also `activateOutfit` (`{ outfit, duration }`) and `disableLens` (`{}`) |
|
|
732
|
+
|
|
733
|
+
#### Plugin actions (`base: "<pluginId>"`)
|
|
734
|
+
|
|
735
|
+
Every installed plugin is also a valid base — use the plugin's id as the `base`, plus the plugin's own action `type` and a `value` object of that plugin's fields. The exact `type` and `value` come from the plugin's manifest, so the reliable way to discover them is to add the action once in the editor and read it back; the shape is always `{ base: "<pluginId>", type: "<action type>", value: { ...plugin fields } }`. Verified examples:
|
|
736
|
+
|
|
737
|
+
```js
|
|
738
|
+
async function() {
|
|
739
|
+
// Tapo smart plug — target is the device IP, or "IP|deviceId"
|
|
740
|
+
await actions([{ base: "tapo_plugin", type: "tapo_turn_on", value: { target: "192.168.50.169" } }]);
|
|
741
|
+
|
|
742
|
+
// Steam — look up a game
|
|
743
|
+
await actions([{ base: "steam", type: "fetch_game", value: { game: "Sonic" } }]);
|
|
744
|
+
|
|
745
|
+
// ClickUp — create a task (assigneeId is comma-separated)
|
|
746
|
+
await actions([{ base: "clickup", type: "create_task", value: { name: "{{message}}", assigneeId: "14928785,14928776" } }]);
|
|
747
|
+
|
|
748
|
+
// ElevenLabs TTS
|
|
749
|
+
await actions([{ base: "elevenlabs_tts", type: "speak", value: { message: "{{message}}", voiceId: "hpp4J3VqNfWAUOO0d1Us", modelId: "eleven_multilingual_v2", volume: 100 } }]);
|
|
750
|
+
done();
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
A plugin action is silently skipped unless its plugin is installed, connected, and enabled.
|
|
755
|
+
|
|
135
756
|
This documentation can get extremely broad for every integration, so if you get stuck please visit our [**Discord**](https://discord.gg/R8rCaKb) to ask us any questions.
|
|
@@ -93,7 +93,7 @@ async function() {
|
|
|
93
93
|
|
|
94
94
|
### Get Variable
|
|
95
95
|
|
|
96
|
-
`getVariable(name: string)`: Retrieve a variables value based on it's name
|
|
96
|
+
`getVariable(name: string)`: Retrieve a variables value based on it's name. Returns the raw stored value (string / number / …), or `undefined` if it isn't set. A command's runtime variable takes priority over the saved one of the same name.
|
|
97
97
|
|
|
98
98
|
```js
|
|
99
99
|
async function() {
|
|
@@ -127,7 +127,7 @@ async function() {
|
|
|
127
127
|
|
|
128
128
|
### Get All Variables
|
|
129
129
|
|
|
130
|
-
`getAllVariables()`: Ability to get all local and global variables with one easy call
|
|
130
|
+
`getAllVariables()`: Ability to get all local and global variables with one easy call. Returns a flat `{ name: value }` map of every saved variable merged with the command's runtime variables (runtime values win on a name clash).
|
|
131
131
|
|
|
132
132
|
```js
|
|
133
133
|
async function() {
|
|
@@ -197,6 +197,8 @@ async function() {
|
|
|
197
197
|
|
|
198
198
|
`getLights()`: Get the list of lights the streamer has along with it's type and id. The type and id is required to send color or power to specific lights
|
|
199
199
|
|
|
200
|
+
Returns an array of `{ id, name, alias, type }`. `type` is the integration key (e.g. `hue`, `govee`, `wled`, `elgato`, `virtuallights`), `alias` is your custom label, and `name` is the device's original name. Pass `{ id, type }` objects to `sendColor`'s `lights` array to target specific lights. Disconnected or disabled integrations are omitted, so an empty array means nothing is connected.
|
|
201
|
+
|
|
200
202
|
```js
|
|
201
203
|
async function() {
|
|
202
204
|
const lights = await getLights()
|
|
@@ -246,6 +248,8 @@ async function() {
|
|
|
246
248
|
|
|
247
249
|
`getApiOptions()`: Contains information like commands, types, connections, and more
|
|
248
250
|
|
|
251
|
+
Returns `{ types, options }`. `types` is the list of every API command type (e.g. `setColor`, `setBrightness`, `alert`, `tts`, `chatCommand`, `chatbotCommand`, `twitchPoints`, `kickPoints`, studio scene/theme/animation, plus value-less system ops). `options` is keyed by those same types: value-less ops are `null`, while the command/alert/tts/studio types carry a `{ values: [...] }` list of the names you can use.
|
|
252
|
+
|
|
249
253
|
```js
|
|
250
254
|
async function() {
|
|
251
255
|
const lights = await getApiOptions()
|
|
@@ -254,7 +258,7 @@ async function() {
|
|
|
254
258
|
|
|
255
259
|
### Get Commands
|
|
256
260
|
|
|
257
|
-
`getCommands({ formatted?: boolean; onlyOn?: boolean; onlyUser?: boolean })`: Returns the list of chat command names. Pass `onlyOn: true` to only include commands that are enabled and shown in the commands list, `onlyUser: true` to only include commands the current user has access to (based on their user levels), and `formatted: true` to get a single comma separated string instead of an array
|
|
261
|
+
`getCommands({ formatted?: boolean; onlyOn?: boolean; onlyUser?: boolean })`: Returns the list of chat and chatbot command names. Pass `onlyOn: true` to only include commands that are enabled and shown in the commands list, `onlyUser: true` to only include commands the current user has access to (based on their user levels), and `formatted: true` to get a single comma separated string instead of an array
|
|
258
262
|
|
|
259
263
|
```js
|
|
260
264
|
async function() {
|
|
@@ -486,7 +490,7 @@ async function() {
|
|
|
486
490
|
|
|
487
491
|
### Get Token
|
|
488
492
|
|
|
489
|
-
`getToken(connection: "twitch" | "twitchChatbot" | "youtube" | "facebook" | "streamlabs" | "streamelements" | "treatstream" | "tipeeestream" | "tiltify" | "patreon" | "woocommerce" | "discord" | "twitter" | "spotify" | "pulsoid" | "wyze" | "homeassistant" | "govee" | "wled" )`: When you need to call a request that we don't directly support you can get the access token from Lumia before making the call. This is helpful for things where you need to call for instance the Twitch API, but you don't want to handle tokens and refreshing inside of your scripts. More examples of this below
|
|
493
|
+
`getToken(connection: "twitch" | "twitchChatbot" | "youtube" | "facebook" | "streamlabs" | "streamelements" | "treatstream" | "tipeeestream" | "tiltify" | "patreon" | "woocommerce" | "discord" | "twitter" | "spotify" | "pulsoid" | "wyze" | "homeassistant" | "govee" | "wled" )`: When you need to call a request that we don't directly support you can get the access token from Lumia before making the call. This is helpful for things where you need to call for instance the Twitch API, but you don't want to handle tokens and refreshing inside of your scripts. More examples of this below. Returns the access-token string, or `null` / `undefined` if that connection isn't authorized.
|
|
490
494
|
|
|
491
495
|
```js
|
|
492
496
|
async function() {
|
|
@@ -497,7 +501,7 @@ async function() {
|
|
|
497
501
|
|
|
498
502
|
### Get Client ID For Twitch
|
|
499
503
|
|
|
500
|
-
`getClientId(connection: "twitch")`: When calling requests with Twitch's API you will need to pass in a Client-ID to the headers. We provide a Client ID that you can use to call the different api's with the permissions the user has selected. Check out [Twitch's developers docs](https://dev.twitch.tv/docs/api/reference) to learn what you can do
|
|
504
|
+
`getClientId(connection: "twitch")`: When calling requests with Twitch's API you will need to pass in a Client-ID to the headers. We provide a Client ID that you can use to call the different api's with the permissions the user has selected. Check out [Twitch's developers docs](https://dev.twitch.tv/docs/api/reference) to learn what you can do. Returns the Twitch Client-ID string; only `"twitch"` is supported and any other value returns `null`.
|
|
501
505
|
|
|
502
506
|
```js
|
|
503
507
|
async function() {
|
|
@@ -110,12 +110,27 @@ Include these two helper aliases in your embeddings so the model can quickly map
|
|
|
110
110
|
"min": { "type": "number" },
|
|
111
111
|
"max": { "type": "number" },
|
|
112
112
|
"visibleIf": {
|
|
113
|
-
"
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
"oneOf": [
|
|
114
|
+
{
|
|
115
|
+
"type": "object",
|
|
116
|
+
"required": ["key", "equals"],
|
|
117
|
+
"properties": {
|
|
118
|
+
"key": { "type": "string" },
|
|
119
|
+
"equals": {}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"type": "array",
|
|
124
|
+
"items": {
|
|
125
|
+
"type": "object",
|
|
126
|
+
"required": ["key", "equals"],
|
|
127
|
+
"properties": {
|
|
128
|
+
"key": { "type": "string" },
|
|
129
|
+
"equals": {}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
]
|
|
119
134
|
},
|
|
120
135
|
"hidden": { "type": "boolean" }
|
|
121
136
|
},
|
|
@@ -399,7 +399,7 @@ A field object can now contain these properties:
|
|
|
399
399
|
| **`value`** | ❌ | Default value that appears the first time the user opens the overlay (also pre populates `Overlay.data`). Omit it to leave the field blank/unchecked on first load. | `"value": 18` |
|
|
400
400
|
| **`options`** | ☑️\* | Key value map of selectable choices. Required **only** for `dropdown`, `multiselect` and `slider`; ignored for other types. For `slider`, `options` supports `step`, `min`, `max`, `prefix`, `suffix`. | `"options": { "step": 5, "min": 0, "max": 100 }` |
|
|
401
401
|
| **`order`** | ❌ | **Display order priority**. Fields with lower numbers appear first. Fields without `order` appear after ordered fields, sorted alphabetically by key. | `"order": 1` |
|
|
402
|
-
| **`visibleIf`** | ❌ | **Conditional render rule**. Field is shown **only if** `Overlay.data[visibleIf.key]` strictly equals one of the values in `visibleIf.equals`.
|
|
402
|
+
| **`visibleIf`** | ❌ | **Conditional render rule**. Field is shown **only if** `Overlay.data[visibleIf.key]` strictly equals one of the values in `visibleIf.equals`. Pass an **array of rules** to require that **every** rule matches (AND across keys). | `"visibleIf": [{ "key": "targetKey", "equals": ["yes", "maybe"] }, { "key": "mode", "equals": "advanced" }]` |
|
|
403
403
|
| **`hidden`** | ❌ | **Hard-hide rule.** When set to `true`, the field is **never displayed** in the Configs sidebar, preventing end users from altering it. The value still flows into `Overlay.data`, so the overlay can rely on it internally.<br>Useful for locking event subscriptions or other advanced settings. | `"hidden": true` |
|
|
404
404
|
|
|
405
405
|
Additional properties for text fields (`type: "input"` and `type: "textarea"`):
|
|
@@ -761,10 +761,13 @@ In this example, the fields would appear in this order:
|
|
|
761
761
|
### Visible If Conditional Fields
|
|
762
762
|
|
|
763
763
|
The `visibleIf` field is a conditional statement that determines whether a field should be visible or not based on a condition.
|
|
764
|
-
The `visibleIf` field
|
|
764
|
+
The `visibleIf` field can be **either**:
|
|
765
|
+
|
|
766
|
+
- a single rule object with two keys, `key` and `equals`, or
|
|
767
|
+
- an **array of rule objects**, in which case the field is shown only when **every** rule matches (logical AND). Use the array form to gate a field on more than one key at once.
|
|
765
768
|
|
|
766
769
|
The `key` key is a string that represents the name of the field whose value should be used in the conditional statement.
|
|
767
|
-
The `equals` key is a string, number, boolean, or an array that represents the value of the field
|
|
770
|
+
The `equals` key is a string, number, boolean, or an array that represents the value(s) of that field that make the condition match. When `equals` is an array, the rule matches if the field's value is one of the listed values (and for `multiselect` fields, if any selected value is in the list).
|
|
768
771
|
|
|
769
772
|
#### Single Primitive Equals Example
|
|
770
773
|
|
|
@@ -804,6 +807,35 @@ The `equals` key is a string, number, boolean, or an array that represents the v
|
|
|
804
807
|
}
|
|
805
808
|
```
|
|
806
809
|
|
|
810
|
+
#### Multiple Keys (Array of Rules) Example
|
|
811
|
+
|
|
812
|
+
Pass an array of rule objects to require that **every** rule matches before the
|
|
813
|
+
field shows. Here `subcolor` only appears when **both** `color` and `alerts`
|
|
814
|
+
are set to one of `red`, `blue`, or `green`:
|
|
815
|
+
|
|
816
|
+
```json
|
|
817
|
+
{
|
|
818
|
+
"color": {
|
|
819
|
+
"type": "input",
|
|
820
|
+
"label": "Colors"
|
|
821
|
+
},
|
|
822
|
+
"subcolor": {
|
|
823
|
+
"type": "input",
|
|
824
|
+
"label": "Subcolors",
|
|
825
|
+
"visibleIf": [
|
|
826
|
+
{
|
|
827
|
+
"key": "color",
|
|
828
|
+
"equals": ["red", "blue", "green"]
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
"key": "alerts",
|
|
832
|
+
"equals": ["red", "blue", "green"]
|
|
833
|
+
}
|
|
834
|
+
]
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
```
|
|
838
|
+
|
|
807
839
|
---
|
|
808
840
|
|
|
809
841
|
## 📊 Data Tab
|
|
@@ -53,7 +53,7 @@ export enum ConfigsFieldType {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
56
|
+
* A single conditional render rule for a config field.
|
|
57
57
|
* The field is shown only when `Overlay.data[key]` strictly equals one of `equals`
|
|
58
58
|
* (scalar) or intersects with `equals` (when either side is an array).
|
|
59
59
|
*
|
|
@@ -68,6 +68,23 @@ export interface ConfigVisibleIf {
|
|
|
68
68
|
equals: string | number | boolean | Array<string | number | boolean>;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Conditional render rule, or a list of rules. When an array is supplied, the
|
|
73
|
+
* field is shown only when **every** rule matches (logical AND), letting you
|
|
74
|
+
* gate a field on several keys at once.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // single key
|
|
78
|
+
* { key: 'mode', equals: 'advanced' }
|
|
79
|
+
* @example
|
|
80
|
+
* // multiple keys — all must match
|
|
81
|
+
* [
|
|
82
|
+
* { key: 'color', equals: ['red', 'blue', 'green'] },
|
|
83
|
+
* { key: 'alerts', equals: ['red', 'blue', 'green'] },
|
|
84
|
+
* ]
|
|
85
|
+
*/
|
|
86
|
+
export type ConfigVisibleIfRule = ConfigVisibleIf | ConfigVisibleIf[];
|
|
87
|
+
|
|
71
88
|
/** Properties common to every config field, regardless of `type`. */
|
|
72
89
|
export interface BaseConfigField {
|
|
73
90
|
/** Human-readable name shown next to the control in the Configs sidebar. */
|
|
@@ -79,9 +96,10 @@ export interface BaseConfigField {
|
|
|
79
96
|
order?: number;
|
|
80
97
|
/**
|
|
81
98
|
* Conditional render rule. When set, the field renders only when
|
|
82
|
-
* `Overlay.data[visibleIf.key]` matches `visibleIf.equals`.
|
|
99
|
+
* `Overlay.data[visibleIf.key]` matches `visibleIf.equals`. Pass an array of
|
|
100
|
+
* rules to require that **every** rule matches (logical AND across keys).
|
|
83
101
|
*/
|
|
84
|
-
visibleIf?:
|
|
102
|
+
visibleIf?: ConfigVisibleIfRule;
|
|
85
103
|
/**
|
|
86
104
|
* Hard-hide rule. When `true`, the field is never displayed in the Configs
|
|
87
105
|
* sidebar, but its `value` still flows into `Overlay.data` for internal use.
|
|
@@ -35,7 +35,7 @@ Source of truth (in order): this file > `gpt-instructions-extended.md` (long-for
|
|
|
35
35
|
- **HTML**: body content only. Stable IDs/classes. No inline `<script>` or `<style>`.
|
|
36
36
|
- **CSS**: stylesheet only. Unquoted CSS variables for colors/sizes/numbers. Quoted only where CSS requires a string (`font-family: "{{font}}";`).
|
|
37
37
|
- **JS**: top-level JS. Use `Overlay.data` for Data values. Use `textContent`/`createElement`/`appendChild` for any chat, alert, or user-generated text — never `innerHTML` with user input. `fetch` is allowed.
|
|
38
|
-
- **Configs**: field types: `input`, `textarea`, `number`, `checkbox`, `dropdown`, `multiselect`, `colorpicker`, `fontpicker`, `slider`, `imageupload`, `soundupload`, `videoupload`, `actionbutton`. Required: `type`, `label`. Use `value` for defaults. `dropdown`/`multiselect` need `options`. `slider` needs `options.min`/`max` (usually `step`/`prefix`/`suffix`). `imageupload`/`soundupload`/`videoupload` accept an `accept` MIME hint. `actionbutton` has no `value` — bind it via `Overlay.on('configAction', ({ key }) => …)`. Use `order` to control sidebar order. Use `visibleIf` for conditional fields. Keys must be machine-friendly (no spaces).
|
|
38
|
+
- **Configs**: field types: `input`, `textarea`, `number`, `checkbox`, `dropdown`, `multiselect`, `colorpicker`, `fontpicker`, `slider`, `imageupload`, `soundupload`, `videoupload`, `actionbutton`. Required: `type`, `label`. Use `value` for defaults. `dropdown`/`multiselect` need `options`. `slider` needs `options.min`/`max` (usually `step`/`prefix`/`suffix`). `imageupload`/`soundupload`/`videoupload` accept an `accept` MIME hint. `actionbutton` has no `value` — bind it via `Overlay.on('configAction', ({ key }) => …)`. Use `order` to control sidebar order. Use `visibleIf` for conditional fields — either a single `{ key, equals }` rule or an array of rules (the field shows only when every rule matches). Keys must be machine-friendly (no spaces).
|
|
39
39
|
- **Data**: every Configs key must have a matching Data key with a matching default. Data can hold internal values that aren't in Configs (rare).
|
|
40
40
|
|
|
41
41
|
## Variables
|
|
@@ -53,7 +53,7 @@ export enum ConfigsFieldType {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
56
|
+
* A single conditional render rule for a config field.
|
|
57
57
|
* The field is shown only when `Overlay.data[key]` strictly equals one of `equals`
|
|
58
58
|
* (scalar) or intersects with `equals` (when either side is an array).
|
|
59
59
|
*
|
|
@@ -68,6 +68,23 @@ export interface ConfigVisibleIf {
|
|
|
68
68
|
equals: string | number | boolean | Array<string | number | boolean>;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Conditional render rule, or a list of rules. When an array is supplied, the
|
|
73
|
+
* field is shown only when **every** rule matches (logical AND), letting you
|
|
74
|
+
* gate a field on several keys at once.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // single key
|
|
78
|
+
* { key: 'mode', equals: 'advanced' }
|
|
79
|
+
* @example
|
|
80
|
+
* // multiple keys — all must match
|
|
81
|
+
* [
|
|
82
|
+
* { key: 'color', equals: ['red', 'blue', 'green'] },
|
|
83
|
+
* { key: 'alerts', equals: ['red', 'blue', 'green'] },
|
|
84
|
+
* ]
|
|
85
|
+
*/
|
|
86
|
+
export type ConfigVisibleIfRule = ConfigVisibleIf | ConfigVisibleIf[];
|
|
87
|
+
|
|
71
88
|
/** Properties common to every config field, regardless of `type`. */
|
|
72
89
|
export interface BaseConfigField {
|
|
73
90
|
/** Human-readable name shown next to the control in the Configs sidebar. */
|
|
@@ -79,9 +96,10 @@ export interface BaseConfigField {
|
|
|
79
96
|
order?: number;
|
|
80
97
|
/**
|
|
81
98
|
* Conditional render rule. When set, the field renders only when
|
|
82
|
-
* `Overlay.data[visibleIf.key]` matches `visibleIf.equals`.
|
|
99
|
+
* `Overlay.data[visibleIf.key]` matches `visibleIf.equals`. Pass an array of
|
|
100
|
+
* rules to require that **every** rule matches (logical AND across keys).
|
|
83
101
|
*/
|
|
84
|
-
visibleIf?:
|
|
102
|
+
visibleIf?: ConfigVisibleIfRule;
|
|
85
103
|
/**
|
|
86
104
|
* Hard-hide rule. When `true`, the field is never displayed in the Configs
|
|
87
105
|
* sidebar, but its `value` still flows into `Overlay.data` for internal use.
|
|
@@ -682,7 +682,7 @@ export const LumiaAlertFriendlyValues = {
|
|
|
682
682
|
[LumiaAlertValues.YOUTUBE_SUBSCRIBER]: 'Youtube Subscriber',
|
|
683
683
|
[LumiaAlertValues.YOUTUBE_SUPERCHAT]: 'Youtube Superchat',
|
|
684
684
|
[LumiaAlertValues.YOUTUBE_SUPERSTICKER]: 'Youtube Supersticker',
|
|
685
|
-
[LumiaAlertValues.YOUTUBE_GIFTS]: 'Youtube Gifts',
|
|
685
|
+
[LumiaAlertValues.YOUTUBE_GIFTS]: 'Youtube Gifts (Jewels)',
|
|
686
686
|
[LumiaAlertValues.YOUTUBE_SESSION_GIFTS]: 'Youtube Session Gifts',
|
|
687
687
|
[LumiaAlertValues.YOUTUBE_ENTRANCE]: 'Youtube Entrance',
|
|
688
688
|
[LumiaAlertValues.YOUTUBE_LIKE]: 'Youtube Like',
|