forked from cadence/out-of-your-element
		
	Replace PK multiple attempts with cache lookup
This commit is contained in:
		
							parent
							
								
									7bfe140d08
								
							
						
					
					
						commit
						ca8bbe076c
					
				
					 3 changed files with 55 additions and 50 deletions
				
			
		| 
						 | 
				
			
			@ -22,9 +22,7 @@ async function editMessage(message, guild, row) {
 | 
			
		|||
	if (row && row.speedbump_webhook_id === message.webhook_id) {
 | 
			
		||||
		// Handle the PluralKit public instance
 | 
			
		||||
		if (row.speedbump_id === "466378653216014359") {
 | 
			
		||||
			const root = await registerPkUser.fetchMessage(message.id)
 | 
			
		||||
			assert(root.member)
 | 
			
		||||
			senderMxid = await registerPkUser.ensureSimJoined(root, roomID)
 | 
			
		||||
			senderMxid = await registerPkUser.syncUser(message.id, message.author, roomID, false)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ const {reg} = require("../../matrix/read-registration")
 | 
			
		|||
const Ty = require("../../types")
 | 
			
		||||
 | 
			
		||||
const passthrough = require("../../passthrough")
 | 
			
		||||
const {sync, db, select} = passthrough
 | 
			
		||||
const {sync, db, select, from} = passthrough
 | 
			
		||||
/** @type {import("../../matrix/api")} */
 | 
			
		||||
const api = sync.require("../../matrix/api")
 | 
			
		||||
/** @type {import("../../matrix/file")} */
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +20,20 @@ const registerUser = sync.require("./register-user")
 | 
			
		|||
 * @prop {string} id
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/** @returns {Promise<Ty.PkMessage>} */
 | 
			
		||||
async function fetchMessage(messageID) {
 | 
			
		||||
	try {
 | 
			
		||||
		var res = await fetch(`https://api.pluralkit.me/v2/messages/${messageID}`)
 | 
			
		||||
	} catch (networkError) {
 | 
			
		||||
		// Network issue, raise a more readable message
 | 
			
		||||
		throw new Error(`Failed to connect to PK API: ${networkError.toString()}`)
 | 
			
		||||
	}
 | 
			
		||||
	if (!res.ok) throw new Error(`PK API returned an error: ${JSON.stringify(await res.text())}`)
 | 
			
		||||
	const root = await res.json()
 | 
			
		||||
	if (!root.member) throw new Error(`PK API didn't return member data: ${JSON.stringify(root)}`)
 | 
			
		||||
	return root
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A sim is an account that is being simulated by the bridge to copy events from the other side.
 | 
			
		||||
 * @param {Ty.PkMessage} pkMessage
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +109,7 @@ async function ensureSimJoined(pkMessage, roomID) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generate profile data based on webhook displayname and configured avatar.
 | 
			
		||||
 * @param {Ty.PkMessage} pkMessage
 | 
			
		||||
 * @param {WebhookAuthor} author
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -115,54 +130,47 @@ async function memberToStateContent(pkMessage, author) {
 | 
			
		|||
 | 
			
		||||
/**
 | 
			
		||||
 * Sync profile data for a sim user. This function follows the following process:
 | 
			
		||||
 * 1. Join the sim to the room if needed
 | 
			
		||||
 * 2. Make an object of what the new room member state content would be, including uploading the profile picture if it hasn't been done before
 | 
			
		||||
 * 3. Compare against the previously known state content, which is helpfully stored in the database
 | 
			
		||||
 * 4. If the state content has changed, send it to Matrix and update it in the database for next time
 | 
			
		||||
 * @param {WebhookAuthor} author
 | 
			
		||||
 * @param {Ty.PkMessage} pkMessage
 | 
			
		||||
 * @param {string} roomID
 | 
			
		||||
 * 1. Look up data about proxy user from API
 | 
			
		||||
 * 2. If this fails, try to use previously cached data (won't sync)
 | 
			
		||||
 * 3. Create and join the sim to the room if needed
 | 
			
		||||
 * 4. Make an object of what the new room member state content would be, including uploading the profile picture if it hasn't been done before
 | 
			
		||||
 * 5. Compare against the previously known state content, which is helpfully stored in the database
 | 
			
		||||
 * 6. If the state content has changed, send it to Matrix and update it in the database for next time
 | 
			
		||||
 * @param {string} messageID to call API with
 | 
			
		||||
 * @param {WebhookAuthor} author for profile data
 | 
			
		||||
 * @param {string} roomID room to join member to
 | 
			
		||||
 * @param {boolean} shouldActuallySync whether to actually sync updated user data or just ensure it's joined
 | 
			
		||||
 * @returns {Promise<string>} mxid of the updated sim
 | 
			
		||||
 */
 | 
			
		||||
async function syncUser(author, pkMessage, roomID) {
 | 
			
		||||
	const mxid = await ensureSimJoined(pkMessage, roomID)
 | 
			
		||||
	// Update the sim_proxy table, so mentions can look up the original sender later
 | 
			
		||||
	db.prepare("INSERT OR IGNORE INTO sim_proxy (user_id, proxy_owner_id, displayname) VALUES (?, ?, ?)").run(pkMessage.member.uuid, pkMessage.sender, author.username)
 | 
			
		||||
	// Sync the member state
 | 
			
		||||
	const content = await memberToStateContent(pkMessage, author)
 | 
			
		||||
	const currentHash = registerUser._hashProfileContent(content, 0)
 | 
			
		||||
	const existingHash = select("sim_member", "hashed_profile_content", {room_id: roomID, mxid}).safeIntegers().pluck().get()
 | 
			
		||||
	// only do the actual sync if the hash has changed since we last looked
 | 
			
		||||
	if (existingHash !== currentHash) {
 | 
			
		||||
		await api.sendState(roomID, "m.room.member", mxid, content, mxid)
 | 
			
		||||
		db.prepare("UPDATE sim_member SET hashed_profile_content = ? WHERE room_id = ? AND mxid = ?").run(currentHash, roomID, mxid)
 | 
			
		||||
async function syncUser(messageID, author, roomID, shouldActuallySync) {
 | 
			
		||||
	try {
 | 
			
		||||
		// API lookup
 | 
			
		||||
		var pkMessage = await fetchMessage(messageID)
 | 
			
		||||
		db.prepare("INSERT OR IGNORE INTO sim_proxy (user_id, proxy_owner_id, displayname) VALUES (?, ?, ?)").run(pkMessage.member.uuid, pkMessage.sender, author.username)
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		// Fall back to offline cache
 | 
			
		||||
		const senderMxid = from("sim_proxy").join("sim", "user_id").join("sim_member", "mxid").where({displayname: author.username, room_id: roomID}).pluck("mxid").get()
 | 
			
		||||
		if (!senderMxid) throw e
 | 
			
		||||
		return senderMxid
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create and join the sim to the room if needed
 | 
			
		||||
	const mxid = await ensureSimJoined(pkMessage, roomID)
 | 
			
		||||
 | 
			
		||||
	if (shouldActuallySync) {
 | 
			
		||||
		// Build current profile data
 | 
			
		||||
		const content = await memberToStateContent(pkMessage, author)
 | 
			
		||||
		const currentHash = registerUser._hashProfileContent(content, 0)
 | 
			
		||||
		const existingHash = select("sim_member", "hashed_profile_content", {room_id: roomID, mxid}).safeIntegers().pluck().get()
 | 
			
		||||
 | 
			
		||||
		// Only do the actual sync if the hash has changed since we last looked
 | 
			
		||||
		if (existingHash !== currentHash) {
 | 
			
		||||
			await api.sendState(roomID, "m.room.member", mxid, content, mxid)
 | 
			
		||||
			db.prepare("UPDATE sim_member SET hashed_profile_content = ? WHERE room_id = ? AND mxid = ?").run(currentHash, roomID, mxid)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return mxid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** @returns {Promise<Ty.PkMessage>} */
 | 
			
		||||
async function fetchMessage(messageID) {
 | 
			
		||||
	// Their backend is weird. Sometimes it says "message not found" (code 20006) on the first try, so we make multiple attempts.
 | 
			
		||||
	let attempts = 0
 | 
			
		||||
	do {
 | 
			
		||||
		try {
 | 
			
		||||
			var res = await fetch(`https://api.pluralkit.me/v2/messages/${messageID}`)
 | 
			
		||||
			if (res.ok) return res.json()
 | 
			
		||||
			var errorGetter = () => res.json()
 | 
			
		||||
		} catch (e) {
 | 
			
		||||
			// Catch any network issues too.
 | 
			
		||||
			errorGetter = () => e.toString()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// I think the backend needs some time to update.
 | 
			
		||||
		await new Promise(resolve => setTimeout(resolve, 1500))
 | 
			
		||||
	} while (++attempts < 3)
 | 
			
		||||
 | 
			
		||||
	throw new Error(`PK API returned an error after ${attempts} tries: ${JSON.stringify(await errorGetter())}`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports._memberToStateContent = memberToStateContent
 | 
			
		||||
module.exports.ensureSim = ensureSim
 | 
			
		||||
module.exports.ensureSimJoined = ensureSimJoined
 | 
			
		||||
module.exports.syncUser = syncUser
 | 
			
		||||
module.exports.fetchMessage = fetchMessage
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,8 +37,7 @@ async function sendMessage(message, channel, guild, row) {
 | 
			
		|||
	} else if (row && row.speedbump_webhook_id === message.webhook_id) {
 | 
			
		||||
		// Handle the PluralKit public instance
 | 
			
		||||
		if (row.speedbump_id === "466378653216014359") {
 | 
			
		||||
			const pkMessage = await registerPkUser.fetchMessage(message.id)
 | 
			
		||||
			senderMxid = await registerPkUser.syncUser(message.author, pkMessage, roomID)
 | 
			
		||||
			senderMxid = await registerPkUser.syncUser(message.id, message.author, roomID, true)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue