| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | const nconf = require('nconf'); | 
					
						
							| 
									
										
										
										
											2024-01-24 22:59:09 -05:00
										 |  |  | const mime = require('mime'); | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | const path = require('path'); | 
					
						
							| 
									
										
										
										
											2024-09-30 10:51:18 -04:00
										 |  |  | const validator = require('validator'); | 
					
						
							| 
									
										
										
										
											2024-05-23 15:29:32 -04:00
										 |  |  | const sanitize = require('sanitize-html'); | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 14:07:27 -05:00
										 |  |  | const meta = require('../meta'); | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | const user = require('../user'); | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | const categories = require('../categories'); | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | const posts = require('../posts'); | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | const topics = require('../topics'); | 
					
						
							| 
									
										
										
										
											2024-03-05 09:56:15 -05:00
										 |  |  | const plugins = require('../plugins'); | 
					
						
							| 
									
										
										
										
											2024-03-07 13:16:13 -05:00
										 |  |  | const slugify = require('../slugify'); | 
					
						
							| 
									
										
										
										
											2024-09-30 10:51:18 -04:00
										 |  |  | const translator = require('../translator'); | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | const utils = require('../utils'); | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | const activitypub = module.parent.exports; | 
					
						
							|  |  |  | const Mocks = module.exports; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-23 15:29:32 -04:00
										 |  |  | /** | 
					
						
							|  |  |  |  * A more restrictive html sanitization run on top of standard sanitization from core. | 
					
						
							|  |  |  |  * Done so the output HTML is stripped of all non-essential items; mainly classes from plugins.. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const sanitizeConfig = { | 
					
						
							| 
									
										
										
										
											2024-06-19 11:23:26 -04:00
										 |  |  | 	allowedTags: sanitize.defaults.allowedTags.concat(['img']), | 
					
						
							| 
									
										
										
										
											2024-05-23 15:29:32 -04:00
										 |  |  | 	allowedClasses: { | 
					
						
							|  |  |  | 		'*': [], | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-02 11:47:23 -04:00
										 |  |  | Mocks.profile = async (actors, hostMap) => { | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 	// Should only ever be called by activitypub.actors.assert
 | 
					
						
							| 
									
										
										
										
											2024-06-07 11:55:21 -04:00
										 |  |  | 	const profiles = await Promise.all(actors.map(async (actor) => { | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 		if (!actor) { | 
					
						
							|  |  |  | 			return null; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const uid = actor.id; | 
					
						
							| 
									
										
										
										
											2024-08-02 11:47:23 -04:00
										 |  |  | 		let hostname = hostMap.get(uid); | 
					
						
							| 
									
										
										
										
											2024-03-07 13:16:13 -05:00
										 |  |  | 		let { | 
					
						
							| 
									
										
										
										
											2024-04-03 13:49:27 -04:00
										 |  |  | 			url, preferredUsername, published, icon, image, | 
					
						
							| 
									
										
										
										
											2024-07-19 14:37:32 -04:00
										 |  |  | 			name, summary, followers, inbox, endpoints, | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 		} = actor; | 
					
						
							| 
									
										
										
										
											2024-08-02 11:47:23 -04:00
										 |  |  | 		preferredUsername = slugify(preferredUsername || name); | 
					
						
							| 
									
										
										
										
											2024-07-19 14:37:32 -04:00
										 |  |  | 		const { followers: followerCount, following: followingCount } = await activitypub.actors.getLocalFollowCounts(uid); | 
					
						
							| 
									
										
										
										
											2024-06-04 12:30:47 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-02 11:47:23 -04:00
										 |  |  | 		if (!hostname) { // if not available via webfinger, infer from id
 | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				({ hostname } = new URL(actor.id)); | 
					
						
							|  |  |  | 			} catch (e) { | 
					
						
							|  |  |  | 				return null; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-07-05 11:29:05 -04:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		let picture; | 
					
						
							|  |  |  | 		if (icon) { | 
					
						
							|  |  |  | 			picture = typeof icon === 'string' ? icon : icon.url; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		const iconBackgrounds = await user.getIconBackgrounds(); | 
					
						
							|  |  |  | 		let bgColor = Array.prototype.reduce.call(preferredUsername, (cur, next) => cur + next.charCodeAt(), 0); | 
					
						
							|  |  |  | 		bgColor = iconBackgrounds[bgColor % iconBackgrounds.length]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const payload = { | 
					
						
							|  |  |  | 			uid, | 
					
						
							|  |  |  | 			username: `${preferredUsername}@${hostname}`, | 
					
						
							|  |  |  | 			userslug: `${preferredUsername}@${hostname}`, | 
					
						
							|  |  |  | 			displayname: name, | 
					
						
							|  |  |  | 			fullname: name, | 
					
						
							| 
									
										
										
										
											2024-07-05 13:33:00 -04:00
										 |  |  | 			joindate: new Date(published).getTime() || Date.now(), | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 			picture, | 
					
						
							| 
									
										
										
										
											2024-01-10 20:51:23 -05:00
										 |  |  | 			status: 'offline', | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 			'icon:text': (preferredUsername[0] || '').toUpperCase(), | 
					
						
							|  |  |  | 			'icon:bgColor': bgColor, | 
					
						
							|  |  |  | 			uploadedpicture: undefined, | 
					
						
							|  |  |  | 			'cover:url': !image || typeof image === 'string' ? image : image.url, | 
					
						
							|  |  |  | 			'cover:position': '50% 50%', | 
					
						
							|  |  |  | 			aboutme: summary, | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 			followerCount, | 
					
						
							|  |  |  | 			followingCount, | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-03 13:49:27 -04:00
										 |  |  | 			url, | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 			inbox, | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 			sharedInbox: endpoints ? endpoints.sharedInbox : null, | 
					
						
							| 
									
										
										
										
											2024-05-06 14:46:26 -04:00
										 |  |  | 			followersUrl: followers, | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return payload; | 
					
						
							| 
									
										
										
										
											2024-06-07 11:55:21 -04:00
										 |  |  | 	})); | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 15:10:35 -05:00
										 |  |  | 	return profiles; | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Mocks.post = async (objects) => { | 
					
						
							|  |  |  | 	let single = false; | 
					
						
							|  |  |  | 	if (!Array.isArray(objects)) { | 
					
						
							|  |  |  | 		single = true; | 
					
						
							|  |  |  | 		objects = [objects]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-14 12:22:06 -04:00
										 |  |  | 	const actorIds = new Set(objects.map(object => object.attributedTo).filter(Boolean)); | 
					
						
							|  |  |  | 	await activitypub.actors.assert(Array.from(actorIds)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 11:29:08 -05:00
										 |  |  | 	const posts = await Promise.all(objects.map(async (object) => { | 
					
						
							| 
									
										
										
										
											2024-04-14 02:42:30 +02:00
										 |  |  | 		if (!activitypub._constants.acceptedPostTypes.includes(object.type)) { | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 			return null; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-14 00:17:45 -04:00
										 |  |  | 		let { | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 			id: pid, | 
					
						
							| 
									
										
										
										
											2024-04-11 13:25:37 -04:00
										 |  |  | 			url, | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 			attributedTo: uid, | 
					
						
							|  |  |  | 			inReplyTo: toPid, | 
					
						
							| 
									
										
										
										
											2024-06-28 11:47:18 -04:00
										 |  |  | 			published, updated, name, content, source, | 
					
						
							| 
									
										
										
										
											2024-06-13 17:05:37 -04:00
										 |  |  | 			to, cc, audience, attachment, tag, | 
					
						
							| 
									
										
										
										
											2024-02-29 11:19:56 -05:00
										 |  |  | 			// conversation, // mastodon-specific, ignored.
 | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 		} = object; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-14 00:17:45 -04:00
										 |  |  | 		const resolved = await activitypub.helpers.resolveLocalId(toPid); | 
					
						
							|  |  |  | 		if (resolved.type === 'post') { | 
					
						
							|  |  |  | 			toPid = resolved.id; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-10 20:51:23 -05:00
										 |  |  | 		const timestamp = new Date(published).getTime(); | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 		let edited = new Date(updated); | 
					
						
							| 
									
										
										
										
											2024-01-10 20:51:23 -05:00
										 |  |  | 		edited = Number.isNaN(edited.valueOf()) ? undefined : edited; | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-16 15:46:12 -04:00
										 |  |  | 		if (content && content.length) { | 
					
						
							|  |  |  | 			content = sanitize(content, sanitizeConfig); | 
					
						
							|  |  |  | 			content = await activitypub.helpers.remoteAnchorToLocalProfile(content); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			content = '<em>This post did not contain any content.</em>'; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-05-30 12:04:37 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 		const payload = { | 
					
						
							|  |  |  | 			uid, | 
					
						
							|  |  |  | 			pid, | 
					
						
							| 
									
										
										
										
											2024-01-18 16:20:37 -05:00
										 |  |  | 			// tid,  --> purposely omitted
 | 
					
						
							|  |  |  | 			name, | 
					
						
							| 
									
										
										
										
											2024-01-10 20:51:23 -05:00
										 |  |  | 			content, | 
					
						
							| 
									
										
										
										
											2024-06-28 16:28:48 -04:00
										 |  |  | 			sourceContent: source && source.mediaType === 'text/markdown' ? source.content : undefined, | 
					
						
							| 
									
										
										
										
											2024-01-10 20:51:23 -05:00
										 |  |  | 			timestamp, | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 			toPid, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			edited, | 
					
						
							|  |  |  | 			editor: edited ? uid : undefined, | 
					
						
							| 
									
										
										
										
											2024-06-13 17:05:37 -04:00
										 |  |  | 			_activitypub: { to, cc, audience, attachment, tag, url }, | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return payload; | 
					
						
							| 
									
										
										
										
											2024-01-12 11:29:08 -05:00
										 |  |  | 	})); | 
					
						
							| 
									
										
										
										
											2024-01-10 14:19:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return single ? posts.pop() : posts; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | Mocks.actors = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Mocks.actors.user = async (uid) => { | 
					
						
							| 
									
										
										
										
											2024-07-09 14:06:59 -04:00
										 |  |  | 	let { username, userslug, displayname, fullname, joindate, aboutme, picture, 'cover:url': cover } = await user.getUserData(uid); | 
					
						
							| 
									
										
										
										
											2024-02-05 16:57:17 -05:00
										 |  |  | 	const publicKey = await activitypub.getPublicKey('uid', uid); | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 10:51:18 -04:00
										 |  |  | 	let aboutmeParsed = ''; | 
					
						
							|  |  |  | 	if (aboutme) { | 
					
						
							|  |  |  | 		aboutme = validator.escape(String(aboutme || '')); | 
					
						
							|  |  |  | 		aboutmeParsed = await plugins.hooks.fire('filter:parse.aboutme', aboutme); | 
					
						
							|  |  |  | 		aboutmeParsed = translator.escape(aboutmeParsed); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-24 22:59:09 -05:00
										 |  |  | 	if (picture) { | 
					
						
							|  |  |  | 		const imagePath = await user.getLocalAvatarPath(uid); | 
					
						
							|  |  |  | 		picture = { | 
					
						
							|  |  |  | 			type: 'Image', | 
					
						
							|  |  |  | 			mediaType: mime.getType(imagePath), | 
					
						
							|  |  |  | 			url: `${nconf.get('url')}${picture}`, | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (cover) { | 
					
						
							|  |  |  | 		const imagePath = await user.getLocalCoverPath(uid); | 
					
						
							|  |  |  | 		cover = { | 
					
						
							|  |  |  | 			type: 'Image', | 
					
						
							|  |  |  | 			mediaType: mime.getType(imagePath), | 
					
						
							|  |  |  | 			url: `${nconf.get('url')}${cover}`, | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 	return { | 
					
						
							| 
									
										
										
										
											2024-06-17 16:27:59 -04:00
										 |  |  | 		'@context': [ | 
					
						
							|  |  |  | 			'https://www.w3.org/ns/activitystreams', | 
					
						
							|  |  |  | 			'https://w3id.org/security/v1', | 
					
						
							|  |  |  | 		], | 
					
						
							| 
									
										
										
										
											2024-01-25 14:23:59 -05:00
										 |  |  | 		id: `${nconf.get('url')}/uid/${uid}`, | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 		url: `${nconf.get('url')}/user/${userslug}`, | 
					
						
							| 
									
										
										
										
											2024-01-29 16:59:13 -05:00
										 |  |  | 		followers: `${nconf.get('url')}/uid/${uid}/followers`, | 
					
						
							|  |  |  | 		following: `${nconf.get('url')}/uid/${uid}/following`, | 
					
						
							|  |  |  | 		inbox: `${nconf.get('url')}/uid/${uid}/inbox`, | 
					
						
							|  |  |  | 		outbox: `${nconf.get('url')}/uid/${uid}/outbox`, | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		type: 'Person', | 
					
						
							| 
									
										
										
										
											2024-04-10 12:17:03 -04:00
										 |  |  | 		name: username !== displayname ? fullname : username, // displayname is escaped, fullname is not
 | 
					
						
							| 
									
										
										
										
											2024-04-12 14:26:37 -04:00
										 |  |  | 		preferredUsername: userslug, | 
					
						
							| 
									
										
										
										
											2024-09-30 10:51:18 -04:00
										 |  |  | 		summary: aboutmeParsed, | 
					
						
							| 
									
										
										
										
											2024-01-24 22:59:09 -05:00
										 |  |  | 		icon: picture, | 
					
						
							|  |  |  | 		image: cover, | 
					
						
							| 
									
										
										
										
											2024-07-09 14:06:59 -04:00
										 |  |  | 		published: new Date(joindate).toISOString(), | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		publicKey: { | 
					
						
							| 
									
										
										
										
											2024-01-29 16:59:13 -05:00
										 |  |  | 			id: `${nconf.get('url')}/uid/${uid}#key`, | 
					
						
							|  |  |  | 			owner: `${nconf.get('url')}/uid/${uid}`, | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 			publicKeyPem: publicKey, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-06-25 11:29:20 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		endpoints: { | 
					
						
							|  |  |  | 			sharedInbox: `${nconf.get('url')}/inbox`, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-01-24 20:10:22 -05:00
										 |  |  | 	}; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | Mocks.actors.category = async (cid) => { | 
					
						
							| 
									
										
										
										
											2024-08-06 15:06:01 -04:00
										 |  |  | 	const { | 
					
						
							| 
									
										
										
										
											2024-03-22 13:06:09 -04:00
										 |  |  | 		name, handle: preferredUsername, slug, | 
					
						
							| 
									
										
										
										
											2024-07-09 15:13:12 -04:00
										 |  |  | 		descriptionParsed: summary, backgroundImage, | 
					
						
							| 
									
										
										
										
											2024-03-22 13:06:09 -04:00
										 |  |  | 	} = await categories.getCategoryData(cid); | 
					
						
							| 
									
										
										
										
											2024-02-06 10:40:46 -05:00
										 |  |  | 	const publicKey = await activitypub.getPublicKey('cid', cid); | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-06 15:06:01 -04:00
										 |  |  | 	let image; | 
					
						
							|  |  |  | 	if (backgroundImage) { | 
					
						
							|  |  |  | 		const filename = path.basename(utils.decodeHTMLEntities(backgroundImage)); | 
					
						
							|  |  |  | 		image = { | 
					
						
							|  |  |  | 			type: 'Image', | 
					
						
							|  |  |  | 			mediaType: mime.getType(filename), | 
					
						
							|  |  |  | 			url: `${nconf.get('url')}${utils.decodeHTMLEntities(backgroundImage)}`, | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-27 13:29:41 -04:00
										 |  |  | 	let icon = await categories.icons.get(cid); | 
					
						
							|  |  |  | 	icon = icon.get('png'); | 
					
						
							| 
									
										
										
										
											2024-08-06 15:06:01 -04:00
										 |  |  | 	icon = { | 
					
						
							| 
									
										
										
										
											2024-02-28 14:07:27 -05:00
										 |  |  | 		type: 'Image', | 
					
						
							| 
									
										
										
										
											2024-09-27 13:29:41 -04:00
										 |  |  | 		mediaType: 'image/png', | 
					
						
							|  |  |  | 		url: `${nconf.get('url')}${icon}`, | 
					
						
							| 
									
										
										
										
											2024-02-28 14:07:27 -05:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return { | 
					
						
							| 
									
										
										
										
											2024-06-17 16:27:59 -04:00
										 |  |  | 		'@context': [ | 
					
						
							|  |  |  | 			'https://www.w3.org/ns/activitystreams', | 
					
						
							|  |  |  | 			'https://w3id.org/security/v1', | 
					
						
							|  |  |  | 		], | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | 		id: `${nconf.get('url')}/category/${cid}`, | 
					
						
							|  |  |  | 		url: `${nconf.get('url')}/category/${slug}`, | 
					
						
							|  |  |  | 		// followers: ,
 | 
					
						
							|  |  |  | 		//  following: ,
 | 
					
						
							|  |  |  | 		inbox: `${nconf.get('url')}/category/${cid}/inbox`, | 
					
						
							|  |  |  | 		outbox: `${nconf.get('url')}/category/${cid}/outbox`, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		type: 'Group', | 
					
						
							|  |  |  | 		name, | 
					
						
							| 
									
										
										
										
											2024-03-22 13:06:09 -04:00
										 |  |  | 		preferredUsername, | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | 		summary, | 
					
						
							| 
									
										
										
										
											2024-08-06 15:06:01 -04:00
										 |  |  | 		image, | 
					
						
							|  |  |  | 		icon, | 
					
						
							| 
									
										
										
										
											2024-02-06 10:40:46 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		publicKey: { | 
					
						
							|  |  |  | 			id: `${nconf.get('url')}/category/${cid}#key`, | 
					
						
							|  |  |  | 			owner: `${nconf.get('url')}/category/${cid}`, | 
					
						
							|  |  |  | 			publicKeyPem: publicKey, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-06-25 11:29:20 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		endpoints: { | 
					
						
							|  |  |  | 			sharedInbox: `${nconf.get('url')}/inbox`, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2024-02-02 17:19:59 -05:00
										 |  |  | 	}; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | Mocks.note = async (post) => { | 
					
						
							|  |  |  | 	const id = `${nconf.get('url')}/post/${post.pid}`; | 
					
						
							| 
									
										
										
										
											2024-05-07 12:15:51 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Return a tombstone for a deleted post
 | 
					
						
							|  |  |  | 	if (post.deleted === true) { | 
					
						
							|  |  |  | 		return Mocks.tombstone({ | 
					
						
							|  |  |  | 			id, | 
					
						
							|  |  |  | 			formerType: 'Note', | 
					
						
							|  |  |  | 			attributedTo: `${nconf.get('url')}/uid/${post.user.uid}`, | 
					
						
							|  |  |  | 			context: `${nconf.get('url')}/topic/${post.topic.tid}`, | 
					
						
							|  |  |  | 			audience: `${nconf.get('url')}/category/${post.category.cid}`, | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-24 12:15:10 -04:00
										 |  |  | 	const published = post.timestampISO; | 
					
						
							|  |  |  | 	const updated = post.edited ? post.editedISO : null; | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-26 15:39:09 -05:00
										 |  |  | 	// todo: post visibility
 | 
					
						
							| 
									
										
										
										
											2024-03-11 11:40:24 -04:00
										 |  |  | 	const to = new Set([activitypub._constants.publicAddress]); | 
					
						
							|  |  |  | 	const cc = new Set([`${nconf.get('url')}/uid/${post.user.uid}/followers`]); | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	let inReplyTo = null; | 
					
						
							| 
									
										
										
										
											2024-02-29 11:35:07 -05:00
										 |  |  | 	let tag = null; | 
					
						
							| 
									
										
										
										
											2024-05-10 15:30:45 -04:00
										 |  |  | 	let followersUrl; | 
					
						
							| 
									
										
										
										
											2024-05-23 12:36:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	let name = null; | 
					
						
							|  |  |  | 	({ titleRaw: name } = await topics.getTopicFields(post.tid, ['title'])); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 	if (post.toPid) { // direct reply
 | 
					
						
							| 
									
										
										
										
											2024-02-09 11:18:23 -05:00
										 |  |  | 		inReplyTo = utils.isNumber(post.toPid) ? `${nconf.get('url')}/post/${post.toPid}` : post.toPid; | 
					
						
							| 
									
										
										
										
											2024-05-23 12:36:03 -04:00
										 |  |  | 		name = `Re: ${name}`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 		const parentId = await posts.getPostField(post.toPid, 'uid'); | 
					
						
							| 
									
										
										
										
											2024-07-05 14:19:15 -04:00
										 |  |  | 		followersUrl = await user.getUserField(parentId, 'followersUrl'); | 
					
						
							| 
									
										
										
										
											2024-03-11 11:40:24 -04:00
										 |  |  | 		to.add(utils.isNumber(parentId) ? `${nconf.get('url')}/uid/${parentId}` : parentId); | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 	} else if (!post.isMainPost) { // reply to OP
 | 
					
						
							| 
									
										
										
										
											2024-02-09 11:18:23 -05:00
										 |  |  | 		inReplyTo = utils.isNumber(post.topic.mainPid) ? `${nconf.get('url')}/post/${post.topic.mainPid}` : post.topic.mainPid; | 
					
						
							| 
									
										
										
										
											2024-05-23 12:36:03 -04:00
										 |  |  | 		name = `Re: ${name}`; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-11 11:40:24 -04:00
										 |  |  | 		to.add(utils.isNumber(post.topic.uid) ? `${nconf.get('url')}/uid/${post.topic.uid}` : post.topic.uid); | 
					
						
							| 
									
										
										
										
											2024-07-05 14:19:15 -04:00
										 |  |  | 		followersUrl = await user.getUserField(post.topic.uid, 'followersUrl'); | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 	} else { // new topic
 | 
					
						
							| 
									
										
										
										
											2024-02-29 11:35:07 -05:00
										 |  |  | 		tag = post.topic.tags.map(tag => ({ | 
					
						
							|  |  |  | 			type: 'Hashtag', | 
					
						
							|  |  |  | 			href: `${nconf.get('url')}/tags/${tag.valueEncoded}`, | 
					
						
							|  |  |  | 			name: `#${tag.value}`, | 
					
						
							|  |  |  | 		})); | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-10 15:30:45 -04:00
										 |  |  | 	if (followersUrl) { | 
					
						
							|  |  |  | 		cc.add(followersUrl); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-20 22:25:09 -04:00
										 |  |  | 	const content = await posts.getPostField(post.pid, 'content'); | 
					
						
							|  |  |  | 	post.content = content; // re-send raw content
 | 
					
						
							| 
									
										
										
										
											2024-08-02 15:47:03 -04:00
										 |  |  | 	const parsed = await posts.parsePost(post, 'activitypub.note'); | 
					
						
							| 
									
										
										
										
											2024-05-23 15:29:32 -04:00
										 |  |  | 	post.content = sanitize(parsed.content, sanitizeConfig); | 
					
						
							| 
									
										
										
										
											2024-04-12 15:30:59 -04:00
										 |  |  | 	post.content = posts.relativeToAbsolute(post.content, posts.urlRegex); | 
					
						
							|  |  |  | 	post.content = posts.relativeToAbsolute(post.content, posts.imgRegex); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-12 13:27:29 -04:00
										 |  |  | 	let source = null; | 
					
						
							|  |  |  | 	const [markdownEnabled, mentionsEnabled] = await Promise.all([ | 
					
						
							|  |  |  | 		plugins.isActive('nodebb-plugin-markdown'), | 
					
						
							|  |  |  | 		plugins.isActive('nodebb-plugin-mentions'), | 
					
						
							|  |  |  | 	]); | 
					
						
							|  |  |  | 	if (markdownEnabled) { | 
					
						
							|  |  |  | 		const raw = await posts.getPostField(post.pid, 'content'); | 
					
						
							|  |  |  | 		source = { | 
					
						
							|  |  |  | 			content: raw, | 
					
						
							|  |  |  | 			mediaType: 'text/markdown', | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-03-05 09:56:15 -05:00
										 |  |  | 	if (mentionsEnabled) { | 
					
						
							|  |  |  | 		const mentions = require.main.require('nodebb-plugin-mentions'); | 
					
						
							|  |  |  | 		const matches = await mentions.getMatches(post.content); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (matches.size) { | 
					
						
							|  |  |  | 			tag = tag || []; | 
					
						
							| 
									
										
										
										
											2024-07-09 15:42:08 -04:00
										 |  |  | 			tag.push(...Array.from(matches).map(({ type, id: href, slug: name }) => { | 
					
						
							| 
									
										
										
										
											2024-03-05 14:24:13 -05:00
										 |  |  | 				if (utils.isNumber(href)) { // local ref
 | 
					
						
							| 
									
										
										
										
											2024-03-07 16:47:09 -05:00
										 |  |  | 					name = name.toLowerCase(); // local slugs are always lowercase
 | 
					
						
							| 
									
										
										
										
											2024-07-09 15:42:08 -04:00
										 |  |  | 					href = `${nconf.get('url')}/${type === 'uid' ? 'user' : `category/${href}`}/${name.slice(1)}`; | 
					
						
							| 
									
										
										
										
											2024-03-05 14:24:13 -05:00
										 |  |  | 					name = `${name}@${nconf.get('url_parsed').hostname}`; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return { | 
					
						
							|  |  |  | 					type: 'Mention', | 
					
						
							|  |  |  | 					href, | 
					
						
							|  |  |  | 					name, | 
					
						
							|  |  |  | 				}; | 
					
						
							| 
									
										
										
										
											2024-03-05 09:56:15 -05:00
										 |  |  | 			})); | 
					
						
							| 
									
										
										
										
											2024-03-08 14:06:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-11 11:40:24 -04:00
										 |  |  | 			Array.from(matches) | 
					
						
							|  |  |  | 				.reduce((ids, { id }) => { | 
					
						
							|  |  |  | 					if (!utils.isNumber(id) && !to.has(id) && !cc.has(id)) { | 
					
						
							|  |  |  | 						ids.push(id); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2024-03-08 14:06:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-11 11:40:24 -04:00
										 |  |  | 					return ids; | 
					
						
							|  |  |  | 				}, []) | 
					
						
							|  |  |  | 				.forEach(id => cc.add(id)); | 
					
						
							| 
									
										
										
										
											2024-03-05 09:56:15 -05:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-10 22:01:44 -04:00
										 |  |  | 	let attachment = await posts.attachments.get(post.pid) || []; | 
					
						
							| 
									
										
										
										
											2024-04-12 14:08:28 -04:00
										 |  |  | 	const uploads = await posts.uploads.listWithSizes(post.pid); | 
					
						
							|  |  |  | 	uploads.forEach(({ name, width, height }) => { | 
					
						
							|  |  |  | 		const mediaType = mime.getType(name); | 
					
						
							| 
									
										
										
										
											2024-04-12 14:10:33 -04:00
										 |  |  | 		const url = `${nconf.get('url') + nconf.get('upload_url')}/${name}`; | 
					
						
							| 
									
										
										
										
											2024-04-12 14:08:28 -04:00
										 |  |  | 		attachment.push({ mediaType, url, width, height }); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-16 15:07:32 -04:00
										 |  |  | 	// Inspect post content for external imagery as well
 | 
					
						
							|  |  |  | 	let match = posts.imgRegex.regex.exec(post.content); | 
					
						
							|  |  |  | 	while (match !== null) { | 
					
						
							|  |  |  | 		if (match[1]) { | 
					
						
							|  |  |  | 			const { hostname, pathname, href: url } = new URL(match[1]); | 
					
						
							|  |  |  | 			if (hostname !== nconf.get('url_parsed').hostname) { | 
					
						
							|  |  |  | 				const mediaType = mime.getType(pathname); | 
					
						
							|  |  |  | 				attachment.push({ mediaType, url }); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		match = posts.imgRegex.regex.exec(post.content); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-12 14:08:28 -04:00
										 |  |  | 	attachment = attachment.map(({ mediaType, url, width, height }) => { | 
					
						
							| 
									
										
										
										
											2024-04-12 12:56:18 -04:00
										 |  |  | 		let type; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (true) { | 
					
						
							|  |  |  | 			case mediaType.startsWith('image'): { | 
					
						
							|  |  |  | 				type = 'Image'; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			default: { | 
					
						
							|  |  |  | 				type = 'Link'; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-12 14:08:28 -04:00
										 |  |  | 		const payload = { type, mediaType, url }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (width || height) { | 
					
						
							|  |  |  | 			payload.width = width; | 
					
						
							|  |  |  | 			payload.height = height; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return payload; | 
					
						
							| 
									
										
										
										
											2024-04-12 12:56:18 -04:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2024-04-10 22:01:44 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 	const object = { | 
					
						
							| 
									
										
										
										
											2024-01-29 16:33:41 -05:00
										 |  |  | 		'@context': 'https://www.w3.org/ns/activitystreams', | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 		id, | 
					
						
							|  |  |  | 		type: 'Note', | 
					
						
							| 
									
										
										
										
											2024-03-11 11:40:24 -04:00
										 |  |  | 		to: Array.from(to), | 
					
						
							|  |  |  | 		cc: Array.from(cc), | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 		inReplyTo, | 
					
						
							|  |  |  | 		published, | 
					
						
							| 
									
										
										
										
											2024-09-24 12:15:10 -04:00
										 |  |  | 		updated, | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 		url: id, | 
					
						
							|  |  |  | 		attributedTo: `${nconf.get('url')}/uid/${post.user.uid}`, | 
					
						
							| 
									
										
										
										
											2024-05-03 14:11:17 -04:00
										 |  |  | 		context: `${nconf.get('url')}/topic/${post.topic.tid}`, | 
					
						
							|  |  |  | 		audience: `${nconf.get('url')}/category/${post.category.cid}`, | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 		sensitive: false, // todo
 | 
					
						
							| 
									
										
										
										
											2024-01-25 16:26:39 -05:00
										 |  |  | 		summary: null, | 
					
						
							| 
									
										
										
										
											2024-01-26 21:39:20 -05:00
										 |  |  | 		name, | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 		content: post.content, | 
					
						
							| 
									
										
										
										
											2024-03-12 13:27:29 -04:00
										 |  |  | 		source, | 
					
						
							| 
									
										
										
										
											2024-02-29 11:35:07 -05:00
										 |  |  | 		tag, | 
					
						
							| 
									
										
										
										
											2024-04-10 22:01:44 -04:00
										 |  |  | 		attachment, | 
					
						
							| 
									
										
										
										
											2024-08-01 14:59:28 -04:00
										 |  |  | 		replies: `${id}/replies`, | 
					
						
							| 
									
										
										
										
											2024-01-25 15:35:45 -05:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return object; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2024-05-07 12:15:51 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | Mocks.tombstone = async properties => ({ | 
					
						
							|  |  |  | 	'@context': 'https://www.w3.org/ns/activitystreams', | 
					
						
							|  |  |  | 	type: 'Tombstone', | 
					
						
							|  |  |  | 	...properties, | 
					
						
							|  |  |  | }); |