From 20cd7ab38e8177edbf742484f1e8db0d6ddd268a Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Fri, 25 Aug 2023 17:23:51 +1200 Subject: [PATCH] sync child room avatars when guild is updated --- d2m/actions/create-room.js | 27 ++++++++++++++++----------- d2m/actions/create-space.js | 24 ++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/d2m/actions/create-room.js b/d2m/actions/create-room.js index 6fbc42e..2095130 100644 --- a/d2m/actions/create-room.js +++ b/d2m/actions/create-room.js @@ -17,6 +17,7 @@ const ks = sync.require("../../matrix/kstate") const inflightRoomCreate = new Map() /** + * Async because it gets all room state from the homeserver. * @param {string} roomID */ async function roomToKState(roomID) { @@ -60,6 +61,7 @@ function convertNameAndTopic(channel, guild, customName) { } /** + * Async because it may upload the guild icon to mxc. * @param {DiscordTypes.APIGuildTextChannel | DiscordTypes.APIThreadChannel} channel * @param {DiscordTypes.APIGuild} guild */ @@ -200,10 +202,10 @@ function channelToGuild(channel) { 3. Get kstate for channel 4. Create room, return new ID - New combined flow with ensure / sync: + Ensure + sync flow: 1. Get IDs 2. Does room exist? - 2.5: If room does exist AND don't need to sync: return here + 2.5: If room does exist AND wasn't asked to sync: return here 3. Get kstate for channel 4. Create room with kstate if room doesn't exist 5. Get and update room state with kstate if room does exist @@ -246,7 +248,7 @@ async function _syncRoom(channelID, shouldActuallySync) { console.log(`[room sync] to matrix: ${channel.name}`) - const {spaceID, channelKState} = await channelToKState(channel, guild) + const {spaceID, channelKState} = await channelToKState(channel, guild) // calling this in both branches because we don't want to calculate this if not syncing // sync channel state to room const roomKState = await roomToKState(roomID) @@ -261,6 +263,16 @@ async function _syncRoom(channelID, shouldActuallySync) { return roomID } +/** Ensures the room exists. If it doesn't, creates the room with an accurate initial state. */ +function ensureRoom(channelID) { + return _syncRoom(channelID, false) +} + +/** Actually syncs. Gets all room state from the homeserver in order to diff, and uploads the icon to mxc if it has changed. */ +function syncRoom(channelID) { + return _syncRoom(channelID, true) +} + async function _unbridgeRoom(channelID) { /** @ts-ignore @type {DiscordTypes.APIGuildChannel} */ const channel = discord.channels.get(channelID) @@ -289,6 +301,7 @@ async function _unbridgeRoom(channelID) { /** + * Async because it gets all space state from the homeserver, then if necessary sends one state event back. * @param {DiscordTypes.APIGuildTextChannel} channel * @param {string} spaceID * @param {string} roomID @@ -311,14 +324,6 @@ async function _syncSpaceMember(channel, spaceID, roomID) { return applyKStateDiffToRoom(spaceID, spaceDiff) } -function ensureRoom(channelID) { - return _syncRoom(channelID, false) -} - -function syncRoom(channelID) { - return _syncRoom(channelID, true) -} - async function createAllForGuild(guildID) { const channelIDs = discord.guildChannelMap.get(guildID) assert.ok(channelIDs) diff --git a/d2m/actions/create-space.js b/d2m/actions/create-space.js index 46fa71f..838bef9 100644 --- a/d2m/actions/create-space.js +++ b/d2m/actions/create-space.js @@ -1,6 +1,6 @@ // @ts-check -const assert = require("assert") +const assert = require("assert").strict const DiscordTypes = require("discord-api-types/v10") const passthrough = require("../../passthrough") @@ -84,11 +84,31 @@ async function syncSpace(guildID) { console.log(`[space sync] to matrix: ${guild.name}`) - // sync channel state to room + // sync guild state to space const spaceKState = await createRoom.roomToKState(spaceID) const spaceDiff = ks.diffKState(spaceKState, guildKState) await createRoom.applyKStateDiffToRoom(spaceID, spaceDiff) + // guild icon was changed, so room avatars need to be updated as well as the space ones + // doing it this way rather than calling syncRoom for great efficiency gains + const newAvatarState = spaceDiff["m.room.avatar/"] + if (guild.icon && newAvatarState?.url) { + // don't try to update rooms with custom avatars though + const roomsWithCustomAvatars = db.prepare("SELECT room_id FROM channel_room WHERE custom_avatar IS NOT NULL").pluck().all() + + const childRooms = ks.kstateToState(spaceKState).filter(({type, state_key, content}) => { + return type === "m.space.child" && "via" in content && roomsWithCustomAvatars.includes(state_key) + }).map(({state_key}) => state_key) + + for (const roomID of childRooms) { + const avatarEventContent = await api.getStateEvent(roomID, "m.room.avatar", "") + if (avatarEventContent.url !== newAvatarState.url) { + await api.sendState(roomID, "m.room.avatar", "", newAvatarState) + } + } + } + + return spaceID }