@owncast/plugin-sdk 0.5.0 → 0.10.1
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/README.md +6 -6
- package/bin/owncast-plugin.js +57 -250
- package/index.d.ts +181 -77
- package/index.js +221 -242
- package/package.json +2 -1
- package/scripts/postinstall.js +11 -66
- package/slug.js +26 -0
- package/testing.js +17 -50
package/index.d.ts
CHANGED
|
@@ -1,34 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Built-in chat message payload (`chat.message.received` and the chat filter).
|
|
3
3
|
*
|
|
4
|
-
* `user` carries the full sender identity
|
|
4
|
+
* `user` carries the full sender identity. Use `user.id` for stable per-user
|
|
5
5
|
* state and `user.scopes` (e.g. `"MODERATOR"`) for reliable, non-spoofable
|
|
6
6
|
* moderation gating rather than matching on the display name. `clientId`
|
|
7
|
-
* identifies the originating connection
|
|
7
|
+
* identifies the originating connection. Pass it to `owncast.chat.sendTo` (or
|
|
8
8
|
* `owncast.chat.replyTo(msg, …)`) to whisper a reply back to the sender.
|
|
9
9
|
*
|
|
10
10
|
* `user` is undefined for the rare message with no associated account.
|
|
11
11
|
*/
|
|
12
12
|
export interface ChatMessage {
|
|
13
13
|
id: string;
|
|
14
|
-
user?:
|
|
14
|
+
user?: User;
|
|
15
15
|
clientId?: number;
|
|
16
16
|
body: string;
|
|
17
17
|
timestamp: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/** A chat user, payload of join/part/rename events. */
|
|
21
|
-
export interface ChatUser {
|
|
22
|
-
id: string;
|
|
23
|
-
displayName: string;
|
|
24
|
-
isBot?: boolean;
|
|
25
|
-
isAuthenticated?: boolean;
|
|
26
|
-
scopes?: string[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
20
|
/** Payload of `chat.user.renamed`, the same user changing their name. */
|
|
30
21
|
export interface ChatUserRename {
|
|
31
|
-
user:
|
|
22
|
+
user: User;
|
|
32
23
|
previousName: string;
|
|
33
24
|
}
|
|
34
25
|
|
|
@@ -36,7 +27,7 @@ export interface ChatUserRename {
|
|
|
36
27
|
export interface ChatMessageModeration {
|
|
37
28
|
messageId: string;
|
|
38
29
|
visible: boolean;
|
|
39
|
-
moderator?:
|
|
30
|
+
moderator?: User;
|
|
40
31
|
}
|
|
41
32
|
|
|
42
33
|
/** Stream-lifecycle payloads. */
|
|
@@ -129,9 +120,11 @@ export const Events: {
|
|
|
129
120
|
readonly SseConnect: "sse.connect";
|
|
130
121
|
readonly SseDisconnect: "sse.disconnect";
|
|
131
122
|
readonly Tick: "tick";
|
|
123
|
+
readonly FediverseActivity: "fediverse.activity";
|
|
132
124
|
readonly FediverseFollow: "fediverse.follow";
|
|
133
125
|
readonly FediverseLike: "fediverse.like";
|
|
134
126
|
readonly FediverseRepost: "fediverse.repost";
|
|
127
|
+
readonly FediverseQuote: "fediverse.quote";
|
|
135
128
|
readonly FediverseMention: "fediverse.mention";
|
|
136
129
|
readonly FediverseReply: "fediverse.reply";
|
|
137
130
|
};
|
|
@@ -146,10 +139,14 @@ export interface FediverseActor {
|
|
|
146
139
|
|
|
147
140
|
export interface FediverseEngagement {
|
|
148
141
|
actor: FediverseActor;
|
|
149
|
-
/** For likes and
|
|
142
|
+
/** For likes, reposts, and quotes: the target object URL. Not set for follows. */
|
|
150
143
|
target?: { url: string };
|
|
151
144
|
}
|
|
152
145
|
|
|
146
|
+
export interface FediverseTargetedEngagement extends FediverseEngagement {
|
|
147
|
+
target: { url: string };
|
|
148
|
+
}
|
|
149
|
+
|
|
153
150
|
/** Inbound fediverse post, a mention or reply that contains content the
|
|
154
151
|
* plugin can act on. Carries both the rendered content (which has the
|
|
155
152
|
* source instance's HTML) and a plain-text version (HTML stripped). */
|
|
@@ -183,13 +180,40 @@ export const Permissions: {
|
|
|
183
180
|
readonly NotificationsSend: "notifications.send";
|
|
184
181
|
readonly UsersRead: "users.read";
|
|
185
182
|
readonly UsersModerate: "users.moderate";
|
|
183
|
+
readonly UsersRegister: "users.register";
|
|
184
|
+
readonly AuthGate: "auth.gate";
|
|
186
185
|
readonly FediversePost: "fediverse.post";
|
|
186
|
+
readonly FediverseInbound: "fediverse.inbound";
|
|
187
187
|
readonly HttpSSE: "http.sse";
|
|
188
188
|
readonly VideoConfigRead: "videoconfig.read";
|
|
189
189
|
readonly VideoConfigWrite: "videoconfig.write";
|
|
190
190
|
readonly UIModify: "ui.modify";
|
|
191
191
|
};
|
|
192
192
|
|
|
193
|
+
/** Request for `owncast.users.register`. */
|
|
194
|
+
export interface UserRegisterRequest {
|
|
195
|
+
/** Stable, provider-scoped external identity (e.g. `"github:583231"`). The
|
|
196
|
+
* host namespaces it by the calling plugin's slug. */
|
|
197
|
+
authId: string;
|
|
198
|
+
/** Optional display name to seed on the user. */
|
|
199
|
+
displayName?: string;
|
|
200
|
+
/** Optional scopes to grant the user (e.g. `["MODERATOR"]`). */
|
|
201
|
+
scopes?: string[];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Result of `owncast.users.register`: the resolved Owncast user ID. */
|
|
205
|
+
export interface UserRegisterResult {
|
|
206
|
+
userId: string;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Request for `owncast.auth.grantSession`. */
|
|
210
|
+
export interface GrantSessionRequest {
|
|
211
|
+
/** The Owncast user ID returned by `owncast.users.register`. */
|
|
212
|
+
userId: string;
|
|
213
|
+
/** Optional session lifetime in seconds. 0/omitted uses the host default. */
|
|
214
|
+
ttl?: number;
|
|
215
|
+
}
|
|
216
|
+
|
|
193
217
|
export interface BrowserPushPayload {
|
|
194
218
|
title: string;
|
|
195
219
|
body?: string;
|
|
@@ -221,10 +245,14 @@ export interface FederationInfo {
|
|
|
221
245
|
isPrivate?: boolean;
|
|
222
246
|
}
|
|
223
247
|
|
|
224
|
-
/** A user
|
|
248
|
+
/** A user. The sender identity carried by every chat payload
|
|
249
|
+
* (chat.message.received, join/part/rename, moderation) and the record
|
|
250
|
+
* returned by owncast.users.list() / .get(). `displayColor` is an index into
|
|
251
|
+
* the instance's configured user-color palette, not a literal color. */
|
|
225
252
|
export interface User {
|
|
226
253
|
id: string;
|
|
227
254
|
displayName: string;
|
|
255
|
+
displayColor: number;
|
|
228
256
|
previousNames?: string[];
|
|
229
257
|
createdAt?: string;
|
|
230
258
|
disabledAt?: string; // ISO-8601 if banned, omitted otherwise
|
|
@@ -262,6 +290,30 @@ export const filter: {
|
|
|
262
290
|
drop(reason?: string): FilterResult;
|
|
263
291
|
};
|
|
264
292
|
|
|
293
|
+
/** Request passed to `onAuthCheck`: the host-resolved identity of the viewer
|
|
294
|
+
* whose session is being re-validated (same `user` shape `onHttpRequest`
|
|
295
|
+
* receives, and the plugin never re-resolves it). */
|
|
296
|
+
export interface AuthCheckRequest {
|
|
297
|
+
user: User;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** Verdict returned from `onAuthCheck`:
|
|
301
|
+
* - `ok` keep the session as-is
|
|
302
|
+
* - `refresh` keep it and re-issue the cookie (optionally with a new `ttl`
|
|
303
|
+
* in seconds) for sliding-expiry
|
|
304
|
+
* - `deny` end the session and bounce the viewer back to the login screen */
|
|
305
|
+
export type AuthCheckResult =
|
|
306
|
+
| { action: "ok" }
|
|
307
|
+
| { action: "refresh"; ttl?: number }
|
|
308
|
+
| { action: "deny"; reason?: string };
|
|
309
|
+
|
|
310
|
+
/** Verdict helpers for `onAuthCheck`. */
|
|
311
|
+
export const authCheck: {
|
|
312
|
+
ok(): AuthCheckResult;
|
|
313
|
+
refresh(opts?: { ttl?: number }): AuthCheckResult;
|
|
314
|
+
deny(reason?: string): AuthCheckResult;
|
|
315
|
+
};
|
|
316
|
+
|
|
265
317
|
/** Incoming HTTP request, paths are relative to the plugin's namespace
|
|
266
318
|
* (i.e. the leading /plugins/<name>/ has been stripped). */
|
|
267
319
|
export interface IncomingHttpRequest {
|
|
@@ -275,7 +327,7 @@ export interface IncomingHttpRequest {
|
|
|
275
327
|
authenticated: boolean;
|
|
276
328
|
/** Identity of the user that made the request, when it came with a
|
|
277
329
|
* user-token. Undefined for anonymous or admin-only requests. */
|
|
278
|
-
user?:
|
|
330
|
+
user?: User;
|
|
279
331
|
}
|
|
280
332
|
|
|
281
333
|
export interface OutgoingHttpResponse {
|
|
@@ -290,7 +342,7 @@ export interface ContentRequest {
|
|
|
290
342
|
slug: string;
|
|
291
343
|
/** The viewing user's chat identity, when available. Undefined for
|
|
292
344
|
* anonymous viewers or when the host cannot resolve an identity. */
|
|
293
|
-
user?:
|
|
345
|
+
user?: User;
|
|
294
346
|
}
|
|
295
347
|
|
|
296
348
|
/** Payload for the sse.connect / sse.disconnect events. Fired when a browser
|
|
@@ -302,7 +354,7 @@ export interface ContentRequest {
|
|
|
302
354
|
export interface SSEConnectionEvent {
|
|
303
355
|
channel: string;
|
|
304
356
|
connectionId: number;
|
|
305
|
-
user?:
|
|
357
|
+
user?: User;
|
|
306
358
|
}
|
|
307
359
|
|
|
308
360
|
/** Payload for the once-a-second tick event (onTick). `now` is the host
|
|
@@ -312,6 +364,14 @@ export interface TickEvent {
|
|
|
312
364
|
}
|
|
313
365
|
|
|
314
366
|
export interface PluginDef {
|
|
367
|
+
/** Declarative chat commands with aliases, moderator gates, and per-user
|
|
368
|
+
* cooldowns. Command messages also remain available to onChatMessage. */
|
|
369
|
+
commands?: Record<string, CommandDefinition>;
|
|
370
|
+
/** Command prefix for the `commands` table. Default "!". */
|
|
371
|
+
commandPrefix?: string;
|
|
372
|
+
/** Match command names case-sensitively. Default false. */
|
|
373
|
+
commandsCaseSensitive?: boolean;
|
|
374
|
+
|
|
315
375
|
/** Notification handler for chat messages. Fire-and-forget. */
|
|
316
376
|
onChatMessage?(msg: ChatMessage): void | Promise<void>;
|
|
317
377
|
|
|
@@ -320,9 +380,9 @@ export interface PluginDef {
|
|
|
320
380
|
filterChatMessage?(msg: ChatMessage): FilterResult;
|
|
321
381
|
|
|
322
382
|
/** User connected to chat. */
|
|
323
|
-
onChatUserJoined?(user:
|
|
383
|
+
onChatUserJoined?(user: User): void | Promise<void>;
|
|
324
384
|
/** User disconnected from chat. */
|
|
325
|
-
onChatUserParted?(user:
|
|
385
|
+
onChatUserParted?(user: User): void | Promise<void>;
|
|
326
386
|
/** User changed their display name. */
|
|
327
387
|
onChatUserRenamed?(change: ChatUserRename): void | Promise<void>;
|
|
328
388
|
/** A chat message was hidden or restored by a moderator. */
|
|
@@ -346,15 +406,20 @@ export interface PluginDef {
|
|
|
346
406
|
* in unix milliseconds. Defining this opts the plugin into the tick. */
|
|
347
407
|
onTick?(event: TickEvent): void | Promise<void>;
|
|
348
408
|
|
|
349
|
-
/**
|
|
409
|
+
/** A verified inbound ActivityPub activity as its raw JSON object. Requires `fediverse.inbound`. */
|
|
410
|
+
onFediverse?(activity: Record<string, unknown>): void | Promise<void>;
|
|
411
|
+
|
|
412
|
+
/** Someone on the fediverse followed the streamer's account. Requires `fediverse.inbound`. */
|
|
350
413
|
onFediverseFollow?(event: FediverseEngagement): void | Promise<void>;
|
|
351
|
-
/** Someone on the fediverse liked a streamer post / federated stream announcement. */
|
|
352
|
-
onFediverseLike?(event:
|
|
353
|
-
/** Someone on the fediverse boosted (reposted) a streamer post. */
|
|
354
|
-
onFediverseRepost?(event:
|
|
355
|
-
/** Someone
|
|
414
|
+
/** Someone on the fediverse liked a streamer post / federated stream announcement. Requires `fediverse.inbound`. */
|
|
415
|
+
onFediverseLike?(event: FediverseTargetedEngagement): void | Promise<void>;
|
|
416
|
+
/** Someone on the fediverse boosted (reposted) a streamer post. Requires `fediverse.inbound`. */
|
|
417
|
+
onFediverseRepost?(event: FediverseTargetedEngagement): void | Promise<void>;
|
|
418
|
+
/** Someone on the fediverse quoted a locally authored post. `target.url` identifies that quoted post. Requires `fediverse.inbound`. */
|
|
419
|
+
onFediverseQuote?(event: FediverseTargetedEngagement): void | Promise<void>;
|
|
420
|
+
/** Someone @-mentioned the streamer in a public post. Requires `fediverse.inbound`. */
|
|
356
421
|
onFediverseMention?(post: FediverseInboundPost): void | Promise<void>;
|
|
357
|
-
/** Someone replied to one of the streamer's federated posts. */
|
|
422
|
+
/** Someone replied to one of the streamer's federated posts. Requires `fediverse.inbound`. */
|
|
358
423
|
onFediverseReply?(post: FediverseInboundPost): void | Promise<void>;
|
|
359
424
|
|
|
360
425
|
/** HTTP request handler. Called for any path under /plugins/<name>/ that
|
|
@@ -362,6 +427,15 @@ export interface PluginDef {
|
|
|
362
427
|
* on `req.authenticated` yourself. Requires `http.serve` permission. */
|
|
363
428
|
onHttpRequest?(req: IncomingHttpRequest): OutgoingHttpResponse;
|
|
364
429
|
|
|
430
|
+
/** Re-validate a viewer's gate session on page load. Only meaningful for the
|
|
431
|
+
* active `auth.gate` plugin: the host calls it on the viewer's `/` request
|
|
432
|
+
* with the resolved `req.user`, and acts on the verdict: `ok` to continue,
|
|
433
|
+
* `refresh` to extend the session, `deny` to revoke it and force re-login.
|
|
434
|
+
* Optional. Without it a granted session lasts until its cookie
|
|
435
|
+
* expires (no mid-session revocation). This is the revocation hook: return
|
|
436
|
+
* `deny` for users your provider has banned/deleted. Requires `auth.gate`. */
|
|
437
|
+
onAuthCheck?(req: AuthCheckRequest): AuthCheckResult;
|
|
438
|
+
|
|
365
439
|
/** Render HTML for a dynamic tab. Called by the host when the tab was
|
|
366
440
|
* declared in the manifest without a static `content` file. Return the
|
|
367
441
|
* full HTML string to inline as the tab body. `req.user` is the viewer's
|
|
@@ -374,6 +448,28 @@ export interface PluginDef {
|
|
|
374
448
|
* `req.user` is the viewer's chat identity when available. */
|
|
375
449
|
onPageContent?(req: ContentRequest): string;
|
|
376
450
|
|
|
451
|
+
/** Return CSS to inline into the viewer page at request time, the dynamic
|
|
452
|
+
* counterpart to `manifest.styles`, applied to the whole UI. Called once
|
|
453
|
+
* per `/api/config` for any plugin holding `ui.modify`. No manifest field
|
|
454
|
+
* is needed, just export this handler. Return nothing (a bare `return`, or
|
|
455
|
+
* `""`) to contribute nothing. The output is appended after any static
|
|
456
|
+
* `manifest.styles` files, so returning only the active override wins
|
|
457
|
+
* within your plugin's own styles. Plugin styles sit below the admin's
|
|
458
|
+
* appearance settings, so an admin's explicit colors override yours.
|
|
459
|
+
* Global (no per-viewer argument) so `/api/config` stays cacheable.
|
|
460
|
+
* Requires `ui.modify`. */
|
|
461
|
+
onPageStyles?(): string | null | void;
|
|
462
|
+
|
|
463
|
+
/** Return JavaScript to append to the viewer page at request time, the
|
|
464
|
+
* dynamic counterpart to `manifest.scripts`. Called once per `/api/config`
|
|
465
|
+
* for any plugin holding `ui.modify`. The host wraps each plugin's script
|
|
466
|
+
* (static and dynamic) in a try/catch so a runtime error can't break other
|
|
467
|
+
* plugins, but it runs in the shared viewer `window`: wrap your code in an
|
|
468
|
+
* IIFE to avoid global collisions, and escape any untrusted strings you
|
|
469
|
+
* embed. Return nothing (a bare `return`, or `""`) to contribute nothing.
|
|
470
|
+
* Requires `ui.modify`. */
|
|
471
|
+
onPageScripts?(): string | null | void;
|
|
472
|
+
|
|
377
473
|
/** Handlers for plugin-emitted custom events. The key is the event type
|
|
378
474
|
* string (e.g. "announcement.broadcast"). Notifications only, to filter
|
|
379
475
|
* custom events, additional API will be needed. */
|
|
@@ -391,56 +487,46 @@ export interface CommandContext {
|
|
|
391
487
|
/** The originating chat message. */
|
|
392
488
|
msg: ChatMessage;
|
|
393
489
|
/** The sender (same as `msg.user`). */
|
|
394
|
-
user?:
|
|
490
|
+
user?: User;
|
|
395
491
|
/** The canonical command name that matched (not the alias used). */
|
|
396
492
|
command: string;
|
|
493
|
+
/** The command name or alias exactly as the sender typed it. */
|
|
494
|
+
invokedAs: string;
|
|
397
495
|
/** Whitespace-split arguments after the command word. */
|
|
398
496
|
args: string[];
|
|
399
497
|
/** The raw argument string (everything after the command word, trimmed). */
|
|
400
498
|
argString: string;
|
|
401
499
|
/** Post a public reply as the plugin's chat bot. */
|
|
402
500
|
reply(text: string): void;
|
|
403
|
-
/** Whisper a reply to the sender
|
|
501
|
+
/** Whisper a reply to the sender, falling back to a public post if their
|
|
404
502
|
* connection is unknown. */
|
|
405
503
|
replyPrivately(text: string): void;
|
|
406
504
|
}
|
|
407
505
|
|
|
408
|
-
/** One command in a
|
|
506
|
+
/** One command in a declarative command table. */
|
|
409
507
|
export interface CommandDefinition {
|
|
508
|
+
/** Short, human-readable summary shown in command listings. */
|
|
509
|
+
description?: string;
|
|
510
|
+
/** Optional usage/example string, e.g. "!latency <0-4>". */
|
|
511
|
+
usage?: string;
|
|
410
512
|
/** Alternate names that invoke this command. */
|
|
411
513
|
aliases?: string[];
|
|
412
|
-
/**
|
|
514
|
+
/** Dispatch only for senders whose scopes include "MODERATOR". */
|
|
413
515
|
modOnly?: boolean;
|
|
414
|
-
/**
|
|
415
|
-
* `msg.timestamp`). */
|
|
516
|
+
/** Non-negative integer milliseconds between invocations per user. */
|
|
416
517
|
cooldownMs?: number;
|
|
417
518
|
/** Invoked when the command runs. */
|
|
418
519
|
run(ctx: CommandContext): void;
|
|
419
|
-
/** Invoked instead of `run` when a non-moderator calls a `modOnly` command. */
|
|
420
|
-
onDenied?(ctx: CommandContext): void;
|
|
421
|
-
/** Invoked instead of `run` when the per-user cooldown hasn't elapsed. */
|
|
422
|
-
onCooldown?(ctx: CommandContext): void;
|
|
423
520
|
}
|
|
424
521
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
/** Default denied/cooldown handlers, used when a command omits its own. */
|
|
434
|
-
onDenied?(ctx: CommandContext): void;
|
|
435
|
-
onCooldown?(ctx: CommandContext): void;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/** Build a chat-command router (prefix parsing, aliases, per-user cooldowns,
|
|
439
|
-
* moderator gating). Feed the returned function a `ChatMessage`; it returns
|
|
440
|
-
* true when the message was a command (even if gated), false otherwise. */
|
|
441
|
-
export function defineCommands(
|
|
442
|
-
config: CommandsConfig,
|
|
443
|
-
): (msg: ChatMessage) => boolean;
|
|
522
|
+
/** Internal payload for a matched command declaration. */
|
|
523
|
+
export interface CommandEvent {
|
|
524
|
+
message: ChatMessage;
|
|
525
|
+
command: string;
|
|
526
|
+
invokedAs: string;
|
|
527
|
+
args: string[];
|
|
528
|
+
argString: string;
|
|
529
|
+
}
|
|
444
530
|
|
|
445
531
|
/** Typed wrappers around the Owncast host. Each method throws if the
|
|
446
532
|
* corresponding permission was not declared in plugin.manifest.json. */
|
|
@@ -464,7 +550,7 @@ export const owncast: {
|
|
|
464
550
|
* `chat.send`. */
|
|
465
551
|
replyTo(msg: ChatMessage | number | bigint, text: string): boolean;
|
|
466
552
|
/** Recent chat history (most recent last). Requires `chat.history`.
|
|
467
|
-
* Default limit is 50
|
|
553
|
+
* Default limit is 50. Pass a smaller number to get fewer. */
|
|
468
554
|
history(limit?: number): ChatMessage[];
|
|
469
555
|
/** Hide a chat message by ID. Requires `chat.moderate`. */
|
|
470
556
|
deleteMessage(messageId: string): void;
|
|
@@ -479,12 +565,30 @@ export const owncast: {
|
|
|
479
565
|
list(): User[];
|
|
480
566
|
/** Fetch one user by ID. Requires `users.read`. */
|
|
481
567
|
get(id: string): User | null;
|
|
482
|
-
/** Enable/disable a user
|
|
568
|
+
/** Enable/disable a user, with an optional reason. Requires `users.moderate`. */
|
|
483
569
|
setEnabled(id: string, enabled: boolean, reason?: string): void;
|
|
484
570
|
/** Ban an IP address. Requires `users.moderate`. */
|
|
485
571
|
banIP(ip: string): void;
|
|
572
|
+
/** Find-or-create an authenticated user for an external identity. `authId`
|
|
573
|
+
* is the stable provider-scoped id (e.g. `"github:583231"`). The host
|
|
574
|
+
* namespaces it by this plugin's slug so plugins can't collide on or spoof
|
|
575
|
+
* each other's users. Returns `{ userId }`. Throws on host error.
|
|
576
|
+
* Requires `users.register`. */
|
|
577
|
+
register(opts: UserRegisterRequest | string): UserRegisterResult;
|
|
578
|
+
};
|
|
579
|
+
/** Viewer-authentication gate. Only a plugin holding `auth.gate` (and enabled
|
|
580
|
+
* by an admin) can issue sessions, and only inside `onHttpRequest`, where the
|
|
581
|
+
* host attaches or clears the signed session cookie on the response. */
|
|
582
|
+
auth: {
|
|
583
|
+
/** Issue a gate session for an already-registered user (see
|
|
584
|
+
* `users.register`). `ttl` is optional seconds (0/omitted = host default).
|
|
585
|
+
* Throws on host error. Requires `auth.gate`. */
|
|
586
|
+
grantSession(opts: GrantSessionRequest | string): void;
|
|
587
|
+
/** Clear the current viewer's gate session (logout). The plugin still owns
|
|
588
|
+
* the response/redirect. Requires `auth.gate`. */
|
|
589
|
+
endSession(): void;
|
|
486
590
|
};
|
|
487
|
-
/** Upload bytes to Owncast's storage backend (local or S3)
|
|
591
|
+
/** Upload bytes to Owncast's storage backend (local or S3). Returns a
|
|
488
592
|
* public URL. Requires `storage.upload`. */
|
|
489
593
|
storage: {
|
|
490
594
|
upload(name: string, data: Uint8Array | string): UploadResult | null;
|
|
@@ -499,7 +603,7 @@ export const owncast: {
|
|
|
499
603
|
readText(path: string): string | null;
|
|
500
604
|
/** Write bytes or a string, creating parent directories as needed. */
|
|
501
605
|
write(path: string, data: Uint8Array | string): FsResult;
|
|
502
|
-
/** List entry names directly inside dir
|
|
606
|
+
/** List entry names directly inside dir. A missing dir lists as empty. */
|
|
503
607
|
list(dir: string): string[];
|
|
504
608
|
/** Remove a single file or empty directory. */
|
|
505
609
|
delete(path: string): FsResult;
|
|
@@ -507,7 +611,7 @@ export const owncast: {
|
|
|
507
611
|
exists(path: string): boolean;
|
|
508
612
|
};
|
|
509
613
|
/** Post to the fediverse on the streamer's behalf. Requires `fediverse.post`,
|
|
510
|
-
* which is high-trust (posts go out under the streamer's own handle)
|
|
614
|
+
* which is high-trust (posts go out under the streamer's own handle), so
|
|
511
615
|
* admins should grant it sparingly. */
|
|
512
616
|
fediverse: {
|
|
513
617
|
/** Publish a public, text-only post. Returns `{ url }` (currently empty
|
|
@@ -536,7 +640,7 @@ export const owncast: {
|
|
|
536
640
|
setJSON(key: string, value: unknown): void;
|
|
537
641
|
};
|
|
538
642
|
/** Read this plugin's admin-configurable settings, declared under
|
|
539
|
-
* `config` in the manifest. Ambient
|
|
643
|
+
* `config` in the manifest. Ambient, so no permission is required. */
|
|
540
644
|
config: {
|
|
541
645
|
/** The effective value of a manifest-declared config key (admin override,
|
|
542
646
|
* else the declared default), parsed to its declared type. Returns
|
|
@@ -544,10 +648,10 @@ export const owncast: {
|
|
|
544
648
|
* value. */
|
|
545
649
|
get<T = unknown>(key: string, fallback?: T): T;
|
|
546
650
|
};
|
|
547
|
-
/** Read files the plugin bundled in its own `assets/` directory
|
|
651
|
+
/** Read files the plugin bundled in its own `assets/` directory: templates,
|
|
548
652
|
* data files, and other bundled resources loaded at request time. Path is
|
|
549
|
-
* relative to `assets/` and must not contain `..`. Ambient
|
|
550
|
-
* required. */
|
|
653
|
+
* relative to `assets/` and must not contain `..`. Ambient, so no permission
|
|
654
|
+
* is required. */
|
|
551
655
|
assets: {
|
|
552
656
|
/** Raw bytes of the file, or `null` if not found. */
|
|
553
657
|
read(path: string): Uint8Array | null;
|
|
@@ -564,30 +668,30 @@ export const owncast: {
|
|
|
564
668
|
actions: {
|
|
565
669
|
/** Append one or more buttons to the plugin's runtime list. Each
|
|
566
670
|
* entry is validated with the same rules as `manifest.actions`
|
|
567
|
-
* (title required
|
|
568
|
-
* rewritten into this plugin's namespace
|
|
671
|
+
* (title required, exactly one of `url` or `html`, relative URLs
|
|
672
|
+
* rewritten into this plugin's namespace, cross-plugin URLs
|
|
569
673
|
* rejected). The next viewer `/api/config` request returns
|
|
570
674
|
* `manifest.actions` ++ the runtime list. */
|
|
571
675
|
add(actions: ActionButton | ActionButton[]): void;
|
|
572
|
-
/** Drop the runtime additions
|
|
676
|
+
/** Drop the runtime additions, so only `manifest.actions` remain on
|
|
573
677
|
* the next viewer `/api/config` request. */
|
|
574
678
|
clear(): void;
|
|
575
679
|
};
|
|
576
680
|
sse: {
|
|
577
681
|
/** Push one Server-Sent-Event to every browser connected to this
|
|
578
682
|
* plugin's `/plugins/<name>/_sse/<channel>` stream. `event` is the SSE
|
|
579
|
-
* event name (`""` → the default "message" event)
|
|
580
|
-
* if a string, otherwise JSON-stringified. Fire-and-forget
|
|
683
|
+
* event name (`""` → the default "message" event). `data` is sent as-is
|
|
684
|
+
* if a string, otherwise JSON-stringified. Fire-and-forget, and frames to a
|
|
581
685
|
* slow client are dropped rather than blocking the plugin. Requires the
|
|
582
686
|
* `http.sse` permission. */
|
|
583
687
|
send(channel: string, event: string, data: unknown): void;
|
|
584
688
|
};
|
|
585
|
-
/** Host-driven timers. The sandbox has no setTimeout
|
|
689
|
+
/** Host-driven timers. The sandbox has no setTimeout, so these ask the host to
|
|
586
690
|
* call your callback back later (in this instance). No permission required.
|
|
587
691
|
* Timers do not survive a plugin reload or host restart. */
|
|
588
692
|
timer: {
|
|
589
693
|
/** Run `fn` once after ~`ms` milliseconds. Returns an id for `clear()`.
|
|
590
|
-
* Very small delays are clamped up by the host
|
|
694
|
+
* Very small delays are clamped up by the host. Throws past the
|
|
591
695
|
* per-plugin pending-timer cap. */
|
|
592
696
|
setTimeout(fn: () => void, ms: number): number;
|
|
593
697
|
/** Run `fn` every ~`ms` milliseconds until `clear()`. The next run is
|
|
@@ -614,7 +718,7 @@ export const owncast: {
|
|
|
614
718
|
tags(): string[];
|
|
615
719
|
};
|
|
616
720
|
/** Read/change video/transcoding configuration. read() requires
|
|
617
|
-
* `videoconfig.read
|
|
721
|
+
* `videoconfig.read`, and write() requires `videoconfig.write`. */
|
|
618
722
|
videoConfig: {
|
|
619
723
|
read(): VideoConfig;
|
|
620
724
|
write(config: VideoConfigUpdate): void;
|
|
@@ -635,7 +739,7 @@ export interface HttpResponse {
|
|
|
635
739
|
|
|
636
740
|
/** An entry in `manifest.actions`, declares an action button the Owncast
|
|
637
741
|
* UI surfaces while this plugin is enabled. Mirrors Owncast's existing
|
|
638
|
-
* ExternalAction shape
|
|
742
|
+
* ExternalAction shape. The host merges enabled-plugin buttons with the
|
|
639
743
|
* admin-configured list.
|
|
640
744
|
*
|
|
641
745
|
* Exactly one of `url` or `html` is required.
|
|
@@ -666,10 +770,10 @@ export interface ActionButton {
|
|
|
666
770
|
|
|
667
771
|
/** `manifest.network`, narrows outbound HTTP scope for plugins that
|
|
668
772
|
* declare the `network.fetch` permission. Required when that permission
|
|
669
|
-
* is granted
|
|
773
|
+
* is granted. The host rejects loads otherwise. */
|
|
670
774
|
export interface NetworkConfig {
|
|
671
775
|
/** Hostname globs the plugin can reach via `owncast.http.fetch`.
|
|
672
|
-
* Bare names match exactly (`"api.discord.com"`)
|
|
776
|
+
* Bare names match exactly (`"api.discord.com"`), and `*` is a wildcard
|
|
673
777
|
* segment (`"*.weather.com"`). The bare wildcard `"*"` matches any
|
|
674
778
|
* host but must be written explicitly. */
|
|
675
779
|
allowedHosts: string[];
|