mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 02:55:58 +01:00 
			
		
		
		
	feat: Announce(Note) and Undo(Announce)
This commit is contained in:
		| @@ -5,6 +5,7 @@ const winston = require('winston'); | |||||||
| const db = require('../database'); | const db = require('../database'); | ||||||
| const user = require('../user'); | const user = require('../user'); | ||||||
| const posts = require('../posts'); | const posts = require('../posts'); | ||||||
|  | const topics = require('../topics'); | ||||||
| const categories = require('../categories'); | const categories = require('../categories'); | ||||||
| const activitypub = require('.'); | const activitypub = require('.'); | ||||||
|  |  | ||||||
| @@ -65,6 +66,46 @@ inbox.like = async (req) => { | |||||||
| 	await posts.upvote(id, actor); | 	await posts.upvote(id, actor); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | inbox.announce = async (req) => { | ||||||
|  | 	const { actor, object, published } = req.body; | ||||||
|  | 	let timestamp = Date.now(); | ||||||
|  | 	try { | ||||||
|  | 		timestamp = new Date(published).getTime(); | ||||||
|  | 	} catch (e) { | ||||||
|  | 		// ok to fail | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const { type, id } = await activitypub.helpers.resolveLocalId(object); | ||||||
|  | 	if (type !== 'post' || !(await posts.exists(id))) { | ||||||
|  | 		throw new Error('[[error:activitypub.invalid-id]]'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const assertion = await activitypub.actors.assert(actor); | ||||||
|  | 	if (!assertion) { | ||||||
|  | 		throw new Error('[[error:activitypub.invalid-id]]'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	const tid = await posts.getPostField(id, 'tid'); | ||||||
|  |  | ||||||
|  | 	// No double-announce allowed | ||||||
|  | 	const existing = await topics.events.find(tid, { | ||||||
|  | 		type: 'announce', | ||||||
|  | 		uid: actor, | ||||||
|  | 		pid: id, | ||||||
|  | 	}); | ||||||
|  | 	if (existing.length) { | ||||||
|  | 		await topics.events.purge(tid, existing); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	await topics.events.log(tid, { | ||||||
|  | 		type: 'announce', | ||||||
|  | 		uid: actor, | ||||||
|  | 		href: `/post/${id}`, | ||||||
|  | 		pid: id, | ||||||
|  | 		timestamp, | ||||||
|  | 	}); | ||||||
|  | }; | ||||||
|  |  | ||||||
| inbox.follow = async (req) => { | inbox.follow = async (req) => { | ||||||
| 	// Sanity checks | 	// Sanity checks | ||||||
| 	const { type, id } = await helpers.resolveLocalId(req.body.object); | 	const { type, id } = await helpers.resolveLocalId(req.body.object); | ||||||
| @@ -157,6 +198,10 @@ inbox.undo = async (req) => { | |||||||
| 	const { actor, object } = req.body; | 	const { actor, object } = req.body; | ||||||
| 	const { type } = object; | 	const { type } = object; | ||||||
|  |  | ||||||
|  | 	if (actor !== object.actor) { | ||||||
|  | 		throw new Error('[[error:activitypub.actor-mismatch]]'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	const assertion = await activitypub.actors.assert(actor); | 	const assertion = await activitypub.actors.assert(actor); | ||||||
| 	if (!assertion) { | 	if (!assertion) { | ||||||
| 		throw new Error('[[error:activitypub.invalid-id]]'); | 		throw new Error('[[error:activitypub.invalid-id]]'); | ||||||
| @@ -202,5 +247,23 @@ inbox.undo = async (req) => { | |||||||
| 			await posts.unvote(id, actor); | 			await posts.unvote(id, actor); | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		case 'Announce': { | ||||||
|  | 			const exists = await posts.exists(id); | ||||||
|  | 			if (localType !== 'post' || !exists) { | ||||||
|  | 				throw new Error('[[error:invalid-pid]]'); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			const tid = await posts.getPostField(id, 'tid'); | ||||||
|  | 			const existing = await topics.events.find(tid, { | ||||||
|  | 				type: 'announce', | ||||||
|  | 				uid: actor, | ||||||
|  | 				pid: id, | ||||||
|  | 			}); | ||||||
|  |  | ||||||
|  | 			if (existing.length) { | ||||||
|  | 				await topics.events.purge(tid, existing); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -237,3 +237,15 @@ ActivityPub.send = async (type, id, targets, payload) => { | |||||||
| 		} | 		} | ||||||
| 	})); | 	})); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | setTimeout(async () => { | ||||||
|  | 	await ActivityPub.send('uid', 1, 'https://localhost/uid/1', { | ||||||
|  | 		// type: 'Undo', | ||||||
|  | 		// object: { | ||||||
|  | 		type: 'Announce', | ||||||
|  | 		actor: `https://localhost/uid/1`, | ||||||
|  | 		object: 'https://localhost/post/1', | ||||||
|  | 		published: new Date().toISOString(), | ||||||
|  | 		// }, | ||||||
|  | 	}); | ||||||
|  | }, 2000); | ||||||
|   | |||||||
| @@ -112,42 +112,11 @@ Controller.getInbox = async (req, res) => { | |||||||
|  |  | ||||||
| Controller.postInbox = async (req, res) => { | Controller.postInbox = async (req, res) => { | ||||||
| 	// Note: underlying methods are internal use only, hence no exposure via src/api | 	// Note: underlying methods are internal use only, hence no exposure via src/api | ||||||
| 	switch (req.body.type) { | 	const method = String(req.body.type).toLowerCase(); | ||||||
| 		case 'Create': { | 	if (req.body.hasOwnProperty(method)) { | ||||||
| 			await activitypub.inbox.create(req); | 		return res.sendStatus(501); | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		case 'Update': { |  | ||||||
| 			await activitypub.inbox.update(req); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		case 'Like': { |  | ||||||
| 			await activitypub.inbox.like(req); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		case 'Follow': { |  | ||||||
| 			await activitypub.inbox.follow(req); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		case 'Accept': { |  | ||||||
| 			await activitypub.inbox.accept(req); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		case 'Undo': { |  | ||||||
| 			await activitypub.inbox.undo(req); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		default: { |  | ||||||
| 			res.sendStatus(501); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	await activitypub.inbox[method](req); | ||||||
| 	res.sendStatus(200); | 	res.sendStatus(200); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -68,6 +68,10 @@ Events._types = { | |||||||
| 		icon: 'fa-code-fork', | 		icon: 'fa-code-fork', | ||||||
| 		translation: async (event, language) => translateEventArgs(event, language, 'topic:user-forked-topic', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)), | 		translation: async (event, language) => translateEventArgs(event, language, 'topic:user-forked-topic', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)), | ||||||
| 	}, | 	}, | ||||||
|  | 	announce: { | ||||||
|  | 		icon: 'fa-share-alt', | ||||||
|  | 		translation: async (event, language) => translateEventArgs(event, language, 'activitypub:topic-event-announce', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)), | ||||||
|  | 	}, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Events.init = async () => { | Events.init = async () => { | ||||||
| @@ -199,8 +203,9 @@ async function modifyEvent({ tid, uid, eventIds, timestamps, events }) { | |||||||
| 		event.id = parseInt(eventIds[idx], 10); | 		event.id = parseInt(eventIds[idx], 10); | ||||||
| 		event.timestamp = timestamps[idx]; | 		event.timestamp = timestamps[idx]; | ||||||
| 		event.timestampISO = new Date(timestamps[idx]).toISOString(); | 		event.timestampISO = new Date(timestamps[idx]).toISOString(); | ||||||
|  | 		event.uid = utils.isNumber(event.uid) ? parseInt(event.uid, 10) : event.uid; | ||||||
| 		if (event.hasOwnProperty('uid')) { | 		if (event.hasOwnProperty('uid')) { | ||||||
| 			event.user = users.get(event.uid === 'system' ? 'system' : parseInt(event.uid, 10)); | 			event.user = users.get(event.uid === 'system' ? 'system' : event.uid); | ||||||
| 		} | 		} | ||||||
| 		if (event.hasOwnProperty('fromCid')) { | 		if (event.hasOwnProperty('fromCid')) { | ||||||
| 			event.fromCategory = fromCategories[event.fromCid]; | 			event.fromCategory = fromCategories[event.fromCid]; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user