| 
									
										
										
										
											2023-06-28 14:59:39 -04:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-16 12:00:50 -05:00
										 |  |  | const winston = require('winston'); | 
					
						
							| 
									
										
										
										
											2024-02-07 00:14:29 -05:00
										 |  |  | const nconf = require('nconf'); | 
					
						
							| 
									
										
										
										
											2024-01-16 12:00:50 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-28 14:59:39 -04:00
										 |  |  | const db = require('../database'); | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | const privileges = require('../privileges'); | 
					
						
							| 
									
										
										
										
											2023-06-28 14:59:39 -04:00
										 |  |  | const user = require('../user'); | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | const posts = require('../posts'); | 
					
						
							| 
									
										
										
										
											2024-02-06 14:57:44 -05:00
										 |  |  | const topics = require('../topics'); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | const categories = require('../categories'); | 
					
						
							| 
									
										
										
										
											2024-02-29 16:09:57 -05:00
										 |  |  | const notifications = require('../notifications'); | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | const messaging = require('../messaging'); | 
					
						
							| 
									
										
										
										
											2024-04-06 19:00:52 +02:00
										 |  |  | const flags = require('../flags'); | 
					
						
							| 
									
										
										
										
											2024-05-07 14:01:27 -04:00
										 |  |  | const api = require('../api'); | 
					
						
							| 
									
										
										
										
											2023-12-13 13:15:03 -05:00
										 |  |  | const activitypub = require('.'); | 
					
						
							| 
									
										
										
										
											2023-06-28 14:59:39 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-29 16:09:57 -05:00
										 |  |  | const socketHelpers = require('../socket.io/helpers'); | 
					
						
							| 
									
										
										
										
											2023-06-28 14:59:39 -04:00
										 |  |  | const helpers = require('./helpers'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const inbox = module.exports; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-25 13:25:12 -04:00
										 |  |  | function reject(type, object, target, senderType = 'uid', id = 0) { | 
					
						
							|  |  |  | 	activitypub.send(senderType, id, target, { | 
					
						
							| 
									
										
										
										
											2024-05-03 17:48:09 +02:00
										 |  |  | 		id: `${helpers.resolveActor(senderType, id)}#/activity/reject/${encodeURIComponent(object.id)}`, | 
					
						
							| 
									
										
										
										
											2024-03-25 13:25:12 -04:00
										 |  |  | 		type: 'Reject', | 
					
						
							|  |  |  | 		object: { | 
					
						
							|  |  |  | 			type, | 
					
						
							|  |  |  | 			target, | 
					
						
							|  |  |  | 			object, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-06-13 18:36:05 -04:00
										 |  |  | 	}).catch(err => winston.error(err.stack)); | 
					
						
							| 
									
										
										
										
											2024-03-25 13:25:12 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-16 12:00:50 -05:00
										 |  |  | inbox.create = async (req) => { | 
					
						
							| 
									
										
										
										
											2025-03-06 10:45:30 -05:00
										 |  |  | 	const { object, actor } = req.body; | 
					
						
							| 
									
										
										
										
											2025-08-27 11:29:43 -04:00
										 |  |  | 	const start = Date.now(); | 
					
						
							| 
									
										
										
										
											2024-10-08 14:26:55 -04:00
										 |  |  | 	// Alternative logic for non-public objects
 | 
					
						
							| 
									
										
										
										
											2025-02-26 12:29:52 -05:00
										 |  |  | 	const isPublic = [...(object.to || []), ...(object.cc || [])].includes(activitypub._constants.publicAddress); | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 	if (!isPublic) { | 
					
						
							| 
									
										
										
										
											2024-10-08 14:26:55 -04:00
										 |  |  | 		return await activitypub.notes.assertPrivate(object); | 
					
						
							| 
									
										
										
										
											2024-02-21 14:58:52 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-08-27 11:29:43 -04:00
										 |  |  | 	console.log(' 4a', Date.now() - start); | 
					
						
							| 
									
										
										
										
											2025-05-13 13:59:34 -04:00
										 |  |  | 	// Category sync, remove when cross-posting available
 | 
					
						
							| 
									
										
										
										
											2025-03-06 10:45:30 -05:00
										 |  |  | 	const { cids } = await activitypub.actors.getLocalFollowers(actor); | 
					
						
							|  |  |  | 	let cid = null; | 
					
						
							|  |  |  | 	if (cids.size > 0) { | 
					
						
							|  |  |  | 		cid = Array.from(cids)[0]; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-08-27 11:29:43 -04:00
										 |  |  | 	console.log(' 4b', Date.now() - start); | 
					
						
							| 
									
										
										
										
											2025-03-06 10:45:30 -05:00
										 |  |  | 	const asserted = await activitypub.notes.assert(0, object, { cid }); | 
					
						
							| 
									
										
										
										
											2025-08-27 11:29:43 -04:00
										 |  |  | 	console.log(' 4c', Date.now() - start); | 
					
						
							| 
									
										
										
										
											2024-06-14 13:55:35 -04:00
										 |  |  | 	if (asserted) { | 
					
						
							| 
									
										
										
										
											2025-05-12 14:59:57 -04:00
										 |  |  | 		await activitypub.feps.announce(object.id, req.body); | 
					
						
							| 
									
										
										
										
											2025-03-28 12:39:19 -04:00
										 |  |  | 		// api.activitypub.add(req, { pid: object.id });
 | 
					
						
							| 
									
										
										
										
											2024-04-05 16:09:19 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-08-27 11:29:43 -04:00
										 |  |  | 	console.log(' 4d', Date.now() - start); | 
					
						
							| 
									
										
										
										
											2024-01-16 13:55:58 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-24 14:36:37 -04:00
										 |  |  | inbox.add = async (req) => { | 
					
						
							| 
									
										
										
										
											2024-10-24 14:43:50 -04:00
										 |  |  | 	const { actor, object, target } = req.body; | 
					
						
							| 
									
										
										
										
											2024-10-24 14:36:37 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Only react on Adds pertaining to local posts
 | 
					
						
							|  |  |  | 	const { type, id: pid } = await activitypub.helpers.resolveLocalId(object); | 
					
						
							|  |  |  | 	if (type === 'post') { | 
					
						
							|  |  |  | 		// Check context of OP
 | 
					
						
							|  |  |  | 		const tid = await posts.getPostField(pid, 'tid'); | 
					
						
							|  |  |  | 		const context = await topics.getTopicField(tid, 'context'); | 
					
						
							| 
									
										
										
										
											2024-10-24 14:43:50 -04:00
										 |  |  | 		if (context) { | 
					
						
							|  |  |  | 			const { attributedTo } = await activitypub.get('uid', 0, context); | 
					
						
							|  |  |  | 			if (context === target && attributedTo === actor) { | 
					
						
							|  |  |  | 				activitypub.helpers.log(`[activitypub/inbox/add] Associating pid ${pid} with new context ${target}`); | 
					
						
							|  |  |  | 				await posts.setPostField(pid, 'context', target); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-10-24 14:36:37 -04:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-16 13:55:58 -05:00
										 |  |  | inbox.update = async (req) => { | 
					
						
							| 
									
										
										
										
											2024-01-30 12:11:10 -05:00
										 |  |  | 	const { actor, object } = req.body; | 
					
						
							| 
									
										
										
										
											2024-10-17 11:19:22 -04:00
										 |  |  | 	const isPublic = [...(object.to || []), ...(object.cc || [])].includes(activitypub._constants.publicAddress); | 
					
						
							| 
									
										
										
										
											2024-10-02 12:08:46 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 12:11:10 -05:00
										 |  |  | 	// Origin checking
 | 
					
						
							|  |  |  | 	const actorHostname = new URL(actor).hostname; | 
					
						
							|  |  |  | 	const objectHostname = new URL(object.id).hostname; | 
					
						
							|  |  |  | 	if (actorHostname !== objectHostname) { | 
					
						
							|  |  |  | 		throw new Error('[[error:activitypub.origin-mismatch]]'); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-16 13:55:58 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-05 17:01:07 -04:00
										 |  |  | 	switch (true) { | 
					
						
							|  |  |  | 		case activitypub._constants.acceptedPostTypes.includes(object.type): { | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 			const [isNote, isMessage] = await Promise.all([ | 
					
						
							|  |  |  | 				posts.exists(object.id), | 
					
						
							|  |  |  | 				messaging.messageExists(object.id), | 
					
						
							|  |  |  | 			]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-12 14:24:38 -04:00
										 |  |  | 			try { | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 				switch (true) { | 
					
						
							|  |  |  | 					case isNote: { | 
					
						
							| 
									
										
										
										
											2025-05-26 14:49:48 -04:00
										 |  |  | 						const cid = await posts.getCidByPid(object.id); | 
					
						
							|  |  |  | 						const allowed = await privileges.categories.can('posts:edit', cid, activitypub._constants.uid); | 
					
						
							|  |  |  | 						if (!allowed) { | 
					
						
							|  |  |  | 							throw new Error('[[error:no-privileges]]'); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 						const postData = await activitypub.mocks.post(object); | 
					
						
							| 
									
										
										
										
											2025-05-06 10:44:47 -04:00
										 |  |  | 						postData.tags = await activitypub.notes._normalizeTags(postData._activitypub.tag, postData.cid); | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 						await posts.edit(postData); | 
					
						
							|  |  |  | 						const isDeleted = await posts.getPostField(object.id, 'deleted'); | 
					
						
							|  |  |  | 						if (isDeleted) { | 
					
						
							|  |  |  | 							await api.posts.restore({ uid: actor }, { pid: object.id }); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						break; | 
					
						
							| 
									
										
										
										
											2024-05-07 14:01:27 -04:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 					case isMessage: { | 
					
						
							| 
									
										
										
										
											2024-10-17 11:16:25 -04:00
										 |  |  | 						const { roomId, deleted } = await messaging.getMessageFields(object.id, ['roomId', 'deleted']); | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 						await messaging.editMessage(actor, object.id, roomId, object.content); | 
					
						
							| 
									
										
										
										
											2024-10-17 11:05:27 -04:00
										 |  |  | 						if (deleted) { | 
					
						
							|  |  |  | 							await api.chats.restoreMessage({ uid: actor }, { mid: object.id }); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					default: { | 
					
						
							|  |  |  | 						if (!isPublic) { | 
					
						
							|  |  |  | 							return await activitypub.notes.assertPrivate(object); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-06 10:45:30 -05:00
										 |  |  | 						const { cids } = await activitypub.actors.getLocalFollowers(actor); | 
					
						
							|  |  |  | 						let cid = null; | 
					
						
							|  |  |  | 						if (cids.size > 0) { | 
					
						
							|  |  |  | 							cid = Array.from(cids)[0]; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						const asserted = await activitypub.notes.assert(0, object.id, { cid }); | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 						if (asserted) { | 
					
						
							| 
									
										
										
										
											2025-02-07 08:21:41 -05:00
										 |  |  | 							activitypub.feps.announce(object.id, req.body); | 
					
						
							| 
									
										
										
										
											2024-10-15 11:27:54 -04:00
										 |  |  | 						} | 
					
						
							|  |  |  | 						break; | 
					
						
							| 
									
										
										
										
											2024-06-14 13:55:35 -04:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2024-03-13 15:05:42 -04:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-03-12 14:24:38 -04:00
										 |  |  | 			} catch (e) { | 
					
						
							| 
									
										
										
										
											2024-03-25 13:25:12 -04:00
										 |  |  | 				reject('Update', object, actor); | 
					
						
							| 
									
										
										
										
											2024-02-28 13:14:15 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-01-26 16:48:16 -05:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-05 17:01:07 -04:00
										 |  |  | 		case activitypub._constants.acceptableActorTypes.has(object.type): { | 
					
						
							| 
									
										
										
										
											2024-04-10 12:16:35 -04:00
										 |  |  | 			await activitypub.actors.assert(object.id, { update: true }); | 
					
						
							| 
									
										
										
										
											2024-01-26 16:48:16 -05:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-05-07 14:01:27 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-05 17:01:07 -04:00
										 |  |  | 		case object.type === 'Tombstone': { | 
					
						
							| 
									
										
										
										
											2024-10-17 11:05:27 -04:00
										 |  |  | 			const [isNote, isMessage/* , isActor */] = await Promise.all([ | 
					
						
							| 
									
										
										
										
											2024-05-07 14:01:27 -04:00
										 |  |  | 				posts.exists(object.id), | 
					
						
							| 
									
										
										
										
											2024-10-17 11:05:27 -04:00
										 |  |  | 				messaging.messageExists(object.id), | 
					
						
							| 
									
										
										
										
											2024-05-08 10:41:11 -04:00
										 |  |  | 				// db.isSortedSetMember('usersRemote:lastCrawled', object.id),
 | 
					
						
							| 
									
										
										
										
											2024-05-07 14:01:27 -04:00
										 |  |  | 			]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			switch (true) { | 
					
						
							|  |  |  | 				case isNote: { | 
					
						
							|  |  |  | 					await api.posts.delete({ uid: actor }, { pid: object.id }); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-17 11:05:27 -04:00
										 |  |  | 				case isMessage: { | 
					
						
							|  |  |  | 					await api.chats.deleteMessage({ uid: actor }, { mid: object.id }); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-07 14:01:27 -04:00
										 |  |  | 				// case isActor: {
 | 
					
						
							| 
									
										
										
										
											2024-05-08 10:41:11 -04:00
										 |  |  | 				// console.log('actor');
 | 
					
						
							|  |  |  | 				// break;
 | 
					
						
							| 
									
										
										
										
											2024-05-07 14:01:27 -04:00
										 |  |  | 				// }
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-16 12:00:50 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | inbox.delete = async (req) => { | 
					
						
							|  |  |  | 	const { actor, object } = req.body; | 
					
						
							| 
									
										
										
										
											2024-11-27 12:47:24 -05:00
										 |  |  | 	if (typeof object !== 'string') { | 
					
						
							|  |  |  | 		const { id } = object; | 
					
						
							|  |  |  | 		if (!id) { | 
					
						
							|  |  |  | 			throw new Error('[[error:invalid-pid]]'); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	const pid = object.id || object; | 
					
						
							|  |  |  | 	let type = object.type || undefined; | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Deletes don't have their objects resolved automatically
 | 
					
						
							|  |  |  | 	let method = 'purge'; | 
					
						
							|  |  |  | 	try { | 
					
						
							| 
									
										
										
										
											2024-11-27 12:47:24 -05:00
										 |  |  | 		if (!type) { | 
					
						
							|  |  |  | 			({ type } = await activitypub.get('uid', 0, pid)); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | 		if (type === 'Tombstone') { | 
					
						
							|  |  |  | 			method = 'delete'; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} catch (e) { | 
					
						
							|  |  |  | 		// probably 410/404
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-27 12:47:24 -05:00
										 |  |  | 	// Deletions must be made by an actor of the same origin
 | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | 	const actorHostname = new URL(actor).hostname; | 
					
						
							| 
									
										
										
										
											2024-11-27 12:47:24 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	const objectHostname = new URL(pid).hostname; | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | 	if (actorHostname !== objectHostname) { | 
					
						
							| 
									
										
										
										
											2025-05-26 14:49:48 -04:00
										 |  |  | 		return reject('Delete', object, actor); | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const [isNote/* , isActor */] = await Promise.all([ | 
					
						
							| 
									
										
										
										
											2024-11-27 12:47:24 -05:00
										 |  |  | 		posts.exists(pid), | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | 		// db.isSortedSetMember('usersRemote:lastCrawled', object.id),
 | 
					
						
							|  |  |  | 	]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (true) { | 
					
						
							|  |  |  | 		case isNote: { | 
					
						
							| 
									
										
										
										
											2025-05-26 14:49:48 -04:00
										 |  |  | 			const cid = await posts.getCidByPid(pid); | 
					
						
							|  |  |  | 			const allowed = await privileges.categories.can('posts:edit', cid, activitypub._constants.uid); | 
					
						
							|  |  |  | 			if (!allowed) { | 
					
						
							|  |  |  | 				return reject('Delete', object, actor); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-27 12:47:24 -05:00
										 |  |  | 			const uid = await posts.getPostField(pid, 'uid'); | 
					
						
							| 
									
										
										
										
											2025-02-07 08:21:41 -05:00
										 |  |  | 			await activitypub.feps.announce(pid, req.body); | 
					
						
							| 
									
										
										
										
											2024-11-27 12:47:24 -05:00
										 |  |  | 			await api.posts[method]({ uid }, { pid }); | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// case isActor: {
 | 
					
						
							|  |  |  | 		// console.log('actor');
 | 
					
						
							|  |  |  | 		// break;
 | 
					
						
							|  |  |  | 		// }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: { | 
					
						
							| 
									
										
										
										
											2024-11-27 12:47:24 -05:00
										 |  |  | 			activitypub.helpers.log(`[activitypub/inbox.delete] Object (${pid}) does not exist locally. Doing nothing.`); | 
					
						
							| 
									
										
										
										
											2024-05-09 15:48:58 -04:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | inbox.like = async (req) => { | 
					
						
							|  |  |  | 	const { actor, object } = req.body; | 
					
						
							| 
									
										
										
										
											2024-04-10 00:20:16 +02:00
										 |  |  | 	const { type, id } = await activitypub.helpers.resolveLocalId(object.id); | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-06 15:20:30 -05:00
										 |  |  | 	if (type !== 'post' || !(await posts.exists(id))) { | 
					
						
							| 
									
										
										
										
											2024-03-25 13:25:12 -04:00
										 |  |  | 		return reject('Like', object, actor); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 	const allowed = await privileges.posts.can('posts:upvote', id, activitypub._constants.uid); | 
					
						
							|  |  |  | 	if (!allowed) { | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 		activitypub.helpers.log(`[activitypub/inbox.like] ${id} not allowed to be upvoted.`); | 
					
						
							| 
									
										
										
										
											2024-03-25 13:25:12 -04:00
										 |  |  | 		return reject('Like', object, actor); | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 	activitypub.helpers.log(`[activitypub/inbox/like] id ${id} via ${actor}`); | 
					
						
							| 
									
										
										
										
											2024-02-07 12:50:26 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-29 16:09:57 -05:00
										 |  |  | 	const result = await posts.upvote(id, actor); | 
					
						
							| 
									
										
										
										
											2025-05-12 14:59:57 -04:00
										 |  |  | 	await activitypub.feps.announce(object.id, req.body); | 
					
						
							| 
									
										
										
										
											2024-02-29 16:09:57 -05:00
										 |  |  | 	socketHelpers.upvote(result, 'notifications:upvoted-your-post-in'); | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-06 14:57:44 -05:00
										 |  |  | inbox.announce = async (req) => { | 
					
						
							| 
									
										
										
										
											2025-05-05 16:50:44 -04:00
										 |  |  | 	let { actor, object, published, to, cc } = req.body; | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 	activitypub.helpers.log(`[activitypub/inbox/announce] Parsing Announce(${object.type}) from ${actor}`); | 
					
						
							| 
									
										
										
										
											2024-02-13 12:03:16 -05:00
										 |  |  | 	let timestamp = new Date(published); | 
					
						
							| 
									
										
										
										
											2024-02-14 10:23:06 -05:00
										 |  |  | 	timestamp = timestamp.toString() !== 'Invalid Date' ? timestamp.getTime() : Date.now(); | 
					
						
							| 
									
										
										
										
											2024-02-06 14:57:44 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	const assertion = await activitypub.actors.assert(actor); | 
					
						
							|  |  |  | 	if (!assertion) { | 
					
						
							|  |  |  | 		throw new Error('[[error:activitypub.invalid-id]]'); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:27 -05:00
										 |  |  | 	let tid; | 
					
						
							|  |  |  | 	let pid; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-13 13:59:34 -04:00
										 |  |  | 	// Category sync, remove when cross-posting available
 | 
					
						
							| 
									
										
										
										
											2024-04-17 12:55:15 -04:00
										 |  |  | 	const { cids } = await activitypub.actors.getLocalFollowers(actor); | 
					
						
							|  |  |  | 	let cid = null; | 
					
						
							|  |  |  | 	if (cids.size > 0) { | 
					
						
							|  |  |  | 		cid = Array.from(cids)[0]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-13 13:59:34 -04:00
										 |  |  | 	// 1b12 announce
 | 
					
						
							|  |  |  | 	const categoryActor = await categories.exists(actor); | 
					
						
							|  |  |  | 	if (categoryActor) { | 
					
						
							|  |  |  | 		cid = actor; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 	switch(true) { | 
					
						
							|  |  |  | 		case object.type === 'Like': { | 
					
						
							|  |  |  | 			const id = object.object.id || object.object; | 
					
						
							|  |  |  | 			const { id: localId } = await activitypub.helpers.resolveLocalId(id); | 
					
						
							|  |  |  | 			const exists = await posts.exists(localId || id); | 
					
						
							|  |  |  | 			if (exists) { | 
					
						
							| 
									
										
										
										
											2025-05-30 16:49:15 -04:00
										 |  |  | 				try { | 
					
						
							|  |  |  | 					const result = await posts.upvote(localId || id, object.actor); | 
					
						
							|  |  |  | 					if (localId) { | 
					
						
							|  |  |  | 						socketHelpers.upvote(result, 'notifications:upvoted-your-post-in'); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} catch (e) { | 
					
						
							|  |  |  | 					// vote denied due to local limitations (frequency, privilege, etc.); noop.
 | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-06-12 14:15:39 -04:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-03-26 13:40:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2024-04-17 12:55:15 -04:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-23 13:17:10 -04:00
										 |  |  | 		case object.type === 'Update': { | 
					
						
							|  |  |  | 			req.body = object; | 
					
						
							|  |  |  | 			await inbox.update(req); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-05 16:50:44 -04:00
										 |  |  | 		case object.type === 'Create': { | 
					
						
							|  |  |  | 			object = object.object; | 
					
						
							|  |  |  | 			// falls through
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Announce(Object)
 | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 		case activitypub._constants.acceptedPostTypes.includes(object.type): { | 
					
						
							|  |  |  | 			if (String(object.id).startsWith(nconf.get('url'))) { // Local object
 | 
					
						
							|  |  |  | 				const { type, id } = await activitypub.helpers.resolveLocalId(object.id); | 
					
						
							|  |  |  | 				if (type !== 'post' || !(await posts.exists(id))) { | 
					
						
							|  |  |  | 					reject('Announce', object, actor); | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-02-21 10:58:20 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 				pid = id; | 
					
						
							|  |  |  | 				tid = await posts.getPostField(id, 'tid'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				socketHelpers.sendNotificationToPostOwner(pid, actor, 'announce', 'notifications:activitypub.announce'); | 
					
						
							|  |  |  | 			} else { // Remote object
 | 
					
						
							|  |  |  | 				// Follower check
 | 
					
						
							|  |  |  | 				if (!cid) { | 
					
						
							|  |  |  | 					const { followers } = await activitypub.actors.getLocalFollowCounts(actor); | 
					
						
							|  |  |  | 					if (!followers) { | 
					
						
							|  |  |  | 						winston.verbose(`[activitypub/inbox.announce] Rejecting ${object.id} via ${actor} due to no followers`); | 
					
						
							|  |  |  | 						reject('Announce', object, actor); | 
					
						
							|  |  |  | 						return; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-02-12 15:25:49 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-13 13:59:34 -04:00
										 |  |  | 				pid = object.id; | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 				pid = await activitypub.resolveId(0, pid); // in case wrong id is passed-in; unlikely, but still.
 | 
					
						
							|  |  |  | 				if (!pid) { | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				const assertion = await activitypub.notes.assert(0, pid, { cid, skipChecks: true }); | 
					
						
							|  |  |  | 				if (!assertion) { | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-02-07 12:50:26 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-23 12:47:16 -04:00
										 |  |  | 				({ tid } = assertion); | 
					
						
							|  |  |  | 				await activitypub.notes.updateLocalRecipients(pid, { to, cc }); | 
					
						
							|  |  |  | 				await activitypub.notes.syncUserInboxes(tid); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (!cid) { // Topic events from actors followed by users only
 | 
					
						
							|  |  |  | 				await activitypub.notes.announce.add(pid, actor, timestamp); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-04-17 12:55:15 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-06 14:57:44 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-22 13:35:09 -05:00
										 |  |  | inbox.follow = async (req) => { | 
					
						
							| 
									
										
										
										
											2024-04-10 00:30:46 +02:00
										 |  |  | 	const { actor, object, id: followId } = req.body; | 
					
						
							| 
									
										
										
										
											2023-06-28 14:59:39 -04:00
										 |  |  | 	// Sanity checks
 | 
					
						
							| 
									
										
										
										
											2024-04-10 00:26:01 +02:00
										 |  |  | 	const { type, id } = await helpers.resolveLocalId(object.id); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	if (!['category', 'user'].includes(type)) { | 
					
						
							|  |  |  | 		throw new Error('[[error:activitypub.invalid-id]]'); | 
					
						
							| 
									
										
										
										
											2023-06-28 14:59:39 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 	const assertion = await activitypub.actors.assert(actor); | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 	if (!assertion) { | 
					
						
							|  |  |  | 		throw new Error('[[error:activitypub.invalid-id]]'); | 
					
						
							| 
									
										
										
										
											2024-01-05 22:45:33 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-04-15 09:48:58 -04:00
										 |  |  | 	const handle = await user.getUserField(actor, 'username'); | 
					
						
							| 
									
										
										
										
											2024-01-05 22:45:33 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	if (type === 'user') { | 
					
						
							| 
									
										
										
										
											2024-07-24 11:59:50 -04:00
										 |  |  | 		const [exists, allowed] = await Promise.all([ | 
					
						
							|  |  |  | 			user.exists(id), | 
					
						
							|  |  |  | 			privileges.global.can('view:users', activitypub._constants.uid), | 
					
						
							|  |  |  | 		]); | 
					
						
							|  |  |  | 		if (!exists || !allowed) { | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 			throw new Error('[[error:invalid-uid]]'); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 		const isFollowed = await inbox.isFollowed(actor, id); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 		if (isFollowed) { | 
					
						
							|  |  |  | 			// No additional parsing required
 | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const now = Date.now(); | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 		await db.sortedSetAdd(`followersRemote:${id}`, now, actor); | 
					
						
							| 
									
										
										
										
											2024-07-19 14:37:32 -04:00
										 |  |  | 		await db.sortedSetAdd(`followingRemote:${actor}`, now, id); // for following backreference (actor pruning)
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		const followerRemoteCount = await db.sortedSetCard(`followersRemote:${id}`); | 
					
						
							|  |  |  | 		await user.setUserField(id, 'followerRemoteCount', followerRemoteCount); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-13 18:36:05 -04:00
										 |  |  | 		await user.onFollow(actor, id); | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 		activitypub.send('uid', id, actor, { | 
					
						
							| 
									
										
										
										
											2024-06-11 22:55:45 +02:00
										 |  |  | 			id: `${nconf.get('url')}/${type}/${id}#activity/accept:follow/${handle}/${Date.now()}`, | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 			type: 'Accept', | 
					
						
							|  |  |  | 			object: { | 
					
						
							| 
									
										
										
										
											2024-04-10 00:30:46 +02:00
										 |  |  | 				id: followId, | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 				type: 'Follow', | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 				actor, | 
					
						
							| 
									
										
										
										
											2024-04-10 00:30:46 +02:00
										 |  |  | 				object: object.id, | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2024-06-13 18:36:05 -04:00
										 |  |  | 		}).catch(err => winston.error(err.stack)); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	} else if (type === 'category') { | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 		const [exists, allowed] = await Promise.all([ | 
					
						
							|  |  |  | 			categories.exists(id), | 
					
						
							| 
									
										
										
										
											2024-07-24 11:54:41 -04:00
										 |  |  | 			privileges.categories.can('read', id, activitypub._constants.uid), | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 		]); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 		if (!exists) { | 
					
						
							|  |  |  | 			throw new Error('[[error:invalid-cid]]'); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 		if (!allowed) { | 
					
						
							| 
									
										
										
										
											2024-03-25 13:25:12 -04:00
										 |  |  | 			return reject('Follow', object, actor); | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 		const watchState = await categories.getWatchState([id], actor); | 
					
						
							| 
									
										
										
										
											2024-02-06 10:40:46 -05:00
										 |  |  | 		if (watchState[0] !== categories.watchStates.tracking) { | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 			await user.setCategoryWatchState(actor, id, categories.watchStates.tracking); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 		activitypub.send('cid', id, actor, { | 
					
						
							| 
									
										
										
										
											2024-06-11 22:55:45 +02:00
										 |  |  | 			id: `${nconf.get('url')}/${type}/${id}#activity/accept:follow/${handle}/${Date.now()}`, | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 			type: 'Accept', | 
					
						
							|  |  |  | 			object: { | 
					
						
							| 
									
										
										
										
											2024-04-10 00:30:46 +02:00
										 |  |  | 				id: followId, | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 				type: 'Follow', | 
					
						
							| 
									
										
										
										
											2024-03-07 13:46:20 -05:00
										 |  |  | 				actor, | 
					
						
							| 
									
										
										
										
											2024-04-10 00:26:01 +02:00
										 |  |  | 				object: object.id, | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2024-06-13 18:36:05 -04:00
										 |  |  | 		}).catch(err => winston.error(err.stack)); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-12-22 13:35:09 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | inbox.isFollowed = async (actorId, uid) => { | 
					
						
							|  |  |  | 	if (actorId.indexOf('@') === -1 || parseInt(uid, 10) <= 0) { | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return await db.isSortedSetMember(`followersRemote:${uid}`, actorId); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2023-12-13 13:15:03 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | inbox.accept = async (req) => { | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 	const { actor, object } = req.body; | 
					
						
							| 
									
										
										
										
											2023-12-13 13:15:03 -05:00
										 |  |  | 	const { type } = object; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-15 14:40:26 -04:00
										 |  |  | 	const { type: localType, id } = await helpers.resolveLocalId(object.actor); | 
					
						
							|  |  |  | 	if (!['user', 'category'].includes(localType)) { | 
					
						
							|  |  |  | 		throw new Error('[[error:invalid-data]]'); | 
					
						
							| 
									
										
										
										
											2024-01-05 22:45:33 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-03 13:54:17 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 	const assertion = await activitypub.actors.assert(actor); | 
					
						
							|  |  |  | 	if (!assertion) { | 
					
						
							|  |  |  | 		throw new Error('[[error:activitypub.invalid-id]]'); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-12-13 13:15:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-05 22:45:33 -05:00
										 |  |  | 	if (type === 'Follow') { | 
					
						
							| 
									
										
										
										
											2024-04-15 14:40:26 -04:00
										 |  |  | 		if (localType === 'user') { | 
					
						
							|  |  |  | 			if (!await db.isSortedSetMember(`followRequests:uid.${id}`, actor)) { | 
					
						
							|  |  |  | 				if (await db.isSortedSetMember(`followingRemote:${id}`, actor)) return; // already following
 | 
					
						
							|  |  |  | 				return reject('Accept', req.body, actor); // not following, not requested, so reject to hopefully stop retries
 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-07-30 14:05:59 -04:00
										 |  |  | 			const timestamp = await db.sortedSetScore(`followRequests:uid.${id}`, actor); | 
					
						
							| 
									
										
										
										
											2024-04-15 14:40:26 -04:00
										 |  |  | 			await Promise.all([ | 
					
						
							|  |  |  | 				db.sortedSetRemove(`followRequests:uid.${id}`, actor), | 
					
						
							| 
									
										
										
										
											2024-07-30 14:05:59 -04:00
										 |  |  | 				db.sortedSetAdd(`followingRemote:${id}`, timestamp, actor), | 
					
						
							|  |  |  | 				db.sortedSetAdd(`followersRemote:${actor}`, timestamp, id), // for followers backreference and notes assertion checking
 | 
					
						
							| 
									
										
										
										
											2024-04-15 14:40:26 -04:00
										 |  |  | 			]); | 
					
						
							|  |  |  | 			const followingRemoteCount = await db.sortedSetCard(`followingRemote:${id}`); | 
					
						
							|  |  |  | 			await user.setUserField(id, 'followingRemoteCount', followingRemoteCount); | 
					
						
							|  |  |  | 		} else if (localType === 'category') { | 
					
						
							|  |  |  | 			if (!await db.isSortedSetMember(`followRequests:cid.${id}`, actor)) { | 
					
						
							|  |  |  | 				if (await db.isSortedSetMember(`cid:${id}:following`, actor)) return; // already following
 | 
					
						
							|  |  |  | 				return reject('Accept', req.body, actor); // not following, not requested, so reject to hopefully stop retries
 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-07-30 14:05:59 -04:00
										 |  |  | 			const timestamp = await db.sortedSetScore(`followRequests:cid.${id}`, actor); | 
					
						
							| 
									
										
										
										
											2024-04-15 14:40:26 -04:00
										 |  |  | 			await Promise.all([ | 
					
						
							|  |  |  | 				db.sortedSetRemove(`followRequests:cid.${id}`, actor), | 
					
						
							| 
									
										
										
										
											2024-07-30 14:05:59 -04:00
										 |  |  | 				db.sortedSetAdd(`cid:${id}:following`, timestamp, actor), | 
					
						
							|  |  |  | 				db.sortedSetAdd(`followersRemote:${actor}`, timestamp, `cid|${id}`), // for notes assertion checking
 | 
					
						
							| 
									
										
										
										
											2024-04-15 14:40:26 -04:00
										 |  |  | 			]); | 
					
						
							| 
									
										
										
										
											2024-04-09 23:58:52 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-12-13 13:15:03 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | inbox.undo = async (req) => { | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	// todo: "actor" in this case should be the one in object, no?
 | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 	const { actor, object } = req.body; | 
					
						
							| 
									
										
										
										
											2023-12-13 13:15:03 -05:00
										 |  |  | 	const { type } = object; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-06 14:57:44 -05:00
										 |  |  | 	if (actor !== object.actor) { | 
					
						
							|  |  |  | 		throw new Error('[[error:activitypub.actor-mismatch]]'); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 	const assertion = await activitypub.actors.assert(actor); | 
					
						
							|  |  |  | 	if (!assertion) { | 
					
						
							|  |  |  | 		throw new Error('[[error:activitypub.invalid-id]]'); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-12-13 13:15:03 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-20 13:43:45 -05:00
										 |  |  | 	let { type: localType, id } = await helpers.resolveLocalId(object.object); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-14 13:31:08 -04:00
										 |  |  | 	winston.verbose(`[activitypub/inbox/undo] ${type} ${localType && id ? `${localType} ${id}` : object.object} via ${actor}`); | 
					
						
							| 
									
										
										
										
											2024-02-07 12:50:26 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 	switch (type) { | 
					
						
							|  |  |  | 		case 'Follow': { | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 			switch (localType) { | 
					
						
							|  |  |  | 				case 'user': { | 
					
						
							|  |  |  | 					const exists = await user.exists(id); | 
					
						
							|  |  |  | 					if (!exists) { | 
					
						
							|  |  |  | 						throw new Error('[[error:invalid-uid]]'); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-19 14:37:32 -04:00
										 |  |  | 					await Promise.all([ | 
					
						
							|  |  |  | 						db.sortedSetRemove(`followersRemote:${id}`, actor), | 
					
						
							|  |  |  | 						db.sortedSetRemove(`followingRemote:${actor}`, id), | 
					
						
							|  |  |  | 					]); | 
					
						
							| 
									
										
										
										
											2024-02-06 10:40:46 -05:00
										 |  |  | 					const followerRemoteCount = await db.sortedSetCard(`followerRemote:${id}`); | 
					
						
							|  |  |  | 					await user.setUserField(id, 'followerRemoteCount', followerRemoteCount); | 
					
						
							| 
									
										
										
										
											2024-02-29 16:09:57 -05:00
										 |  |  | 					notifications.rescind(`follow:${id}:uid:${actor}`); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				case 'category': { | 
					
						
							|  |  |  | 					const exists = await categories.exists(id); | 
					
						
							|  |  |  | 					if (!exists) { | 
					
						
							|  |  |  | 						throw new Error('[[error:invalid-cid]]'); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					await user.setCategoryWatchState(actor, id, categories.watchStates.notwatching); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case 'Like': { | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 			const exists = await posts.exists(id); | 
					
						
							|  |  |  | 			if (localType !== 'post' || !exists) { | 
					
						
							| 
									
										
										
										
											2025-05-31 22:46:47 -04:00
										 |  |  | 				reject('Like', object, actor); | 
					
						
							|  |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 			const allowed = await privileges.posts.can('posts:upvote', id, activitypub._constants.uid); | 
					
						
							|  |  |  | 			if (!allowed) { | 
					
						
							| 
									
										
										
										
											2024-06-14 13:31:08 -04:00
										 |  |  | 				winston.verbose(`[activitypub/inbox.like] ${id} not allowed to be upvoted.`); | 
					
						
							| 
									
										
										
										
											2024-03-25 13:25:12 -04:00
										 |  |  | 				reject('Like', object, actor); | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 			await posts.unvote(id, actor); | 
					
						
							| 
									
										
										
										
											2025-02-07 08:21:41 -05:00
										 |  |  | 			activitypub.feps.announce(object.object, req.body); | 
					
						
							| 
									
										
										
										
											2024-02-29 16:09:57 -05:00
										 |  |  | 			notifications.rescind(`upvote:post:${id}:uid:${actor}`); | 
					
						
							| 
									
										
										
										
											2024-02-01 15:59:29 -05:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-02-06 14:57:44 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		case 'Announce': { | 
					
						
							| 
									
										
										
										
											2024-02-20 13:43:45 -05:00
										 |  |  | 			id = id || object.object; // remote announces
 | 
					
						
							| 
									
										
										
										
											2024-02-06 14:57:44 -05:00
										 |  |  | 			const exists = await posts.exists(id); | 
					
						
							| 
									
										
										
										
											2024-02-20 13:43:45 -05:00
										 |  |  | 			if (!exists) { | 
					
						
							| 
									
										
										
										
											2024-10-12 22:49:24 -04:00
										 |  |  | 				activitypub.helpers.log(`[activitypub/inbox/undo] Attempted to undo announce of ${id} but couldn't find it, so doing nothing.`); | 
					
						
							| 
									
										
										
										
											2024-06-07 12:13:28 -04:00
										 |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2024-02-06 14:57:44 -05:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-01 14:44:29 -04:00
										 |  |  | 			await activitypub.notes.announce.remove(id, actor); | 
					
						
							| 
									
										
										
										
											2024-02-29 16:09:57 -05:00
										 |  |  | 			notifications.rescind(`announce:post:${id}:uid:${actor}`); | 
					
						
							| 
									
										
										
										
											2024-04-14 02:02:17 +02:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		case 'Flag': { | 
					
						
							|  |  |  | 			if (!Array.isArray(object.object)) { | 
					
						
							|  |  |  | 				object.object = [object.object]; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			await Promise.all(object.object.map(async (subject) => { | 
					
						
							|  |  |  | 				const { type, id } = await activitypub.helpers.resolveLocalId(subject.id); | 
					
						
							|  |  |  | 				try { | 
					
						
							|  |  |  | 					await flags.rescindReport(type, id, actor); | 
					
						
							|  |  |  | 				} catch (e) { | 
					
						
							|  |  |  | 					reject('Undo', { type: 'Flag', object: [subject] }, actor); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			})); | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2024-02-06 14:57:44 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-12-13 13:15:03 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2024-04-06 19:00:52 +02:00
										 |  |  | inbox.flag = async (req) => { | 
					
						
							|  |  |  | 	const { actor, object, content } = req.body; | 
					
						
							|  |  |  | 	const objects = Array.isArray(object) ? object : [object]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check if the actor is valid
 | 
					
						
							|  |  |  | 	if (!await activitypub.actors.assert(actor)) { | 
					
						
							| 
									
										
										
										
											2024-04-06 19:10:49 +02:00
										 |  |  | 		return reject('Flag', objects, actor); | 
					
						
							| 
									
										
										
										
											2024-04-06 19:00:52 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-10 00:06:24 +02:00
										 |  |  | 	await Promise.all(objects.map(async (subject, index) => { | 
					
						
							| 
									
										
										
										
											2024-04-14 02:42:30 +02:00
										 |  |  | 		const { type, id } = await activitypub.helpers.resolveObjects(subject.id); | 
					
						
							| 
									
										
										
										
											2024-04-06 19:00:52 +02:00
										 |  |  | 		try { | 
					
						
							| 
									
										
										
										
											2024-04-14 02:42:30 +02:00
										 |  |  | 			await flags.create(activitypub.helpers.mapToLocalType(type), id, actor, content); | 
					
						
							| 
									
										
										
										
											2024-04-06 19:00:52 +02:00
										 |  |  | 		} catch (e) { | 
					
						
							|  |  |  | 			reject('Flag', objects[index], actor); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	})); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2024-05-06 22:49:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | inbox.reject = async (req) => { | 
					
						
							|  |  |  | 	const { actor, object } = req.body; | 
					
						
							|  |  |  | 	const { type, id } = object; | 
					
						
							|  |  |  | 	const { hostname } = new URL(actor); | 
					
						
							|  |  |  | 	const queueId = `${type}:${id}:${hostname}`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// stop retrying rejected requests
 | 
					
						
							| 
									
										
										
										
											2025-07-30 09:32:58 -04:00
										 |  |  | 	await Promise.all([ | 
					
						
							|  |  |  | 		db.sortedSetRemove('ap:retry:queue', queueId), | 
					
						
							|  |  |  | 		db.delete(`ap:retry:queue:${queueId}`), | 
					
						
							|  |  |  | 	]); | 
					
						
							| 
									
										
										
										
											2024-05-06 22:49:31 +02:00
										 |  |  | }; |