@noematicsllc/talk-sdk 0.0.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/dist/index.mjs ADDED
@@ -0,0 +1,1805 @@
1
+ // src/transport/http-client.ts
2
+ var HttpClientError = class extends Error {
3
+ constructor(message, status, ocsStatus, ocsMessage) {
4
+ super(message);
5
+ this.status = status;
6
+ this.ocsStatus = ocsStatus;
7
+ this.ocsMessage = ocsMessage;
8
+ this.name = "HttpClientError";
9
+ }
10
+ };
11
+ var HttpClient = class {
12
+ baseUrl;
13
+ authHeader;
14
+ fetchImpl;
15
+ constructor(config) {
16
+ this.baseUrl = config.host.replace(/\/$/, "");
17
+ const credentials = `${config.username}:${config.password}`;
18
+ const encoded = typeof btoa === "function" ? btoa(credentials) : Buffer.from(credentials).toString("base64");
19
+ this.authHeader = `Basic ${encoded}`;
20
+ const globalFetch = globalThis.fetch;
21
+ this.fetchImpl = config.fetch ?? (globalFetch ? globalFetch.bind(globalThis) : void 0);
22
+ if (!this.fetchImpl) {
23
+ throw new Error("Fetch API is not available. Please provide a fetch implementation.");
24
+ }
25
+ }
26
+ /**
27
+ * Build the full URL with query parameters
28
+ */
29
+ buildUrl(path, params) {
30
+ const url = new URL(path, this.baseUrl);
31
+ if (params) {
32
+ Object.entries(params).forEach(([key, value]) => {
33
+ if (value !== void 0) {
34
+ url.searchParams.set(key, String(value));
35
+ }
36
+ });
37
+ }
38
+ return url.toString();
39
+ }
40
+ /**
41
+ * Extract Nextcloud-specific headers from response
42
+ */
43
+ extractMeta(headers) {
44
+ const meta = {};
45
+ const talkHash = headers.get("X-Nextcloud-Talk-Hash");
46
+ if (talkHash) meta.talkHash = talkHash;
47
+ const modifiedBefore = headers.get("X-Nextcloud-Talk-Modified-Before");
48
+ if (modifiedBefore) meta.modifiedBefore = parseInt(modifiedBefore, 10);
49
+ const chatLastGiven = headers.get("X-Chat-Last-Given");
50
+ if (chatLastGiven) meta.chatLastGiven = parseInt(chatLastGiven, 10);
51
+ const chatLastCommonRead = headers.get("X-Chat-Last-Common-Read");
52
+ if (chatLastCommonRead) meta.chatLastCommonRead = parseInt(chatLastCommonRead, 10);
53
+ const federationInvites = headers.get("X-Nextcloud-Talk-Federation-Invites");
54
+ if (federationInvites) meta.federationInvites = parseInt(federationInvites, 10);
55
+ return meta;
56
+ }
57
+ /**
58
+ * Make an HTTP request to the Nextcloud OCS API
59
+ *
60
+ * @param path - API path (e.g., '/ocs/v2.php/apps/spreed/api/v4/room')
61
+ * @param options - Request options
62
+ * @returns Promise resolving to the unwrapped OCS data
63
+ *
64
+ * @throws {HttpClientError} When the request fails or returns an error status
65
+ *
66
+ * @remarks
67
+ * OCS API returns status codes in the response body:
68
+ * - 100: Success (OCS v1)
69
+ * - 200: Success (OCS v2)
70
+ * - Other codes indicate errors specific to the endpoint
71
+ */
72
+ async request(path, options = {}) {
73
+ const {
74
+ method = "GET",
75
+ body,
76
+ params,
77
+ headers: customHeaders = {},
78
+ timeout = 3e4
79
+ } = options;
80
+ const url = this.buildUrl(path, params);
81
+ const headers = {
82
+ "OCS-APIRequest": "true",
83
+ "Accept": "application/json",
84
+ "Authorization": this.authHeader,
85
+ ...customHeaders
86
+ };
87
+ if (body !== void 0) {
88
+ headers["Content-Type"] = "application/json";
89
+ }
90
+ const controller = new AbortController();
91
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
92
+ try {
93
+ const response = await this.fetchImpl(url, {
94
+ method,
95
+ headers,
96
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
97
+ signal: controller.signal
98
+ });
99
+ if (response.status === 304) {
100
+ return {
101
+ data: [],
102
+ status: response.status,
103
+ meta: this.extractMeta(response.headers),
104
+ headers: response.headers
105
+ };
106
+ }
107
+ if (!response.ok) {
108
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
109
+ let ocsStatus;
110
+ let ocsMessage;
111
+ try {
112
+ const errorBody = await response.json();
113
+ ocsStatus = errorBody.ocs?.meta?.statuscode;
114
+ ocsMessage = errorBody.ocs?.meta?.message;
115
+ if (ocsMessage) {
116
+ errorMessage = `${errorMessage} - ${ocsMessage}`;
117
+ }
118
+ } catch {
119
+ }
120
+ throw new HttpClientError(errorMessage, response.status, ocsStatus, ocsMessage);
121
+ }
122
+ const json = await response.json();
123
+ if (!json.ocs) {
124
+ throw new HttpClientError("Invalid OCS response: missing ocs envelope", response.status);
125
+ }
126
+ return {
127
+ data: json.ocs.data,
128
+ status: response.status,
129
+ meta: this.extractMeta(response.headers),
130
+ headers: response.headers
131
+ };
132
+ } catch (error) {
133
+ if (error instanceof HttpClientError) {
134
+ throw error;
135
+ }
136
+ if (error instanceof Error && error.name === "AbortError") {
137
+ throw new HttpClientError("Request timeout", 408);
138
+ }
139
+ throw new HttpClientError(
140
+ error instanceof Error ? error.message : "Unknown error",
141
+ 0
142
+ );
143
+ } finally {
144
+ clearTimeout(timeoutId);
145
+ }
146
+ }
147
+ /** GET request helper */
148
+ async get(path, params) {
149
+ return this.request(path, { method: "GET", params });
150
+ }
151
+ /** POST request helper */
152
+ async post(path, body, params) {
153
+ return this.request(path, { method: "POST", body, params });
154
+ }
155
+ /** PUT request helper */
156
+ async put(path, body, params) {
157
+ return this.request(path, { method: "PUT", body, params });
158
+ }
159
+ /** DELETE request helper */
160
+ async delete(path, params) {
161
+ return this.request(path, { method: "DELETE", params });
162
+ }
163
+ /** Get the base URL for this client */
164
+ get host() {
165
+ return this.baseUrl;
166
+ }
167
+ };
168
+
169
+ // src/normalization/room-mapper.ts
170
+ function normalizeRoom(raw) {
171
+ return {
172
+ // Identifiers
173
+ id: raw.id,
174
+ token: raw.token,
175
+ // Basic info
176
+ type: raw.type,
177
+ name: raw.name,
178
+ displayName: raw.displayName,
179
+ description: raw.description,
180
+ // Current user context
181
+ actorType: raw.actorType,
182
+ actorId: raw.actorId,
183
+ attendeeId: raw.attendeeId,
184
+ participantType: raw.participantType,
185
+ permissions: raw.permissions,
186
+ attendeePermissions: raw.attendeePermissions,
187
+ // Room state
188
+ hasPassword: raw.hasPassword,
189
+ hasCall: raw.hasCall,
190
+ callFlag: raw.callFlag,
191
+ callStartTime: raw.callStartTime,
192
+ callRecording: raw.callRecording,
193
+ // Settings
194
+ readOnly: raw.readOnly,
195
+ listable: raw.listable,
196
+ lobbyState: raw.lobbyState,
197
+ lobbyTimer: raw.lobbyTimer,
198
+ sipEnabled: raw.sipEnabled,
199
+ mentionPermissions: raw.mentionPermissions,
200
+ messageExpiration: raw.messageExpiration,
201
+ recordingConsent: raw.recordingConsent,
202
+ // Chat state
203
+ lastActivity: raw.lastActivity,
204
+ lastReadMessage: raw.lastReadMessage,
205
+ lastCommonReadMessage: raw.lastCommonReadMessage,
206
+ unreadMessages: raw.unreadMessages,
207
+ unreadMention: raw.unreadMention,
208
+ unreadMentionDirect: raw.unreadMentionDirect,
209
+ // Capabilities
210
+ canStartCall: raw.canStartCall,
211
+ canLeaveConversation: raw.canLeaveConversation,
212
+ canDeleteConversation: raw.canDeleteConversation,
213
+ canEnableSIP: raw.canEnableSIP,
214
+ // Flags
215
+ isFavorite: raw.isFavorite,
216
+ isArchived: raw.isArchived,
217
+ isImportant: raw.isImportant,
218
+ isSensitive: raw.isSensitive,
219
+ // Optional status
220
+ status: raw.status,
221
+ statusIcon: raw.statusIcon,
222
+ statusMessage: raw.statusMessage,
223
+ statusClearAt: raw.statusClearAt,
224
+ // Federation
225
+ remoteServer: raw.remoteServer,
226
+ remoteToken: raw.remoteToken
227
+ };
228
+ }
229
+ function normalizeRooms(rawRooms) {
230
+ return rawRooms.map(normalizeRoom);
231
+ }
232
+
233
+ // src/normalization/message-mapper.ts
234
+ function isDeletedRef(parent) {
235
+ return "deleted" in parent && parent.deleted === true;
236
+ }
237
+ function normalizeMessage(raw) {
238
+ return {
239
+ id: raw.id,
240
+ token: raw.token,
241
+ actorType: raw.actorType,
242
+ actorId: raw.actorId,
243
+ actorDisplayName: raw.actorDisplayName,
244
+ timestamp: raw.timestamp,
245
+ message: raw.message,
246
+ messageType: raw.messageType,
247
+ systemMessage: raw.systemMessage,
248
+ messageParameters: raw.messageParameters,
249
+ // Features
250
+ isReplyable: raw.isReplyable,
251
+ markdown: raw.markdown,
252
+ referenceId: raw.referenceId,
253
+ expirationTimestamp: raw.expirationTimestamp,
254
+ // Reactions
255
+ reactions: raw.reactions,
256
+ reactionsSelf: raw.reactionsSelf ?? [],
257
+ // Editing
258
+ lastEditActorType: raw.lastEditActorType,
259
+ lastEditActorId: raw.lastEditActorId,
260
+ lastEditActorDisplayName: raw.lastEditActorDisplayName,
261
+ lastEditTimestamp: raw.lastEditTimestamp,
262
+ // Threads
263
+ threadId: raw.threadId,
264
+ isThread: raw.isThread ?? false,
265
+ threadTitle: raw.threadTitle,
266
+ threadReplies: raw.threadReplies,
267
+ // Metadata
268
+ metaData: raw.metaData
269
+ };
270
+ }
271
+ function normalizeMessageWithParent(raw) {
272
+ const normalized = normalizeMessage(raw);
273
+ if (raw.parent) {
274
+ if (isDeletedRef(raw.parent)) {
275
+ normalized.parent = { id: raw.parent.id, deleted: true };
276
+ } else {
277
+ normalized.parent = normalizeMessage(raw.parent);
278
+ }
279
+ }
280
+ return normalized;
281
+ }
282
+ function normalizeMessages(rawMessages) {
283
+ return rawMessages.map(normalizeMessageWithParent);
284
+ }
285
+
286
+ // src/normalization/participant-mapper.ts
287
+ function normalizeParticipant(raw) {
288
+ return {
289
+ actorType: raw.actorType,
290
+ actorId: raw.actorId,
291
+ displayName: raw.displayName,
292
+ attendeeId: raw.attendeeId,
293
+ participantType: raw.participantType,
294
+ permissions: raw.permissions,
295
+ attendeePermissions: raw.attendeePermissions,
296
+ attendeePin: raw.attendeePin,
297
+ roomToken: raw.roomToken,
298
+ sessionIds: raw.sessionIds,
299
+ inCall: raw.inCall,
300
+ lastPing: raw.lastPing,
301
+ // Status
302
+ status: raw.status,
303
+ statusIcon: raw.statusIcon,
304
+ statusMessage: raw.statusMessage,
305
+ statusClearAt: raw.statusClearAt,
306
+ // Phone
307
+ phoneNumber: raw.phoneNumber,
308
+ callId: raw.callId,
309
+ // Computed helpers (based on InCallFlag enum values)
310
+ isInCall: (raw.inCall & 1) !== 0,
311
+ // IN_CALL = 1
312
+ hasAudio: (raw.inCall & 2) !== 0,
313
+ // WITH_AUDIO = 2
314
+ hasVideo: (raw.inCall & 4) !== 0,
315
+ // WITH_VIDEO = 4
316
+ isOnPhone: (raw.inCall & 8) !== 0
317
+ // WITH_PHONE = 8
318
+ };
319
+ }
320
+ function normalizeParticipants(rawParticipants) {
321
+ return rawParticipants.map(normalizeParticipant);
322
+ }
323
+
324
+ // src/resources/room-resource.ts
325
+ var BASE_PATH = "/ocs/v2.php/apps/spreed/api/v4/room";
326
+ var RoomResource = class {
327
+ constructor(http) {
328
+ this.http = http;
329
+ }
330
+ /**
331
+ * List all conversations the current user is part of
332
+ *
333
+ * @param options - List options (includeStatus, modifiedSince, etc.)
334
+ * @returns Array of normalized room objects
335
+ *
336
+ * @remarks
337
+ * OCS Status Codes:
338
+ * - 200: Success
339
+ */
340
+ async list(options = {}) {
341
+ const response = await this.http.get(BASE_PATH, {
342
+ noStatusUpdate: options.noStatusUpdate ? 1 : void 0,
343
+ includeStatus: options.includeStatus,
344
+ modifiedSince: options.modifiedSince,
345
+ includeLastMessage: options.includeLastMessage
346
+ });
347
+ return normalizeRooms(response.data);
348
+ }
349
+ /**
350
+ * Get a single room by token
351
+ *
352
+ * @param token - Room token
353
+ * @returns Normalized room object
354
+ *
355
+ * @remarks
356
+ * OCS Status Codes:
357
+ * - 200: Success
358
+ * - 401: Unauthorized
359
+ * - 404: Room not found
360
+ */
361
+ async get(token) {
362
+ const response = await this.http.get(`${BASE_PATH}/${token}`);
363
+ return normalizeRoom(response.data);
364
+ }
365
+ /**
366
+ * Create a new room
367
+ *
368
+ * @param options - Room creation options
369
+ * @returns Created room object
370
+ *
371
+ * @remarks
372
+ * OCS Status Codes:
373
+ * - 200: Existing room returned (for 1-1 conversations)
374
+ * - 201: Room created
375
+ * - 202: Room created with invalid invitations
376
+ */
377
+ async create(options) {
378
+ const response = await this.http.post(BASE_PATH, options);
379
+ return normalizeRoom(response.data);
380
+ }
381
+ /**
382
+ * Update room name
383
+ *
384
+ * @param token - Room token
385
+ * @param roomName - New room name
386
+ *
387
+ * @remarks
388
+ * Requires moderator permissions.
389
+ */
390
+ async rename(token, roomName) {
391
+ await this.http.put(`${BASE_PATH}/${token}`, { roomName });
392
+ }
393
+ /**
394
+ * Update room description
395
+ *
396
+ * @param token - Room token
397
+ * @param description - New description (max 2000 chars)
398
+ *
399
+ * @remarks
400
+ * Requires moderator permissions.
401
+ */
402
+ async setDescription(token, description) {
403
+ await this.http.put(`${BASE_PATH}/${token}/description`, { description });
404
+ }
405
+ /**
406
+ * Delete a room
407
+ *
408
+ * @param token - Room token
409
+ *
410
+ * @remarks
411
+ * Requires moderator permissions.
412
+ */
413
+ async delete(token) {
414
+ await this.http.delete(`${BASE_PATH}/${token}`);
415
+ }
416
+ /**
417
+ * Set room password
418
+ *
419
+ * @param token - Room token
420
+ * @param password - Room password (empty string to remove)
421
+ *
422
+ * @remarks
423
+ * Requires moderator permissions. Only for public rooms.
424
+ */
425
+ async setPassword(token, password) {
426
+ await this.http.put(`${BASE_PATH}/${token}/password`, { password });
427
+ }
428
+ /**
429
+ * Make room public (allow guests)
430
+ *
431
+ * @param token - Room token
432
+ *
433
+ * @remarks
434
+ * Requires logged-in moderator.
435
+ */
436
+ async makePublic(token) {
437
+ await this.http.post(`${BASE_PATH}/${token}/public`);
438
+ }
439
+ /**
440
+ * Make room private
441
+ *
442
+ * @param token - Room token
443
+ *
444
+ * @remarks
445
+ * Requires logged-in moderator.
446
+ */
447
+ async makePrivate(token) {
448
+ await this.http.delete(`${BASE_PATH}/${token}/public`);
449
+ }
450
+ /**
451
+ * Set read-only state
452
+ *
453
+ * @param token - Room token
454
+ * @param state - Read-only state (0 = read-write, 1 = read-only)
455
+ */
456
+ async setReadOnly(token, state) {
457
+ await this.http.put(`${BASE_PATH}/${token}/read-only`, { state });
458
+ }
459
+ /**
460
+ * Set listable scope
461
+ *
462
+ * @param token - Room token
463
+ * @param scope - Listable scope (0 = none, 1 = users, 2 = everyone)
464
+ */
465
+ async setListable(token, scope) {
466
+ await this.http.put(`${BASE_PATH}/${token}/listable`, { scope });
467
+ }
468
+ /**
469
+ * Add room to favorites
470
+ *
471
+ * @param token - Room token
472
+ */
473
+ async addToFavorites(token) {
474
+ await this.http.post(`${BASE_PATH}/${token}/favorite`);
475
+ }
476
+ /**
477
+ * Remove room from favorites
478
+ *
479
+ * @param token - Room token
480
+ */
481
+ async removeFromFavorites(token) {
482
+ await this.http.delete(`${BASE_PATH}/${token}/favorite`);
483
+ }
484
+ /**
485
+ * Archive room
486
+ *
487
+ * @param token - Room token
488
+ */
489
+ async archive(token) {
490
+ await this.http.post(`${BASE_PATH}/${token}/archive`);
491
+ }
492
+ /**
493
+ * Unarchive room
494
+ *
495
+ * @param token - Room token
496
+ */
497
+ async unarchive(token) {
498
+ await this.http.delete(`${BASE_PATH}/${token}/archive`);
499
+ }
500
+ /**
501
+ * Mark room as important (notifications on DND)
502
+ *
503
+ * @param token - Room token
504
+ */
505
+ async markImportant(token) {
506
+ await this.http.post(`${BASE_PATH}/${token}/important`);
507
+ }
508
+ /**
509
+ * Unmark room as important
510
+ *
511
+ * @param token - Room token
512
+ */
513
+ async unmarkImportant(token) {
514
+ await this.http.delete(`${BASE_PATH}/${token}/important`);
515
+ }
516
+ /**
517
+ * Mark room as sensitive (hide preview)
518
+ *
519
+ * @param token - Room token
520
+ */
521
+ async markSensitive(token) {
522
+ await this.http.post(`${BASE_PATH}/${token}/sensitive`);
523
+ }
524
+ /**
525
+ * Unmark room as sensitive
526
+ *
527
+ * @param token - Room token
528
+ */
529
+ async unmarkSensitive(token) {
530
+ await this.http.delete(`${BASE_PATH}/${token}/sensitive`);
531
+ }
532
+ /**
533
+ * Set notification level
534
+ *
535
+ * @param token - Room token
536
+ * @param level - Notification level (0-3)
537
+ */
538
+ async setNotificationLevel(token, level) {
539
+ await this.http.post(`${BASE_PATH}/${token}/notify`, { level });
540
+ }
541
+ /**
542
+ * Set call notification setting
543
+ *
544
+ * @param token - Room token
545
+ * @param level - Call notification level (0 = off, 1 = on)
546
+ */
547
+ async setCallNotifications(token, level) {
548
+ await this.http.post(`${BASE_PATH}/${token}/notify-calls`, { level });
549
+ }
550
+ /**
551
+ * Configure lobby
552
+ *
553
+ * @param token - Room token
554
+ * @param state - Lobby state (0 = disabled, 1 = enabled)
555
+ * @param timer - Optional Unix timestamp when lobby opens
556
+ */
557
+ async setLobby(token, state, timer) {
558
+ await this.http.put(`${BASE_PATH}/${token}/webinar/lobby`, { state, timer });
559
+ }
560
+ /**
561
+ * Set default or call permissions for the room
562
+ *
563
+ * @param token - Room token
564
+ * @param mode - Permission mode ('default' or 'call')
565
+ * @param permissions - Permission bitmask
566
+ */
567
+ async setPermissions(token, mode, permissions) {
568
+ await this.http.put(`${BASE_PATH}/${token}/permissions/${mode}`, { permissions });
569
+ }
570
+ /**
571
+ * Set message expiration time
572
+ *
573
+ * @param token - Room token
574
+ * @param seconds - Expiration time in seconds (0 = never)
575
+ */
576
+ async setMessageExpiration(token, seconds) {
577
+ await this.http.post(`${BASE_PATH}/${token}/message-expiration`, { seconds });
578
+ }
579
+ /**
580
+ * Get raw response with metadata headers (for advanced use)
581
+ *
582
+ * @param token - Room token
583
+ * @returns Full HTTP response with data and metadata
584
+ */
585
+ async getRaw(token) {
586
+ return this.http.get(`${BASE_PATH}/${token}`);
587
+ }
588
+ };
589
+
590
+ // src/resources/chat-resource.ts
591
+ var BASE_PATH2 = "/ocs/v2.php/apps/spreed/api/v1/chat";
592
+ var ChatResource = class {
593
+ constructor(http) {
594
+ this.http = http;
595
+ }
596
+ /**
597
+ * Receive messages from a room
598
+ *
599
+ * @param token - Room token
600
+ * @param options - Receive options (lookIntoFuture, limit, lastKnownMessageId, etc.)
601
+ * @returns Array of normalized messages
602
+ *
603
+ * @remarks
604
+ * OCS Status Codes:
605
+ * - 200: Messages returned
606
+ * - 304: No new messages (long-poll timeout)
607
+ */
608
+ async receiveMessages(token, options) {
609
+ const response = await this.http.get(
610
+ `${BASE_PATH2}/${token}`,
611
+ {
612
+ lookIntoFuture: options.lookIntoFuture,
613
+ limit: options.limit,
614
+ lastKnownMessageId: options.lastKnownMessageId,
615
+ timeout: options.timeout,
616
+ setReadMarker: options.setReadMarker === false ? 0 : 1,
617
+ includeLastKnown: options.includeLastKnown ? 1 : 0,
618
+ markNotificationsAsRead: options.markNotificationsAsRead === false ? 0 : 1,
619
+ threadId: options.threadId
620
+ }
621
+ );
622
+ return normalizeMessages(response.data);
623
+ }
624
+ /**
625
+ * Get message history (past messages)
626
+ *
627
+ * @param token - Room token
628
+ * @param options - Optional limit and lastKnownMessageId for pagination
629
+ * @returns Array of normalized messages
630
+ */
631
+ async getHistory(token, options = {}) {
632
+ return this.receiveMessages(token, {
633
+ lookIntoFuture: 0,
634
+ ...options
635
+ });
636
+ }
637
+ /**
638
+ * Poll for new messages (long-polling)
639
+ *
640
+ * This implements the spec's efficient update pattern for receiving
641
+ * new messages. The server holds the connection for up to `timeout` seconds.
642
+ *
643
+ * @param token - Room token
644
+ * @param options - Poll options with required lastKnownMessageId
645
+ * @returns Poll result with messages and metadata for next poll
646
+ *
647
+ * @example
648
+ * ```ts
649
+ * let lastKnownMessageId = 0;
650
+ *
651
+ * while (polling) {
652
+ * const result = await chat.pollMessages(token, { lastKnownMessageId });
653
+ * for (const message of result.messages) {
654
+ * console.log(message.message);
655
+ * }
656
+ * if (result.lastKnownMessageId) {
657
+ * lastKnownMessageId = result.lastKnownMessageId;
658
+ * }
659
+ * }
660
+ * ```
661
+ */
662
+ async pollMessages(token, options) {
663
+ const response = await this.http.request(
664
+ `${BASE_PATH2}/${token}`,
665
+ {
666
+ method: "GET",
667
+ params: {
668
+ lookIntoFuture: 1,
669
+ limit: options.limit ?? 100,
670
+ lastKnownMessageId: options.lastKnownMessageId,
671
+ timeout: options.timeout ?? 30,
672
+ threadId: options.threadId
673
+ },
674
+ timeout: ((options.timeout ?? 30) + 5) * 1e3
675
+ // Add buffer to server timeout
676
+ }
677
+ );
678
+ return {
679
+ messages: normalizeMessages(response.data),
680
+ lastKnownMessageId: response.meta.chatLastGiven,
681
+ lastCommonRead: response.meta.chatLastCommonRead
682
+ };
683
+ }
684
+ /**
685
+ * Send a message to a room
686
+ *
687
+ * @param token - Room token
688
+ * @param options - Message options
689
+ * @returns Sent message
690
+ *
691
+ * @remarks
692
+ * OCS Status Codes:
693
+ * - 201: Message sent
694
+ * - 400: Sending not possible
695
+ * - 404: Actor not found
696
+ * - 413: Message too long (max 32000 chars)
697
+ * - 429: Mention rate limit exceeded
698
+ */
699
+ async sendMessage(token, options) {
700
+ const response = await this.http.post(
701
+ `${BASE_PATH2}/${token}`,
702
+ {
703
+ message: options.message,
704
+ actorDisplayName: options.actorDisplayName,
705
+ referenceId: options.referenceId,
706
+ replyTo: options.replyTo,
707
+ silent: options.silent,
708
+ threadId: options.threadId,
709
+ threadTitle: options.threadTitle
710
+ }
711
+ );
712
+ return normalizeMessageWithParent(response.data);
713
+ }
714
+ /**
715
+ * Edit a message
716
+ *
717
+ * @param token - Room token
718
+ * @param messageId - Message ID to edit
719
+ * @param message - New message content
720
+ * @returns Updated message
721
+ */
722
+ async editMessage(token, messageId, message) {
723
+ const response = await this.http.put(
724
+ `${BASE_PATH2}/${token}/${messageId}`,
725
+ { message }
726
+ );
727
+ return normalizeMessageWithParent(response.data);
728
+ }
729
+ /**
730
+ * Delete a message
731
+ *
732
+ * @param token - Room token
733
+ * @param messageId - Message ID to delete
734
+ * @returns Deleted message placeholder
735
+ */
736
+ async deleteMessage(token, messageId) {
737
+ const response = await this.http.delete(
738
+ `${BASE_PATH2}/${token}/${messageId}`
739
+ );
740
+ return normalizeMessageWithParent(response.data);
741
+ }
742
+ /**
743
+ * Get message context (surrounding messages)
744
+ *
745
+ * @param token - Room token
746
+ * @param messageId - Message ID to get context for
747
+ * @param options - Optional limit and threadId
748
+ * @returns Array of messages around the target message
749
+ */
750
+ async getContext(token, messageId, options = {}) {
751
+ const response = await this.http.get(
752
+ `${BASE_PATH2}/${token}/${messageId}/context`,
753
+ {
754
+ limit: options.limit,
755
+ threadId: options.threadId
756
+ }
757
+ );
758
+ return normalizeMessages(response.data);
759
+ }
760
+ /**
761
+ * Share a rich object (file, deck card, etc.)
762
+ *
763
+ * @param token - Room token
764
+ * @param options - Share options
765
+ * @returns Shared message
766
+ */
767
+ async shareObject(token, options) {
768
+ const response = await this.http.post(
769
+ `${BASE_PATH2}/${token}/share`,
770
+ {
771
+ objectType: options.objectType,
772
+ objectId: options.objectId,
773
+ metaData: options.metaData,
774
+ referenceId: options.referenceId,
775
+ threadId: options.threadId
776
+ }
777
+ );
778
+ return normalizeMessageWithParent(response.data);
779
+ }
780
+ /**
781
+ * Clear chat history
782
+ *
783
+ * @param token - Room token
784
+ *
785
+ * @remarks
786
+ * Requires moderator permissions.
787
+ */
788
+ async clearHistory(token) {
789
+ await this.http.delete(`${BASE_PATH2}/${token}`);
790
+ }
791
+ /**
792
+ * Set read marker to a specific message
793
+ *
794
+ * @param token - Room token
795
+ * @param lastReadMessage - Message ID to mark as last read
796
+ */
797
+ async setReadMarker(token, lastReadMessage) {
798
+ await this.http.post(`${BASE_PATH2}/${token}/read`, { lastReadMessage });
799
+ }
800
+ /**
801
+ * Mark chat as unread
802
+ *
803
+ * @param token - Room token
804
+ */
805
+ async markUnread(token) {
806
+ await this.http.delete(`${BASE_PATH2}/${token}/read`);
807
+ }
808
+ /**
809
+ * Search for mention suggestions
810
+ *
811
+ * @param token - Room token
812
+ * @param search - Search query
813
+ * @param options - Optional limit and includeStatus
814
+ * @returns Array of mention suggestions
815
+ */
816
+ async searchMentions(token, search, options = {}) {
817
+ const response = await this.http.get(
818
+ `${BASE_PATH2}/${token}/mentions`,
819
+ {
820
+ search,
821
+ limit: options.limit ?? 20,
822
+ includeStatus: options.includeStatus
823
+ }
824
+ );
825
+ return response.data;
826
+ }
827
+ /**
828
+ * Pin a message
829
+ *
830
+ * @param token - Room token
831
+ * @param messageId - Message ID to pin
832
+ * @param pinUntil - Optional Unix timestamp when pin expires
833
+ *
834
+ * @remarks
835
+ * Requires moderator permissions.
836
+ */
837
+ async pinMessage(token, messageId, pinUntil) {
838
+ await this.http.post(`${BASE_PATH2}/${token}/${messageId}/pin`, { pinUntil });
839
+ }
840
+ /**
841
+ * Unpin a message
842
+ *
843
+ * @param token - Room token
844
+ * @param messageId - Message ID to unpin
845
+ *
846
+ * @remarks
847
+ * Requires moderator permissions.
848
+ */
849
+ async unpinMessage(token, messageId) {
850
+ await this.http.delete(`${BASE_PATH2}/${token}/${messageId}/pin`);
851
+ }
852
+ /**
853
+ * Hide a pinned message for the current user only
854
+ *
855
+ * @param token - Room token
856
+ * @param messageId - Message ID to hide
857
+ */
858
+ async hidePinnedMessage(token, messageId) {
859
+ await this.http.delete(`${BASE_PATH2}/${token}/${messageId}/pin/self`);
860
+ }
861
+ /**
862
+ * Set a reminder for a message
863
+ *
864
+ * @param token - Room token
865
+ * @param messageId - Message ID
866
+ * @param timestamp - Unix timestamp for reminder
867
+ */
868
+ async setReminder(token, messageId, timestamp) {
869
+ await this.http.post(`${BASE_PATH2}/${token}/${messageId}/reminder`, { timestamp });
870
+ }
871
+ /**
872
+ * Delete a message reminder
873
+ *
874
+ * @param token - Room token
875
+ * @param messageId - Message ID
876
+ */
877
+ async deleteReminder(token, messageId) {
878
+ await this.http.delete(`${BASE_PATH2}/${token}/${messageId}/reminder`);
879
+ }
880
+ };
881
+
882
+ // src/resources/participant-resource.ts
883
+ var BASE_PATH3 = "/ocs/v2.php/apps/spreed/api/v4/room";
884
+ var ParticipantResource = class {
885
+ constructor(http) {
886
+ this.http = http;
887
+ }
888
+ /**
889
+ * Get list of participants in a room
890
+ *
891
+ * @param token - Room token
892
+ * @param includeStatus - Include user status information
893
+ * @returns Array of normalized participants
894
+ */
895
+ async list(token, includeStatus = false) {
896
+ const response = await this.http.get(
897
+ `${BASE_PATH3}/${token}/participants`,
898
+ { includeStatus }
899
+ );
900
+ return normalizeParticipants(response.data);
901
+ }
902
+ /**
903
+ * Add a participant to a room
904
+ *
905
+ * @param token - Room token
906
+ * @param options - Participant options
907
+ *
908
+ * @remarks
909
+ * Requires logged-in moderator.
910
+ */
911
+ async add(token, options) {
912
+ await this.http.post(`${BASE_PATH3}/${token}/participants`, {
913
+ newParticipant: options.newParticipant,
914
+ source: options.source
915
+ });
916
+ }
917
+ /**
918
+ * Remove a participant from a room
919
+ *
920
+ * @param token - Room token
921
+ * @param attendeeId - Attendee ID to remove
922
+ *
923
+ * @remarks
924
+ * Requires moderator permissions.
925
+ */
926
+ async remove(token, attendeeId) {
927
+ await this.http.delete(`${BASE_PATH3}/${token}/attendees`, { attendeeId });
928
+ }
929
+ /**
930
+ * Remove self from a room
931
+ *
932
+ * @param token - Room token
933
+ */
934
+ async removeSelf(token) {
935
+ await this.http.delete(`${BASE_PATH3}/${token}/participants/self`);
936
+ }
937
+ /**
938
+ * Join a room (create session)
939
+ *
940
+ * @param token - Room token
941
+ * @param options - Join options
942
+ * @returns Room data after joining
943
+ *
944
+ * @remarks
945
+ * OCS Status Codes:
946
+ * - 200: Joined successfully
947
+ * - 409: Conflict - already joined on another device
948
+ *
949
+ * On 409, the response contains session conflict info.
950
+ */
951
+ async join(token, options = {}) {
952
+ const response = await this.http.post(
953
+ `${BASE_PATH3}/${token}/participants/active`,
954
+ {
955
+ password: options.password,
956
+ force: options.force
957
+ }
958
+ );
959
+ return normalizeRoom(response.data);
960
+ }
961
+ /**
962
+ * Leave a room (end session)
963
+ *
964
+ * @param token - Room token
965
+ */
966
+ async leave(token) {
967
+ await this.http.delete(`${BASE_PATH3}/${token}/participants/active`);
968
+ }
969
+ /**
970
+ * Set session state (active/inactive)
971
+ *
972
+ * @param token - Room token
973
+ * @param state - 0 = inactive, 1 = active
974
+ */
975
+ async setSessionState(token, state) {
976
+ await this.http.put(`${BASE_PATH3}/${token}/participants/state`, { state });
977
+ }
978
+ /**
979
+ * Promote a participant to moderator
980
+ *
981
+ * @param token - Room token
982
+ * @param attendeeId - Attendee ID to promote
983
+ */
984
+ async promoteModerator(token, attendeeId) {
985
+ await this.http.post(`${BASE_PATH3}/${token}/moderators`, { attendeeId });
986
+ }
987
+ /**
988
+ * Demote a moderator to regular participant
989
+ *
990
+ * @param token - Room token
991
+ * @param attendeeId - Attendee ID to demote
992
+ */
993
+ async demoteModerator(token, attendeeId) {
994
+ await this.http.delete(`${BASE_PATH3}/${token}/moderators`, { attendeeId });
995
+ }
996
+ /**
997
+ * Set permissions for an attendee
998
+ *
999
+ * @param token - Room token
1000
+ * @param options - Permission options
1001
+ */
1002
+ async setPermissions(token, options) {
1003
+ await this.http.put(`${BASE_PATH3}/${token}/attendees/permissions`, {
1004
+ attendeeId: options.attendeeId,
1005
+ method: options.method,
1006
+ permissions: options.permissions
1007
+ });
1008
+ }
1009
+ /**
1010
+ * Resend invitations to pending participants
1011
+ *
1012
+ * @param token - Room token
1013
+ * @param attendeeId - Optional specific attendee, or all if omitted
1014
+ */
1015
+ async resendInvitations(token, attendeeId) {
1016
+ await this.http.post(`${BASE_PATH3}/${token}/participants/resend-invitations`, {
1017
+ attendeeId
1018
+ });
1019
+ }
1020
+ };
1021
+
1022
+ // src/types/constants.ts
1023
+ var ConversationType = /* @__PURE__ */ ((ConversationType2) => {
1024
+ ConversationType2[ConversationType2["ONE_TO_ONE"] = 1] = "ONE_TO_ONE";
1025
+ ConversationType2[ConversationType2["GROUP"] = 2] = "GROUP";
1026
+ ConversationType2[ConversationType2["PUBLIC"] = 3] = "PUBLIC";
1027
+ ConversationType2[ConversationType2["CHANGELOG"] = 4] = "CHANGELOG";
1028
+ ConversationType2[ConversationType2["ONE_TO_ONE_FORMER"] = 5] = "ONE_TO_ONE_FORMER";
1029
+ ConversationType2[ConversationType2["NOTE_TO_SELF"] = 6] = "NOTE_TO_SELF";
1030
+ return ConversationType2;
1031
+ })(ConversationType || {});
1032
+ var ParticipantType = /* @__PURE__ */ ((ParticipantType2) => {
1033
+ ParticipantType2[ParticipantType2["OWNER"] = 1] = "OWNER";
1034
+ ParticipantType2[ParticipantType2["MODERATOR"] = 2] = "MODERATOR";
1035
+ ParticipantType2[ParticipantType2["USER"] = 3] = "USER";
1036
+ ParticipantType2[ParticipantType2["GUEST"] = 4] = "GUEST";
1037
+ ParticipantType2[ParticipantType2["USER_SELF_JOINED"] = 5] = "USER_SELF_JOINED";
1038
+ ParticipantType2[ParticipantType2["GUEST_MODERATOR"] = 6] = "GUEST_MODERATOR";
1039
+ return ParticipantType2;
1040
+ })(ParticipantType || {});
1041
+ var Permission = /* @__PURE__ */ ((Permission2) => {
1042
+ Permission2[Permission2["DEFAULT"] = 0] = "DEFAULT";
1043
+ Permission2[Permission2["CUSTOM"] = 1] = "CUSTOM";
1044
+ Permission2[Permission2["CALL_START"] = 2] = "CALL_START";
1045
+ Permission2[Permission2["CALL_JOIN"] = 4] = "CALL_JOIN";
1046
+ Permission2[Permission2["LOBBY_IGNORE"] = 8] = "LOBBY_IGNORE";
1047
+ Permission2[Permission2["PUBLISH_AUDIO"] = 16] = "PUBLISH_AUDIO";
1048
+ Permission2[Permission2["PUBLISH_VIDEO"] = 32] = "PUBLISH_VIDEO";
1049
+ Permission2[Permission2["PUBLISH_SCREEN"] = 64] = "PUBLISH_SCREEN";
1050
+ Permission2[Permission2["CHAT"] = 128] = "CHAT";
1051
+ Permission2[Permission2["FILE_SHARE"] = 256] = "FILE_SHARE";
1052
+ return Permission2;
1053
+ })(Permission || {});
1054
+ var InCallFlag = /* @__PURE__ */ ((InCallFlag2) => {
1055
+ InCallFlag2[InCallFlag2["DISCONNECTED"] = 0] = "DISCONNECTED";
1056
+ InCallFlag2[InCallFlag2["IN_CALL"] = 1] = "IN_CALL";
1057
+ InCallFlag2[InCallFlag2["WITH_AUDIO"] = 2] = "WITH_AUDIO";
1058
+ InCallFlag2[InCallFlag2["WITH_VIDEO"] = 4] = "WITH_VIDEO";
1059
+ InCallFlag2[InCallFlag2["WITH_PHONE"] = 8] = "WITH_PHONE";
1060
+ return InCallFlag2;
1061
+ })(InCallFlag || {});
1062
+ var ActorType = /* @__PURE__ */ ((ActorType2) => {
1063
+ ActorType2["USERS"] = "users";
1064
+ ActorType2["GUESTS"] = "guests";
1065
+ ActorType2["EMAILS"] = "emails";
1066
+ ActorType2["GROUPS"] = "groups";
1067
+ ActorType2["CIRCLES"] = "circles";
1068
+ ActorType2["BOTS"] = "bots";
1069
+ ActorType2["BRIDGED"] = "bridged";
1070
+ ActorType2["FEDERATED_USERS"] = "federated_users";
1071
+ ActorType2["PHONES"] = "phones";
1072
+ return ActorType2;
1073
+ })(ActorType || {});
1074
+ var MessageType = /* @__PURE__ */ ((MessageType2) => {
1075
+ MessageType2["COMMENT"] = "comment";
1076
+ MessageType2["SYSTEM"] = "system";
1077
+ MessageType2["COMMAND"] = "command";
1078
+ MessageType2["COMMENT_DELETED"] = "comment_deleted";
1079
+ MessageType2["VOICE_MESSAGE"] = "voice-message";
1080
+ MessageType2["RECORD_AUDIO"] = "record-audio";
1081
+ MessageType2["RECORD_VIDEO"] = "record-video";
1082
+ MessageType2["POLL"] = "poll";
1083
+ return MessageType2;
1084
+ })(MessageType || {});
1085
+ var NotificationLevel = /* @__PURE__ */ ((NotificationLevel2) => {
1086
+ NotificationLevel2[NotificationLevel2["DEFAULT"] = 0] = "DEFAULT";
1087
+ NotificationLevel2[NotificationLevel2["ALWAYS"] = 1] = "ALWAYS";
1088
+ NotificationLevel2[NotificationLevel2["MENTION"] = 2] = "MENTION";
1089
+ NotificationLevel2[NotificationLevel2["NEVER"] = 3] = "NEVER";
1090
+ return NotificationLevel2;
1091
+ })(NotificationLevel || {});
1092
+ var ReadOnlyState = /* @__PURE__ */ ((ReadOnlyState2) => {
1093
+ ReadOnlyState2[ReadOnlyState2["READ_WRITE"] = 0] = "READ_WRITE";
1094
+ ReadOnlyState2[ReadOnlyState2["READ_ONLY"] = 1] = "READ_ONLY";
1095
+ return ReadOnlyState2;
1096
+ })(ReadOnlyState || {});
1097
+ var LobbyState = /* @__PURE__ */ ((LobbyState2) => {
1098
+ LobbyState2[LobbyState2["DISABLED"] = 0] = "DISABLED";
1099
+ LobbyState2[LobbyState2["ENABLED"] = 1] = "ENABLED";
1100
+ return LobbyState2;
1101
+ })(LobbyState || {});
1102
+ var SIPState = /* @__PURE__ */ ((SIPState2) => {
1103
+ SIPState2[SIPState2["DISABLED"] = 0] = "DISABLED";
1104
+ SIPState2[SIPState2["ENABLED_NO_PIN"] = 1] = "ENABLED_NO_PIN";
1105
+ SIPState2[SIPState2["ENABLED_WITH_PIN"] = 2] = "ENABLED_WITH_PIN";
1106
+ return SIPState2;
1107
+ })(SIPState || {});
1108
+ var RecordingStatus = /* @__PURE__ */ ((RecordingStatus2) => {
1109
+ RecordingStatus2[RecordingStatus2["NONE"] = 0] = "NONE";
1110
+ RecordingStatus2[RecordingStatus2["VIDEO"] = 1] = "VIDEO";
1111
+ RecordingStatus2[RecordingStatus2["AUDIO"] = 2] = "AUDIO";
1112
+ RecordingStatus2[RecordingStatus2["STARTING_VIDEO"] = 3] = "STARTING_VIDEO";
1113
+ RecordingStatus2[RecordingStatus2["STARTING_AUDIO"] = 4] = "STARTING_AUDIO";
1114
+ RecordingStatus2[RecordingStatus2["FAILED"] = 5] = "FAILED";
1115
+ return RecordingStatus2;
1116
+ })(RecordingStatus || {});
1117
+ var ListableScope = /* @__PURE__ */ ((ListableScope2) => {
1118
+ ListableScope2[ListableScope2["NONE"] = 0] = "NONE";
1119
+ ListableScope2[ListableScope2["USERS"] = 1] = "USERS";
1120
+ ListableScope2[ListableScope2["EVERYONE"] = 2] = "EVERYONE";
1121
+ return ListableScope2;
1122
+ })(ListableScope || {});
1123
+ var PollResultMode = /* @__PURE__ */ ((PollResultMode2) => {
1124
+ PollResultMode2[PollResultMode2["PUBLIC"] = 0] = "PUBLIC";
1125
+ PollResultMode2[PollResultMode2["HIDDEN"] = 1] = "HIDDEN";
1126
+ return PollResultMode2;
1127
+ })(PollResultMode || {});
1128
+ var BreakoutRoomMode = /* @__PURE__ */ ((BreakoutRoomMode2) => {
1129
+ BreakoutRoomMode2[BreakoutRoomMode2["NOT_CONFIGURED"] = 0] = "NOT_CONFIGURED";
1130
+ BreakoutRoomMode2[BreakoutRoomMode2["AUTOMATIC"] = 1] = "AUTOMATIC";
1131
+ BreakoutRoomMode2[BreakoutRoomMode2["MANUAL"] = 2] = "MANUAL";
1132
+ BreakoutRoomMode2[BreakoutRoomMode2["FREE"] = 3] = "FREE";
1133
+ return BreakoutRoomMode2;
1134
+ })(BreakoutRoomMode || {});
1135
+ var SharedObjectType = /* @__PURE__ */ ((SharedObjectType2) => {
1136
+ SharedObjectType2["FILE"] = "file";
1137
+ SharedObjectType2["AUDIO"] = "audio";
1138
+ SharedObjectType2["VOICE"] = "voice";
1139
+ SharedObjectType2["VIDEO"] = "video";
1140
+ SharedObjectType2["LOCATION"] = "location";
1141
+ SharedObjectType2["DECK_CARD"] = "deckcard";
1142
+ SharedObjectType2["OTHER"] = "other";
1143
+ SharedObjectType2["POLL"] = "poll";
1144
+ SharedObjectType2["RECORDING"] = "recording";
1145
+ return SharedObjectType2;
1146
+ })(SharedObjectType || {});
1147
+
1148
+ // src/resources/call-resource.ts
1149
+ var CALL_PATH = "/ocs/v2.php/apps/spreed/api/v4/call";
1150
+ var SIGNALING_PATH = "/ocs/v2.php/apps/spreed/api/v3/signaling";
1151
+ var CallResource = class {
1152
+ constructor(http) {
1153
+ this.http = http;
1154
+ }
1155
+ /**
1156
+ * Get peers currently in a call
1157
+ *
1158
+ * @param token - Room token
1159
+ * @returns Array of call peers
1160
+ */
1161
+ async getPeers(token) {
1162
+ const response = await this.http.get(`${CALL_PATH}/${token}`);
1163
+ return response.data;
1164
+ }
1165
+ /**
1166
+ * Join a call
1167
+ *
1168
+ * @param token - Room token
1169
+ * @param options - Join options (flags, silent, etc.)
1170
+ *
1171
+ * @remarks
1172
+ * Default flags are IN_CALL | WITH_AUDIO | WITH_VIDEO (7).
1173
+ */
1174
+ async join(token, options = {}) {
1175
+ const flags = options.flags ?? 1 /* IN_CALL */ | 2 /* WITH_AUDIO */ | 4 /* WITH_VIDEO */;
1176
+ await this.http.post(`${CALL_PATH}/${token}`, {
1177
+ flags,
1178
+ silent: options.silent,
1179
+ recordingConsent: options.recordingConsent,
1180
+ silentFor: options.silentFor
1181
+ });
1182
+ }
1183
+ /**
1184
+ * Update call flags (audio/video status)
1185
+ *
1186
+ * @param token - Room token
1187
+ * @param flags - New in-call flags
1188
+ */
1189
+ async updateFlags(token, flags) {
1190
+ await this.http.put(`${CALL_PATH}/${token}`, { flags });
1191
+ }
1192
+ /**
1193
+ * Leave a call
1194
+ *
1195
+ * @param token - Room token
1196
+ * @param options - Leave options
1197
+ */
1198
+ async leave(token, options = {}) {
1199
+ await this.http.delete(`${CALL_PATH}/${token}`, { all: options.all });
1200
+ }
1201
+ /**
1202
+ * Ring a specific attendee
1203
+ *
1204
+ * @param token - Room token
1205
+ * @param attendeeId - Attendee ID to ring
1206
+ *
1207
+ * @remarks
1208
+ * Requires START_CALL permission.
1209
+ */
1210
+ async ringAttendee(token, attendeeId) {
1211
+ await this.http.post(`${CALL_PATH}/${token}/ring/${attendeeId}`);
1212
+ }
1213
+ /**
1214
+ * Initiate SIP dial-out to a phone participant
1215
+ *
1216
+ * @param token - Room token
1217
+ * @param attendeeId - Attendee ID to dial
1218
+ */
1219
+ async dialOut(token, attendeeId) {
1220
+ await this.http.post(`${CALL_PATH}/${token}/dialout/${attendeeId}`);
1221
+ }
1222
+ /**
1223
+ * Get signaling settings for WebRTC
1224
+ *
1225
+ * @param token - Room token
1226
+ * @returns Signaling configuration including STUN/TURN servers
1227
+ */
1228
+ async getSignalingSettings(token) {
1229
+ const response = await this.http.get(
1230
+ `${SIGNALING_PATH}/settings`,
1231
+ { token }
1232
+ );
1233
+ return response.data;
1234
+ }
1235
+ // Convenience methods for common flag operations
1236
+ /**
1237
+ * Enable audio in call
1238
+ *
1239
+ * @param token - Room token
1240
+ * @param currentFlags - Current in-call flags
1241
+ */
1242
+ async enableAudio(token, currentFlags) {
1243
+ await this.updateFlags(token, currentFlags | 2 /* WITH_AUDIO */);
1244
+ }
1245
+ /**
1246
+ * Disable audio in call
1247
+ *
1248
+ * @param token - Room token
1249
+ * @param currentFlags - Current in-call flags
1250
+ */
1251
+ async disableAudio(token, currentFlags) {
1252
+ await this.updateFlags(token, currentFlags & ~2 /* WITH_AUDIO */);
1253
+ }
1254
+ /**
1255
+ * Enable video in call
1256
+ *
1257
+ * @param token - Room token
1258
+ * @param currentFlags - Current in-call flags
1259
+ */
1260
+ async enableVideo(token, currentFlags) {
1261
+ await this.updateFlags(token, currentFlags | 4 /* WITH_VIDEO */);
1262
+ }
1263
+ /**
1264
+ * Disable video in call
1265
+ *
1266
+ * @param token - Room token
1267
+ * @param currentFlags - Current in-call flags
1268
+ */
1269
+ async disableVideo(token, currentFlags) {
1270
+ await this.updateFlags(token, currentFlags & ~4 /* WITH_VIDEO */);
1271
+ }
1272
+ };
1273
+
1274
+ // src/resources/poll-resource.ts
1275
+ var BASE_PATH4 = "/ocs/v2.php/apps/spreed/api/v1/poll";
1276
+ var PollResource = class {
1277
+ constructor(http) {
1278
+ this.http = http;
1279
+ }
1280
+ /**
1281
+ * Create a new poll
1282
+ *
1283
+ * @param token - Room token
1284
+ * @param options - Poll creation options
1285
+ * @returns Created poll
1286
+ */
1287
+ async create(token, options) {
1288
+ const response = await this.http.post(`${BASE_PATH4}/${token}`, {
1289
+ question: options.question,
1290
+ options: options.options,
1291
+ resultMode: options.resultMode ?? 0,
1292
+ maxVotes: options.maxVotes ?? 1,
1293
+ draft: options.draft,
1294
+ threadId: options.threadId
1295
+ });
1296
+ return response.data;
1297
+ }
1298
+ /**
1299
+ * Get a poll by ID
1300
+ *
1301
+ * @param token - Room token
1302
+ * @param pollId - Poll ID
1303
+ * @returns Poll data
1304
+ */
1305
+ async get(token, pollId) {
1306
+ const response = await this.http.get(`${BASE_PATH4}/${token}/${pollId}`);
1307
+ return response.data;
1308
+ }
1309
+ /**
1310
+ * Vote on a poll
1311
+ *
1312
+ * @param token - Room token
1313
+ * @param pollId - Poll ID
1314
+ * @param options - Vote options (option indices)
1315
+ * @returns Updated poll
1316
+ */
1317
+ async vote(token, pollId, options) {
1318
+ const response = await this.http.post(`${BASE_PATH4}/${token}/${pollId}`, {
1319
+ optionIds: options.optionIds
1320
+ });
1321
+ return response.data;
1322
+ }
1323
+ /**
1324
+ * Close or delete a poll
1325
+ *
1326
+ * @param token - Room token
1327
+ * @param pollId - Poll ID
1328
+ * @returns Closed poll
1329
+ */
1330
+ async close(token, pollId) {
1331
+ const response = await this.http.delete(`${BASE_PATH4}/${token}/${pollId}`);
1332
+ return response.data;
1333
+ }
1334
+ /**
1335
+ * Get poll drafts
1336
+ *
1337
+ * @param token - Room token
1338
+ * @returns Array of draft polls
1339
+ */
1340
+ async getDrafts(token) {
1341
+ const response = await this.http.get(`${BASE_PATH4}/${token}/drafts`);
1342
+ return response.data;
1343
+ }
1344
+ /**
1345
+ * Update a poll draft
1346
+ *
1347
+ * @param token - Room token
1348
+ * @param pollId - Poll draft ID
1349
+ * @param options - Updated poll options
1350
+ * @returns Updated draft
1351
+ */
1352
+ async updateDraft(token, pollId, options) {
1353
+ const response = await this.http.post(`${BASE_PATH4}/${token}/draft/${pollId}`, {
1354
+ question: options.question,
1355
+ options: options.options,
1356
+ resultMode: options.resultMode,
1357
+ maxVotes: options.maxVotes
1358
+ });
1359
+ return response.data;
1360
+ }
1361
+ };
1362
+
1363
+ // src/resources/reaction-resource.ts
1364
+ var BASE_PATH5 = "/ocs/v2.php/apps/spreed/api/v1/reaction";
1365
+ var ReactionResource = class {
1366
+ constructor(http) {
1367
+ this.http = http;
1368
+ }
1369
+ /**
1370
+ * Add a reaction to a message
1371
+ *
1372
+ * @param token - Room token
1373
+ * @param messageId - Message ID
1374
+ * @param reaction - Emoji reaction (e.g., "👍")
1375
+ * @returns Updated reactions map
1376
+ */
1377
+ async add(token, messageId, reaction) {
1378
+ const response = await this.http.post(
1379
+ `${BASE_PATH5}/${token}/${messageId}`,
1380
+ { reaction }
1381
+ );
1382
+ return response.data;
1383
+ }
1384
+ /**
1385
+ * Remove a reaction from a message
1386
+ *
1387
+ * @param token - Room token
1388
+ * @param messageId - Message ID
1389
+ * @param reaction - Emoji reaction to remove
1390
+ * @returns Updated reactions map
1391
+ */
1392
+ async remove(token, messageId, reaction) {
1393
+ const response = await this.http.delete(
1394
+ `${BASE_PATH5}/${token}/${messageId}`,
1395
+ { reaction }
1396
+ );
1397
+ return response.data;
1398
+ }
1399
+ /**
1400
+ * Get reactions for a message
1401
+ *
1402
+ * @param token - Room token
1403
+ * @param messageId - Message ID
1404
+ * @param reaction - Optional specific emoji to filter
1405
+ * @returns Reactions grouped by emoji
1406
+ */
1407
+ async get(token, messageId, reaction) {
1408
+ const response = await this.http.get(
1409
+ `${BASE_PATH5}/${token}/${messageId}`,
1410
+ { reaction }
1411
+ );
1412
+ return response.data;
1413
+ }
1414
+ };
1415
+
1416
+ // src/domain/room.ts
1417
+ var Room = class {
1418
+ /** Room token - primary API identifier */
1419
+ token;
1420
+ /** Room internal database ID */
1421
+ id;
1422
+ /** Conversation type */
1423
+ type;
1424
+ /** Room name */
1425
+ name;
1426
+ /** Computed display name */
1427
+ displayName;
1428
+ /** Room description */
1429
+ description;
1430
+ /** Current user's participant type/role */
1431
+ participantType;
1432
+ /** Current user's effective permissions */
1433
+ permissions;
1434
+ /** Whether room has a password */
1435
+ hasPassword;
1436
+ /** Whether there's an active call */
1437
+ hasCall;
1438
+ /** Read-only state */
1439
+ readOnly;
1440
+ /** Lobby state */
1441
+ lobbyState;
1442
+ /** Number of unread messages */
1443
+ unreadMessages;
1444
+ /** Whether there are unread mentions */
1445
+ unreadMention;
1446
+ /** Whether this is a favorite room */
1447
+ isFavorite;
1448
+ /** Whether this room is archived */
1449
+ isArchived;
1450
+ /** Last activity timestamp */
1451
+ lastActivity;
1452
+ /** Raw normalized data for advanced access */
1453
+ _data;
1454
+ resources;
1455
+ constructor(data, resources) {
1456
+ this._data = data;
1457
+ this.resources = resources;
1458
+ this.token = data.token;
1459
+ this.id = data.id;
1460
+ this.type = data.type;
1461
+ this.name = data.name;
1462
+ this.displayName = data.displayName;
1463
+ this.description = data.description;
1464
+ this.participantType = data.participantType;
1465
+ this.permissions = data.permissions;
1466
+ this.hasPassword = data.hasPassword;
1467
+ this.hasCall = data.hasCall;
1468
+ this.readOnly = data.readOnly;
1469
+ this.lobbyState = data.lobbyState;
1470
+ this.unreadMessages = data.unreadMessages;
1471
+ this.unreadMention = data.unreadMention;
1472
+ this.lastActivity = data.lastActivity;
1473
+ this.isFavorite = data.isFavorite;
1474
+ this.isArchived = data.isArchived;
1475
+ }
1476
+ // ==================== Chat Operations ====================
1477
+ /**
1478
+ * Send a message to this room
1479
+ *
1480
+ * @param message - Message content or full options
1481
+ * @returns Sent message
1482
+ */
1483
+ async sendMessage(message) {
1484
+ const options = typeof message === "string" ? { message } : message;
1485
+ return this.resources.chat.sendMessage(this.token, options);
1486
+ }
1487
+ /**
1488
+ * Get message history
1489
+ *
1490
+ * @param options - Optional limit and pagination
1491
+ * @returns Array of messages
1492
+ */
1493
+ async getHistory(options = {}) {
1494
+ return this.resources.chat.getHistory(this.token, options);
1495
+ }
1496
+ /**
1497
+ * Poll for new messages (long-polling)
1498
+ *
1499
+ * @param options - Poll options with lastKnownMessageId
1500
+ * @returns Poll result with messages and metadata
1501
+ */
1502
+ async pollMessages(options) {
1503
+ return this.resources.chat.pollMessages(this.token, options);
1504
+ }
1505
+ /**
1506
+ * Edit a message
1507
+ *
1508
+ * @param messageId - Message ID to edit
1509
+ * @param newContent - New message content
1510
+ * @returns Updated message
1511
+ */
1512
+ async editMessage(messageId, newContent) {
1513
+ return this.resources.chat.editMessage(this.token, messageId, newContent);
1514
+ }
1515
+ /**
1516
+ * Delete a message
1517
+ *
1518
+ * @param messageId - Message ID to delete
1519
+ * @returns Deleted message placeholder
1520
+ */
1521
+ async deleteMessage(messageId) {
1522
+ return this.resources.chat.deleteMessage(this.token, messageId);
1523
+ }
1524
+ /**
1525
+ * Set the read marker
1526
+ *
1527
+ * @param lastReadMessage - Message ID to mark as last read
1528
+ */
1529
+ async setReadMarker(lastReadMessage) {
1530
+ return this.resources.chat.setReadMarker(this.token, lastReadMessage);
1531
+ }
1532
+ // ==================== Participant Operations ====================
1533
+ /**
1534
+ * Get participants in this room
1535
+ *
1536
+ * @param includeStatus - Include user status information
1537
+ * @returns Array of participants
1538
+ */
1539
+ async getParticipants(includeStatus = false) {
1540
+ return this.resources.participants.list(this.token, includeStatus);
1541
+ }
1542
+ /**
1543
+ * Add a user to this room
1544
+ *
1545
+ * @param userId - User ID to add
1546
+ * @param source - Source type (default: 'users')
1547
+ */
1548
+ async addParticipant(userId, source = "users") {
1549
+ return this.resources.participants.add(this.token, {
1550
+ newParticipant: userId,
1551
+ source
1552
+ });
1553
+ }
1554
+ /**
1555
+ * Remove a participant from this room
1556
+ *
1557
+ * @param attendeeId - Attendee ID to remove
1558
+ */
1559
+ async removeParticipant(attendeeId) {
1560
+ return this.resources.participants.remove(this.token, attendeeId);
1561
+ }
1562
+ /**
1563
+ * Leave this room
1564
+ */
1565
+ async leave() {
1566
+ return this.resources.participants.removeSelf(this.token);
1567
+ }
1568
+ // ==================== Call Operations ====================
1569
+ /**
1570
+ * Get peers currently in the call
1571
+ *
1572
+ * @returns Array of call peers
1573
+ */
1574
+ async getCallPeers() {
1575
+ return this.resources.call.getPeers(this.token);
1576
+ }
1577
+ /**
1578
+ * Join the call
1579
+ *
1580
+ * @param options - Join options (flags, silent, etc.)
1581
+ */
1582
+ async joinCall(options = {}) {
1583
+ return this.resources.call.join(this.token, options);
1584
+ }
1585
+ /**
1586
+ * Leave the call
1587
+ *
1588
+ * @param endForAll - End call for everyone (moderator only)
1589
+ */
1590
+ async leaveCall(endForAll = false) {
1591
+ return this.resources.call.leave(this.token, { all: endForAll });
1592
+ }
1593
+ // ==================== Poll Operations ====================
1594
+ /**
1595
+ * Create a poll in this room
1596
+ *
1597
+ * @param options - Poll creation options
1598
+ * @returns Created poll
1599
+ */
1600
+ async createPoll(options) {
1601
+ return this.resources.poll.create(this.token, options);
1602
+ }
1603
+ // ==================== Reaction Operations ====================
1604
+ /**
1605
+ * Add a reaction to a message
1606
+ *
1607
+ * @param messageId - Message ID
1608
+ * @param reaction - Emoji reaction
1609
+ */
1610
+ async addReaction(messageId, reaction) {
1611
+ await this.resources.reaction.add(this.token, messageId, reaction);
1612
+ }
1613
+ /**
1614
+ * Remove a reaction from a message
1615
+ *
1616
+ * @param messageId - Message ID
1617
+ * @param reaction - Emoji reaction to remove
1618
+ */
1619
+ async removeReaction(messageId, reaction) {
1620
+ await this.resources.reaction.remove(this.token, messageId, reaction);
1621
+ }
1622
+ // ==================== Computed Properties ====================
1623
+ /** Whether the current user is a moderator or owner */
1624
+ get isModerator() {
1625
+ return this.participantType === 1 /* OWNER */ || this.participantType === 2 /* MODERATOR */ || this.participantType === 6 /* GUEST_MODERATOR */;
1626
+ }
1627
+ /** Whether this is a one-to-one conversation */
1628
+ get isOneToOne() {
1629
+ return this.type === 1 /* ONE_TO_ONE */;
1630
+ }
1631
+ /** Whether this is a group conversation */
1632
+ get isGroup() {
1633
+ return this.type === 2 /* GROUP */;
1634
+ }
1635
+ /** Whether this is a public (open) conversation */
1636
+ get isPublic() {
1637
+ return this.type === 3 /* PUBLIC */;
1638
+ }
1639
+ /** Whether the room is currently read-only */
1640
+ get isReadOnly() {
1641
+ return this.readOnly === 1 /* READ_ONLY */;
1642
+ }
1643
+ /** Whether the lobby is enabled */
1644
+ get hasLobby() {
1645
+ return this.lobbyState === 1 /* ENABLED */;
1646
+ }
1647
+ };
1648
+
1649
+ // src/client.ts
1650
+ var RoomsAccessor = class {
1651
+ constructor(roomResource, roomResources) {
1652
+ this.roomResource = roomResource;
1653
+ this.roomResources = roomResources;
1654
+ }
1655
+ /**
1656
+ * List all conversations the current user is part of
1657
+ *
1658
+ * @param options - List options
1659
+ * @returns Array of Room entities
1660
+ *
1661
+ * @example
1662
+ * ```ts
1663
+ * const rooms = await client.rooms.list();
1664
+ * const targetRoom = rooms.find(r => r.token === 'abcdefgh');
1665
+ * ```
1666
+ */
1667
+ async list(options = {}) {
1668
+ const normalized = await this.roomResource.list(options);
1669
+ return normalized.map((data) => new Room(data, this.roomResources));
1670
+ }
1671
+ /**
1672
+ * Get a single room by token
1673
+ *
1674
+ * @param token - Room token
1675
+ * @returns Room entity
1676
+ */
1677
+ async get(token) {
1678
+ const normalized = await this.roomResource.get(token);
1679
+ return new Room(normalized, this.roomResources);
1680
+ }
1681
+ /**
1682
+ * Create a new room
1683
+ *
1684
+ * @param options - Room creation options
1685
+ * @returns Created Room entity
1686
+ *
1687
+ * @example
1688
+ * ```ts
1689
+ * // Create a group room
1690
+ * const room = await client.rooms.create({
1691
+ * roomType: 2,
1692
+ * roomName: 'Team Chat',
1693
+ * });
1694
+ *
1695
+ * // Create a 1-1 chat
1696
+ * const dm = await client.rooms.create({
1697
+ * roomType: 1,
1698
+ * invite: 'other-user',
1699
+ * source: 'users',
1700
+ * });
1701
+ * ```
1702
+ */
1703
+ async create(options) {
1704
+ const normalized = await this.roomResource.create(options);
1705
+ return new Room(normalized, this.roomResources);
1706
+ }
1707
+ /**
1708
+ * Delete a room
1709
+ *
1710
+ * @param token - Room token
1711
+ */
1712
+ async delete(token) {
1713
+ await this.roomResource.delete(token);
1714
+ }
1715
+ /**
1716
+ * Rename a room
1717
+ *
1718
+ * @param token - Room token
1719
+ * @param name - New room name
1720
+ */
1721
+ async rename(token, name) {
1722
+ await this.roomResource.rename(token, name);
1723
+ }
1724
+ };
1725
+ var TalkClient = class {
1726
+ /** HTTP client for direct API access */
1727
+ http;
1728
+ /** Rooms accessor for room operations */
1729
+ rooms;
1730
+ /** Direct access to chat resource */
1731
+ chat;
1732
+ /** Direct access to participant resource */
1733
+ participants;
1734
+ /** Direct access to call resource */
1735
+ call;
1736
+ /** Direct access to poll resource */
1737
+ poll;
1738
+ /** Direct access to reaction resource */
1739
+ reaction;
1740
+ /** Direct access to room resource */
1741
+ roomResource;
1742
+ constructor(config) {
1743
+ this.http = new HttpClient({
1744
+ host: config.host,
1745
+ username: config.username,
1746
+ password: config.password,
1747
+ fetch: config.fetch
1748
+ });
1749
+ this.roomResource = new RoomResource(this.http);
1750
+ this.chat = new ChatResource(this.http);
1751
+ this.participants = new ParticipantResource(this.http);
1752
+ this.call = new CallResource(this.http);
1753
+ this.poll = new PollResource(this.http);
1754
+ this.reaction = new ReactionResource(this.http);
1755
+ const roomResources = {
1756
+ chat: this.chat,
1757
+ participants: this.participants,
1758
+ call: this.call,
1759
+ poll: this.poll,
1760
+ reaction: this.reaction
1761
+ };
1762
+ this.rooms = new RoomsAccessor(this.roomResource, roomResources);
1763
+ }
1764
+ /**
1765
+ * Get the base URL for this client
1766
+ */
1767
+ get host() {
1768
+ return this.http.host;
1769
+ }
1770
+ };
1771
+ export {
1772
+ ActorType,
1773
+ BreakoutRoomMode,
1774
+ CallResource,
1775
+ ChatResource,
1776
+ ConversationType,
1777
+ HttpClient,
1778
+ HttpClientError,
1779
+ InCallFlag,
1780
+ ListableScope,
1781
+ LobbyState,
1782
+ MessageType,
1783
+ NotificationLevel,
1784
+ ParticipantResource,
1785
+ ParticipantType,
1786
+ Permission,
1787
+ PollResource,
1788
+ PollResultMode,
1789
+ ReactionResource,
1790
+ ReadOnlyState,
1791
+ RecordingStatus,
1792
+ Room,
1793
+ RoomResource,
1794
+ RoomsAccessor,
1795
+ SIPState,
1796
+ SharedObjectType,
1797
+ TalkClient,
1798
+ normalizeMessage,
1799
+ normalizeMessageWithParent,
1800
+ normalizeMessages,
1801
+ normalizeParticipant,
1802
+ normalizeParticipants,
1803
+ normalizeRoom,
1804
+ normalizeRooms
1805
+ };