mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 11:05:54 +01:00 
			
		
		
		
	refactor: expose buildRecipients as a main activitypub lib method, call buildReceipients in ap controller actors for notes
This commit is contained in:
		| @@ -8,6 +8,8 @@ const { CronJob } = require('cron'); | |||||||
| const request = require('../request'); | const request = require('../request'); | ||||||
| const db = require('../database'); | const db = require('../database'); | ||||||
| const meta = require('../meta'); | const meta = require('../meta'); | ||||||
|  | const posts = require('../posts'); | ||||||
|  | const messaging = require('../messaging'); | ||||||
| const user = require('../user'); | const user = require('../user'); | ||||||
| const utils = require('../utils'); | const utils = require('../utils'); | ||||||
| const ttl = require('../cache/ttl'); | const ttl = require('../cache/ttl'); | ||||||
| @@ -379,3 +381,65 @@ ActivityPub.record = async ({ id, type, actor }) => { | |||||||
| 		analytics.increment(['activities', `activities:byType:${type}`, `activities:byHost:${hostname}`]), | 		analytics.increment(['activities', `activities:byType:${type}`, `activities:byHost:${hostname}`]), | ||||||
| 	]); | 	]); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | ActivityPub.buildRecipients = async function (object, { pid, uid, cid }) { | ||||||
|  | 	/** | ||||||
|  | 	 * - Builds a list of targets for activitypub.send to consume | ||||||
|  | 	 * - Extends to and cc since the activity can be addressed more widely | ||||||
|  | 	 * - Optional parameters: | ||||||
|  | 	 *     - `cid`: includes followers of the passed-in cid (local only) | ||||||
|  | 	 *     - `uid`: includes followers of the passed-in uid (local only) | ||||||
|  | 	 *     - `pid`: includes announcers and all authors up the toPid chain | ||||||
|  | 	 */ | ||||||
|  | 	let { to, cc } = object; | ||||||
|  | 	to = new Set(to); | ||||||
|  | 	cc = new Set(cc); | ||||||
|  |  | ||||||
|  | 	let followers = []; | ||||||
|  | 	if (uid) { | ||||||
|  | 		followers = await db.getSortedSetMembers(`followersRemote:${uid}`); | ||||||
|  | 		const followersUrl = `${nconf.get('url')}/uid/${uid}/followers`; | ||||||
|  | 		if (!to.has(followersUrl)) { | ||||||
|  | 			cc.add(followersUrl); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (cid) { | ||||||
|  | 		const cidFollowers = await ActivityPub.notes.getCategoryFollowers(cid); | ||||||
|  | 		followers = followers.concat(cidFollowers); | ||||||
|  | 		const followersUrl = `${nconf.get('url')}/category/${cid}/followers`; | ||||||
|  | 		if (!to.has(followersUrl)) { | ||||||
|  | 			cc.add(followersUrl); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const targets = new Set([...followers, ...to, ...cc]); | ||||||
|  |  | ||||||
|  | 	// Remove any ids that aren't asserted actors | ||||||
|  | 	const exists = await db.isSortedSetMembers('usersRemote:lastCrawled', [...targets]); | ||||||
|  | 	Array.from(targets).forEach((uri, idx) => { | ||||||
|  | 		if (!exists[idx]) { | ||||||
|  | 			targets.delete(uri); | ||||||
|  | 		} | ||||||
|  | 	}); | ||||||
|  |  | ||||||
|  | 	// Topic posters, post announcers and their followers | ||||||
|  | 	if (pid) { | ||||||
|  | 		const tid = await posts.getPostField(pid, 'tid'); | ||||||
|  | 		const participants = (await db.getSortedSetMembers(`tid:${tid}:posters`)) | ||||||
|  | 			.filter(uid => !utils.isNumber(uid)); // remote users only | ||||||
|  | 		const announcers = (await ActivityPub.notes.announce.list({ pid })).map(({ actor }) => actor); | ||||||
|  | 		const auxiliaries = Array.from(new Set([...participants, ...announcers])); | ||||||
|  | 		const auxiliaryFollowers = (await user.getUsersFields(auxiliaries, ['followersUrl'])) | ||||||
|  | 			.filter(o => o.hasOwnProperty('followersUrl')) | ||||||
|  | 			.map(({ followersUrl }) => followersUrl); | ||||||
|  | 		[...auxiliaries].forEach(uri => uri && targets.add(uri)); | ||||||
|  | 		[...auxiliaries, ...auxiliaryFollowers].forEach(uri => uri && cc.add(uri)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return { | ||||||
|  | 		to: [...to], | ||||||
|  | 		cc: [...cc], | ||||||
|  | 		targets, | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|   | |||||||
| @@ -103,68 +103,6 @@ activitypubApi.unfollow = enabledCheck(async (caller, { type, id, actor }) => { | |||||||
|  |  | ||||||
| activitypubApi.create = {}; | activitypubApi.create = {}; | ||||||
|  |  | ||||||
| async function buildRecipients(object, { pid, uid, cid }) { |  | ||||||
| 	/** |  | ||||||
| 	 * - Builds a list of targets for activitypub.send to consume |  | ||||||
| 	 * - Extends to and cc since the activity can be addressed more widely |  | ||||||
| 	 * - Optional parameters: |  | ||||||
| 	 *     - `cid`: includes followers of the passed-in cid (local only) |  | ||||||
| 	 *     - `uid`: includes followers of the passed-in uid (local only) |  | ||||||
| 	 *     - `pid`: includes announcers and all authors up the toPid chain |  | ||||||
| 	 */ |  | ||||||
| 	let { to, cc } = object; |  | ||||||
| 	to = new Set(to); |  | ||||||
| 	cc = new Set(cc); |  | ||||||
|  |  | ||||||
| 	let followers = []; |  | ||||||
| 	if (uid) { |  | ||||||
| 		followers = await db.getSortedSetMembers(`followersRemote:${uid}`); |  | ||||||
| 		const followersUrl = `${nconf.get('url')}/uid/${uid}/followers`; |  | ||||||
| 		if (!to.has(followersUrl)) { |  | ||||||
| 			cc.add(followersUrl); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (cid) { |  | ||||||
| 		const cidFollowers = await activitypub.notes.getCategoryFollowers(cid); |  | ||||||
| 		followers = followers.concat(cidFollowers); |  | ||||||
| 		const followersUrl = `${nconf.get('url')}/category/${cid}/followers`; |  | ||||||
| 		if (!to.has(followersUrl)) { |  | ||||||
| 			cc.add(followersUrl); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const targets = new Set([...followers, ...to, ...cc]); |  | ||||||
|  |  | ||||||
| 	// Remove any ids that aren't asserted actors |  | ||||||
| 	const exists = await db.isSortedSetMembers('usersRemote:lastCrawled', [...targets]); |  | ||||||
| 	Array.from(targets).forEach((uri, idx) => { |  | ||||||
| 		if (!exists[idx]) { |  | ||||||
| 			targets.delete(uri); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	// Topic posters, post announcers and their followers |  | ||||||
| 	if (pid) { |  | ||||||
| 		const tid = await posts.getPostField(pid, 'tid'); |  | ||||||
| 		const participants = (await db.getSortedSetMembers(`tid:${tid}:posters`)) |  | ||||||
| 			.filter(uid => !utils.isNumber(uid)); // remote users only |  | ||||||
| 		const announcers = (await activitypub.notes.announce.list({ pid })).map(({ actor }) => actor); |  | ||||||
| 		const auxiliaries = Array.from(new Set([...participants, ...announcers])); |  | ||||||
| 		const auxiliaryFollowers = (await user.getUsersFields(auxiliaries, ['followersUrl'])) |  | ||||||
| 			.filter(o => o.hasOwnProperty('followersUrl')) |  | ||||||
| 			.map(({ followersUrl }) => followersUrl); |  | ||||||
| 		[...auxiliaries].forEach(uri => uri && targets.add(uri)); |  | ||||||
| 		[...auxiliaries, ...auxiliaryFollowers].forEach(uri => uri && cc.add(uri)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return { |  | ||||||
| 		to: [...to], |  | ||||||
| 		cc: [...cc], |  | ||||||
| 		targets, |  | ||||||
| 	}; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| activitypubApi.create.note = enabledCheck(async (caller, { pid, post }) => { | activitypubApi.create.note = enabledCheck(async (caller, { pid, post }) => { | ||||||
| 	if (!post) { | 	if (!post) { | ||||||
| 		post = (await posts.getPostSummaryByPids([pid], caller.uid, { stripTags: false })).pop(); | 		post = (await posts.getPostSummaryByPids([pid], caller.uid, { stripTags: false })).pop(); | ||||||
| @@ -182,7 +120,9 @@ activitypubApi.create.note = enabledCheck(async (caller, { pid, post }) => { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	const object = await activitypub.mocks.notes.public(post); | 	const object = await activitypub.mocks.notes.public(post); | ||||||
| 	const { to, cc, targets } = await buildRecipients(object, { pid, uid: post.user.uid }); | 	const { to, cc, targets } = await activitypub.buildRecipients(object, { pid, uid: post.user.uid }); | ||||||
|  | 	object.to = to; | ||||||
|  | 	object.cc = cc; | ||||||
| 	const { cid } = post.category; | 	const { cid } = post.category; | ||||||
| 	const followers = await activitypub.notes.getCategoryFollowers(cid); | 	const followers = await activitypub.notes.getCategoryFollowers(cid); | ||||||
|  |  | ||||||
| @@ -268,7 +208,9 @@ activitypubApi.update.note = enabledCheck(async (caller, { post }) => { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	const object = await activitypub.mocks.notes.public(post); | 	const object = await activitypub.mocks.notes.public(post); | ||||||
| 	const { to, cc, targets } = await buildRecipients(object, { pid: post.pid, uid: post.user.uid }); | 	const { to, cc, targets } = await activitypub.buildRecipients(object, { pid: post.pid, uid: post.user.uid }); | ||||||
|  | 	object.to = to; | ||||||
|  | 	object.cc = cc; | ||||||
|  |  | ||||||
| 	const allowed = await privileges.posts.can('topics:read', post.pid, activitypub._constants.uid); | 	const allowed = await privileges.posts.can('topics:read', post.pid, activitypub._constants.uid); | ||||||
| 	if (!allowed) { | 	if (!allowed) { | ||||||
| @@ -321,7 +263,7 @@ activitypubApi.delete.note = enabledCheck(async (caller, { pid }) => { | |||||||
| 	const id = `${nconf.get('url')}/post/${pid}`; | 	const id = `${nconf.get('url')}/post/${pid}`; | ||||||
| 	const post = (await posts.getPostSummaryByPids([pid], caller.uid, { stripTags: false })).pop(); | 	const post = (await posts.getPostSummaryByPids([pid], caller.uid, { stripTags: false })).pop(); | ||||||
| 	const object = await activitypub.mocks.notes.public(post); | 	const object = await activitypub.mocks.notes.public(post); | ||||||
| 	const { to, cc, targets } = await buildRecipients(object, { pid, uid: post.user.uid }); | 	const { to, cc, targets } = await activitypub.buildRecipients(object, { pid, uid: post.user.uid }); | ||||||
|  |  | ||||||
| 	const allowed = await privileges.posts.can('topics:read', pid, activitypub._constants.uid); | 	const allowed = await privileges.posts.can('topics:read', pid, activitypub._constants.uid); | ||||||
| 	if (!allowed) { | 	if (!allowed) { | ||||||
| @@ -377,7 +319,7 @@ activitypubApi.announce.note = enabledCheck(async (caller, { tid }) => { | |||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	const { to, cc, targets } = await buildRecipients({ | 	const { to, cc, targets } = await activitypub.buildRecipients({ | ||||||
| 		id: pid, | 		id: pid, | ||||||
| 		to: [activitypub._constants.publicAddress], | 		to: [activitypub._constants.publicAddress], | ||||||
| 		cc: [`${nconf.get('url')}/uid/${caller.uid}/followers`, uid], | 		cc: [`${nconf.get('url')}/uid/${caller.uid}/followers`, uid], | ||||||
| @@ -453,7 +395,7 @@ activitypubApi.add = enabledCheck((async (_, { pid }) => { | |||||||
| 	let to = [activitypub._constants.publicAddress]; | 	let to = [activitypub._constants.publicAddress]; | ||||||
| 	let cc = []; | 	let cc = []; | ||||||
| 	let targets; | 	let targets; | ||||||
| 	({ to, cc, targets } = await buildRecipients({ to, cc }, { pid: localId || pid, cid })); | 	({ to, cc, targets } = await activitypub.buildRecipients({ to, cc }, { pid: localId || pid, cid })); | ||||||
|  |  | ||||||
| 	await activitypub.send('cid', cid, targets, { | 	await activitypub.send('cid', cid, targets, { | ||||||
| 		id: `${nconf.get('url')}/post/${encodeURIComponent(localId || pid)}#activity/add/${Date.now()}`, | 		id: `${nconf.get('url')}/post/${encodeURIComponent(localId || pid)}#activity/add/${Date.now()}`, | ||||||
|   | |||||||
| @@ -77,6 +77,10 @@ Actors.note = async function (req, res) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	const payload = await activitypub.mocks.notes.public(post); | 	const payload = await activitypub.mocks.notes.public(post); | ||||||
|  | 	const { to, cc } = await activitypub.buildRecipients(payload, { pid: post.pid, uid: post.user.uid }); | ||||||
|  | 	payload.to = to; | ||||||
|  | 	payload.cc = cc; | ||||||
|  |  | ||||||
| 	res.status(200).json(payload); | 	res.status(200).json(payload); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user