Include system data on PK profiles

This commit is contained in:
Cadence Ember 2024-01-22 22:30:31 +13:00
parent 6a06dc14ce
commit a71c9515ec
4 changed files with 49 additions and 25 deletions

View file

@ -24,7 +24,7 @@ async function editMessage(message, guild, row) {
if (row.speedbump_id === "466378653216014359") {
const root = await registerPkUser.fetchMessage(message.id)
assert(root.member)
senderMxid = await registerPkUser.ensureSimJoined(root.member, roomID)
senderMxid = await registerPkUser.ensureSimJoined(root, roomID)
}
}

View file

@ -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.
* @param {Ty.PkMember} member
* @param {Ty.PkMessage} pkMessage
* @returns mxid
*/
async function createSim(member) {
async function createSim(pkMessage) {
// Choose sim name
const simName = "_pk_" + member.id
const simName = "_pk_" + pkMessage.member.id
const localpart = reg.ooye.namespace_prefix + simName
const mxid = `@${localpart}:${reg.ooye.server_name}`
// 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
try {
@ -34,7 +34,7 @@ async function createSim(member) {
} catch (e) {
// 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.)
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
}
return mxid
@ -43,32 +43,32 @@ async function createSim(member) {
/**
* 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.
* @param {Ty.PkMember} member
* @param {Ty.PkMessage} pkMessage
* @returns {Promise<string>} mxid
*/
async function ensureSim(member) {
async function ensureSim(pkMessage) {
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) {
mxid = existing
} else {
mxid = await createSim(member)
mxid = await createSim(pkMessage)
}
return mxid
}
/**
* 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
* @returns {Promise<string>} mxid
*/
async function ensureSimJoined(member, roomID) {
async function ensureSimJoined(pkMessage, roomID) {
// Ensure room ID is really an ID, not an alias
assert.ok(roomID[0] === "!")
// Ensure user
const mxid = await ensureSim(member)
const mxid = await ensureSim(pkMessage)
// Ensure joined
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) {
const displayname = member.display_name || member.name
const avatar = member.avatar_url || member.webhook_avatar_url
async function memberToStateContent(pkMessage) {
const systemname = pkMessage.system.tag || ""
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 = {
displayname,
membership: "join",
"moe.cadence.ooye.pk_member": member
"moe.cadence.ooye.pk_member": pkMessage.member
}
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
* 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 {Ty.PkMember} member
* @param {Ty.PkMessage} pkMessage
* @returns {Promise<string>} mxid of the updated sim
*/
async function syncUser(member, roomID) {
const mxid = await ensureSimJoined(member, roomID)
const content = await memberToStateContent(member)
async function syncUser(pkMessage, roomID) {
const mxid = await ensureSimJoined(pkMessage, roomID)
const content = await memberToStateContent(pkMessage)
const currentHash = registerUser._hashProfileContent(content)
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
@ -127,7 +128,7 @@ async function syncUser(member, roomID) {
return mxid
}
/** @returns {Promise<{member?: Ty.PkMember}>} */
/** @returns {Promise<Ty.PkMessage>} */
function fetchMessage(messageID) {
return fetch(`https://api.pluralkit.me/v2/messages/${messageID}`).then(res => res.json())
}

View file

@ -36,8 +36,13 @@ async function sendMessage(message, guild, row) {
// Handle the PluralKit public instance
if (row.speedbump_id === "466378653216014359") {
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.
senderMxid = await registerPkUser.syncUser(root.member, roomID)
// Member is null if member was deleted. We just got this message, so member surely exists.
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
View file

@ -34,6 +34,19 @@ export type WebhookCreds = {
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 = {
id: string
uuid: string
@ -54,6 +67,11 @@ export type PkMember = {
last_message_timestamp: string
}
export type PkMessage = {
system: PkSystem
member: PkMember
}
export namespace Event {
export type Outer<T> = {
type: string