121 lines
4.0 KiB
JavaScript
121 lines
4.0 KiB
JavaScript
const { ACTIVITY_TYPES, OBJECT_TYPES } = require('@semapps/activitypub');
|
|
const { PodActivitiesHandlerMixin } = require('@activitypods/app');
|
|
const { arrayOf } = require('@semapps/ldp');
|
|
|
|
module.exports = {
|
|
name: 'registration',
|
|
mixins: [PodActivitiesHandlerMixin],
|
|
async started() {
|
|
this.logger.info('Registration watcher subscribed');
|
|
},
|
|
methods: {
|
|
/**
|
|
* The Event's `apods:attendees` collection is attached asynchronously by
|
|
* PodCollectionsHandlerMixin on create, so a Join/Leave processed in that window sees no
|
|
* collection. Returning immediately would drop the activity forever (the client overlay
|
|
* shows joined, nothing reconciles). Re-fetch the Event a bounded number of times until the
|
|
* collection appears; only then give up.
|
|
*/
|
|
async fetchAttendeesCollectionUri(ctx, event, actorUri) {
|
|
if (event['apods:attendees'] || !event.id) return event['apods:attendees'];
|
|
|
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
try {
|
|
const { ok, body } = await ctx.call('pod-resources.get', { resourceUri: event.id, actorUri });
|
|
if (ok && body && body['apods:attendees']) return body['apods:attendees'];
|
|
} catch (e) {
|
|
this.logger.warn(`Could not re-fetch ${event.id} while waiting for its attendees collection: ${e.message}`);
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
},
|
|
activities: {
|
|
join: {
|
|
match: {
|
|
type: ACTIVITY_TYPES.JOIN,
|
|
object: {
|
|
type: OBJECT_TYPES.EVENT
|
|
}
|
|
},
|
|
async onEmit(ctx, activity, actorUri) {
|
|
const event = activity.object;
|
|
const organizerUri = event['dc:creator'];
|
|
if (!organizerUri) return;
|
|
|
|
const { body: actor } = await ctx.call('pod-resources.get', { resourceUri: actorUri, actorUri });
|
|
if (!actor || !actor['apods:contacts']) return;
|
|
|
|
const contactsGroupUri = await ctx.call('pod-wac-groups.getUriFromCollectionUri', {
|
|
collectionUri: actor['apods:contacts']
|
|
});
|
|
|
|
await ctx.call('pod-wac-groups.addMember', {
|
|
groupUri: contactsGroupUri,
|
|
memberUri: organizerUri,
|
|
actorUri
|
|
});
|
|
},
|
|
async onReceive(ctx, activity, actorUri) {
|
|
const event = activity.object;
|
|
const attendeesCollectionUri = await this.fetchAttendeesCollectionUri(ctx, event, actorUri);
|
|
|
|
if (!attendeesCollectionUri) {
|
|
this.logger.warn(`No attendees collection on ${event.id}, skipping Join`);
|
|
return;
|
|
}
|
|
|
|
const attendees = await ctx.call('pod-collections.getItems', {
|
|
collectionUri: attendeesCollectionUri,
|
|
actorUri
|
|
});
|
|
|
|
if (arrayOf(attendees).includes(activity.actor)) {
|
|
this.logger.info(`${activity.actor} is already attending ${event.id}`);
|
|
return;
|
|
}
|
|
|
|
await ctx.call('attendees.add', {
|
|
collectionUri: attendeesCollectionUri,
|
|
itemUri: activity.actor,
|
|
actorUri
|
|
});
|
|
}
|
|
},
|
|
leave: {
|
|
match: {
|
|
type: ACTIVITY_TYPES.LEAVE,
|
|
object: {
|
|
type: OBJECT_TYPES.EVENT
|
|
}
|
|
},
|
|
async onReceive(ctx, activity, actorUri) {
|
|
const event = activity.object;
|
|
const attendeesCollectionUri = await this.fetchAttendeesCollectionUri(ctx, event, actorUri);
|
|
|
|
if (!attendeesCollectionUri) {
|
|
this.logger.warn(`No attendees collection on ${event.id}, skipping Leave`);
|
|
return;
|
|
}
|
|
|
|
const attendees = await ctx.call('pod-collections.getItems', {
|
|
collectionUri: attendeesCollectionUri,
|
|
actorUri
|
|
});
|
|
|
|
if (!arrayOf(attendees).includes(activity.actor)) {
|
|
this.logger.info(`${activity.actor} is not attending ${event.id}, skipping Leave`);
|
|
return;
|
|
}
|
|
|
|
await ctx.call('attendees.remove', {
|
|
collectionUri: attendeesCollectionUri,
|
|
itemUri: activity.actor,
|
|
actorUri
|
|
});
|
|
}
|
|
}
|
|
}
|
|
};
|