From 92cd628a6c845dfd16d0eb4e0e8611020759b49a Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 23 Aug 2023 12:39:37 +1200 Subject: [PATCH] remember any room avatars set on matrix-side --- d2m/actions/create-room.js | 22 ++++++++++++++++------ m2d/event-dispatcher.js | 13 ++++++++++++- matrix/api.js | 2 ++ matrix/kstate.js | 1 + types.d.ts | 9 +++++++++ 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/d2m/actions/create-room.js b/d2m/actions/create-room.js index 3be2429..166d52f 100644 --- a/d2m/actions/create-room.js +++ b/d2m/actions/create-room.js @@ -61,11 +61,16 @@ async function channelToKState(channel, guild) { const spaceID = db.prepare("SELECT space_id FROM guild_space WHERE guild_id = ?").pluck().get(guild.id) assert.ok(typeof spaceID === "string") - const customName = db.prepare("SELECT nick FROM channel_room WHERE channel_id = ?").pluck().get(channel.id) + const row = db.prepare("SELECT nick, custom_avatar FROM channel_room WHERE channel_id = ?").get(channel.id) + assert(row) + const customName = row.nick + const customAvatar = row.custom_avatar const [convertedName, convertedTopic] = convertNameAndTopic(channel, guild, customName) const avatarEventContent = {} - if (guild.icon) { + if (customAvatar) { + avatarEventContent.url = customAvatar + } else if (guild.icon) { avatarEventContent.discord_path = file.guildIcon(guild) avatarEventContent.url = await file.uploadDiscordFileToMxc(avatarEventContent.discord_path) // TODO: somehow represent future values in kstate (callbacks?), while still allowing for diffing, so test cases don't need to touch the media API } @@ -183,6 +188,8 @@ async function _syncRoom(channelID, shouldActuallySync) { return creation // Naturally, the newly created room is already up to date, so we can always skip syncing here. } + const roomID = existing.room_id + if (!shouldActuallySync) { return existing.room_id // only need to ensure room exists, and it does. return the room ID } @@ -192,15 +199,16 @@ async function _syncRoom(channelID, shouldActuallySync) { const {spaceID, channelKState} = await channelToKState(channel, guild) // sync channel state to room - const roomKState = await roomToKState(existing.room_id) + const roomKState = await roomToKState(roomID) const roomDiff = ks.diffKState(roomKState, channelKState) - const roomApply = applyKStateDiffToRoom(existing.room_id, roomDiff) + const roomApply = applyKStateDiffToRoom(roomID, roomDiff) + db.prepare("UPDATE channel_room SET name = ? WHERE room_id = ?").run(channel.name, roomID) // sync room as space member - const spaceApply = _syncSpaceMember(channel, spaceID, existing.room_id) + const spaceApply = _syncSpaceMember(channel, spaceID, roomID) await Promise.all([roomApply, spaceApply]) - return existing.room_id + return roomID } async function _unbridgeRoom(channelID) { @@ -279,5 +287,7 @@ module.exports.ensureRoom = ensureRoom module.exports.syncRoom = syncRoom module.exports.createAllForGuild = createAllForGuild module.exports.channelToKState = channelToKState +module.exports.roomToKState = roomToKState +module.exports.applyKStateDiffToRoom = applyKStateDiffToRoom module.exports._convertNameAndTopic = convertNameAndTopic module.exports._unbridgeRoom = _unbridgeRoom diff --git a/m2d/event-dispatcher.js b/m2d/event-dispatcher.js index 44eba85..c62d805 100644 --- a/m2d/event-dispatcher.js +++ b/m2d/event-dispatcher.js @@ -6,7 +6,7 @@ const util = require("util") const Ty = require("../types") -const {sync, as} = require("../passthrough") +const {db, sync, as} = require("../passthrough") /** @type {import("./actions/send-event")} */ const sendEvent = sync.require("./actions/send-event") @@ -69,3 +69,14 @@ async event => { if (utils.eventSenderIsFromDiscord(event.sender)) return await addReaction.addReaction(event) })) + +sync.addTemporaryListener(as, "type:m.room.avatar", guard("m.room.avatar", +/** + * @param {Ty.Event.StateOuter} event + */ +async event => { + if (event.state_key !== "") return + if (utils.eventSenderIsFromDiscord(event.sender)) return + const url = event.content.url || null + db.prepare("UPDATE channel_room SET custom_avatar = ? WHERE room_id = ?").run(url, event.room_id) +})) diff --git a/matrix/api.js b/matrix/api.js index b382631..9eff6c7 100644 --- a/matrix/api.js +++ b/matrix/api.js @@ -167,6 +167,8 @@ async function profileSetAvatarUrl(mxid, avatar_url) { * @param {number} power */ async function setUserPower(roomID, mxid, power) { + assert(roomID[0] === "!") + assert(mxid[0] === "@") // Yes it's this hard https://github.com/matrix-org/matrix-appservice-bridge/blob/2334b0bae28a285a767fe7244dad59f5a5963037/src/components/intent.ts#L352 const powerLevels = await getStateEvent(roomID, "m.room.power_levels", "") const users = powerLevels.users || {} diff --git a/matrix/kstate.js b/matrix/kstate.js index 398b1b6..1b2ca14 100644 --- a/matrix/kstate.js +++ b/matrix/kstate.js @@ -42,6 +42,7 @@ function diffKState(actual, target) { const diff = {} // go through each key that it should have for (const key of Object.keys(target)) { + if (!key.includes("/")) throw new Error(`target kstate's key "${key}" does not contain a slash separator; if a blank state_key was intended, add a trailing slash to the kstate key.`) if (key in actual) { // diff try { diff --git a/types.d.ts b/types.d.ts index b9f7ed6..faf4c70 100644 --- a/types.d.ts +++ b/types.d.ts @@ -38,6 +38,10 @@ namespace Event { event_id: string } + export type StateOuter = Outer & { + state_key: string + } + export type ReplacementContent = T & { "m.new_content": T "m.relates_to": { @@ -74,6 +78,11 @@ namespace Event { avatar_url?: string } + export type M_Room_Avatar = { + discord_path?: string + url?: string + } + export type M_Reaction = { "m.relates_to": { rel_type: "m.annotation"