From a71c9515ec17d6cbc873b2249f3e12c9a0c4138b Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Mon, 22 Jan 2024 22:30:31 +1300 Subject: [PATCH] Include system data on PK profiles --- d2m/actions/edit-message.js | 2 +- d2m/actions/register-pk-user.js | 45 +++++++++++++++++---------------- d2m/actions/send-message.js | 9 +++++-- types.d.ts | 18 +++++++++++++ 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/d2m/actions/edit-message.js b/d2m/actions/edit-message.js index d8c5f97c..d52fcbde 100644 --- a/d2m/actions/edit-message.js +++ b/d2m/actions/edit-message.js @@ -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) } } diff --git a/d2m/actions/register-pk-user.js b/d2m/actions/register-pk-user.js index f0fd492b..da94f012 100644 --- a/d2m/actions/register-pk-user.js +++ b/d2m/actions/register-pk-user.js @@ -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} 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} 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} 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} */ function fetchMessage(messageID) { return fetch(`https://api.pluralkit.me/v2/messages/${messageID}`).then(res => res.json()) } diff --git a/d2m/actions/send-message.js b/d2m/actions/send-message.js index 8d02c430..72011a10 100644 --- a/d2m/actions/send-message.js +++ b/d2m/actions/send-message.js @@ -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) } } diff --git a/types.d.ts b/types.d.ts index daf62ad9..a01241c7 100644 --- a/types.d.ts +++ b/types.d.ts @@ -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 = { type: string