| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * DEVELOPMENT NOTE | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THIS FILE IS UNDER ACTIVE DEVELOPMENT AND IS EXPLICITLY EXCLUDED FROM IMMUTABILITY GUARANTEES | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * If you use api methods in this file, be prepared that they may be removed or modified with no warning. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-22 15:53:04 -05:00
										 |  |  | const nconf = require('nconf'); | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | const winston = require('winston'); | 
					
						
							| 
									
										
										
										
											2023-12-22 15:53:04 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | const db = require('../database'); | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | const meta = require('../meta'); | 
					
						
							|  |  |  | const privileges = require('../privileges'); | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | const activitypub = require('../activitypub'); | 
					
						
							| 
									
										
										
										
											2024-01-24 11:44:10 -05:00
										 |  |  | const posts = require('../posts'); | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | const activitypubApi = module.exports; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | function noop() {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function enabledCheck(next) { | 
					
						
							|  |  |  | 	return async function (caller, params) { | 
					
						
							|  |  |  | 		if (!meta.config.activitypubEnabled) { | 
					
						
							|  |  |  | 			return noop; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		next(caller, params); | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | activitypubApi.follow = enabledCheck(async (caller, { uid } = {}) => { | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 	const result = await activitypub.helpers.query(uid); | 
					
						
							|  |  |  | 	if (!result) { | 
					
						
							| 
									
										
										
										
											2024-01-08 14:30:09 -05:00
										 |  |  | 		throw new Error('[[error:activitypub.invalid-id]]'); | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	await activitypub.send('uid', caller.uid, [result.actorUri], { | 
					
						
							| 
									
										
										
										
											2024-04-09 23:58:52 +02:00
										 |  |  | 		id: `${nconf.get('url')}/uid/${caller.uid}#activity/follow/${result.username}@${result.hostname}`, | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 		type: 'Follow', | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 		object: result.actorUri, | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2024-04-09 23:58:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	await db.sortedSetAdd(`followRequests:${caller.uid}`, Date.now(), result.actorUri); | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | // should be .undo.follow
 | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | activitypubApi.unfollow = enabledCheck(async (caller, { uid }) => { | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 	const result = await activitypub.helpers.query(uid); | 
					
						
							|  |  |  | 	if (!result) { | 
					
						
							| 
									
										
										
										
											2024-01-08 14:30:09 -05:00
										 |  |  | 		throw new Error('[[error:activitypub.invalid-id]]'); | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	await activitypub.send('uid', caller.uid, [result.actorUri], { | 
					
						
							| 
									
										
										
										
											2024-04-10 01:11:49 +02:00
										 |  |  | 		id: `${nconf.get('url')}/uid/${caller.uid}#activity/undo:follow/${result.username}@${result.hostname}`, | 
					
						
							| 
									
										
										
										
											2023-12-22 15:53:04 -05:00
										 |  |  | 		type: 'Undo', | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 		object: { | 
					
						
							| 
									
										
										
										
											2024-04-10 01:11:49 +02:00
										 |  |  | 			id: `${nconf.get('url')}/uid/${caller.uid}#activity/follow/${result.username}@${result.hostname}`, | 
					
						
							| 
									
										
										
										
											2023-12-22 15:53:04 -05:00
										 |  |  | 			type: 'Follow', | 
					
						
							| 
									
										
										
										
											2024-01-26 22:35:02 -05:00
										 |  |  | 			actor: `${nconf.get('url')}/uid/${caller.uid}`, | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 			object: result.actorUri, | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	await Promise.all([ | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 		db.sortedSetRemove(`followingRemote:${caller.uid}`, result.actorUri), | 
					
						
							| 
									
										
										
										
											2023-12-08 10:55:16 -05:00
										 |  |  | 		db.decrObjectField(`user:${caller.uid}`, 'followingRemoteCount'), | 
					
						
							|  |  |  | 	]); | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2024-01-24 11:44:10 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | activitypubApi.create = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 11:25:45 -05:00
										 |  |  | // this might be better genericised... tbd. some of to/cc is built in mocks.
 | 
					
						
							|  |  |  | async function buildRecipients(object, uid) { | 
					
						
							|  |  |  | 	const followers = await db.getSortedSetMembers(`followersRemote:${uid}`); | 
					
						
							| 
									
										
										
										
											2024-03-09 21:09:50 -05:00
										 |  |  | 	let { to, cc } = object; | 
					
						
							|  |  |  | 	to = new Set(to); | 
					
						
							|  |  |  | 	cc = new Set(cc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Directly address user if inReplyTo
 | 
					
						
							| 
									
										
										
										
											2024-01-30 11:25:45 -05:00
										 |  |  | 	const parentId = await posts.getPostField(object.inReplyTo, 'uid'); | 
					
						
							| 
									
										
										
										
											2024-03-09 21:09:50 -05:00
										 |  |  | 	if (activitypub.helpers.isUri(parentId) && to.has(parentId)) { | 
					
						
							|  |  |  | 		to.add(parentId); | 
					
						
							| 
									
										
										
										
											2024-01-30 11:25:45 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-09 21:09:50 -05:00
										 |  |  | 	const targets = new Set([...followers, ...to, ...cc]); | 
					
						
							|  |  |  | 	targets.delete(`${nconf.get('url')}/uid/${uid}/followers`); // followers URL not targeted
 | 
					
						
							|  |  |  | 	targets.delete(activitypub._constants.publicAddress); // public address not targeted
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	object.to = Array.from(to); | 
					
						
							|  |  |  | 	object.cc = Array.from(cc); | 
					
						
							| 
									
										
										
										
											2024-01-30 11:25:45 -05:00
										 |  |  | 	return { targets }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | activitypubApi.create.post = enabledCheck(async (caller, { pid }) => { | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 	const post = (await posts.getPostSummaryByPids([pid], caller.uid, { stripTags: false })).pop(); | 
					
						
							|  |  |  | 	if (!post) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-08 20:45:54 -05:00
										 |  |  | 	const allowed = await privileges.posts.can('topics:read', pid, activitypub._constants.uid); | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | 	if (!allowed) { | 
					
						
							|  |  |  | 		winston.verbose(`[activitypub/api] Not federating creation of pid ${pid} to the fediverse due to privileges.`); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 11:25:45 -05:00
										 |  |  | 	const object = await activitypub.mocks.note(post); | 
					
						
							|  |  |  | 	const { targets } = await buildRecipients(object, post.user.uid); | 
					
						
							| 
									
										
										
										
											2024-03-22 14:39:18 -04:00
										 |  |  | 	const { cid } = post.category; | 
					
						
							|  |  |  | 	const followers = await activitypub.notes.getCategoryFollowers(cid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const payloads = { | 
					
						
							|  |  |  | 		create: { | 
					
						
							| 
									
										
										
										
											2024-04-09 23:58:52 +02:00
										 |  |  | 			id: `${object.id}#activity/create`, | 
					
						
							| 
									
										
										
										
											2024-03-22 14:39:18 -04:00
										 |  |  | 			type: 'Create', | 
					
						
							|  |  |  | 			to: object.to, | 
					
						
							|  |  |  | 			cc: object.cc, | 
					
						
							|  |  |  | 			object, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		announce: { | 
					
						
							| 
									
										
										
										
											2024-04-09 23:58:52 +02:00
										 |  |  | 			id: `${object.id}#activity/announce`, | 
					
						
							| 
									
										
										
										
											2024-03-22 14:39:18 -04:00
										 |  |  | 			type: 'Announce', | 
					
						
							|  |  |  | 			to: [`${nconf.get('url')}/category/${cid}/followers`], | 
					
						
							|  |  |  | 			cc: [activitypub._constants.publicAddress], | 
					
						
							|  |  |  | 			object, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-01-24 11:44:10 -05:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-22 14:39:18 -04:00
										 |  |  | 	await activitypub.send('uid', caller.uid, Array.from(targets), payloads.create); | 
					
						
							| 
									
										
										
										
											2024-03-22 15:28:01 -04:00
										 |  |  | 	if (followers.length) { | 
					
						
							|  |  |  | 		await activitypub.send('cid', cid, followers, payloads.announce); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | activitypubApi.update = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | activitypubApi.update.profile = enabledCheck(async (caller, { uid }) => { | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 	const [object, followers] = await Promise.all([ | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | 		activitypub.mocks.actors.user(uid), | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 		db.getSortedSetMembers(`followersRemote:${caller.uid}`), | 
					
						
							|  |  |  | 	]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	await activitypub.send('uid', caller.uid, followers, { | 
					
						
							| 
									
										
										
										
											2024-04-12 16:43:33 +02:00
										 |  |  | 		id: `${object.id}#activity/update/${Date.now()}`, | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 		type: 'Update', | 
					
						
							|  |  |  | 		to: [activitypub._constants.publicAddress], | 
					
						
							|  |  |  | 		cc: [], | 
					
						
							|  |  |  | 		object, | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2024-01-30 11:25:45 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-06 01:18:46 +02:00
										 |  |  | activitypubApi.update.note = enabledCheck(async (caller, { post }) => { | 
					
						
							| 
									
										
										
										
											2024-01-30 11:25:45 -05:00
										 |  |  | 	const object = await activitypub.mocks.note(post); | 
					
						
							|  |  |  | 	const { targets } = await buildRecipients(object, post.user.uid); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | 	const allowed = await privileges.posts.can('topics:read', post.pid, activitypub._constants.uid); | 
					
						
							|  |  |  | 	if (!allowed) { | 
					
						
							|  |  |  | 		winston.verbose(`[activitypub/api] Not federating update of pid ${post.pid} to the fediverse due to privileges.`); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 11:25:45 -05:00
										 |  |  | 	const payload = { | 
					
						
							| 
									
										
										
										
											2024-04-09 23:58:52 +02:00
										 |  |  | 		id: `${object.id}#activity/update/${post.edited}`, | 
					
						
							| 
									
										
										
										
											2024-01-30 11:25:45 -05:00
										 |  |  | 		type: 'Update', | 
					
						
							|  |  |  | 		to: object.to, | 
					
						
							|  |  |  | 		cc: object.cc, | 
					
						
							|  |  |  | 		object, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	await activitypub.send('uid', caller.uid, Array.from(targets), payload); | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | activitypubApi.like = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | activitypubApi.like.note = enabledCheck(async (caller, { pid }) => { | 
					
						
							|  |  |  | 	if (!activitypub.helpers.isUri(pid)) { // remote only
 | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const uid = await posts.getPostField(pid, 'uid'); | 
					
						
							|  |  |  | 	if (!activitypub.helpers.isUri(uid)) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	await activitypub.send('uid', caller.uid, [uid], { | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 		type: 'Like', | 
					
						
							|  |  |  | 		object: pid, | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | activitypubApi.undo = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // activitypubApi.undo.follow =
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | activitypubApi.undo.like = enabledCheck(async (caller, { pid }) => { | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 	if (!activitypub.helpers.isUri(pid)) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const uid = await posts.getPostField(pid, 'uid'); | 
					
						
							|  |  |  | 	if (!activitypub.helpers.isUri(uid)) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	await activitypub.send('uid', caller.uid, [uid], { | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 		type: 'Undo', | 
					
						
							|  |  |  | 		object: { | 
					
						
							|  |  |  | 			actor: `${nconf.get('url')}/uid/${caller.uid}`, | 
					
						
							|  |  |  | 			type: 'Like', | 
					
						
							|  |  |  | 			object: pid, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | }); |