| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const winston = require('winston'); | 
					
						
							| 
									
										
										
										
											2024-02-16 12:07:29 -05:00
										 |  |  | const crypto = require('crypto'); | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | const db = require('../database'); | 
					
						
							| 
									
										
										
										
											2024-02-27 15:25:13 -05:00
										 |  |  | const meta = require('../meta'); | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | const privileges = require('../privileges'); | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | const user = require('../user'); | 
					
						
							| 
									
										
										
										
											2024-01-17 11:54:20 -05:00
										 |  |  | const topics = require('../topics'); | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | const posts = require('../posts'); | 
					
						
							| 
									
										
										
										
											2024-01-18 16:20:37 -05:00
										 |  |  | const utils = require('../utils'); | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | // const pubsub = require('../pubsub');
 | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:16 -05:00
										 |  |  | const slugify = require('../slugify'); | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | const activitypub = module.parent.exports; | 
					
						
							|  |  |  | const Notes = module.exports; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // todo: when asserted, notes aren't added to a global sorted set
 | 
					
						
							|  |  |  | // also, db.exists call is probably expensive
 | 
					
						
							| 
									
										
										
										
											2024-01-16 13:55:58 -05:00
										 |  |  | Notes.assert = async (uid, input, options = {}) => { | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 	// Ensures that each note has been saved to the database
 | 
					
						
							| 
									
										
										
										
											2024-02-20 11:57:50 -05:00
										 |  |  | 	const actors = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 	await Promise.all(input.map(async (item) => { | 
					
						
							| 
									
										
										
										
											2024-02-21 14:05:54 -05:00
										 |  |  | 		// Dereference only if a url is received
 | 
					
						
							|  |  |  | 		if (activitypub.helpers.isUri(item)) { | 
					
						
							|  |  |  | 			item = await activitypub.resolveId(uid, item); | 
					
						
							|  |  |  | 			if (!item) { | 
					
						
							|  |  |  | 				winston.warn(`[activitypub/notes.assert] Not asserting ${item}`); | 
					
						
							| 
									
										
										
										
											2024-02-21 10:58:20 -05:00
										 |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-02-21 10:26:26 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-02-21 10:58:20 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-21 14:05:54 -05:00
										 |  |  | 		const id = activitypub.helpers.isUri(item) ? item : item.pid; | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 		const key = `post:${id}`; | 
					
						
							| 
									
										
										
										
											2024-01-24 14:35:21 -05:00
										 |  |  | 		const exists = await db.exists(key); | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 		winston.verbose(`[activitypub/notes.assert] Asserting note id ${id}`); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-24 14:35:21 -05:00
										 |  |  | 		if (!exists || options.update === true) { | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 			let postData; | 
					
						
							| 
									
										
										
										
											2024-02-09 11:31:42 -05:00
										 |  |  | 			winston.verbose(`[activitypub/notes.assert] Not found, retrieving note for persistence...`); | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 			if (activitypub.helpers.isUri(item)) { | 
					
						
							| 
									
										
										
										
											2024-02-09 11:31:42 -05:00
										 |  |  | 				// get failure throws for now but should save intermediate object
 | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 				const object = await activitypub.get('uid', uid, item); | 
					
						
							| 
									
										
										
										
											2024-02-20 11:57:50 -05:00
										 |  |  | 				actors.add(object.attributedTo); | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 				postData = await activitypub.mocks.post(object); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				postData = item; | 
					
						
							| 
									
										
										
										
											2024-02-20 11:57:50 -05:00
										 |  |  | 				actors.add(item.uid); | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-20 11:57:50 -05:00
										 |  |  | 			// Parse ActivityPub-specific data if exists (if not, was parsed already)
 | 
					
						
							| 
									
										
										
										
											2024-02-20 14:19:50 -05:00
										 |  |  | 			if (postData.hasOwnProperty('_activitypub')) { | 
					
						
							| 
									
										
										
										
											2024-02-20 11:57:50 -05:00
										 |  |  | 				const { to, cc, attachment } = postData._activitypub; | 
					
						
							|  |  |  | 				await Notes.updateLocalRecipients(id, { to, cc }); | 
					
						
							|  |  |  | 				await Notes.saveAttachments(id, attachment); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			const hash = { ...postData }; | 
					
						
							|  |  |  | 			delete hash._activitypub; | 
					
						
							| 
									
										
										
										
											2024-02-20 14:26:21 -05:00
										 |  |  | 			// should call internal method here to create/edit post
 | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | 			await db.setObject(key, hash); | 
					
						
							| 
									
										
										
										
											2024-02-09 11:31:42 -05:00
										 |  |  | 			winston.verbose(`[activitypub/notes.assert] Note ${id} saved.`); | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | 	})); | 
					
						
							| 
									
										
										
										
											2024-02-20 11:57:50 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (actors.size) { | 
					
						
							|  |  |  | 		activitypub.actors.assert(Array.from(actors)); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-12 16:23:21 -05:00
										 |  |  | Notes.updateLocalRecipients = async (id, { to, cc }) => { | 
					
						
							| 
									
										
										
										
											2024-02-13 12:03:16 -05:00
										 |  |  | 	const recipients = new Set([...(to || []), ...(cc || [])]); | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | 	const uids = new Set(); | 
					
						
							|  |  |  | 	await Promise.all(Array.from(recipients).map(async (recipient) => { | 
					
						
							|  |  |  | 		const { type, id } = await activitypub.helpers.resolveLocalId(recipient); | 
					
						
							|  |  |  | 		if (type === 'user' && await user.exists(id)) { | 
					
						
							|  |  |  | 			uids.add(parseInt(id, 10)); | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const followedUid = await db.getObjectField('followersUrl:uid', recipient); | 
					
						
							|  |  |  | 		if (followedUid) { | 
					
						
							|  |  |  | 			const followers = await db.getSortedSetMembers(`followersRemote:${followedUid}`); | 
					
						
							|  |  |  | 			if (followers.length) { | 
					
						
							|  |  |  | 				uids.add(...followers.map(uid => parseInt(uid, 10))); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// return;
 | 
					
						
							| 
									
										
										
										
											2024-01-24 14:35:21 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 	})); | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-12 16:23:21 -05:00
										 |  |  | 	if (uids.size > 0) { | 
					
						
							|  |  |  | 		await db.setAdd(`post:${id}:recipients`, Array.from(uids)); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-16 12:07:29 -05:00
										 |  |  | Notes.saveAttachments = async (id, attachments) => { | 
					
						
							| 
									
										
										
										
											2024-02-16 15:59:04 -05:00
										 |  |  | 	if (!attachments) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-16 12:07:29 -05:00
										 |  |  | 	const bulkOps = { | 
					
						
							|  |  |  | 		hash: [], | 
					
						
							|  |  |  | 		zset: { | 
					
						
							|  |  |  | 			score: [], | 
					
						
							|  |  |  | 			value: [], | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-20 11:57:50 -05:00
										 |  |  | 	attachments.filter(Boolean).forEach(({ mediaType, url, name, width, height }, idx) => { | 
					
						
							| 
									
										
										
										
											2024-02-16 12:07:29 -05:00
										 |  |  | 		if (!url) { // only required property
 | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const hash = crypto.createHash('sha256').update(url).digest('hex'); | 
					
						
							|  |  |  | 		const key = `attachment:${hash}`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		bulkOps.hash.push([key, { mediaType, url, name, width, height }]); | 
					
						
							|  |  |  | 		bulkOps.zset.score.push(idx); | 
					
						
							|  |  |  | 		bulkOps.zset.value.push(hash); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	await Promise.all([ | 
					
						
							|  |  |  | 		db.setObjectBulk(bulkOps.hash), | 
					
						
							|  |  |  | 		db.sortedSetAdd(`post:${id}:attachments`, bulkOps.zset.score, bulkOps.zset.value), | 
					
						
							|  |  |  | 	]); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | Notes.getParentChain = async (uid, input) => { | 
					
						
							|  |  |  | 	// Traverse upwards via `inReplyTo` until you find the root-level Note
 | 
					
						
							|  |  |  | 	const id = activitypub.helpers.isUri(input) ? input : input.id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const chain = new Set(); | 
					
						
							|  |  |  | 	const traverse = async (uid, id) => { | 
					
						
							| 
									
										
										
										
											2024-02-09 11:15:03 -05:00
										 |  |  | 		// Handle remote reference to local post
 | 
					
						
							|  |  |  | 		const { type, id: localId } = await activitypub.helpers.resolveLocalId(id); | 
					
						
							|  |  |  | 		if (type === 'post' && localId) { | 
					
						
							| 
									
										
										
										
											2024-02-28 12:45:11 -05:00
										 |  |  | 			return await traverse(uid, localId); | 
					
						
							| 
									
										
										
										
											2024-02-09 11:15:03 -05:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 		const exists = await db.exists(`post:${id}`); | 
					
						
							|  |  |  | 		if (exists) { | 
					
						
							| 
									
										
										
										
											2024-01-12 16:39:29 -05:00
										 |  |  | 			const postData = await posts.getPostData(id); | 
					
						
							|  |  |  | 			chain.add(postData); | 
					
						
							|  |  |  | 			if (postData.toPid) { | 
					
						
							|  |  |  | 				await traverse(uid, postData.toPid); | 
					
						
							| 
									
										
										
										
											2024-02-28 12:45:11 -05:00
										 |  |  | 			} else if (utils.isNumber(id)) { // local pid without toPid, could be OP or reply to OP
 | 
					
						
							|  |  |  | 				const mainPid = await topics.getTopicField(postData.tid, 'mainPid'); | 
					
						
							|  |  |  | 				if (mainPid !== id) { | 
					
						
							|  |  |  | 					await traverse(uid, mainPid); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2024-02-09 11:31:42 -05:00
										 |  |  | 			let object; | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				object = await activitypub.get('uid', uid, id); | 
					
						
							| 
									
										
										
										
											2024-02-08 11:33:27 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-12 14:54:13 -05:00
										 |  |  | 				// Handle incorrect id passed in
 | 
					
						
							|  |  |  | 				if (id !== object.id) { | 
					
						
							|  |  |  | 					return await traverse(uid, object.id); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-02-08 11:33:27 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-12 14:54:13 -05:00
										 |  |  | 				object = await activitypub.mocks.post(object); | 
					
						
							|  |  |  | 				if (object) { | 
					
						
							|  |  |  | 					chain.add(object); | 
					
						
							|  |  |  | 					if (object.toPid) { | 
					
						
							|  |  |  | 						await traverse(uid, object.toPid); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-02-12 14:54:13 -05:00
										 |  |  | 			} catch (e) { | 
					
						
							|  |  |  | 				winston.warn(`[activitypub/notes/getParentChain] Cannot retrieve ${id}, terminating here.`); | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	await traverse(uid, id); | 
					
						
							|  |  |  | 	return chain; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 11:31:04 -05:00
										 |  |  | Notes.assertParentChain = async (chain, tid) => { | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 	const data = []; | 
					
						
							|  |  |  | 	chain.reduce((child, parent) => { | 
					
						
							|  |  |  | 		data.push([`pid:${parent.pid}:replies`, child.timestamp, child.pid]); | 
					
						
							|  |  |  | 		return parent; | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 11:31:04 -05:00
										 |  |  | 	await Promise.all([ | 
					
						
							|  |  |  | 		db.sortedSetAddBulk(data), | 
					
						
							|  |  |  | 		db.setObjectBulk(chain.map(post => [`post:${post.pid}`, { tid }])), | 
					
						
							|  |  |  | 	]); | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | Notes.assertTopic = async (uid, id) => { | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * Given the id of any post, traverses up to cache the entire threaded context | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * Unfortunately, due to limitations and fragmentation of the existing ActivityPub landscape, | 
					
						
							|  |  |  | 	 * retrieving the entire reply tree is not possible at this time. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 	const chain = Array.from(await Notes.getParentChain(uid, id)); | 
					
						
							| 
									
										
										
										
											2024-02-12 15:25:49 -05:00
										 |  |  | 	if (!chain.length) { | 
					
						
							|  |  |  | 		return null; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 11:31:04 -05:00
										 |  |  | 	let { pid: mainPid, tid, uid: authorId, timestamp, name, content } = chain[chain.length - 1]; | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:16 -05:00
										 |  |  | 	const members = await db.isSortedSetMembers(`tid:${tid}:posts`, chain.map(p => p.pid)); | 
					
						
							| 
									
										
										
										
											2024-01-19 11:31:04 -05:00
										 |  |  | 	if (tid && members.every(Boolean)) { | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 		// All cached, return early.
 | 
					
						
							|  |  |  | 		winston.info('[notes/assertTopic] No new notes to process.'); | 
					
						
							|  |  |  | 		return tid; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 	const cid = tid ? await topics.getTopicField(tid, 'cid') : -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Privilege check for local categories
 | 
					
						
							|  |  |  | 	const privilege = `topics:${tid ? 'reply' : 'create'}`; | 
					
						
							|  |  |  | 	const allowed = await privileges.categories.can(privilege, cid, activitypub._constants.uid); | 
					
						
							|  |  |  | 	if (!allowed) { | 
					
						
							|  |  |  | 		return null; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 15:25:13 -05:00
										 |  |  | 	let title = tid ? await topics.getTopicField(tid, 'title') : name || utils.decodeHTMLEntities(utils.stripHTMLTags(content)); | 
					
						
							|  |  |  | 	if (title.length > meta.config.maximumTitleLength) { | 
					
						
							|  |  |  | 		title = `${title.slice(0, meta.config.maximumTitleLength)}...`; | 
					
						
							| 
									
										
										
										
											2024-01-19 11:31:04 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-27 15:25:13 -05:00
										 |  |  | 	tid = tid || utils.generateUUID(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 	const unprocessed = chain.filter((p, idx) => !members[idx]); | 
					
						
							| 
									
										
										
										
											2024-01-17 11:47:57 -05:00
										 |  |  | 	winston.info(`[notes/assertTopic] ${unprocessed.length} new note(s) found.`); | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 	const [ids, timestamps] = [ | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 		unprocessed.map(n => n.pid), | 
					
						
							|  |  |  | 		unprocessed.map(n => n.timestamp), | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 	]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:16 -05:00
										 |  |  | 	// mainPid doesn't belong in posts zset
 | 
					
						
							|  |  |  | 	ids.pop(); | 
					
						
							|  |  |  | 	timestamps.pop(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 16:39:29 -05:00
										 |  |  | 	await Promise.all([ | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:16 -05:00
										 |  |  | 		db.setObject(`topic:${tid}`, { | 
					
						
							| 
									
										
										
										
											2024-01-12 16:39:29 -05:00
										 |  |  | 			tid, | 
					
						
							| 
									
										
										
										
											2024-01-16 12:00:40 -05:00
										 |  |  | 			uid: authorId, | 
					
						
							| 
									
										
										
										
											2024-02-26 11:39:32 -05:00
										 |  |  | 			cid: cid, | 
					
						
							| 
									
										
										
										
											2024-01-19 11:31:04 -05:00
										 |  |  | 			mainPid, | 
					
						
							| 
									
										
										
										
											2024-01-18 16:20:37 -05:00
										 |  |  | 			title, | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:16 -05:00
										 |  |  | 			slug: `${tid}/${slugify(title)}`, | 
					
						
							| 
									
										
										
										
											2024-01-17 12:15:58 -05:00
										 |  |  | 			timestamp, | 
					
						
							| 
									
										
										
										
											2024-01-12 16:39:29 -05:00
										 |  |  | 		}), | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:16 -05:00
										 |  |  | 		db.sortedSetAdd(`tid:${tid}:posts`, timestamps, ids), | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 		Notes.assert(uid, unprocessed), | 
					
						
							|  |  |  | 	]); | 
					
						
							|  |  |  | 	await Promise.all([ // must be done after .assert()
 | 
					
						
							| 
									
										
										
										
											2024-01-19 11:31:04 -05:00
										 |  |  | 		Notes.assertParentChain(chain, tid), | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 		Notes.updateTopicCounts(tid), | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | 		Notes.syncUserInboxes(tid), | 
					
						
							| 
									
										
										
										
											2024-01-17 12:15:58 -05:00
										 |  |  | 		topics.updateLastPostTimeFromLastPid(tid), | 
					
						
							|  |  |  | 		topics.updateTeaser(tid), | 
					
						
							| 
									
										
										
										
											2024-01-12 16:39:29 -05:00
										 |  |  | 	]); | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return tid; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | Notes.updateTopicCounts = async function (tid) { | 
					
						
							| 
									
										
										
										
											2024-02-14 11:38:31 -05:00
										 |  |  | 	const mainPid = await topics.getTopicField(tid, 'mainPid'); | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:16 -05:00
										 |  |  | 	const pids = await db.getSortedSetMembers(`tid:${tid}:posts`); | 
					
						
							| 
									
										
										
										
											2024-02-14 11:38:31 -05:00
										 |  |  | 	pids.unshift(mainPid); | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 	let uids = await db.getObjectsFields(pids.map(p => `post:${p}`), ['uid']); | 
					
						
							| 
									
										
										
										
											2024-02-14 11:38:31 -05:00
										 |  |  | 	uids = new Set(uids.map(o => o.uid)); | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:16 -05:00
										 |  |  | 	db.setObject(`topic:${tid}`, { | 
					
						
							| 
									
										
										
										
											2024-01-16 10:44:47 -05:00
										 |  |  | 		postercount: uids.size, | 
					
						
							|  |  |  | 		postcount: pids.length, | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | Notes.syncUserInboxes = async function (tid) { | 
					
						
							| 
									
										
										
										
											2024-02-12 14:51:21 -05:00
										 |  |  | 	const [pids, { cid, mainPid }] = await Promise.all([ | 
					
						
							|  |  |  | 		db.getSortedSetMembers(`tid:${tid}:posts`), | 
					
						
							| 
									
										
										
										
											2024-02-12 14:59:13 -05:00
										 |  |  | 		topics.getTopicFields(tid, ['tid', 'cid', 'mainPid']), | 
					
						
							| 
									
										
										
										
											2024-02-12 14:51:21 -05:00
										 |  |  | 	]); | 
					
						
							|  |  |  | 	pids.unshift(mainPid); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | 	const recipients = await db.getSetsMembers(pids.map(id => `post:${id}:recipients`)); | 
					
						
							|  |  |  | 	const uids = recipients.reduce((set, uids) => new Set([...set, ...uids.map(u => parseInt(u, 10))]), new Set()); | 
					
						
							|  |  |  | 	const keys = Array.from(uids).map(uid => `uid:${uid}:inbox`); | 
					
						
							|  |  |  | 	const score = await db.sortedSetScore(`cid:${cid}:tids`, tid); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-12 14:41:22 -05:00
										 |  |  | 	winston.verbose(`[activitypub/syncUserInboxes] Syncing tid ${tid} with ${uids.size} inboxes`); | 
					
						
							| 
									
										
										
										
											2024-02-12 15:01:10 -05:00
										 |  |  | 	await db.sortedSetsAdd(keys, keys.map(() => score || Date.now()), tid); | 
					
						
							| 
									
										
										
										
											2024-02-12 14:34:37 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | Notes.getTopicPosts = async (tid, uid, start, stop) => { | 
					
						
							| 
									
										
										
										
											2024-02-07 12:28:16 -05:00
										 |  |  | 	const pids = await db.getSortedSetRange(`tid:${tid}:posts`, start, stop); | 
					
						
							| 
									
										
										
										
											2024-01-12 15:23:30 -05:00
										 |  |  | 	return await posts.getPostsByPids(pids, uid); | 
					
						
							|  |  |  | }; |