mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 02:55:58 +01:00 
			
		
		
		
	feat: mentions support
This commit is contained in:
		| @@ -93,30 +93,39 @@ Actors.assert = async (ids, options = {}) => { | ||||
| 	const exists = await db.isSortedSetMembers('usersRemote:lastCrawled', profiles.map(p => p.uid)); | ||||
| 	const uidsForCurrent = profiles.map((p, idx) => (exists[idx] ? p.uid : 0)); | ||||
| 	const current = await user.getUsersFields(uidsForCurrent, ['username', 'fullname']); | ||||
| 	const searchQueries = profiles.reduce((memo, profile, idx) => { | ||||
| 	const queries = profiles.reduce((memo, profile, idx) => { | ||||
| 		if (profile) { | ||||
| 			const { username, fullname } = current[idx]; | ||||
|  | ||||
| 			if (username !== profile.username) { | ||||
| 				memo.remove.push(['ap.preferredUsername:sorted', `${username.toLowerCase()}:${profile.uid}`]); | ||||
| 				memo.add.push(['ap.preferredUsername:sorted', 0, `${profile.username.toLowerCase()}:${profile.uid}`]); | ||||
| 				if (uidsForCurrent[idx] !== 0) { | ||||
| 					memo.searchRemove.push(['ap.preferredUsername:sorted', `${username.toLowerCase()}:${profile.uid}`]); | ||||
| 					memo.handleRemove.push(username.toLowerCase()); | ||||
| 				} | ||||
|  | ||||
| 				memo.searchAdd.push(['ap.preferredUsername:sorted', 0, `${profile.username.toLowerCase()}:${profile.uid}`]); | ||||
| 				memo.handleAdd[profile.username.toLowerCase()] = profile.uid; | ||||
| 			} | ||||
|  | ||||
| 			if (fullname !== profile.fullname) { | ||||
| 				memo.remove.push(['ap.name:sorted', `${fullname.toLowerCase()}:${profile.uid}`]); | ||||
| 				memo.add.push(['ap.name:sorted', 0, `${profile.fullname.toLowerCase()}:${profile.uid}`]); | ||||
| 				if (uidsForCurrent[idx] !== 0) { | ||||
| 					memo.searchRemove.push(['ap.name:sorted', `${fullname.toLowerCase()}:${profile.uid}`]); | ||||
| 				} | ||||
|  | ||||
| 				memo.searchAdd.push(['ap.name:sorted', 0, `${profile.fullname.toLowerCase()}:${profile.uid}`]); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return memo; | ||||
| 	}, { remove: [], add: [] }); | ||||
| 	}, { searchRemove: [], searchAdd: [], handleRemove: [], handleAdd: {} }); | ||||
|  | ||||
| 	await Promise.all([ | ||||
| 		db.setObjectBulk(bulkSet), | ||||
| 		db.sortedSetAdd('usersRemote:lastCrawled', ids.map((id, idx) => (profiles[idx] ? now : null)).filter(Boolean), ids.filter((id, idx) => profiles[idx])), | ||||
| 		db.sortedSetRemove('ap.preferredUsername:sorted', searchQueries.remove), | ||||
| 		db.sortedSetRemoveBulk(searchQueries.remove), | ||||
| 		db.sortedSetAddBulk(searchQueries.add), | ||||
| 		db.sortedSetAdd('usersRemote:lastCrawled', profiles.map(p => now), profiles.map(p => p.uid)), | ||||
| 		db.sortedSetRemoveBulk(queries.searchRemove), | ||||
| 		db.sortedSetAddBulk(queries.searchAdd), | ||||
| 		db.deleteObjectFields('handle:uid', queries.handleRemove), | ||||
| 		db.setObject('handle:uid', queries.handleAdd), | ||||
| 	]); | ||||
|  | ||||
| 	return actors.every(Boolean); | ||||
|   | ||||
| @@ -235,9 +235,17 @@ Mocks.note = async (post) => { | ||||
|  | ||||
| 		if (matches.size) { | ||||
| 			tag = tag || []; | ||||
| 			Array.from(matches).map(match => ({ | ||||
| 				type: 'Mention', | ||||
| 				name: match, | ||||
| 			tag.push(...Array.from(matches).map(({ id: href, slug: name }) => { | ||||
| 				if (utils.isNumber(href)) { // local ref | ||||
| 					href = `${nconf.get('url')}/user/${name.slice(1)}`; | ||||
| 					name = `${name}@${nconf.get('url_parsed').hostname}`; | ||||
| 				} | ||||
|  | ||||
| 				return { | ||||
| 					type: 'Mention', | ||||
| 					href, | ||||
| 					name, | ||||
| 				}; | ||||
| 			})); | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -9,6 +9,7 @@ const privileges = require('../privileges'); | ||||
| const user = require('../user'); | ||||
| const topics = require('../topics'); | ||||
| const posts = require('../posts'); | ||||
| const plugins = require('../plugins'); | ||||
| const utils = require('../utils'); | ||||
|  | ||||
| const activitypub = module.parent.exports; | ||||
| @@ -59,6 +60,9 @@ Notes.assert = async (uid, input, options = {}) => { | ||||
| 			delete hash._activitypub; | ||||
| 			// should call internal method here to create/edit post | ||||
| 			await db.setObject(key, hash); | ||||
| 			const post = await posts.getPostData(id); | ||||
| 			post._activitypub = postData._activitypub; | ||||
| 			plugins.hooks.fire(`action:post.${(exists && options.update) ? 'edit' : 'save'}`, { post }); | ||||
| 			winston.verbose(`[activitypub/notes.assert] Note ${id} saved.`); | ||||
| 		} | ||||
| 	})); | ||||
| @@ -176,17 +180,14 @@ Notes.getParentChain = async (uid, input) => { | ||||
| 	return chain; | ||||
| }; | ||||
|  | ||||
| Notes.assertParentChain = async (chain, tid) => { | ||||
| Notes.assertParentChain = async (chain) => { | ||||
| 	const data = []; | ||||
| 	chain.reduce((child, parent) => { | ||||
| 		data.push([`pid:${parent.pid}:replies`, child.timestamp, child.pid]); | ||||
| 		return parent; | ||||
| 	}); | ||||
|  | ||||
| 	await Promise.all([ | ||||
| 		db.sortedSetAddBulk(data), | ||||
| 		db.setObjectBulk(chain.map(post => [`post:${post.pid}`, { tid }])), | ||||
| 	]); | ||||
| 	await db.sortedSetAddBulk(data); | ||||
| }; | ||||
|  | ||||
| Notes.assertTopic = async (uid, id) => { | ||||
| @@ -238,7 +239,10 @@ Notes.assertTopic = async (uid, id) => { | ||||
| 	tid = tid || utils.generateUUID(); | ||||
| 	mainPost.tid = tid; | ||||
|  | ||||
| 	const unprocessed = chain.filter((p, idx) => !members[idx]); | ||||
| 	const unprocessed = chain.map((post) => { | ||||
| 		post.tid = tid; // add tid to post hash | ||||
| 		return post; | ||||
| 	}).filter((p, idx) => !members[idx]); | ||||
| 	winston.verbose(`[notes/assertTopic] ${unprocessed.length} new note(s) found.`); | ||||
|  | ||||
| 	const [ids, timestamps] = [ | ||||
| @@ -275,7 +279,7 @@ Notes.assertTopic = async (uid, id) => { | ||||
| 		Notes.assert(uid, unprocessed), | ||||
| 	]); | ||||
| 	await Promise.all([ // must be done after .assert() | ||||
| 		Notes.assertParentChain(chain, tid), | ||||
| 		Notes.assertParentChain(chain), | ||||
| 		Notes.updateTopicCounts(tid), | ||||
| 		Notes.syncUserInboxes(tid), | ||||
| 		topics.updateLastPostTimeFromLastPid(tid), | ||||
|   | ||||
| @@ -4,12 +4,14 @@ const querystring = require('querystring'); | ||||
|  | ||||
| const posts = require('../posts'); | ||||
| const privileges = require('../privileges'); | ||||
| const utils = require('../utils'); | ||||
|  | ||||
| const helpers = require('./helpers'); | ||||
|  | ||||
| const postsController = module.exports; | ||||
|  | ||||
| postsController.redirectToPost = async function (req, res, next) { | ||||
| 	const pid = parseInt(req.params.pid, 10); | ||||
| 	const pid = utils.isNumber(req.params.pid) ? parseInt(req.params.pid, 10) : req.params.pid; | ||||
| 	if (!pid) { | ||||
| 		return next(); | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										28
									
								
								src/upgrades/4.0.0/searchable_remote_users.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/upgrades/4.0.0/searchable_remote_users.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| const db = require('../../database'); | ||||
|  | ||||
| module.exports = { | ||||
| 	name: 'Allow remote user profiles to be searched', | ||||
| 	// remember, month is zero-indexed (so January is 0, December is 11) | ||||
| 	timestamp: Date.UTC(2024, 2, 1), | ||||
| 	method: async () => { | ||||
| 		const ids = await db.getSortedSetMembers('usersRemote:lastCrawled'); | ||||
| 		const data = await db.getObjectsFields(ids.map(id => `userRemote:${id}`), ['username', 'fullname']); | ||||
|  | ||||
| 		const queries = data.reduce((memo, profile, idx) => { | ||||
| 			if (profile && profile.username && profile.fullname) { | ||||
| 				memo.zset.push(['ap.preferredUsername:sorted', 0, `${profile.username.toLowerCase()}:${ids[idx]}`]); | ||||
| 				memo.zset.push(['ap.name:sorted', 0, `${profile.fullname.toLowerCase()}:${ids[idx]}`]); | ||||
| 				memo.hash[profile.username.toLowerCase()] = ids[idx]; | ||||
| 			} | ||||
|  | ||||
| 			return memo; | ||||
| 		}, { zset: [], hash: {} }); | ||||
|  | ||||
| 		await Promise.all([ | ||||
| 			db.sortedSetAddBulk(queries.zset), | ||||
| 			db.setObject('handle:uid', queries.hash), | ||||
| 		]); | ||||
| 	}, | ||||
| }; | ||||
| @@ -111,11 +111,8 @@ User.getUidByUserslug = async function (userslug) { | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	// fix this! Forces a remote call, this is bad. Should be done in actors.assert | ||||
| 	// then mentions. should return actor uri or url or something to parsePost. | ||||
| 	if (userslug.includes('@')) { | ||||
| 		const { actorUri } = await activitypub.helpers.query(userslug); | ||||
| 		return actorUri; | ||||
| 		return (await db.getObjectField('handle:uid', userslug)) || 0; | ||||
| 	} | ||||
|  | ||||
| 	return await db.sortedSetScore('userslug:uid', userslug); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user