Include system data on PK profiles
This commit is contained in:
		
							parent
							
								
									6a06dc14ce
								
							
						
					
					
						commit
						a71c9515ec
					
				
					 4 changed files with 49 additions and 25 deletions
				
			
		| 
						 | 
					@ -24,7 +24,7 @@ async function editMessage(message, guild, row) {
 | 
				
			||||||
		if (row.speedbump_id === "466378653216014359") {
 | 
							if (row.speedbump_id === "466378653216014359") {
 | 
				
			||||||
			const root = await registerPkUser.fetchMessage(message.id)
 | 
								const root = await registerPkUser.fetchMessage(message.id)
 | 
				
			||||||
			assert(root.member)
 | 
								assert(root.member)
 | 
				
			||||||
			senderMxid = await registerPkUser.ensureSimJoined(root.member, roomID)
 | 
								senderMxid = await registerPkUser.ensureSimJoined(root, roomID)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,17 +16,17 @@ const registerUser = sync.require("./register-user")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * A sim is an account that is being simulated by the bridge to copy events from the other side.
 | 
					 * A sim is an account that is being simulated by the bridge to copy events from the other side.
 | 
				
			||||||
 * @param {Ty.PkMember} member
 | 
					 * @param {Ty.PkMessage} pkMessage
 | 
				
			||||||
 * @returns mxid
 | 
					 * @returns mxid
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function createSim(member) {
 | 
					async function createSim(pkMessage) {
 | 
				
			||||||
	// Choose sim name
 | 
						// Choose sim name
 | 
				
			||||||
	const simName = "_pk_" + member.id
 | 
						const simName = "_pk_" + pkMessage.member.id
 | 
				
			||||||
	const localpart = reg.ooye.namespace_prefix + simName
 | 
						const localpart = reg.ooye.namespace_prefix + simName
 | 
				
			||||||
	const mxid = `@${localpart}:${reg.ooye.server_name}`
 | 
						const mxid = `@${localpart}:${reg.ooye.server_name}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Save chosen name in the database forever
 | 
						// Save chosen name in the database forever
 | 
				
			||||||
	db.prepare("INSERT INTO sim (user_id, sim_name, localpart, mxid) VALUES (?, ?, ?, ?)").run(member.uuid, simName, localpart, mxid)
 | 
						db.prepare("INSERT INTO sim (user_id, sim_name, localpart, mxid) VALUES (?, ?, ?, ?)").run(pkMessage.member.uuid, simName, localpart, mxid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Register matrix user with that name
 | 
						// Register matrix user with that name
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@ async function createSim(member) {
 | 
				
			||||||
	} catch (e) {
 | 
						} catch (e) {
 | 
				
			||||||
		// If user creation fails, manually undo the database change. Still isn't perfect, but should help.
 | 
							// If user creation fails, manually undo the database change. Still isn't perfect, but should help.
 | 
				
			||||||
		// (I would prefer a transaction, but it's not safe to leave transactions open across event loop ticks.)
 | 
							// (I would prefer a transaction, but it's not safe to leave transactions open across event loop ticks.)
 | 
				
			||||||
		db.prepare("DELETE FROM sim WHERE user_id = ?").run(member.uuid)
 | 
							db.prepare("DELETE FROM sim WHERE user_id = ?").run(pkMessage.member.uuid)
 | 
				
			||||||
		throw e
 | 
							throw e
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return mxid
 | 
						return mxid
 | 
				
			||||||
| 
						 | 
					@ -43,32 +43,32 @@ async function createSim(member) {
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Ensure a sim is registered for the user.
 | 
					 * Ensure a sim is registered for the user.
 | 
				
			||||||
 * If there is already a sim, use that one. If there isn't one yet, register a new sim.
 | 
					 * If there is already a sim, use that one. If there isn't one yet, register a new sim.
 | 
				
			||||||
 * @param {Ty.PkMember} member
 | 
					 * @param {Ty.PkMessage} pkMessage
 | 
				
			||||||
 * @returns {Promise<string>} mxid
 | 
					 * @returns {Promise<string>} mxid
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function ensureSim(member) {
 | 
					async function ensureSim(pkMessage) {
 | 
				
			||||||
	let mxid = null
 | 
						let mxid = null
 | 
				
			||||||
	const existing = select("sim", "mxid", {user_id: member.uuid}).pluck().get()
 | 
						const existing = select("sim", "mxid", {user_id: pkMessage.member.uuid}).pluck().get()
 | 
				
			||||||
	if (existing) {
 | 
						if (existing) {
 | 
				
			||||||
		mxid = existing
 | 
							mxid = existing
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		mxid = await createSim(member)
 | 
							mxid = await createSim(pkMessage)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return mxid
 | 
						return mxid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Ensure a sim is registered for the user and is joined to the room.
 | 
					 * Ensure a sim is registered for the user and is joined to the room.
 | 
				
			||||||
 * @param {Ty.PkMember} member
 | 
					 * @param {Ty.PkMessage} pkMessage
 | 
				
			||||||
 * @param {string} roomID
 | 
					 * @param {string} roomID
 | 
				
			||||||
 * @returns {Promise<string>} mxid
 | 
					 * @returns {Promise<string>} mxid
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function ensureSimJoined(member, roomID) {
 | 
					async function ensureSimJoined(pkMessage, roomID) {
 | 
				
			||||||
	// Ensure room ID is really an ID, not an alias
 | 
						// Ensure room ID is really an ID, not an alias
 | 
				
			||||||
	assert.ok(roomID[0] === "!")
 | 
						assert.ok(roomID[0] === "!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Ensure user
 | 
						// Ensure user
 | 
				
			||||||
	const mxid = await ensureSim(member)
 | 
						const mxid = await ensureSim(pkMessage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Ensure joined
 | 
						// Ensure joined
 | 
				
			||||||
	const existing = select("sim_member", "mxid", {room_id: roomID, mxid}).pluck().get()
 | 
						const existing = select("sim_member", "mxid", {room_id: roomID, mxid}).pluck().get()
 | 
				
			||||||
| 
						 | 
					@ -89,16 +89,17 @@ async function ensureSimJoined(member, roomID) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @param {Ty.PkMember} member
 | 
					 * @param {Ty.PkMessage} pkMessage
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function memberToStateContent(member) {
 | 
					async function memberToStateContent(pkMessage) {
 | 
				
			||||||
	const displayname = member.display_name || member.name
 | 
						const systemname = pkMessage.system.tag || ""
 | 
				
			||||||
	const avatar = member.avatar_url || member.webhook_avatar_url
 | 
						const displayname = (pkMessage.member.display_name || pkMessage.member.name) + systemname
 | 
				
			||||||
 | 
						const avatar = pkMessage.member.avatar_url || pkMessage.member.webhook_avatar_url || pkMessage.system.avatar_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const content = {
 | 
						const content = {
 | 
				
			||||||
		displayname,
 | 
							displayname,
 | 
				
			||||||
		membership: "join",
 | 
							membership: "join",
 | 
				
			||||||
		"moe.cadence.ooye.pk_member": member
 | 
							"moe.cadence.ooye.pk_member": pkMessage.member
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (avatar) content.avatar_url = await file.uploadDiscordFileToMxc(avatar)
 | 
						if (avatar) content.avatar_url = await file.uploadDiscordFileToMxc(avatar)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,12 +112,12 @@ async function memberToStateContent(member) {
 | 
				
			||||||
 * 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
 | 
					 * 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
 | 
					 * 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
 | 
					 * 4. If the state content has changed, send it to Matrix and update it in the database for next time
 | 
				
			||||||
 * @param {Ty.PkMember} member
 | 
					 * @param {Ty.PkMessage} pkMessage
 | 
				
			||||||
 * @returns {Promise<string>} mxid of the updated sim
 | 
					 * @returns {Promise<string>} mxid of the updated sim
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function syncUser(member, roomID) {
 | 
					async function syncUser(pkMessage, roomID) {
 | 
				
			||||||
	const mxid = await ensureSimJoined(member, roomID)
 | 
						const mxid = await ensureSimJoined(pkMessage, roomID)
 | 
				
			||||||
	const content = await memberToStateContent(member)
 | 
						const content = await memberToStateContent(pkMessage)
 | 
				
			||||||
	const currentHash = registerUser._hashProfileContent(content)
 | 
						const currentHash = registerUser._hashProfileContent(content)
 | 
				
			||||||
	const existingHash = select("sim_member", "hashed_profile_content", {room_id: roomID, mxid}).safeIntegers().pluck().get()
 | 
						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
 | 
						// only do the actual sync if the hash has changed since we last looked
 | 
				
			||||||
| 
						 | 
					@ -127,7 +128,7 @@ async function syncUser(member, roomID) {
 | 
				
			||||||
	return mxid
 | 
						return mxid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** @returns {Promise<{member?: Ty.PkMember}>} */
 | 
					/** @returns {Promise<Ty.PkMessage>} */
 | 
				
			||||||
function fetchMessage(messageID) {
 | 
					function fetchMessage(messageID) {
 | 
				
			||||||
	return fetch(`https://api.pluralkit.me/v2/messages/${messageID}`).then(res => res.json())
 | 
						return fetch(`https://api.pluralkit.me/v2/messages/${messageID}`).then(res => res.json())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,8 +36,13 @@ async function sendMessage(message, guild, row) {
 | 
				
			||||||
		// Handle the PluralKit public instance
 | 
							// Handle the PluralKit public instance
 | 
				
			||||||
		if (row.speedbump_id === "466378653216014359") {
 | 
							if (row.speedbump_id === "466378653216014359") {
 | 
				
			||||||
			const root = await registerPkUser.fetchMessage(message.id)
 | 
								const root = await registerPkUser.fetchMessage(message.id)
 | 
				
			||||||
			assert(root.member) // Member is null if member was deleted. We just got this message, so member surely exists.
 | 
								// Member is null if member was deleted. We just got this message, so member surely exists.
 | 
				
			||||||
			senderMxid = await registerPkUser.syncUser(root.member, roomID)
 | 
								if (!root.member) {
 | 
				
			||||||
 | 
									const e = new Error("PK API did not return a member")
 | 
				
			||||||
 | 
									e["response"] = root
 | 
				
			||||||
 | 
									throw e
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								senderMxid = await registerPkUser.syncUser(root, roomID)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										18
									
								
								types.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								types.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -34,6 +34,19 @@ export type WebhookCreds = {
 | 
				
			||||||
	token: string
 | 
						token: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type PkSystem = {
 | 
				
			||||||
 | 
						id: string
 | 
				
			||||||
 | 
						uuid: string
 | 
				
			||||||
 | 
						name: string | null
 | 
				
			||||||
 | 
						description: string | null
 | 
				
			||||||
 | 
						tag: string | null
 | 
				
			||||||
 | 
						pronouns: string | null
 | 
				
			||||||
 | 
						avatar_url: string | null
 | 
				
			||||||
 | 
						banner: string | null
 | 
				
			||||||
 | 
						color: string | null
 | 
				
			||||||
 | 
						created: string | null
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type PkMember = {
 | 
					export type PkMember = {
 | 
				
			||||||
	id: string
 | 
						id: string
 | 
				
			||||||
	uuid: string
 | 
						uuid: string
 | 
				
			||||||
| 
						 | 
					@ -54,6 +67,11 @@ export type PkMember = {
 | 
				
			||||||
	last_message_timestamp: string
 | 
						last_message_timestamp: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type PkMessage = {
 | 
				
			||||||
 | 
						system: PkSystem
 | 
				
			||||||
 | 
						member: PkMember
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export namespace Event {
 | 
					export namespace Event {
 | 
				
			||||||
	export type Outer<T> = {
 | 
						export type Outer<T> = {
 | 
				
			||||||
		type: string
 | 
							type: string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue