anear-js-api 0.4.1 → 0.4.3

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.
@@ -46,7 +46,7 @@ class AnearApi extends ApiService {
46
46
  const json = await this.post("transitions", {event_name: eventName}, relationships)
47
47
  const attrs = json.data.attributes
48
48
  logger.info(`API: newState is ${attrs.state}`)
49
- return json
49
+ return attrs
50
50
  } catch(err) {
51
51
  logger.error(err)
52
52
  }
package/lib/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  const JsonApiResource = require('./models/JsonApiResource')
4
4
  const JsonApiArrayResource = require('./models/JsonApiArrayResource')
5
- const {AnearEvent, logger} = require('./models/AnearEvent')
5
+ const AnearEvent = require('./models/AnearEvent')
6
+ const logger = require('./utils/Logger')
6
7
  const AnearParticipant = require('./models/AnearParticipant')
7
8
  const AnearMessaging = require('./messaging/AnearMessaging')
8
9
  const AnearApiService = require('./api/ApiService')
@@ -1,9 +1,7 @@
1
1
  "use strict"
2
2
  const Ably = require('ably/promises')
3
3
  const AnearApi = require('../api/AnearApi')
4
- const ParticipantTimer = require('../utils/ParticipantTimer')
5
4
  const logger = require('../utils/Logger')
6
- const { Mutex } = require('async-mutex')
7
5
 
8
6
  const AppId = process.env.ANEARAPP_APP_ID
9
7
 
@@ -29,7 +27,6 @@ class AnearMessaging {
29
27
  this.api = new AnearApi()
30
28
  this.AnearEventClass = AnearEventClass
31
29
  this.AnearParticipantClass = AnearParticipantClass
32
- this.mutex = new Mutex()
33
30
  this.anearEvents = {}
34
31
 
35
32
  const baseUrl = this.api.api_base_url
@@ -53,7 +50,6 @@ class AnearMessaging {
53
50
  }
54
51
 
55
52
  this.eventChannels = {}
56
- this.participantTimers = {}
57
53
 
58
54
  this.initRealtime(clientOptions)
59
55
  }
@@ -85,76 +81,12 @@ class AnearMessaging {
85
81
  return this.realtime.channels.get(channelName, channelParams)
86
82
  }
87
83
 
88
- ensureParticipantTimer(anearEvent, participant, timeoutMsecs) {
89
- // this is called when a new timer is being started for a privateMessage
90
- // sent to a participant, or public message to all participants
91
- // If the timer already exists and is paused, it is resumed with the timeRemaining
92
- // [a starter function, timeRemaining] is returned
93
- let timeRemaining = timeoutMsecs
94
- let timerStarter = () => {}
95
-
96
- if (timeoutMsecs > 0) {
97
- const timer = this.participantTimers[participant.id] || this.createTimer(anearEvent, participant, timeoutMsecs)
98
-
99
- if (timer.isRunning) {
100
- timeRemaining = timer.interrupt()
101
- } else {
102
- timerStarter = () => timer.start(timeoutMsecs)
103
- }
104
- }
105
- logger.debug(`ensureParticipantTimer(timeRemaining: ${timeRemaining})`)
106
-
107
- return [timerStarter, timeRemaining]
108
- }
109
-
110
- createTimer(anearEvent, participant, timeoutMsecs) {
111
- const timer = new ParticipantTimer(
112
- participant.id,
113
- async () => await this.timerExpired(anearEvent, participant, timeoutMsecs)
114
- )
115
- this.participantTimers[participant.id] = timer
116
-
117
- return timer
118
- }
119
-
120
- destroyParticipantTimer(participantId) {
121
- // participant will not be receiving any more display messages
122
- // so we close out and delete the ParticipantTimer
123
- const timer = this.participantTimers[participantId]
124
-
125
- if (timer) {
126
- timer.reset()
127
- delete this.participantTimers[participantId]
128
- }
129
- }
130
-
131
- interruptParticipantTimer(participantId) {
132
- const timer = this.participantTimers[participantId]
133
-
134
- if (timer && timer.isRunning) timer.interrupt()
135
- }
136
-
137
- resetParticipantTimer(participantId) {
138
- // called after participant takes Action before timer expires
139
- const timer = this.participantTimers[participantId]
140
- if (timer) timer.reset()
141
- }
142
-
143
- async timerExpired(anearEvent, participant, timeoutMsecs) {
144
- logger.debug(`participant (${anearEvent.id}, ${participant.id}) TIMED OUT after ${timeoutMsecs} msecs`)
145
-
146
- delete this.participantTimers[participant.id]
147
-
148
- await anearEvent.participantTimedOut(participant)
149
- await anearEvent.update()
150
- }
151
-
152
84
  async getAnearEventFromStorage(eventId) {
153
85
  return await this.AnearEventClass.getFromStorage(eventId, this)
154
86
  }
155
87
 
156
88
  async getAnearParticipantFromStorage(participantId) {
157
- return await this.AnearParticipantClass.getFromStorage(participantId)
89
+ return await this.AnearParticipantClass.getFromStorage(participantId, this)
158
90
  }
159
91
 
160
92
  async initEventRealtimeMessaging(anearEvent) {
@@ -195,41 +127,35 @@ class AnearMessaging {
195
127
  // if we are getting this event create message from history after a quick restart,
196
128
  // we just return if the event already exists
197
129
  //
198
- await this.loadOrPersistEventAndInitialize(anearEvent)
199
-
200
- } catch(err) {
201
- logger.error(err)
202
- }
203
- }
204
-
205
- async loadOrPersistEventAndInitialize(anearEvent) {
206
- const eventExists = await anearEvent.exists()
130
+ const eventExists = await anearEvent.exists()
131
+
132
+ logger.info(`Event ${anearEvent.id} ${eventExists ? "already exists" : "does not exist"} in Storage`)
133
+
134
+ let loadedEvent = anearEvent
135
+
136
+ if (!eventExists) {
137
+ await anearEvent.runExclusive(`createEventCallback ${anearEvent.id}`, async () => {
138
+ await anearEvent.createdEventCallback()
139
+ await anearEvent.persist()
140
+ // start the state machine before initialiing Realtime Messaging
141
+ // as REFRESH events come in and the state machine should be ready
142
+ // to handle those XState events
143
+ anearEvent.startStateMachine()
144
+ await this.initEventRealtimeMessaging(anearEvent)
145
+ })
146
+ } else {
147
+ loadedEvent = await this.getAnearEventFromStorage(anearEvent.id)
148
+ await this.initEventRealtimeMessaging(loadedEvent)
149
+ loadedEvent.startStateMachine()
150
+ }
207
151
 
208
- logger.info(`Event ${anearEvent.id} ${eventExists ? "already exists" : "does not exist"} in Storage`)
152
+ logger.info(`New ${loadedEvent.constructor.name} Event: `, loadedEvent.toJSON())
209
153
 
210
- let loadedEvent = anearEvent
154
+ this.anearEvents[loadedEvent.id] = loadedEvent
211
155
 
212
- if (!eventExists) {
213
- await this.runExclusive("createEventCallback", async () => {
214
- await anearEvent.createdEventCallback()
215
- await anearEvent.persist()
216
- // start the state machine before initialiing Realtime Messaging
217
- // as REFRESH events come in and the state machine should be ready
218
- // to handle those XState events
219
- anearEvent.startStateMachine()
220
- await this.initEventRealtimeMessaging(anearEvent)
221
- })
222
- } else {
223
- loadedEvent = await this.getAnearEventFromStorage(anearEvent.id)
224
- await this.initEventRealtimeMessaging(loadedEvent)
225
- loadedEvent.startStateMachine()
156
+ } catch(err) {
157
+ logger.error(err)
226
158
  }
227
-
228
- logger.info(`New ${loadedEvent.constructor.name} Event: `, loadedEvent.toJSON())
229
-
230
- this.anearEvents[loadedEvent.id] = loadedEvent
231
-
232
- return loadedEvent
233
159
  }
234
160
 
235
161
  async reloadAnyEventsInProgress(appId) {
@@ -242,11 +168,16 @@ class AnearMessaging {
242
168
  for (const eventData of events) {
243
169
  const eventJson = await this.api.getEvent(eventData.id)
244
170
  const anearEvent = new this.AnearEventClass(eventJson, this)
245
- // THIS does NOT work yet. This is logic that restarte events in progress
246
- // if the nodeJS process crahsed during play
171
+ await this.initEventRealtimeMessaging(anearEvent)
172
+
173
+ const attachedParticipants = this.getPresentParticipants(anearEvent)
174
+ // TBD: might want to change the attach and presence logic on
175
+ // the actions channel. The Ably docs show subscribing to the
176
+ // presence events on the actions channel, and instead of using History,
177
+ // it does a get() to fetch all of the current members. This behavior
178
+ // is useful for both event start, and event restart within this function
179
+ // anearEvent.startStateMachine()
247
180
  //
248
- // const loadedEvent = await this.loadOrPersistEventAndInitialize(anearEvent)
249
- // await this.refreshActiveParticipants(loadedEvent) DOES NOT WORK YET
250
181
  }
251
182
  }
252
183
  } catch (err) {
@@ -254,20 +185,6 @@ class AnearMessaging {
254
185
  }
255
186
  }
