egirlskey/packages/backend/src/core/activitypub/ApAudienceService.ts

108 lines
2.5 KiB
TypeScript
Raw Normal View History

import { Injectable } from '@nestjs/common';
2022-09-17 18:27:08 +00:00
import promiseLimit from 'promise-limit';
2023-02-13 06:50:22 +00:00
import type { RemoteUser, User } from '@/models/entities/User.js';
import { concat, unique } from '@/misc/prelude/array.js';
import { bindThis } from '@/decorators.js';
import { getApIds } from './type.js';
2022-09-17 18:27:08 +00:00
import { ApPersonService } from './models/ApPersonService.js';
import type { ApObject } from './type.js';
import type { Resolver } from './ApResolverService.js';
type Visibility = 'public' | 'home' | 'followers' | 'specified';
type AudienceInfo = {
visibility: Visibility,
2023-02-13 06:28:07 +00:00
mentionedUsers: User[],
visibleUsers: User[],
2022-09-17 18:27:08 +00:00
};
@Injectable()
export class ApAudienceService {
constructor(
private apPersonService: ApPersonService,
) {
}
@bindThis
2023-02-13 06:50:22 +00:00
public async parseAudience(actor: RemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
2022-09-18 18:11:50 +00:00
const toGroups = this.groupingAudience(getApIds(to), actor);
const ccGroups = this.groupingAudience(getApIds(cc), actor);
2022-09-17 18:27:08 +00:00
const others = unique(concat([toGroups.other, ccGroups.other]));
2023-02-13 06:28:07 +00:00
const limit = promiseLimit<User | null>(2);
2022-09-17 18:27:08 +00:00
const mentionedUsers = (await Promise.all(
others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))),
2023-02-13 06:28:07 +00:00
)).filter((x): x is User => x != null);
2022-09-17 18:27:08 +00:00
if (toGroups.public.length > 0) {
return {
visibility: 'public',
mentionedUsers,
visibleUsers: [],
};
}
2022-09-17 18:27:08 +00:00
if (ccGroups.public.length > 0) {
return {
visibility: 'home',
mentionedUsers,
visibleUsers: [],
};
}
2022-09-17 18:27:08 +00:00
if (toGroups.followers.length > 0) {
return {
visibility: 'followers',
mentionedUsers,
visibleUsers: [],
};
}
2022-09-17 18:27:08 +00:00
return {
visibility: 'specified',
mentionedUsers,
visibleUsers: mentionedUsers,
};
}
@bindThis
2023-02-13 06:50:22 +00:00
private groupingAudience(ids: string[], actor: RemoteUser) {
2022-09-17 18:27:08 +00:00
const groups = {
public: [] as string[],
followers: [] as string[],
other: [] as string[],
};
2022-09-17 18:27:08 +00:00
for (const id of ids) {
2022-09-18 18:11:50 +00:00
if (this.isPublic(id)) {
2022-09-17 18:27:08 +00:00
groups.public.push(id);
2022-09-18 18:11:50 +00:00
} else if (this.isFollowers(id, actor)) {
2022-09-17 18:27:08 +00:00
groups.followers.push(id);
} else {
groups.other.push(id);
}
}
2022-09-17 18:27:08 +00:00
groups.other = unique(groups.other);
2022-09-17 18:27:08 +00:00
return groups;
}
@bindThis
2022-09-18 18:11:50 +00:00
private isPublic(id: string) {
2022-09-17 18:27:08 +00:00
return [
'https://www.w3.org/ns/activitystreams#Public',
'as#Public',
'Public',
].includes(id);
}
@bindThis
2023-02-13 06:50:22 +00:00
private isFollowers(id: string, actor: RemoteUser) {
2022-09-17 18:27:08 +00:00
return (
id === (actor.followersUri ?? `${actor.uri}/followers`)
);
}
}