@lumiastream/lumia-types 3.9.3 → 3.9.5

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.
@@ -0,0 +1,1693 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.API_COMMANDS = exports.API_TRANSPORT_NOTES = exports.API_ENVELOPE_PARAMS = void 0;
4
+ exports.API_ENVELOPE_PARAMS = {
5
+ skipQueue: {
6
+ type: 'boolean',
7
+ required: false,
8
+ description: 'Queue-execution types only: bypass the activity queue and run immediately.',
9
+ },
10
+ bypassQueue: {
11
+ type: 'boolean',
12
+ required: false,
13
+ description: 'Alias of skipQueue (skipQueue wins when both are set).',
14
+ },
15
+ hold: {
16
+ type: 'boolean',
17
+ required: false,
18
+ description: 'Queue-execution types only: instead of executing, capture the payload as the new default state (used by SDK buttons held >2s).',
19
+ },
20
+ extraSettings: {
21
+ type: 'object',
22
+ required: false,
23
+ description: 'Free-form key/value bag merged into the queued activity. Used to pass template variables to commands, alert variables/variation overrides to alerts, etc.',
24
+ },
25
+ };
26
+ exports.API_TRANSPORT_NOTES = [
27
+ "The wire `type` must be the kebab-case LumiaActivityCommandTypes VALUE (e.g. 'chat-command'). Sending an enum KEY (e.g. 'CHAT_COMMAND') is rejected with 'Api has different type'.",
28
+ "`required: true` means the runtime guards the param (request errors without it). Params that are functionally mandatory but unguarded are `required: false` with a note; omitting them silently resolves success while doing nothing.",
29
+ "Every handler resolves HTTP 200 with value `true` unless its case explicitly throws. Failures that happen later (queue execution, platform API rejections, missing permissions) are NOT reflected in the response.",
30
+ 'GET-style requests deliver params as a string: it must survive JSON.parse applied twice (double-encoded JSON). extraSettings, skipQueue and bypassQueue are read BEFORE that parse, so they are silently lost for string params; hold is read after and survives.',
31
+ 'Numeric params generally tolerate numeric strings (parseInt/JS coercion downstream); boolean params are generally evaluated by JS truthiness, so the string "false" counts as true — EXCEPT the Twitch chat-mode toggles (set-slow-mode/set-subscriber-mode/set-follow-mode/set-emotes-mode), which parse the strings "false", "off", "0" and "" as false.',
32
+ "Platform-gated commands silently resolve `true` for a missing, unknown or unsupported platform (the lone exceptions are pin-message/unpin-message which resolve `false`). The `platforms` array reflects the integration managers actually invoked, not the docs.",
33
+ "`responds: 'value'` types resolve a meaningful value (variable value, points balance); every other type resolves the literal `true`.",
34
+ ];
35
+ exports.API_COMMANDS = {
36
+ 'start-lumia': {
37
+ type: 'start-lumia',
38
+ title: 'Start Lumia (lights on)',
39
+ execution: 'direct',
40
+ params: {
41
+ value: {
42
+ type: 'boolean',
43
+ required: false,
44
+ description: 'Desired power state. Omitted: toggles the current state.',
45
+ },
46
+ },
47
+ responds: 'boolean',
48
+ documented: true,
49
+ notes: 'start-lumia, stop-lumia and set-lumia share one handler that ignores which of the three types was sent: only params.value and the current power state matter. A provided value is evaluated by JS truthiness (string "false" turns Lumia ON); omitting value toggles.',
50
+ },
51
+ 'stop-lumia': {
52
+ type: 'stop-lumia',
53
+ title: 'Stop Lumia (lights off)',
54
+ execution: 'direct',
55
+ params: {
56
+ value: {
57
+ type: 'boolean',
58
+ required: false,
59
+ description: 'Desired power state. Omitted: toggles the current state.',
60
+ },
61
+ },
62
+ responds: 'boolean',
63
+ documented: true,
64
+ notes: 'Shares the start-lumia handler; the type itself is ignored, so stop-lumia without a value TOGGLES rather than stops. Send value: false to guarantee off.',
65
+ },
66
+ 'set-lumia': {
67
+ type: 'set-lumia',
68
+ title: 'Set Lumia power state',
69
+ execution: 'direct',
70
+ params: {
71
+ value: {
72
+ type: 'boolean',
73
+ required: false,
74
+ description: 'Desired power state. Omitted: toggles the current state.',
75
+ },
76
+ },
77
+ responds: 'boolean',
78
+ documented: true,
79
+ notes: 'Shares the start-lumia handler. Truthiness quirk applies: string "false" counts as ON.',
80
+ },
81
+ 'to-default': {
82
+ type: 'to-default',
83
+ title: 'Set lights to default state',
84
+ execution: 'direct',
85
+ params: {},
86
+ responds: 'boolean',
87
+ documented: true,
88
+ },
89
+ 'resume-queue': {
90
+ type: 'resume-queue',
91
+ title: 'Resume activity queue',
92
+ execution: 'direct',
93
+ params: {},
94
+ responds: 'boolean',
95
+ documented: true,
96
+ },
97
+ 'pause-queue': {
98
+ type: 'pause-queue',
99
+ title: 'Pause activity queue',
100
+ execution: 'direct',
101
+ params: {},
102
+ responds: 'boolean',
103
+ documented: true,
104
+ },
105
+ 'clear-queue': {
106
+ type: 'clear-queue',
107
+ title: 'Clear activity queue',
108
+ execution: 'direct',
109
+ params: {},
110
+ responds: 'boolean',
111
+ documented: true,
112
+ },
113
+ 'clear-cooldowns': {
114
+ type: 'clear-cooldowns',
115
+ title: 'Clear all command cooldowns',
116
+ execution: 'direct',
117
+ params: {},
118
+ responds: 'boolean',
119
+ documented: true,
120
+ notes: 'Resets every cooldown (global and per-user) via CooldownManager.reset(true).',
121
+ },
122
+ 'song-request': {
123
+ type: 'song-request',
124
+ title: 'Control song requests',
125
+ execution: 'direct',
126
+ params: {
127
+ action: {
128
+ type: 'string',
129
+ required: false,
130
+ description: "What to do. Defaults to 'add'; any unrecognized action also falls through to 'add'.",
131
+ enum: ['add', 'skip', 'next', 'play', 'pause', 'playpause', 'remove', 'clear'],
132
+ },
133
+ value: {
134
+ type: 'string',
135
+ required: false,
136
+ description: "Search query or URL for action 'add'. Not guarded: an empty query still attempts an add.",
137
+ },
138
+ username: {
139
+ type: 'string',
140
+ required: false,
141
+ description: "Requester username recorded on the request (action 'add').",
142
+ },
143
+ platform: {
144
+ type: 'string',
145
+ required: false,
146
+ description: "Requester platform recorded on the request (action 'add').",
147
+ },
148
+ songRequestId: {
149
+ type: 'string',
150
+ required: false,
151
+ description: "Id of the request to remove (action 'remove'). Omitted: removes the most recent request.",
152
+ },
153
+ play: {
154
+ type: 'boolean',
155
+ required: false,
156
+ description: "Explicit play state for action 'playpause'. Omitted: toggles.",
157
+ },
158
+ },
159
+ responds: 'boolean',
160
+ documented: false,
161
+ notes: "Requests are added with source STREAMER. 'play'/'pause'/'playpause' drive Spotify playback. In Spotify-native queue mode, remove/clear cannot touch songs already handed to Spotify's own queue.",
162
+ },
163
+ 'send-spotify-command': {
164
+ type: 'send-spotify-command',
165
+ title: 'Send Spotify action',
166
+ execution: 'direct',
167
+ params: {
168
+ type: {
169
+ type: 'string',
170
+ required: false,
171
+ description: 'Spotify action type to run. Not guarded: a missing/unknown type silently does nothing.',
172
+ enum: [
173
+ 'setPlayState',
174
+ 'seek',
175
+ 'skipToNext',
176
+ 'skipToPrevious',
177
+ 'setShuffle',
178
+ 'setVolume',
179
+ 'setRepeat',
180
+ 'shuffleAndPlay',
181
+ 'searchAndPlay',
182
+ 'searchAndPlayImmediately',
183
+ 'searchAndAddToQueue',
184
+ 'searchAndAddToPlaylist',
185
+ 'searchPublichPlaylistAndPlay',
186
+ 'searchPrivatePlaylistAndPlay',
187
+ ],
188
+ },
189
+ value: {
190
+ type: 'any',
191
+ required: false,
192
+ description: "Action payload (query, volume, position...). Defaults to ''.",
193
+ },
194
+ },
195
+ responds: 'boolean',
196
+ documented: false,
197
+ notes: "Wraps the Spotify action pipeline (HandleActions base 'spotify'). The enum value 'searchPublichPlaylistAndPlay' (typo included) is the real wire value.",
198
+ },
199
+ 'modqueue-decision': {
200
+ type: 'modqueue-decision',
201
+ title: 'Approve/deny mod queue item',
202
+ execution: 'direct',
203
+ params: {
204
+ modQueueId: {
205
+ type: 'string',
206
+ required: false,
207
+ description: 'Id of the pending mod-queue item. Not guarded: a missing/unknown id silently does nothing.',
208
+ },
209
+ approved: {
210
+ type: 'boolean',
211
+ required: false,
212
+ description: "Approve (true) or deny. Accepts boolean true or the string 'true'; anything else counts as deny.",
213
+ },
214
+ },
215
+ responds: 'boolean',
216
+ documented: false,
217
+ notes: 'Not a member of LumiaActivityCommandTypes (string-cast locally in ExternalApiHelper).',
218
+ },
219
+ 'toggle-stream-mode': {
220
+ type: 'toggle-stream-mode',
221
+ title: 'Toggle stream mode',
222
+ execution: 'direct',
223
+ params: {
224
+ value: {
225
+ type: 'boolean',
226
+ required: false,
227
+ description: 'Desired stream-mode state. Omitted: toggles.',
228
+ },
229
+ },
230
+ responds: 'boolean',
231
+ documented: true,
232
+ notes: 'Provided value is used by JS truthiness (string "false" turns stream mode ON).',
233
+ },
234
+ 'set-command-state': {
235
+ type: 'set-command-state',
236
+ title: 'Enable/disable a command',
237
+ execution: 'direct',
238
+ params: {
239
+ name: {
240
+ type: 'string',
241
+ required: true,
242
+ description: 'Command name.',
243
+ },
244
+ value: {
245
+ type: 'boolean',
246
+ required: true,
247
+ description: 'Enabled state. Guarded with isNully, so false is accepted.',
248
+ },
249
+ kind: {
250
+ type: 'string',
251
+ required: false,
252
+ description: 'Which command list the name belongs to. Defaults to chat.',
253
+ enum: ['chat', 'chatbot', 'twitch-points', 'kick-points', 'twitch-extension'],
254
+ },
255
+ },
256
+ responds: 'boolean',
257
+ documented: true,
258
+ notes: 'Rebuilds the chat command matcher (SetupCommands) after dispatch. On newer Lumia, unknown names 404 (validated per kind) and twitch-points/kick-points also sync the platform reward state while twitch-extension recompiles the extension config; older versions only handled kind chat and silently no-opped unknown names.',
259
+ },
260
+ 'create-chatbot-command': {
261
+ type: 'create-chatbot-command',
262
+ title: 'Create a chatbot command',
263
+ execution: 'direct',
264
+ params: {
265
+ name: {
266
+ type: 'string',
267
+ required: true,
268
+ description: 'Command name without the chat prefix; slugified (lowercase, spaces to dashes).',
269
+ },
270
+ message: {
271
+ type: 'string',
272
+ required: true,
273
+ description: 'The chatbot reply text. Supports {{variables}}.',
274
+ },
275
+ description: { type: 'string', required: false, description: 'Internal description.' },
276
+ alias: { type: 'array', required: false, description: 'Alternate trigger names, slugified.' },
277
+ showInCommandsList: { type: 'boolean', required: false, description: 'Show on the public commands page. Defaults to false.' },
278
+ on: { type: 'boolean', required: false, description: 'Enabled state. Defaults to true.' },
279
+ cooldownDuration: { type: 'number', required: false, description: 'Cooldown in milliseconds. Defaults to 5000.' },
280
+ },
281
+ responds: 'value',
282
+ documented: true,
283
+ notes: 'Newer Lumia only. Built from the base chatbot command; 409 already_exists on case-insensitive name collision; chatbot commands are unlimited on all plans. Runs SetupCommands and schedules a state persist.',
284
+ },
285
+ 'update-chatbot-command': {
286
+ type: 'update-chatbot-command',
287
+ title: 'Update a chatbot command',
288
+ execution: 'direct',
289
+ params: {
290
+ name: { type: 'string', required: true, description: 'Existing command name (case-insensitive).' },
291
+ message: { type: 'string', required: false, description: 'New reply text.' },
292
+ newName: { type: 'string', required: false, description: 'Rename to this (refused for system commands).' },
293
+ description: { type: 'string', required: false, description: 'New description.' },
294
+ alias: { type: 'array', required: false, description: 'Replacement alias list.' },
295
+ showInCommandsList: { type: 'boolean', required: false, description: 'Public list visibility.' },
296
+ on: { type: 'boolean', required: false, description: 'Enabled state.' },
297
+ cooldownDuration: { type: 'number', required: false, description: 'Cooldown in milliseconds.' },
298
+ },
299
+ responds: 'value',
300
+ documented: true,
301
+ notes: 'Newer Lumia only. Merges onto the existing command; 404 not_found when missing, 409 on rename collision, 403 system_command when renaming system commands.',
302
+ },
303
+ 'delete-chatbot-command': {
304
+ type: 'delete-chatbot-command',
305
+ title: 'Delete a chatbot command',
306
+ execution: 'direct',
307
+ params: {
308
+ name: { type: 'string', required: true, description: 'Existing command name (case-insensitive).' },
309
+ },
310
+ responds: 'value',
311
+ documented: true,
312
+ notes: 'Newer Lumia only. 404 not_found when missing; 403 system_command for built-in system commands (the reducer itself has no such guard).',
313
+ },
314
+ 'set-folder-state': {
315
+ type: 'set-folder-state',
316
+ title: 'Enable/disable a command folder',
317
+ execution: 'direct',
318
+ params: {
319
+ name: {
320
+ type: 'string',
321
+ required: true,
322
+ description: 'Folder name (folder ids equal their names).',
323
+ },
324
+ value: {
325
+ type: 'boolean',
326
+ required: true,
327
+ description: 'Enabled state. Guarded with isNully, so false is accepted.',
328
+ },
329
+ },
330
+ responds: 'boolean',
331
+ documented: true,
332
+ notes: 'Unknown folder silently no-ops. Also reconciles Twitch extension commands and Twitch channel-point rewards contained in the folder.',
333
+ },
334
+ 'update-variable-value': {
335
+ type: 'update-variable-value',
336
+ title: 'Update variable value',
337
+ execution: 'direct',
338
+ params: {
339
+ name: {
340
+ type: 'string',
341
+ required: true,
342
+ description: 'Variable name.',
343
+ },
344
+ value: {
345
+ type: 'any',
346
+ required: true,
347
+ description: "New value (string/number/boolean/object). Guarded with isNully, so 0/''/false are accepted.",
348
+ },
349
+ },
350
+ responds: 'boolean',
351
+ documented: true,
352
+ notes: "Creates the variable (non-system) if missing. Counter variables run the value through CalculateCounter ('+N'/'-N'/'=N'/bare number); a counter whose current value is empty increments by 1 regardless of the sent value. Values exceeding the size cap are dropped with a warning toast.",
353
+ },
354
+ 'set-counter-value': {
355
+ type: 'set-counter-value',
356
+ title: 'Set counter to absolute value',
357
+ execution: 'direct',
358
+ params: {
359
+ name: {
360
+ type: 'string',
361
+ required: true,
362
+ description: 'Counter variable name.',
363
+ },
364
+ value: {
365
+ type: 'number',
366
+ required: true,
367
+ description: 'Absolute value to set. Guarded with isNully, so 0 is accepted.',
368
+ },
369
+ },
370
+ responds: 'boolean',
371
+ documented: false,
372
+ notes: "Creates the variable and flags it as a counter if needed, then applies '=' + value for an absolute set (bypassing counter add semantics).",
373
+ },
374
+ 'get-variable-value': {
375
+ type: 'get-variable-value',
376
+ title: 'Get variable value',
377
+ execution: 'direct',
378
+ params: {
379
+ name: {
380
+ type: 'string',
381
+ required: true,
382
+ description: 'Variable name.',
383
+ },
384
+ },
385
+ responds: 'value',
386
+ documented: true,
387
+ notes: 'Resolves the raw stored value. An unknown variable resolves undefined (not an error).',
388
+ },
389
+ 'start-fuze': {
390
+ type: 'start-fuze',
391
+ title: 'Start Fuze',
392
+ execution: 'queue',
393
+ params: {
394
+ value: {
395
+ type: 'boolean',
396
+ required: false,
397
+ description: 'Desired Fuze state. Omitted: toggles the current state.',
398
+ },
399
+ },
400
+ responds: 'boolean',
401
+ documented: true,
402
+ notes: 'start-fuze, stop-fuze and toggle-fuze share one handler that ignores the requested type: a provided value (JS truthiness) picks start/stop, an omitted value toggles. Executed via the activity queue.',
403
+ },
404
+ 'stop-fuze': {
405
+ type: 'stop-fuze',
406
+ title: 'Stop Fuze',
407
+ execution: 'queue',
408
+ params: {
409
+ value: {
410
+ type: 'boolean',
411
+ required: false,
412
+ description: 'Desired Fuze state. Omitted: toggles the current state.',
413
+ },
414
+ },
415
+ responds: 'boolean',
416
+ documented: true,
417
+ notes: 'Shares the start-fuze handler; stop-fuze without a value TOGGLES rather than stops. Send value: false to guarantee off.',
418
+ },
419
+ 'toggle-fuze': {
420
+ type: 'toggle-fuze',
421
+ title: 'Toggle Fuze',
422
+ execution: 'queue',
423
+ params: {
424
+ value: {
425
+ type: 'boolean',
426
+ required: false,
427
+ description: 'Desired Fuze state. Omitted: toggles the current state.',
428
+ },
429
+ },
430
+ responds: 'boolean',
431
+ documented: true,
432
+ notes: 'Shares the start-fuze handler.',
433
+ },
434
+ 'fuze-audio-sensitivity': {
435
+ type: 'fuze-audio-sensitivity',
436
+ title: 'Set Fuze audio sensitivity',
437
+ execution: 'queue',
438
+ params: {
439
+ value: {
440
+ type: 'number',
441
+ required: false,
442
+ description: 'Sensitivity value. Not guarded: a missing value queues an activity with undefined sensitivity.',
443
+ },
444
+ },
445
+ responds: 'boolean',
446
+ documented: true,
447
+ },
448
+ chat: {
449
+ type: 'chat',
450
+ title: 'Inject chat message',
451
+ execution: 'queue',
452
+ params: {
453
+ value: {
454
+ type: 'any',
455
+ required: true,
456
+ description: 'Either the message string (site taken from the platform param) or an object { site, message, user?, tiers?, extraSettings?, id?, userId?, avatar?, createdTime?, bridgeHeartbeat? }. Guarded: throws unless both site and message resolve (bridgeHeartbeat exempts facebook).',
457
+ },
458
+ platform: {
459
+ type: 'string',
460
+ required: false,
461
+ description: 'Fallback site when value is a plain string or the object omits site.',
462
+ },
463
+ username: {
464
+ type: 'string',
465
+ required: false,
466
+ description: 'Fallback username when the value object omits user/username.',
467
+ },
468
+ },
469
+ responds: 'boolean',
470
+ documented: false,
471
+ notes: "Simulates an INCOMING chat message run through the full chat pipeline (CheckMessage), so it can trigger chat commands and moderation. The default user is the broadcaster with every tier flag true (mod/sub/follower/member) unless overridden via value.tiers. site 'facebook' bypasses the queue and injects directly into the chatbox (chrome-extension bridge; bridgeHeartbeat only marks the bridge alive). Any connected chat platform key is accepted as site.",
472
+ },
473
+ 'chat-command': {
474
+ type: 'chat-command',
475
+ title: 'Trigger chat command',
476
+ execution: 'queue',
477
+ params: {
478
+ value: {
479
+ type: 'string',
480
+ required: false,
481
+ description: 'Command name to trigger. Not guarded: a missing/unknown command fails later inside the queue processor while the API has already resolved success.',
482
+ },
483
+ },
484
+ responds: 'boolean',
485
+ documented: true,
486
+ notes: 'The command name is mirrored into extraSettings.command. Pass template variables (username, message, ...) through extraSettings.',
487
+ },
488
+ 'chatbot-command': {
489
+ type: 'chatbot-command',
490
+ title: 'Trigger chatbot command',
491
+ execution: 'queue',
492
+ params: {
493
+ value: {
494
+ type: 'string',
495
+ required: false,
496
+ description: 'Chatbot command name to trigger. Not guarded (see chat-command).',
497
+ },
498
+ },
499
+ responds: 'boolean',
500
+ documented: true,
501
+ notes: 'Same handler as chat-command; extraSettings passes template variables.',
502
+ },
503
+ 'twitch-points': {
504
+ type: 'twitch-points',
505
+ title: 'Trigger Twitch channel points command',
506
+ execution: 'queue',
507
+ params: {
508
+ value: {
509
+ type: 'string',
510
+ required: false,
511
+ description: 'Name of the Lumia Twitch points command to trigger. Not guarded (see chat-command).',
512
+ },
513
+ },
514
+ responds: 'boolean',
515
+ documented: true,
516
+ notes: 'Triggers the Lumia command configured for the reward; it does not redeem anything on Twitch itself.',
517
+ },
518
+ 'twitch-extension': {
519
+ type: 'twitch-extension',
520
+ title: 'Trigger Twitch extension command',
521
+ execution: 'queue',
522
+ params: {
523
+ value: {
524
+ type: 'string',
525
+ required: false,
526
+ description: 'Name of the Lumia Twitch extension command to trigger. Not guarded (see chat-command).',
527
+ },
528
+ },
529
+ responds: 'boolean',
530
+ documented: true,
531
+ },
532
+ 'kick-points': {
533
+ type: 'kick-points',
534
+ title: 'Trigger Kick points command',
535
+ execution: 'queue',
536
+ params: {
537
+ value: {
538
+ type: 'string',
539
+ required: false,
540
+ description: 'Name of the Lumia Kick points command to trigger. Not guarded (see chat-command).',
541
+ },
542
+ },
543
+ responds: 'boolean',
544
+ documented: true,
545
+ },
546
+ 'studio-scene': {
547
+ type: 'studio-scene',
548
+ title: 'Trigger studio scene',
549
+ execution: 'queue',
550
+ params: {
551
+ value: {
552
+ type: 'string',
553
+ required: false,
554
+ description: 'Scene name (matched case-insensitively). Not guarded: an unknown scene errors during queue execution, after the API already resolved success.',
555
+ },
556
+ duration: {
557
+ type: 'number',
558
+ required: false,
559
+ description: 'How long the scene holds before reverting to default, in milliseconds. Omitted: the scene stays (no reset).',
560
+ },
561
+ },
562
+ responds: 'boolean',
563
+ documented: true,
564
+ },
565
+ 'studio-theme': {
566
+ type: 'studio-theme',
567
+ title: 'Trigger studio theme',
568
+ execution: 'queue',
569
+ params: {
570
+ value: {
571
+ type: 'string',
572
+ required: false,
573
+ description: 'Theme name (matched case-insensitively). Not guarded (see studio-scene).',
574
+ },
575
+ duration: {
576
+ type: 'number',
577
+ required: false,
578
+ description: 'How long the theme holds before reverting to default, in milliseconds. Omitted: stays active.',
579
+ },
580
+ },
581
+ responds: 'boolean',
582
+ documented: true,
583
+ },
584
+ 'studio-animation': {
585
+ type: 'studio-animation',
586
+ title: 'Trigger studio animation',
587
+ execution: 'queue',
588
+ params: {
589
+ value: {
590
+ type: 'string',
591
+ required: false,
592
+ description: 'Animation name (matched case-insensitively). The params OBJECT itself is guarded (throws when params is missing/not an object), the value key is not.',
593
+ },
594
+ cycles: {
595
+ type: 'number',
596
+ required: false,
597
+ description: 'Requested number of cycles. Currently IGNORED due to a runtime bug (see notes); effectively always 1 cycle.',
598
+ },
599
+ },
600
+ responds: 'boolean',
601
+ documented: true,
602
+ notes: "Runtime bug: cycles is computed as `isTruly(cycles) || 1`, which yields true or 1 — the requested count never reaches the engine, so every request runs 1 effective cycle. Timing is forced to CYCLE with reset. The REST docs advertise a `duration` param for studio-animation that the handler never reads.",
603
+ },
604
+ 'chatbot-message': {
605
+ type: 'chatbot-message',
606
+ title: 'Send chatbot message',
607
+ execution: 'direct',
608
+ params: {
609
+ value: {
610
+ type: 'string',
611
+ required: true,
612
+ description: 'Message to send. Template variables ({{...}}) are resolved before sending.',
613
+ },
614
+ platform: {
615
+ type: 'string',
616
+ required: false,
617
+ description: "Target platform. Omitted (or 'lumiastream'/'lumia'): broadcasts to every connected chatbot client, including plugin chatbots.",
618
+ },
619
+ userToChatAs: {
620
+ type: 'string',
621
+ required: false,
622
+ description: "Which account sends the message (e.g. 'bot' or 'self').",
623
+ },
624
+ replyParentMessageId: {
625
+ type: 'string',
626
+ required: false,
627
+ description: 'Message id to reply to (threaded reply, Twitch). Forwarded as extraSettings.messageId.',
628
+ },
629
+ },
630
+ platforms: ['twitch', 'youtube', 'kick', 'facebook', 'discord'],
631
+ responds: 'boolean',
632
+ documented: true,
633
+ notes: 'Requires the chatbot connection to be on; otherwise it silently no-ops. A site without a native client is routed to a connected plugin chatbot when available. The REST docs list only twitch/youtube/facebook — stale versus the runtime.',
634
+ },
635
+ tts: {
636
+ type: 'tts',
637
+ title: 'Text to speech',
638
+ execution: 'direct',
639
+ params: {
640
+ value: {
641
+ type: 'string',
642
+ required: true,
643
+ description: 'Text to speak.',
644
+ },
645
+ voice: {
646
+ type: 'string',
647
+ required: false,
648
+ description: 'Voice name. Omitted: the default voice.',
649
+ },
650
+ volume: {
651
+ type: 'number',
652
+ required: false,
653
+ description: 'Volume 0-100 (parseInt applied). Defaults to 100.',
654
+ },
655
+ },
656
+ responds: 'boolean',
657
+ documented: true,
658
+ },
659
+ 'hex-color': {
660
+ type: 'hex-color',
661
+ title: 'Send hex color to lights',
662
+ execution: 'queue',
663
+ params: {
664
+ value: {
665
+ type: 'string',
666
+ required: false,
667
+ description: "Hex color (e.g. '#0000ff'). The params OBJECT itself is guarded, the value key is not.",
668
+ },
669
+ brightness: {
670
+ type: 'number',
671
+ required: false,
672
+ description: 'Brightness 0-100.',
673
+ },
674
+ transition: {
675
+ type: 'number',
676
+ required: false,
677
+ description: 'Fade/transition time in milliseconds.',
678
+ },
679
+ duration: {
680
+ type: 'number',
681
+ required: false,
682
+ description: 'How long the color holds before reverting to default, in milliseconds.',
683
+ },
684
+ lights: {
685
+ type: 'array',
686
+ required: false,
687
+ description: "Target lights: [{ type: '<integration key, e.g. hue>', value: '<light id>' }]. The id may also be sent under an `id` key. Omitted: all default lights.",
688
+ },
689
+ },
690
+ responds: 'boolean',
691
+ documented: true,
692
+ notes: 'Entries in lights missing a type are dropped; an empty/omitted array targets the default lights (mapper returns null).',
693
+ },
694
+ 'rgb-color': {
695
+ type: 'rgb-color',
696
+ title: 'Send RGB color to lights',
697
+ execution: 'queue',
698
+ params: {
699
+ value: {
700
+ type: 'array',
701
+ required: false,
702
+ description: 'RGB triple [r, g, b], each 0-255. The params OBJECT itself is guarded, the value key is not.',
703
+ },
704
+ brightness: {
705
+ type: 'number',
706
+ required: false,
707
+ description: 'Brightness 0-100.',
708
+ },
709
+ transition: {
710
+ type: 'number',
711
+ required: false,
712
+ description: 'Fade/transition time in milliseconds.',
713
+ },
714
+ duration: {
715
+ type: 'number',
716
+ required: false,
717
+ description: 'How long the color holds before reverting to default, in milliseconds.',
718
+ },
719
+ lights: {
720
+ type: 'array',
721
+ required: false,
722
+ description: 'Target lights; same shape as hex-color.',
723
+ },
724
+ },
725
+ responds: 'boolean',
726
+ documented: true,
727
+ },
728
+ alert: {
729
+ type: 'alert',
730
+ title: 'Trigger alert',
731
+ execution: 'queue',
732
+ params: {
733
+ value: {
734
+ type: 'string',
735
+ required: true,
736
+ description: "Alert key, e.g. 'twitch-follower' (LumiaAlertValues).",
737
+ },
738
+ },
739
+ responds: 'boolean',
740
+ documented: true,
741
+ notes: 'Use extraSettings to supply alert variables (username, amount, message, ...) and to force a specific variation.',
742
+ },
743
+ 'overlay-set-visibility': {
744
+ type: 'overlay-set-visibility',
745
+ title: 'Set overlay visibility',
746
+ execution: 'direct',
747
+ params: {
748
+ layer: {
749
+ type: 'string',
750
+ required: false,
751
+ description: 'The OVERLAY to target (overlay name or uuid), despite the param being called layer. Not guarded.',
752
+ },
753
+ value: {
754
+ type: 'boolean',
755
+ required: false,
756
+ description: 'Visible state (coerced with !!, so a missing value hides).',
757
+ },
758
+ },
759
+ responds: 'boolean',
760
+ documented: true,
761
+ },
762
+ 'overlay-set-layer-visibility': {
763
+ type: 'overlay-set-layer-visibility',
764
+ title: 'Set overlay layer visibility',
765
+ execution: 'direct',
766
+ params: {
767
+ layer: {
768
+ type: 'string',
769
+ required: false,
770
+ description: 'Layer id (from the layer settings sidebar). Not guarded.',
771
+ },
772
+ value: {
773
+ type: 'boolean',
774
+ required: false,
775
+ description: 'Visible state (coerced with !!).',
776
+ },
777
+ },
778
+ responds: 'boolean',
779
+ documented: true,
780
+ },
781
+ 'overlay-set-layer-position': {
782
+ type: 'overlay-set-layer-position',
783
+ title: 'Set overlay layer position',
784
+ execution: 'direct',
785
+ params: {
786
+ layer: {
787
+ type: 'string',
788
+ required: false,
789
+ description: 'Layer id. Not guarded.',
790
+ },
791
+ x: {
792
+ type: 'number',
793
+ required: false,
794
+ description: 'X position (number or numeric string).',
795
+ },
796
+ y: {
797
+ type: 'number',
798
+ required: false,
799
+ description: 'Y position (number or numeric string).',
800
+ },
801
+ transitionTime: {
802
+ type: 'number',
803
+ required: false,
804
+ description: 'Transition duration in milliseconds.',
805
+ },
806
+ transitionInterpolation: {
807
+ type: 'string',
808
+ required: false,
809
+ description: 'Transition easing.',
810
+ enum: ['none', 'ease', 'linear', 'ease-in', 'ease-out', 'ease-in-out', 'cubic-bezier'],
811
+ },
812
+ transitionCubicBezier: {
813
+ type: 'string',
814
+ required: false,
815
+ description: "Cubic-bezier control points when transitionInterpolation is 'cubic-bezier'.",
816
+ },
817
+ },
818
+ responds: 'boolean',
819
+ documented: true,
820
+ notes: 'x/y are template-interpolated into "x,y"; omitting either produces the literal string "undefined" in the payload.',
821
+ },
822
+ 'overlay-set-layer-size': {
823
+ type: 'overlay-set-layer-size',
824
+ title: 'Set overlay layer size',
825
+ execution: 'direct',
826
+ params: {
827
+ layer: {
828
+ type: 'string',
829
+ required: false,
830
+ description: 'Layer id. Not guarded.',
831
+ },
832
+ width: {
833
+ type: 'number',
834
+ required: false,
835
+ description: 'Width (number or numeric string). Omitted: empty string is sent (keeps the current width).',
836
+ },
837
+ height: {
838
+ type: 'number',
839
+ required: false,
840
+ description: 'Height (number or numeric string). Omitted: empty string is sent (keeps the current height).',
841
+ },
842
+ transitionTime: {
843
+ type: 'number',
844
+ required: false,
845
+ description: 'Transition duration in milliseconds.',
846
+ },
847
+ transitionInterpolation: {
848
+ type: 'string',
849
+ required: false,
850
+ description: 'Transition easing.',
851
+ enum: ['none', 'ease', 'linear', 'ease-in', 'ease-out', 'ease-in-out', 'cubic-bezier'],
852
+ },
853
+ transitionCubicBezier: {
854
+ type: 'string',
855
+ required: false,
856
+ description: "Cubic-bezier control points when transitionInterpolation is 'cubic-bezier'.",
857
+ },
858
+ },
859
+ responds: 'boolean',
860
+ documented: false,
861
+ notes: 'Unlike position, missing dimensions default to empty strings rather than "undefined". Absent from the REST docs (control-overlays.md covers visibility/position/content only).',
862
+ },
863
+ 'overlay-set-content': {
864
+ type: 'overlay-set-content',
865
+ title: 'Set overlay layer content',
866
+ execution: 'direct',
867
+ params: {
868
+ layer: {
869
+ type: 'string',
870
+ required: false,
871
+ description: 'Layer id. Not guarded.',
872
+ },
873
+ content: {
874
+ type: 'string',
875
+ required: false,
876
+ description: 'New text content for the layer.',
877
+ },
878
+ },
879
+ responds: 'boolean',
880
+ documented: true,
881
+ },
882
+ 'hud-toggle': {
883
+ type: 'hud-toggle',
884
+ title: 'Toggle HUD',
885
+ execution: 'direct',
886
+ params: {},
887
+ responds: 'boolean',
888
+ documented: false,
889
+ },
890
+ 'hud-volume-set': {
891
+ type: 'hud-volume-set',
892
+ title: 'Set HUD volume',
893
+ execution: 'direct',
894
+ params: {
895
+ value: {
896
+ type: 'number',
897
+ required: false,
898
+ description: 'Volume level. Not guarded.',
899
+ },
900
+ },
901
+ responds: 'boolean',
902
+ documented: false,
903
+ },
904
+ 'hud-opacity-set': {
905
+ type: 'hud-opacity-set',
906
+ title: 'Set HUD opacity',
907
+ execution: 'direct',
908
+ params: {
909
+ value: {
910
+ type: 'number',
911
+ required: false,
912
+ description: 'Opacity level. Not guarded.',
913
+ },
914
+ },
915
+ responds: 'boolean',
916
+ documented: false,
917
+ notes: 'Internally the opacity value travels under the `volume` key of the overlay action payload.',
918
+ },
919
+ 'hud-overlay-change': {
920
+ type: 'hud-overlay-change',
921
+ title: 'Change HUD overlay',
922
+ execution: 'direct',
923
+ params: {
924
+ value: {
925
+ type: 'string',
926
+ required: false,
927
+ description: 'Overlay name or id to display in the HUD. Not guarded.',
928
+ },
929
+ },
930
+ responds: 'boolean',
931
+ documented: false,
932
+ },
933
+ 'change-stream-title': {
934
+ type: 'change-stream-title',
935
+ title: 'Change stream title',
936
+ execution: 'direct',
937
+ params: {
938
+ platform: {
939
+ type: 'string',
940
+ required: false,
941
+ description: 'Target platform. Not guarded: missing/other platforms silently resolve true.',
942
+ enum: ['twitch', 'youtube', 'kick'],
943
+ },
944
+ value: {
945
+ type: 'string',
946
+ required: false,
947
+ description: 'New stream title. Template variables are resolved (extraSettings supplies values).',
948
+ },
949
+ },
950
+ platforms: ['twitch', 'youtube', 'kick'],
951
+ responds: 'boolean',
952
+ documented: false,
953
+ notes: 'On YouTube the value updates the live broadcast title (the description is left untouched).',
954
+ },
955
+ 'change-current-category': {
956
+ type: 'change-current-category',
957
+ title: 'Change stream category/game',
958
+ execution: 'direct',
959
+ params: {
960
+ platform: {
961
+ type: 'string',
962
+ required: false,
963
+ description: 'Target platform. Not guarded.',
964
+ enum: ['twitch', 'kick'],
965
+ },
966
+ value: {
967
+ type: 'string',
968
+ required: false,
969
+ description: 'Category/game name (searched on the platform).',
970
+ },
971
+ },
972
+ platforms: ['twitch', 'kick'],
973
+ responds: 'boolean',
974
+ documented: false,
975
+ notes: 'YouTube is NOT supported (silently resolves true).',
976
+ },
977
+ 'create-stream-marker': {
978
+ type: 'create-stream-marker',
979
+ title: 'Create stream marker',
980
+ execution: 'direct',
981
+ params: {
982
+ platform: {
983
+ type: 'string',
984
+ required: false,
985
+ description: 'Must be twitch; anything else silently resolves true.',
986
+ enum: ['twitch'],
987
+ },
988
+ value: {
989
+ type: 'string',
990
+ required: false,
991
+ description: 'Marker description.',
992
+ },
993
+ },
994
+ platforms: ['twitch'],
995
+ responds: 'boolean',
996
+ documented: false,
997
+ notes: 'Requires channel:manage:broadcast; Twitch rejects markers while the channel is offline (failure invisible to the caller).',
998
+ },
999
+ 'run-commercial': {
1000
+ type: 'run-commercial',
1001
+ title: 'Run commercial',
1002
+ execution: 'direct',
1003
+ params: {
1004
+ platform: {
1005
+ type: 'string',
1006
+ required: false,
1007
+ description: 'Must be twitch; anything else silently resolves true.',
1008
+ enum: ['twitch'],
1009
+ },
1010
+ duration: {
1011
+ type: 'number',
1012
+ required: false,
1013
+ description: 'Commercial length in seconds (Twitch accepts 30–180). Falls back to params.value when omitted.',
1014
+ },
1015
+ value: {
1016
+ type: 'number',
1017
+ required: false,
1018
+ description: 'Fallback for duration (used only when params.duration is absent).',
1019
+ },
1020
+ },
1021
+ platforms: ['twitch'],
1022
+ responds: 'boolean',
1023
+ documented: false,
1024
+ notes: 'Functionally requires a duration (params.duration or params.value): without one Helix rejects the request for a missing length while the API still resolves true. Requires channel:edit:commercial.',
1025
+ },
1026
+ 'set-slow-mode': {
1027
+ type: 'set-slow-mode',
1028
+ title: 'Set slow mode',
1029
+ execution: 'direct',
1030
+ params: {
1031
+ platform: {
1032
+ type: 'string',
1033
+ required: false,
1034
+ description: 'Must be twitch; anything else silently resolves true.',
1035
+ enum: ['twitch'],
1036
+ },
1037
+ value: {
1038
+ type: 'boolean',
1039
+ required: false,
1040
+ description: "Enable/disable slow mode. Strings 'false', 'off' and '0' count as false; omitted counts as false (disables).",
1041
+ },
1042
+ duration: {
1043
+ type: 'number',
1044
+ required: false,
1045
+ description: 'Wait time between messages in seconds (Twitch accepts 3–120). Defaults to 30 in the manager.',
1046
+ },
1047
+ },
1048
+ platforms: ['twitch'],
1049
+ responds: 'boolean',
1050
+ documented: false,
1051
+ notes: 'Requires moderator:manage:chat_settings. extraSettings is used solely for variable resolution, not settings.',
1052
+ },
1053
+ 'set-subscriber-mode': {
1054
+ type: 'set-subscriber-mode',
1055
+ title: 'Set subscriber-only mode',
1056
+ execution: 'direct',
1057
+ params: {
1058
+ platform: {
1059
+ type: 'string',
1060
+ required: false,
1061
+ description: 'Must be twitch; anything else silently resolves true.',
1062
+ enum: ['twitch'],
1063
+ },
1064
+ value: {
1065
+ type: 'boolean',
1066
+ required: false,
1067
+ description: "Enable/disable subscriber-only mode. Strings 'false', 'off' and '0' count as false; omitted counts as false (disables).",
1068
+ },
1069
+ },
1070
+ platforms: ['twitch'],
1071
+ responds: 'boolean',
1072
+ documented: false,
1073
+ notes: 'Requires moderator:manage:chat_settings.',
1074
+ },
1075
+ 'set-follow-mode': {
1076
+ type: 'set-follow-mode',
1077
+ title: 'Set follower-only mode',
1078
+ execution: 'direct',
1079
+ params: {
1080
+ platform: {
1081
+ type: 'string',
1082
+ required: false,
1083
+ description: 'Must be twitch; anything else silently resolves true.',
1084
+ enum: ['twitch'],
1085
+ },
1086
+ value: {
1087
+ type: 'boolean',
1088
+ required: false,
1089
+ description: "Enable/disable follower-only mode. Strings 'false', 'off' and '0' count as false; omitted counts as false (disables).",
1090
+ },
1091
+ duration: {
1092
+ type: 'number',
1093
+ required: false,
1094
+ description: 'Minimum follow age in MINUTES (Twitch accepts 0–129600). Omitted: Twitch defaults to 0 (any follower).',
1095
+ },
1096
+ },
1097
+ platforms: ['twitch'],
1098
+ responds: 'boolean',
1099
+ documented: false,
1100
+ notes: 'Requires moderator:manage:chat_settings.',
1101
+ },
1102
+ 'set-emotes-mode': {
1103
+ type: 'set-emotes-mode',
1104
+ title: 'Set emote-only mode',
1105
+ execution: 'direct',
1106
+ params: {
1107
+ platform: {
1108
+ type: 'string',
1109
+ required: false,
1110
+ description: 'Must be twitch; anything else silently resolves true.',
1111
+ enum: ['twitch'],
1112
+ },
1113
+ value: {
1114
+ type: 'boolean',
1115
+ required: false,
1116
+ description: "Enable/disable emote-only mode. Strings 'false', 'off' and '0' count as false; omitted counts as false (disables).",
1117
+ },
1118
+ },
1119
+ platforms: ['twitch'],
1120
+ responds: 'boolean',
1121
+ documented: false,
1122
+ notes: 'Requires moderator:manage:chat_settings.',
1123
+ },
1124
+ 'send-announcement': {
1125
+ type: 'send-announcement',
1126
+ title: 'Send chat announcement',
1127
+ execution: 'direct',
1128
+ params: {
1129
+ platform: {
1130
+ type: 'string',
1131
+ required: false,
1132
+ description: 'Must be twitch; anything else silently resolves true.',
1133
+ enum: ['twitch'],
1134
+ },
1135
+ value: {
1136
+ type: 'string',
1137
+ required: false,
1138
+ description: 'Announcement text (template variables resolved).',
1139
+ },
1140
+ },
1141
+ platforms: ['twitch'],
1142
+ responds: 'boolean',
1143
+ documented: false,
1144
+ notes: "Announcement color is read from the manager's value.color, which the API layer never sets — always defaults to 'primary'. Requires moderator:manage:announcements.",
1145
+ },
1146
+ clip: {
1147
+ type: 'clip',
1148
+ title: 'Create clip',
1149
+ execution: 'direct',
1150
+ params: {
1151
+ platform: {
1152
+ type: 'string',
1153
+ required: false,
1154
+ description: 'Must be twitch; anything else silently resolves true.',
1155
+ enum: ['twitch'],
1156
+ },
1157
+ value: {
1158
+ type: 'string',
1159
+ required: false,
1160
+ description: 'Forwarded as message but IGNORED by the clip handler.',
1161
+ },
1162
+ },
1163
+ platforms: ['twitch'],
1164
+ responds: 'boolean',
1165
+ documented: false,
1166
+ notes: 'Creates a clip with default settings; the manager supports title/duration but the API layer never plumbs them. The resulting id/url/thumbnail are stored in the twitch_last_clip_id / twitch_last_clip_url / twitch_last_clip_thumbnail_url variables — the response itself carries nothing. Requires clips:edit.',
1167
+ },
1168
+ 'create-poll': {
1169
+ type: 'create-poll',
1170
+ title: 'Create Twitch poll',
1171
+ execution: 'direct',
1172
+ params: {
1173
+ platform: {
1174
+ type: 'string',
1175
+ required: false,
1176
+ description: 'Must be twitch; anything else silently resolves true.',
1177
+ enum: ['twitch'],
1178
+ },
1179
+ name: {
1180
+ type: 'string',
1181
+ required: false,
1182
+ description: 'Poll title.',
1183
+ },
1184
+ value: {
1185
+ type: 'string',
1186
+ required: false,
1187
+ description: 'Comma-separated choice titles (NOT trimmed — spaces after commas are kept).',
1188
+ },
1189
+ duration: {
1190
+ type: 'number',
1191
+ required: false,
1192
+ description: 'Poll duration in seconds (Twitch accepts 15–1800). Functionally mandatory: Helix rejects the poll without it.',
1193
+ },
1194
+ },
1195
+ platforms: ['twitch'],
1196
+ responds: 'boolean',
1197
+ documented: false,
1198
+ notes: 'When creation succeeds the poll id is stored in the twitch_current_poll_id variable. Requires channel:manage:polls.',
1199
+ },
1200
+ 'end-poll': {
1201
+ type: 'end-poll',
1202
+ title: 'End Twitch poll',
1203
+ execution: 'direct',
1204
+ params: {
1205
+ platform: {
1206
+ type: 'string',
1207
+ required: false,
1208
+ description: 'Must be twitch; anything else silently resolves true.',
1209
+ enum: ['twitch'],
1210
+ },
1211
+ name: {
1212
+ type: 'string',
1213
+ required: false,
1214
+ description: "Poll status: 'ARCHIVED' (default) or 'TERMINATED'.",
1215
+ enum: ['ARCHIVED', 'TERMINATED'],
1216
+ },
1217
+ value: {
1218
+ type: 'string',
1219
+ required: false,
1220
+ description: 'Forwarded as message but IGNORED by the end-poll handler.',
1221
+ },
1222
+ },
1223
+ platforms: ['twitch'],
1224
+ responds: 'boolean',
1225
+ documented: false,
1226
+ notes: 'Ends the poll whose id is stored in the twitch_current_poll_id variable (i.e. the last poll created through Lumia). Requires channel:manage:polls.',
1227
+ },
1228
+ 'create-prediction': {
1229
+ type: 'create-prediction',
1230
+ title: 'Create Twitch prediction',
1231
+ execution: 'direct',
1232
+ params: {
1233
+ platform: {
1234
+ type: 'string',
1235
+ required: false,
1236
+ description: 'Must be twitch; anything else silently resolves true.',
1237
+ enum: ['twitch'],
1238
+ },
1239
+ name: {
1240
+ type: 'string',
1241
+ required: false,
1242
+ description: 'Prediction title.',
1243
+ },
1244
+ value: {
1245
+ type: 'string',
1246
+ required: false,
1247
+ description: 'Comma-separated outcome titles (trimmed).',
1248
+ },
1249
+ duration: {
1250
+ type: 'number',
1251
+ required: false,
1252
+ description: 'Prediction window in seconds (Twitch accepts 30–1800). Functionally mandatory: Helix rejects the prediction without it.',
1253
+ },
1254
+ },
1255
+ platforms: ['twitch'],
1256
+ responds: 'boolean',
1257
+ documented: false,
1258
+ notes: 'On success the id and outcomes are cached (twitch_current_prediction_id) for end-prediction matching. Requires channel:manage:predictions.',
1259
+ },
1260
+ 'end-prediction': {
1261
+ type: 'end-prediction',
1262
+ title: 'End Twitch prediction',
1263
+ execution: 'direct',
1264
+ params: {
1265
+ platform: {
1266
+ type: 'string',
1267
+ required: false,
1268
+ description: 'Must be twitch; anything else silently resolves true.',
1269
+ enum: ['twitch'],
1270
+ },
1271
+ name: {
1272
+ type: 'string',
1273
+ required: false,
1274
+ description: "Prediction status: 'RESOLVED' (default) or 'CANCELED'.",
1275
+ enum: ['RESOLVED', 'CANCELED'],
1276
+ },
1277
+ value: {
1278
+ type: 'string',
1279
+ required: false,
1280
+ description: 'Winning outcome TITLE, matched case-insensitively against the outcomes cached at creation time. Required when resolving; ignored when canceling.',
1281
+ },
1282
+ },
1283
+ platforms: ['twitch'],
1284
+ responds: 'boolean',
1285
+ documented: false,
1286
+ notes: 'Ends the prediction stored in twitch_current_prediction_id. A non-matching outcome title aborts in the manager (toast) while the API resolves true; RESOLVED without a winning outcome id is rejected by Helix. Requires channel:manage:predictions.',
1287
+ },
1288
+ 'clear-chat': {
1289
+ type: 'clear-chat',
1290
+ title: 'Clear chat',
1291
+ execution: 'direct',
1292
+ params: {
1293
+ platform: {
1294
+ type: 'string',
1295
+ required: false,
1296
+ description: 'Must be twitch; anything else silently resolves true.',
1297
+ enum: ['twitch'],
1298
+ },
1299
+ value: {
1300
+ type: 'string',
1301
+ required: false,
1302
+ description: 'Forwarded as message but IGNORED by the clear-chat handler.',
1303
+ },
1304
+ },
1305
+ platforms: ['twitch'],
1306
+ responds: 'boolean',
1307
+ documented: false,
1308
+ notes: 'Deletes all chat messages via Helix. Requires chat:edit.',
1309
+ },
1310
+ 'add-loyalty-points': {
1311
+ type: 'add-loyalty-points',
1312
+ title: 'Add loyalty points',
1313
+ execution: 'direct',
1314
+ params: {
1315
+ username: {
1316
+ type: 'string',
1317
+ required: true,
1318
+ description: 'Viewer username.',
1319
+ },
1320
+ platform: {
1321
+ type: 'string',
1322
+ required: true,
1323
+ description: 'Platform namespace the username belongs to (any chat platform key).',
1324
+ },
1325
+ value: {
1326
+ type: 'number',
1327
+ required: true,
1328
+ description: 'Points to add. Negative values subtract. Guarded with a falsy check, so 0 is REJECTED.',
1329
+ },
1330
+ },
1331
+ responds: 'value',
1332
+ documented: true,
1333
+ notes: "Recorded as a 'manual' points event. Resolves the user's new points balance (0 when the user cannot be loaded).",
1334
+ },
1335
+ 'get-loyalty-points': {
1336
+ type: 'get-loyalty-points',
1337
+ title: 'Get loyalty points',
1338
+ execution: 'direct',
1339
+ params: {
1340
+ username: {
1341
+ type: 'string',
1342
+ required: true,
1343
+ description: 'Viewer username.',
1344
+ },
1345
+ platform: {
1346
+ type: 'string',
1347
+ required: true,
1348
+ description: 'Platform namespace the username belongs to.',
1349
+ },
1350
+ },
1351
+ responds: 'value',
1352
+ documented: false,
1353
+ notes: 'Resolves the points balance; unknown users resolve 0.',
1354
+ },
1355
+ 'delete-message': {
1356
+ type: 'delete-message',
1357
+ title: 'Delete chat message',
1358
+ execution: 'direct',
1359
+ params: {
1360
+ platform: {
1361
+ type: 'string',
1362
+ required: false,
1363
+ description: 'Platform the message lives on. Not guarded: unknown/missing platform silently resolves true.',
1364
+ enum: ['twitch', 'youtube', 'kick', 'facebook', 'tiktok', 'discord'],
1365
+ },
1366
+ value: {
1367
+ type: 'string',
1368
+ required: false,
1369
+ description: 'Message id to delete.',
1370
+ },
1371
+ },
1372
+ platforms: ['twitch', 'youtube', 'kick', 'facebook', 'tiktok', 'discord'],
1373
+ responds: 'boolean',
1374
+ documented: true,
1375
+ notes: "Only twitch and youtube delete the message on the platform; kick, facebook, tiktok and discord merely remove it from Lumia's local chatbox log.",
1376
+ },
1377
+ 'pin-message': {
1378
+ type: 'pin-message',
1379
+ title: 'Pin chat message',
1380
+ execution: 'direct',
1381
+ params: {
1382
+ platform: {
1383
+ type: 'string',
1384
+ required: false,
1385
+ description: "Must be 'twitch'. Any other platform makes the request resolve the value false (still HTTP 200) — the only commands that do this.",
1386
+ enum: ['twitch'],
1387
+ },
1388
+ value: {
1389
+ type: 'string',
1390
+ required: false,
1391
+ description: 'Twitch message id to pin. Missing id warns in-app and no-ops while the API resolves true.',
1392
+ },
1393
+ },
1394
+ platforms: ['twitch'],
1395
+ responds: 'boolean',
1396
+ documented: false,
1397
+ notes: 'Requires moderator:manage:chat_messages. Uses a locally cast activity type (present in current LumiaActivityCommandTypes as PIN_MESSAGE).',
1398
+ },
1399
+ 'unpin-message': {
1400
+ type: 'unpin-message',
1401
+ title: 'Unpin chat message',
1402
+ execution: 'direct',
1403
+ params: {
1404
+ platform: {
1405
+ type: 'string',
1406
+ required: false,
1407
+ description: "Must be 'twitch'. Any other platform resolves the value false (still HTTP 200).",
1408
+ enum: ['twitch'],
1409
+ },
1410
+ value: {
1411
+ type: 'string',
1412
+ required: false,
1413
+ description: 'Twitch message id to unpin. Omitted: looks up the currently pinned message and unpins it.',
1414
+ },
1415
+ },
1416
+ platforms: ['twitch'],
1417
+ responds: 'boolean',
1418
+ documented: false,
1419
+ notes: 'Requires moderator:manage:chat_messages.',
1420
+ },
1421
+ 'translate-message': {
1422
+ type: 'translate-message',
1423
+ title: 'Translate chat message',
1424
+ execution: 'direct',
1425
+ params: {
1426
+ platform: {
1427
+ type: 'string',
1428
+ required: false,
1429
+ description: 'Platform whose chatbot posts the translation. Not guarded.',
1430
+ enum: ['twitch', 'youtube', 'kick', 'facebook', 'tiktok', 'discord'],
1431
+ },
1432
+ username: {
1433
+ type: 'string',
1434
+ required: false,
1435
+ description: 'Author credited in the translated output.',
1436
+ },
1437
+ value: {
1438
+ type: 'string',
1439
+ required: false,
1440
+ description: 'Message text to translate.',
1441
+ },
1442
+ userToChatAs: {
1443
+ type: 'string',
1444
+ required: false,
1445
+ description: "Account that posts the translation (defaults to 'bot').",
1446
+ },
1447
+ language: {
1448
+ type: 'string',
1449
+ required: false,
1450
+ description: "Target language code. Defaults to the app language (fallback 'en').",
1451
+ },
1452
+ },
1453
+ platforms: ['twitch', 'youtube', 'kick', 'facebook', 'tiktok', 'discord'],
1454
+ responds: 'boolean',
1455
+ documented: true,
1456
+ notes: 'The translation is posted to chat as "Translation from @username: ..." via the chatbot; nothing is returned in the API response.',
1457
+ },
1458
+ shoutout: {
1459
+ type: 'shoutout',
1460
+ title: 'Shoutout user',
1461
+ execution: 'direct',
1462
+ params: {
1463
+ platform: {
1464
+ type: 'string',
1465
+ required: false,
1466
+ description: 'Target platform. Not guarded: unsupported platforms silently resolve true.',
1467
+ enum: ['twitch', 'youtube', 'kick'],
1468
+ },
1469
+ value: {
1470
+ type: 'string',
1471
+ required: false,
1472
+ description: 'Username to shout out (forwarded as both message and username).',
1473
+ },
1474
+ userToChatAs: {
1475
+ type: 'string',
1476
+ required: false,
1477
+ description: "Account that posts the shoutout (defaults to 'bot').",
1478
+ },
1479
+ },
1480
+ platforms: ['twitch', 'youtube', 'kick'],
1481
+ responds: 'boolean',
1482
+ documented: true,
1483
+ notes: 'Runs the configured shoutout system command (clip retrieval, chat template, optional native Twitch shoutout). Fails with an in-app warning when no shoutout command exists.',
1484
+ },
1485
+ 'add-moderator': {
1486
+ type: 'add-moderator',
1487
+ title: 'Add moderator',
1488
+ execution: 'direct',
1489
+ params: {
1490
+ platform: {
1491
+ type: 'string',
1492
+ required: false,
1493
+ description: 'Must be twitch; anything else silently resolves true.',
1494
+ enum: ['twitch'],
1495
+ },
1496
+ value: {
1497
+ type: 'string',
1498
+ required: false,
1499
+ description: 'Username to mod.',
1500
+ },
1501
+ },
1502
+ platforms: ['twitch'],
1503
+ responds: 'boolean',
1504
+ documented: false,
1505
+ notes: 'Fire-and-forget: the call is NOT awaited (unlike remove-moderator), so even the platform round-trip has not started when the API resolves. Requires channel:manage:moderators.',
1506
+ },
1507
+ 'remove-moderator': {
1508
+ type: 'remove-moderator',
1509
+ title: 'Remove moderator',
1510
+ execution: 'direct',
1511
+ params: {
1512
+ platform: {
1513
+ type: 'string',
1514
+ required: false,
1515
+ description: 'Must be twitch; anything else silently resolves true.',
1516
+ enum: ['twitch'],
1517
+ },
1518
+ value: {
1519
+ type: 'string',
1520
+ required: false,
1521
+ description: 'Username to unmod.',
1522
+ },
1523
+ },
1524
+ platforms: ['twitch'],
1525
+ responds: 'boolean',
1526
+ documented: false,
1527
+ notes: 'Requires channel:manage:moderators.',
1528
+ },
1529
+ 'add-vip': {
1530
+ type: 'add-vip',
1531
+ title: 'Add VIP',
1532
+ execution: 'direct',
1533
+ params: {
1534
+ platform: {
1535
+ type: 'string',
1536
+ required: false,
1537
+ description: 'Must be twitch; anything else silently resolves true (the docs imply broader support).',
1538
+ enum: ['twitch'],
1539
+ },
1540
+ value: {
1541
+ type: 'string',
1542
+ required: false,
1543
+ description: 'Username to VIP.',
1544
+ },
1545
+ },
1546
+ platforms: ['twitch'],
1547
+ responds: 'boolean',
1548
+ documented: true,
1549
+ notes: 'Fire-and-forget: NOT awaited (unlike remove-vip). Requires channel:manage:vips.',
1550
+ },
1551
+ 'remove-vip': {
1552
+ type: 'remove-vip',
1553
+ title: 'Remove VIP',
1554
+ execution: 'direct',
1555
+ params: {
1556
+ platform: {
1557
+ type: 'string',
1558
+ required: false,
1559
+ description: 'Must be twitch; anything else silently resolves true.',
1560
+ enum: ['twitch'],
1561
+ },
1562
+ value: {
1563
+ type: 'string',
1564
+ required: false,
1565
+ description: 'Username to remove VIP from.',
1566
+ },
1567
+ },
1568
+ platforms: ['twitch'],
1569
+ responds: 'boolean',
1570
+ documented: true,
1571
+ notes: 'Requires channel:manage:vips.',
1572
+ },
1573
+ 'ban-user': {
1574
+ type: 'ban-user',
1575
+ title: 'Ban user',
1576
+ execution: 'direct',
1577
+ params: {
1578
+ platform: {
1579
+ type: 'string',
1580
+ required: false,
1581
+ description: 'Target platform. Not guarded: unsupported platforms silently resolve true.',
1582
+ enum: ['twitch', 'youtube', 'kick'],
1583
+ },
1584
+ value: {
1585
+ type: 'string',
1586
+ required: false,
1587
+ description: 'Username to ban.',
1588
+ },
1589
+ cleanupOnBan: {
1590
+ type: 'boolean',
1591
+ required: false,
1592
+ description: "Also clean up the user's recent messages. Accepts boolean true or the string 'true'; everything else is false.",
1593
+ },
1594
+ },
1595
+ platforms: ['twitch', 'youtube', 'kick'],
1596
+ responds: 'boolean',
1597
+ documented: true,
1598
+ notes: 'No ban reason can be passed from the API layer (the managers accept one, but it is not plumbed). Twitch/Kick require moderation scopes; failures are invisible to the caller.',
1599
+ },
1600
+ 'unban-user': {
1601
+ type: 'unban-user',
1602
+ title: 'Unban user',
1603
+ execution: 'direct',
1604
+ params: {
1605
+ platform: {
1606
+ type: 'string',
1607
+ required: false,
1608
+ description: 'Target platform. Not guarded.',
1609
+ enum: ['twitch', 'youtube', 'kick'],
1610
+ },
1611
+ value: {
1612
+ type: 'string',
1613
+ required: false,
1614
+ description: 'Username to unban.',
1615
+ },
1616
+ },
1617
+ platforms: ['twitch', 'youtube', 'kick'],
1618
+ responds: 'boolean',
1619
+ documented: true,
1620
+ },
1621
+ 'timeout-user': {
1622
+ type: 'timeout-user',
1623
+ title: 'Timeout user',
1624
+ execution: 'direct',
1625
+ params: {
1626
+ platform: {
1627
+ type: 'string',
1628
+ required: false,
1629
+ description: 'Target platform. Not guarded.',
1630
+ enum: ['twitch', 'youtube', 'kick'],
1631
+ },
1632
+ value: {
1633
+ type: 'string',
1634
+ required: false,
1635
+ description: 'Username to time out.',
1636
+ },
1637
+ duration: {
1638
+ type: 'number',
1639
+ required: false,
1640
+ description: 'Timeout length. UNIT MISMATCH: the REST docs say minutes, but the value is passed raw — Twitch and YouTube interpret it as SECONDS; only Kick treats it as minutes.',
1641
+ },
1642
+ name: {
1643
+ type: 'string',
1644
+ required: false,
1645
+ description: "Timeout reason (yes, it travels in the `name` param). Defaults to 'Time out'.",
1646
+ },
1647
+ },
1648
+ platforms: ['twitch', 'youtube', 'kick'],
1649
+ responds: 'boolean',
1650
+ documented: true,
1651
+ notes: "Dead default: duration is computed as `parseInt(duration) ?? 10` — parseInt yields NaN (never null) for a missing value, so the 10 fallback is unreachable and NaN is sent to the platform. Always pass duration explicitly.",
1652
+ },
1653
+ 'overlay-screenshot': {
1654
+ type: 'overlay-screenshot',
1655
+ title: 'Capture overlay screenshot',
1656
+ execution: 'direct',
1657
+ params: {
1658
+ name: {
1659
+ type: 'string',
1660
+ required: false,
1661
+ description: 'Overlay name or uuid (also accepts overlay:layer quick-find syntax). Missing name warns in-app and no-ops while the API resolves true.',
1662
+ },
1663
+ },
1664
+ responds: 'boolean',
1665
+ documented: false,
1666
+ notes: 'Waits up to 6s for a connected overlay (OBS/HUD, visible) to answer. The PNG is saved to disk and the path is stored in the last_overlay_screenshot_path variable — the API response is always true and never carries the path. Format is fixed to png/original from this entry point.',
1667
+ },
1668
+ 'overlay-screenshot-response': {
1669
+ type: 'overlay-screenshot-response',
1670
+ title: 'Overlay screenshot response (internal)',
1671
+ execution: 'direct',
1672
+ params: {
1673
+ requestId: {
1674
+ type: 'string',
1675
+ required: false,
1676
+ description: 'Id of the pending screenshot request (eventId also accepted).',
1677
+ },
1678
+ filePath: {
1679
+ type: 'string',
1680
+ required: false,
1681
+ description: 'Saved screenshot path; also written to the last_overlay_screenshot_path variable.',
1682
+ },
1683
+ error: {
1684
+ type: 'string',
1685
+ required: false,
1686
+ description: 'Error message that rejects the pending request.',
1687
+ },
1688
+ },
1689
+ responds: 'boolean',
1690
+ documented: false,
1691
+ notes: 'Internal reply channel used by overlay clients to fulfil overlay-screenshot. Not intended for external callers; unknown/missing requestId silently no-ops.',
1692
+ },
1693
+ };