256
187
 
257
- async refreshActiveParticipants(anearEvent) {
258
- const allParticipants = anearEvent.participants.active
259
-
260
- return Promise.all(
261
- allParticipants.map(
262
- participant => this.processParticipantEnter(
263
- anearEvent,
264
- participant.id,
265
- participant.geoLocation
266
- )
267
- )
268
- )
269
- }
270
-
271
188
  async setupCreateEventChannel() {
272
189
  logger.info(`attaching to channel ${AnearCreateEventChannelName}`)
273
190
 
@@ -307,6 +224,21 @@ class AnearMessaging {
307
224
  )
308
225
  }
309
226
 
227
+ async getSpectatorCount(anearEvent) {
228
+ if (!anearEvent.allowsSpectators()) return 0
229
+
230
+ const channel = this.eventChannels[anearEvent.id].spectators
231
+ const members = await channel.presence.get()
232
+ return members.length
233
+ }
234
+
235
+ async getPresentParticipants(anearEvent) {
236
+ // returns the participant presence data for each member who is present on
237
+ // the event's actions channel
238
+ const channel = this.eventChannels[anearEvent.id].actions
239
+ return await channel.presence.get()
240
+ }
241
+
310
242
  async setupActionsChannel(anearEvent) {
311
243
  const actionsChannel = this.getChannel(anearEvent.actionsChannelName())
312
244
 
@@ -372,7 +304,9 @@ class AnearMessaging {
372
304
  await this.closeParticipant(
373
305
  anearEvent,
374
306
  participantId,
375
- (anearEvent, participant) => anearEvent.participantClose(participant)
307
+ async (anearEvent, participant) => {
308
+ await anearEvent.participantExit(participant)
309
+ }
376
310
  )
377
311
  }
378
312
 
@@ -391,7 +325,7 @@ class AnearMessaging {
391
325
  await this.processParticipantEnter(anearEvent, participantId, geoLocation)
392
326
  }
393
327
 
394
- async processParticipantEnter(anearEvent, participantId, geoLocation) {
328
+ async processParticipantEnter(anearEvent, participantId, geoLocation = null) {
395
329
 
396
330
  logger.debug(`processing Participant Enter for event: ${anearEvent.id}, participant: ${participantId}`)
397
331
  //
@@ -401,17 +335,17 @@ class AnearMessaging {
401
335
  //
402
336
  try {
403
337
  const participantJson = await this.api.getEventParticipantJson(participantId)
404
- const participant = new this.AnearParticipantClass(participantJson)
338
+ const participant = new this.AnearParticipantClass(participantJson, anearEvent)
405
339
 
406
- participant.geoLocation = geoLocation
407
-
408
- const persistedAnearParticipant = await this.AnearParticipantClass.getFromStorage(participantId)
340
+ const persistedAnearParticipant = await this.AnearParticipantClass.getFromStorage(participantId, anearEvent)
409
341
 
410
342
  if (persistedAnearParticipant) {
411
343
  participant.context = persistedAnearParticipant.context
412
344
  }
413
345
 
414
- await this.runExclusive("participantEnterCallback", async () => {
346
+ await anearEvent.runExclusive(`participantEnterCallback ${participant.id}`, async () => {
347
+ participant.geoLocation = geoLocation
348
+
415
349
  await this.setupPrivatePublishingChannel(participant)
416
350
  await anearEvent.participantEnter(participant)
417
351
  await anearEvent.update()
@@ -438,27 +372,32 @@ class AnearMessaging {
438
372
  }
439
373
 
440
374
  async participantLeaveMessagingCallback(anearEvent, message) {
441
- // this can be just a temporary leave (refresh browser for example), so pause any participant timers
375
+ // this can be just a temporary leave from a participant refreshing their browser.
376
+ // currently, no action taken
442
377
  const userId = message.clientId
443
378
  const participantId = message.data.id
444
379
 
445
380
  logger.debug(`**** LEAVE PARTICIPANT **** participantLeaveMessagingCallback(participant: ${participantId})`)
446
-
447
- this.interruptParticipantTimer(participantId)
448
381
  }
449
382
 
450
- async closeParticipant(anearEvent, participantId, callback) {
383
+ async closeParticipant(anearEvent, participantId, callback = null) {
384
+ // closes out a single Participant. This is invoked when a single
385
+ // participant leaves an event, and the event may possibly continue,
386
+ // or possibly exit. Or this may be called by the event when exiting
387
+ // and cleaning out any remaining participants.
451
388
  logger.debug(`closeParticipant(${participantId})`)
452
389
 
453
- this.destroyParticipantTimer(participantId)
454
-
455
- const participant = await this.getAnearParticipantFromStorage(participantId)
390
+ const participant = anearEvent.participants.getById(participantId)
456
391
 
457
392
  if (participant) {
393
+ participant.destroyTimer()
394
+
458
395
  await this.detachParticipantPrivateChannel(anearEvent.id, participant)
459
396
 
460
- await this.runExclusive("closeParticipant", async () => {
461
- await callback(anearEvent, participant)
397
+ await anearEvent.runExclusive(`closeParticipant ${participant.id}`, async () => {
398
+ if (callback) {
399
+ await callback(anearEvent, participant)
400
+ }
462
401
  await anearEvent.update()
463
402
  })
464
403
  }
@@ -487,16 +426,16 @@ class AnearMessaging {
487
426
  const participantId = message.data.participantId
488
427
  const payload = message.data.payload
489
428
 
490
- this.resetParticipantTimer(participantId) // participant responded in time, reset any running timer
429
+ const participant = anearEvent.participants.getById(participantId)
430
+
431
+ participant.resetTimer() // participant responded in time, reset any running timer
491
432
 
492
433
  logger.debug(`participantActionMessagingCallback(${anearEvent.id}, ${participantId})`)
493
434
 
494
435
  const actionJSON = JSON.parse(payload)
495
436
  const [actionEventName, actionPayload] = Object.entries(actionJSON)[0]
496
437
 
497
- const participant = await this.getAnearParticipantFromStorage(participantId)
498
-
499
- await this.runExclusive("participantActionCallback", async () => {
438
+ await anearEvent.runExclusive(`participantActionCallback ${participant.id}`, async () => {
500
439
  await anearEvent.participantAction(participant, actionEventName, actionPayload)
501
440
  await anearEvent.update()
502
441
  await participant.update()
@@ -526,18 +465,6 @@ class AnearMessaging {
526
465
  }
527
466
  }
528
467
 
529
- async runExclusive(name, callback) {
530
- logger.debug(`waiting for ${name} mutex`)
531
-
532
- await this.mutex.runExclusive(
533
- async () => {
534
- logger.debug(`mutex ${name} locked!`)
535
- await callback()
536
- }
537
- )
538
- logger.debug(`mutex ${name} released!`)
539
- }
540
-
541
468
  subscribeEventMessages(channel, messageType, callback) {
542
469
  channel.subscribe(messageType, callback)
543
470
  logger.debug(`subscribed to ${messageType} messages on ${channel.name}`)
@@ -579,14 +506,14 @@ class AnearMessaging {
579
506
  )
580
507
  }
581
508
 
582
- setMultipleParticipantTimers(anearEvent, participants, timeoutMsecs) {
509
+ setMultipleParticipantTimers(participants, timeoutMsecs) {
583
510
  if (timeoutMsecs === 0) return [() => {}, 0]
584
511
 
585
512
  const participantTimers = []
586
513
 
587
514
  participants.forEach(
588
515
  participant => {
589
- const [startTimer, _timeRemaining] = this.ensureParticipantTimer(anearEvent, participant, timeoutMsecs)
516
+ const [startTimer, _timeRemaining] = participant.ensureTimer(timeoutMsecs)
590
517
  participantTimers.push(startTimer)
591
518
  }
592
519
  )
@@ -598,7 +525,7 @@ class AnearMessaging {
598
525
  const eventId = anearEvent.id
599
526
  const channel = this.eventChannels[eventId].participants
600
527
 
601
- const [startTimers, timeRemaining] = this.setMultipleParticipantTimers(eventId, participants, timeoutMsecs)
528
+ const [startTimers, timeRemaining] = this.setMultipleParticipantTimers(participants, timeoutMsecs)
602
529
 
603
530
  await this.publishMessage(
604
531
  channel,
@@ -623,7 +550,7 @@ class AnearMessaging {
623
550
  const channel = this.eventChannels[anearEvent.id].privates[userId]
624
551
  if (!channel) throw new Error(`private channel not found. invalid user id ${userId}`)
625
552
 
626
- const [startTimer, timeRemaining] = this.ensureParticipantTimer(anearEvent, participant, timeoutMsecs)
553
+ const [startTimer, timeRemaining] = participant.ensureTimer(timeoutMsecs)
627
554
 
628
555
  await this.publishMessage(
629
556
  channel,
@@ -659,10 +586,9 @@ class AnearMessaging {
659
586
  )
660
587
  }
661
588
 
662
- async publishEventTransitionMessage(anearEvent, eventJson) {
589
+ async publishEventTransitionMessage(anearEvent, newState) {
663
590
  const channel = this.eventChannels[anearEvent.id].events
664
- const newState = eventJson.attributes.state
665
- const payload = {content: {state: newState, event: eventJson}}
591
+ const payload = {content: {state: newState}}
666
592
 
667
593
  logger.debug(`publishEventTransitionMessage: event ${anearEvent.id} transitioning to ${newState}`)
668
594
 
@@ -13,6 +13,10 @@ class AnearMessaging {
13
13
  async publishEventTransitionMessage(eventId, newState, callback) {
14
14
  return
15
15
  }
16
+
17
+ resetAllParticipantTimers() {
18
+ return
19
+ }
16
20
  }
17
21
 
18
22
  module.exports = AnearMessaging
@@ -1,5 +1,7 @@
1
1
  "use strict"
2
2
 
3
+ const { Mutex } = require('async-mutex')
4
+
3
5
  const AnearXstate = require('../utils/AnearXstate')
4
6
  const { DefaultConfigFunc, DefaultOptionsFunc } = require('../utils/AnearXstateDefaults')
5
7
 
@@ -13,19 +15,19 @@ class AnearEvent extends JsonApiResource {
13
15
 
14
16
  constructor(json, messaging) {
15
17
  super(json)
16
-
17
- this.messaging = messaging
18
18
  this.zone = this.findIncluded(this.relationships.zone)
19
19
  this.app = this.findIncluded(this.zone.relationships.app)
20
- this.participants = new Participants(json.participants)
20
+ this.messaging = messaging
21
21
  this.anearStateMachine = this.initStateMachine(json.previousState)
22
+ this.participants = new Participants(this, json.participants)
23
+ this.mutex = new Mutex()
22
24
  }
23
25
 
24
26
  toJSON() {
25
27
  return {
26
28
  ...super.toJSON(),
27
- context: this.stateMachineContext,
28
29
  participants: this.participants.toJSON(),
30
+ context: this.stateMachineContext,
29
31
  previousState: this.anearStateMachine.currentState
30
32
  }
31
33
  }
@@ -120,6 +122,10 @@ class AnearEvent extends JsonApiResource {
120
122
  return !this.hasFlag("no_spectators")
121
123
  }
122
124
 
125
+ async spectatorCount() {
126
+ return await this.messaging.getSpectatorCount(this)
127
+ }
128
+
123
129
  async createdEventCallback(participantCreator) {
124
130
  // You may implement createdEventCallback() in your AnearEvent sub-class
125
131
  }
@@ -136,8 +142,8 @@ class AnearEvent extends JsonApiResource {
136
142
  // You may implement an async spectatorRefreshEventCallback() in your AnearEvent sub-class
137
143
  }
138
144
 
139
- async participantCloseEventCallback(participant) {
140
- throw new Error('You must implement an async participantCloseEventCallback() in your AnearEvent sub-class');
145
+ async participantExitEventCallback(participant) {
146
+ throw new Error('You must implement an async participantExitEventCallback() in your AnearEvent sub-class');
141
147
  }
142
148
 
143
149
  async participantActionEventCallback(participant, actionEventName, message) {
@@ -173,6 +179,18 @@ class AnearEvent extends JsonApiResource {
173
179
  )
174
180
  }
175
181
 
182
+ async runExclusive(name, callback) {
183
+ logger.debug(`waiting for ${name} mutex`)
184
+
185
+ await this.mutex.runExclusive(
186
+ async () => {
187
+ logger.debug(`mutex ${name} locked!`)
188
+ await callback()
189
+ }
190
+ )
191
+ logger.debug(`mutex ${name} released!`)
192
+ }
193
+
176
194
  async publishEventSpectatorsMessage(message) {
177
195
  await this.messaging.publishEventSpectatorsMessage(this, this.css, message)
178
196
  }
@@ -203,13 +221,13 @@ class AnearEvent extends JsonApiResource {
203
221
  if (this.participants.exists(participant)) {
204
222
  logger.info(`AnearEvent: participant ${participant.id} exists. Refreshing...`)
205
223
 
206
- this.participants.add(this, participant) // update the participants entry
224
+ this.participants.add(participant) // update the participants entry
207
225
 
208
226
  this.anearStateMachine.sendRefreshEvent({ participant })
209
227
 
210
228
  await participant.update()
211
229
  } else {
212
- this.participants.add(this, participant) // add the participants entry
230
+ this.participants.add(participant) // add the participants entry
213
231
 
214
232
  this.anearStateMachine.sendJoinEvent({ participant })
215
233
 
@@ -217,11 +235,15 @@ class AnearEvent extends JsonApiResource {
217
235
  }
218
236
  }
219
237
 
220
- async participantClose(participant) {
221
- this.participants.purge(participant)
222
-
223
- this.anearStateMachine.sendCloseEvent({ participant })
238
+ async participantExit(participant) {
239
+ // this informs the state machine that the participant has exited the event
240
+ // and removes that participant completely
241
+ this.anearStateMachine.sendParticipantExitEvent({ participant })
242
+ await this.participantPurge(participant)
243
+ }
224
244
 
245
+ async participantPurge(participant) {
246
+ this.participants.purge(participant)
225
247
  await participant.remove()
226
248
  }
227
249
 
@@ -250,11 +272,10 @@ class AnearEvent extends JsonApiResource {
250
272
  logger.debug(`AnearEvent: transitionEvent(${eventName})`)
251
273
 
252
274
  try {
253
- const eventJson = await this.messaging.api.transitionEvent(this.id, eventName)
254
- const newState = eventJson.attributes.state
255
-
256
- await this.messaging.publishEventTransitionMessage(this, eventJson)
275
+ const responseAttributes = await this.messaging.api.transitionEvent(this.id, eventName)
276
+ const newState = responseAttributes.state
257
277
  this.attributes.state = newState
278
+ await this.messaging.publishEventTransitionMessage(this, newState)
258
279
  } catch(err) {
259
280
  logger.error(`AnearEvent: transitionEvent error: ${err}`)
260
281
  }
@@ -285,6 +306,33 @@ class AnearEvent extends JsonApiResource {
285
306
  await this.transitionEvent('cancel')
286
307
  }
287
308
 
309
+ async closeOutParticipants() {
310
+ // upon exiting the event, this will clean up any participants remaining
311
+ return Promise.all(
312
+ this.participants.all.map(
313
+ async p => {
314
+ await this.messaging.closeParticipant(
315
+ this,
316
+ p.id,
317
+ async (anearEvent, participant) => {
318
+ await anearEvent.participantPurge(participant)
319
+ }
320
+ )
321
+ }
322
+ )
323
+ )
324
+ }
325
+
326
+ async purgeParticipants() {
327
+ // remove participants from Participants class and from Storage
328
+ const all = this.participants.all
329
+ if (this.participants.host) all.push(this.participants.host)
330
+
331
+ await Promise.all(
332
+ all.map(p => this.participantPurge(p))
333
+ )
334
+ }
335
+
288
336
  eventChannelName () {
289
337
  return this.getChannelName('event')
290
338
  }
@@ -305,6 +353,11 @@ class AnearEvent extends JsonApiResource {
305
353
  return this.attributes[`${key}-channel-name`]
306
354
  }
307
355
 
356
+ async closeEvent() {
357
+ await this.closeOutParticipants()
358
+ await this.closeMessaging()
359
+ }
360
+
308
361
  async closeMessaging () {
309
362
  await this.messaging.detachAll(this.id)
310
363
  }
@@ -318,7 +371,4 @@ class AnearEvent extends JsonApiResource {
318
371
  }
319
372
  }
320
373
 
321
- module.exports = {
322
- AnearEvent,
323
- logger
324
- }
374
+ module.exports = AnearEvent