@reactoo/watchtogether-sdk-js 2.8.59 → 2.8.60

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactoo/watchtogether-sdk-js",
3
- "version": "2.8.59",
3
+ "version": "2.8.60",
4
4
  "description": "Javascript SDK for Reactoo",
5
5
  "main": "dist/watchtogether-sdk.min.js",
6
6
  "module": "dist/watchtogether-sdk.min.js",
@@ -311,7 +311,9 @@ class Iot {
311
311
  event === 'handsCleared' ||
312
312
  event === 'volume_set' ||
313
313
  event === 'asset_created' ||
314
- event === 'attribute_updated'
314
+ event === 'attribute_updated' ||
315
+ event === 'session_terminating' ||
316
+ event === 'session_terminated'
315
317
  ) {
316
318
  this.emit('message', {event, ...message, roomId})
317
319
  }
@@ -1,362 +0,0 @@
1
- import emitter from './wt-emitter';
2
- import { decodeJanusDisplay } from "../models/utils";
3
- import { mqtt, iot } from 'aws-iot-device-sdk-v2';
4
-
5
- class Iot {
6
- constructor(enableDebugFlag) {
7
- Object.assign(this, emitter());
8
- this.decoder = new TextDecoder('utf-8');
9
- this.log = Iot.noop;
10
- this.credentialsExpirationCheckIntervalId = null;
11
- this.currentCredentialsExpirationStamp = null;
12
- this.lastConnectParams = null;
13
- this.connection = null;
14
- this.subscribedTopics = new Set();
15
-
16
- if (enableDebugFlag) {
17
- this.enableDebug();
18
- }
19
- }
20
-
21
- static noop() {}
22
-
23
- enableDebug() {
24
- this.log = console.log.bind(console);
25
- }
26
-
27
- connect(apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration) {
28
- this.log('iot connect called, we disconnect first just to be sure');
29
- return this.disconnect()
30
- .catch(() => {
31
- // we dont care if disconnect fails
32
- return Promise.resolve();
33
- })
34
- .then(() => {
35
- this.log('iot connect');
36
- this.startCredentialsExpirationCheck(expiration);
37
- this.lastConnectParams = { apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration };
38
-
39
- const configBuilder = iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets();
40
-
41
- configBuilder.with_clean_session(true);
42
- configBuilder.with_client_id(apiMqttClientId);
43
- configBuilder.with_endpoint(apiMqttUrl);
44
- configBuilder.with_credentials(region, accessKeyId, secretAccessKey, sessionToken);
45
- configBuilder.with_keep_alive_seconds(30);
46
- configBuilder.with_ping_timeout_ms(3000);
47
- configBuilder.with_reconnect_max_sec(5);
48
- configBuilder.with_reconnect_min_sec(1);
49
-
50
- const config = configBuilder.build();
51
-
52
- const client = new mqtt.MqttClient();
53
- this.connection = client.new_connection(config);
54
-
55
- this.connection.on('connect', () => {
56
- this.emit('connect');
57
- });
58
-
59
- this.connection.on('closed', (error) => {
60
- this.emit('closed', error);
61
- });
62
-
63
- this.connection.on('disconnect', () => {
64
- this.emit('disconnect');
65
- });
66
-
67
- this.connection.on('error', (error) => {
68
- this.emit('error', error);
69
- });
70
-
71
- this.connection.on('interrupt', (error) => {
72
- this.emit('interrupt', error);
73
- });
74
-
75
- this.connection.on('resume', (error) => {
76
- this.emit('resume', error);
77
- });
78
-
79
- this.connection.on('message', (topic, payload) => {
80
- this.handleMessage(topic, new Uint8Array(payload));
81
- });
82
-
83
- this.connection.on('connection_success', (error) => {
84
- this.emit('connection_success', error);
85
- });
86
-
87
- this.connection.on('connection_failure', (error) => {
88
- this.emit('connection_failure', error);
89
- });
90
-
91
- return new Promise((resolve, reject) => {
92
- const timeoutId = setTimeout(() => {
93
- reject(new Error('Connection timeout'));
94
- }, 5000); // 5 seconds timeout is enough
95
- this.connection.connect()
96
- .then((r) => {
97
- clearTimeout(timeoutId);
98
- resolve(r);
99
- })
100
- .catch((error) => {
101
- clearTimeout(timeoutId);
102
- reject(error);
103
- });
104
- });
105
- });
106
- }
107
-
108
-
109
- disconnect() {
110
- this.log('iot disconnect');
111
- this.stopCredentialsExpirationCheck();
112
- if (this.connection) {
113
- return new Promise((resolve, reject) => {
114
- const timeoutId = setTimeout(() => {
115
- reject(new Error('Disconnect timeout'));
116
- }, 5000); // 5 seconds timeout is enough
117
- this.connection.disconnect()
118
- .then((r) => {
119
- clearTimeout(timeoutId);
120
- resolve(r);
121
- })
122
- .catch((error) => {
123
- clearTimeout(timeoutId);
124
- reject(error);
125
- });
126
- });
127
- } else return Promise.resolve();
128
- }
129
-
130
- isConnected() {
131
- return this.connection && this.connection.currentState === 0 && this.connection.desiredState === 0;
132
- }
133
-
134
- clearTopics() {
135
- this.subscribedTopics.clear();
136
- }
137
-
138
- subscribe(topic) {
139
- this.log('iot subscribe', topic);
140
- if (this.connection && this.connection.currentState === 0 && this.connection.desiredState === 0 && typeof topic === 'string' && topic.trim() !== '') {
141
- this.subscribedTopics.add(topic);
142
- return this.connection.subscribe(topic, mqtt.QoS.AtLeastOnce)
143
- .catch(err => {
144
- this.log('Error subscribing to topic:', err);
145
- this.subscribedTopics.delete(topic);
146
- return Promise.reject(err);
147
- });
148
- } else {
149
- this.log('Invalid topic or not connected:', topic);
150
- return Promise.reject(new Error('Invalid topic or not connected'));
151
- }
152
- }
153
-
154
- unsubscribe(topic) {
155
- this.log('iot unsubscribe', topic);
156
- if (this.connection && this.connection.currentState === 0 && this.connection.desiredState === 0 && typeof topic === 'string' && topic.trim() !== '') {
157
- this.subscribedTopics.delete(topic);
158
- return this.connection.unsubscribe(topic)
159
- .catch(err => {
160
- this.log('Error unsubscribing from topic:', err);
161
- this.subscribedTopics.add(topic);
162
- return Promise.reject(err);
163
- });
164
- } else {
165
- this.log('Invalid topic or not connected:', topic);
166
- return Promise.reject(new Error('Invalid topic or not connected'));
167
- }
168
- }
169
-
170
- send(topic, message) {
171
- this.log('iot send', topic, message);
172
- let msg = typeof message === 'object' ? JSON.stringify(message) : message;
173
- if (this.connection && this.connection.currentState === 0 && this.connection.desiredState === 0) {
174
- this.connection.publish(topic, msg, mqtt.QoS.AtLeastOnce, false);
175
- } else {
176
- throw new Error('Invalid topic or not connected:', topic);
177
- }
178
- }
179
-
180
- handleMessage(topic, payload) {
181
- const topicParts = topic.split('/');
182
- let message;
183
- try {
184
- message = JSON.parse(this.decoder.decode(payload));
185
- } catch (error) {
186
- this.log('Error parsing message:', error);
187
- return;
188
- }
189
-
190
- if(message.display) {
191
- const decodedDisplay = decodeJanusDisplay(message.display);
192
- if(decodedDisplay.userId) {
193
- message = {...message, userId: decodedDisplay.userId, role: decodedDisplay.role, start: decodedDisplay.start};
194
- }
195
- }
196
-
197
- if(topicParts[0] === 'user') { // user
198
- const userId = topicParts[1].replace("_", ':');
199
- this.emit('message', {userId, ...message, event: message.event ? `user:${message.event}` : 'user'});
200
- } else if(topicParts[0] === 'wt') {
201
- const event = message.event;
202
- const roomId = topicParts[2];
203
- if(topicParts[1] === 'room') { // room
204
- if(
205
- event === 'message' ||
206
- event === 'template_updated' ||
207
- event === 'record_start' ||
208
- event === 'record_stop' ||
209
- event === 'record_configured' ||
210
- event === 'record_livestream_available' ||
211
- event === 'record_livestream_kick' ||
212
- event === 'user_update_displayname' ||
213
- event === 'user_update_avatar' ||
214
- event === 'user_update_bio' ||
215
- event === 'user_update_customattributes' ||
216
- event === 'user_update_privateattributes' ||
217
- event === 'channel_changed' ||
218
- event === "instance_homepage_changed" ||
219
- event === "instance_settings_changed" ||
220
- event === "externalmix_changed" ||
221
- event === "video_uploaded" ||
222
- event === "change_user_devices" ||
223
- event === "queue" ||
224
- event === "title_changed" ||
225
- event === "videowall_changed" ||
226
- event === 'left' || //user removed room a.k.a. left the room
227
- event === 'kicked' ||
228
- event === 'banned' ||
229
- event === 'unbanned' ||
230
- event === 'approved' ||
231
- event === 'muted' ||
232
- event === 'unmuted' ||
233
- event === 'messageRemoved' ||
234
- event === 'messageReported' ||
235
- event === 'chatClear' ||
236
- event === 'handRaised' ||
237
- event === 'handLowered' ||
238
- event === 'handsCleared' ||
239
- event === 'volume_set' ||
240
- event === 'asset_created'
241
- ) {
242
- this.emit('message', {event, ...message, roomId})
243
- }
244
- else if(event === 'joined' || event === 'leaving') {
245
- this.emit('message', {event, ...message, isObserver:!!message.isObserver, roomId});
246
- }
247
- }
248
- else if(topicParts[1] === 'instanceroom') { // instance
249
- if(event === 'add_room' || event === 'remove_room' || event === 'set_room' || event === "instance_homepage_changed" || event === 'instance_settings_changed') {
250
- this.emit('message', {event, ...message});
251
- }
252
- }
253
- else if(topicParts[1] === 'externalmix') {
254
- const event = message.event;
255
- this.emit('message', {event, ...message});
256
- }
257
- else if(topicParts[1] === 'asset') {
258
- this.emit('message', {event: 'asset', assetId: topicParts[2], ...message});
259
- }
260
- } else if(topicParts[0] === 'wtr' || topicParts[0] === 'gwtr') {
261
- const recorderId = topicParts[1];
262
- const sessionId = topicParts[2];
263
- if(topicParts[3] === 'control') {
264
- this.emit('message', {event: 'recorder_control', ...message, recorderId, sessionId});
265
- } // recorder control
266
- else if(topicParts[3] === 'monitor') {
267
- this.emit('message', {event: 'recorder_monitor', ...message, recorderId, sessionId});
268
- } // recorder monitor
269
- }
270
- }
271
-
272
- startCredentialsExpirationCheck(expiration) {
273
- this.stopCredentialsExpirationCheck();
274
- this.currentCredentialsExpirationStamp = new Date(expiration).getTime();
275
- this.credentialsExpirationCheckIntervalId = setInterval(() => {
276
- const currentTimeStamp = new Date().getTime();
277
- // update 15 minutes before expiration
278
- if(this.currentCredentialsExpirationStamp - currentTimeStamp <= 900000) {
279
- this.log('iot credentials expired, updating');
280
- this.emit('updateCredentials');
281
- }
282
- }, 5000);
283
- }
284
-
285
- stopCredentialsExpirationCheck() {
286
- clearInterval(this.credentialsExpirationCheckIntervalId);
287
- this.credentialsExpirationCheckIntervalId = null;
288
- }
289
-
290
- updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken, expiration) {
291
- this.log('iot updateWebSocketCredentials');
292
- this.lastConnectParams = {...this.lastConnectParams, accessKeyId, secretAccessKey, sessionToken, expiration };
293
- const currentTopics = new Set(this.subscribedTopics);
294
- // disconnect is part of connect process
295
- return this.connect(
296
- this.lastConnectParams.apiMqttUrl,
297
- this.lastConnectParams.apiMqttClientId,
298
- this.lastConnectParams.region,
299
- accessKeyId,
300
- secretAccessKey,
301
- sessionToken,
302
- expiration
303
- ).then(() => {
304
- // Resubscribe to topics
305
- currentTopics.forEach(topic => this.subscribe(topic));
306
- return true;
307
- })
308
- }
309
-
310
- checkConnection() {
311
- return new Promise((resolve, reject) => {
312
- if (!this.connection || !(this.connection.currentState === 0 && this.connection.desiredState === 0)) {
313
- reject(new Error('Not connected'));
314
- return;
315
- }
316
-
317
- if (this.subscribedTopics.size === 0) {
318
- reject(new Error('No subscribed topics available for connection check', {cause: -1}));
319
- return;
320
- }
321
-
322
- // Find a suitable topic for the connection check
323
- let suitableTopic = Array.from(this.subscribedTopics).find(topic => topic.indexOf('user') > -1);
324
-
325
- if (!suitableTopic) {
326
- reject(new Error('No suitable topic found for connection check', {cause: -1}));
327
- return;
328
- }
329
-
330
- const testMessage = {
331
- type: 'keep_alive',
332
- timestamp: Date.now()
333
- };
334
-
335
- const timeoutId = setTimeout(() => {
336
- this.off('message', checkMessageHandler);
337
- reject(new Error('Connection check timeout'));
338
- }, 5000); // 5 seconds timeout
339
-
340
- const checkMessageHandler = (message) => {
341
- if (message.type === 'keep_alive' && message.timestamp === testMessage.timestamp) {
342
- clearTimeout(timeoutId);
343
- this.off('message', checkMessageHandler);
344
- resolve();
345
- }
346
- };
347
-
348
- this.on('message', checkMessageHandler);
349
-
350
- try {
351
- this.send(suitableTopic, testMessage);
352
- } catch (error) {
353
- clearTimeout(timeoutId);
354
- this.off('message', checkMessageHandler);
355
- reject(new Error(`Publish error: ${error.message}`));
356
- }
357
- });
358
- }
359
- }
360
-
361
- export default Iot;
362
-