summaryrefslogtreecommitdiff
path: root/src/remote/activitypub/kernel/announce/note.ts
blob: a5db5b8ca7e7498e69673689c1c99e4728048735 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import Resolver from '../../resolver';
import post from '../../../../services/note/create';
import { IRemoteUser, User } from '../../../../models/entities/user';
import { IAnnounce, IObject, getApId, getApIds } from '../../type';
import { fetchNote, resolveNote } from '../../models/note';
import { resolvePerson } from '../../models/person';
import { apLogger } from '../../logger';
import { extractDbHost } from '../../../../misc/convert-host';
import { fetchMeta } from '../../../../misc/fetch-meta';
import { getApLock } from '../../../../misc/app-lock';

const logger = apLogger;

/**
 * アナウンスアクティビティを捌きます
 */
export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: IObject): Promise<void> {
	const uri = getApId(activity);

	// アナウンサーが凍結されていたらスキップ
	if (actor.isSuspended) {
		return;
	}

	// アナウンス先をブロックしてたら中断
	const meta = await fetchMeta();
	if (meta.blockedHosts.includes(extractDbHost(uri))) return;

	const unlock = await getApLock(uri);

	try {
		// 既に同じURIを持つものが登録されていないかチェック
		const exist = await fetchNote(uri);
		if (exist) {
			return;
		}

		// Announce対象をresolve
		let renote;
		try {
			renote = await resolveNote(note);
		} catch (e) {
			// 対象が4xxならスキップ
			if (e.statusCode >= 400 && e.statusCode < 500) {
				logger.warn(`Ignored announce target ${note.inReplyTo} - ${e.statusCode}`);
				return;
			}
			logger.warn(`Error in announce target ${note.inReplyTo} - ${e.statusCode || e}`);
			throw e;
		}

		logger.info(`Creating the (Re)Note: ${uri}`);

		//#region Visibility
		const to = getApIds(activity.to);
		const cc = getApIds(activity.cc);

		const visibility = getVisibility(to, cc, actor);

		let visibleUsers: User[] = [];
		if (visibility == 'specified') {
			visibleUsers = await Promise.all(to.map(uri => resolvePerson(uri)));
		}
		//#endergion

		await post(actor, {
			createdAt: activity.published ? new Date(activity.published) : null,
			renote,
			visibility,
			visibleUsers,
			uri
		});
	} finally {
		unlock();
	}
}

type visibility = 'public' | 'home' | 'followers' | 'specified';

function getVisibility(to: string[], cc: string[], actor: IRemoteUser): visibility {
	const PUBLIC = 'https://www.w3.org/ns/activitystreams#Public';

	if (to.includes(PUBLIC)) {
		return 'public';
	} else if (cc.includes(PUBLIC)) {
		return 'home';
	} else if (to.includes(`${actor.uri}/followers`)) {
		return 'followers';
	} else {
		return 'specified';
	}
}