From a8fab062a4527d52d45a7be54da11f9c176bd6db Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 23 Aug 2023 17:08:20 +1200 Subject: [PATCH] workaround synapse bug that would make broken PLs --- d2m/actions/create-room.js | 60 ++++++++++++++++++++++++++++++------- d2m/actions/create-space.js | 31 +++++++++---------- matrix/kstate.js | 4 +-- test/data.js | 7 ++++- 4 files changed, 72 insertions(+), 30 deletions(-) diff --git a/d2m/actions/create-room.js b/d2m/actions/create-room.js index c8f0661..29bcdc0 100644 --- a/d2m/actions/create-room.js +++ b/d2m/actions/create-room.js @@ -95,6 +95,11 @@ async function channelToKState(channel, guild) { type: "m.room_membership", room_id: spaceID }] + }, + "m.room.power_levels/": { + events: { + "m.room.avatar": 0 + } } } @@ -114,24 +119,56 @@ async function createRoom(channel, guild, spaceID, kstate) { if (channel.type === DiscordTypes.ChannelType.PublicThread) threadParent = channel.parent_id const invite = threadParent ? [] : ["@cadence:cadence.moe"] // TODO - const [convertedName, convertedTopic] = convertNameAndTopic(channel, guild, null) - const roomID = await api.createRoom({ - name: convertedName, - topic: convertedTopic, - preset: "private_chat", - visibility: "private", - invite, - initial_state: ks.kstateToState(kstate) + const roomID = await postApplyPowerLevels(kstate, async kstate => { + const [convertedName, convertedTopic] = convertNameAndTopic(channel, guild, null) + const roomID = await api.createRoom({ + name: convertedName, + topic: convertedTopic, + preset: "private_chat", + visibility: "private", + invite, + initial_state: ks.kstateToState(kstate) + }) + + db.prepare("INSERT INTO channel_room (channel_id, room_id, name, nick, thread_parent) VALUES (?, ?, ?, NULL, ?)").run(channel.id, roomID, channel.name, threadParent) + + return roomID }) - db.prepare("INSERT INTO channel_room (channel_id, room_id, name, nick, thread_parent) VALUES (?, ?, ?, NULL, ?)").run(channel.id, roomID, channel.name, threadParent) - - // Put the newly created child into the space + // Put the newly created child into the space, no need to await this _syncSpaceMember(channel, spaceID, roomID) return roomID } +/** + * Handling power levels separately. The spec doesn't specify what happens, Dendrite differs, + * and Synapse does an absolutely insane *shallow merge* of what I provide on top of what it creates. + * We don't want the `events` key to be overridden completely. + * https://github.com/matrix-org/synapse/blob/develop/synapse/handlers/room.py#L1170-L1210 + * https://github.com/matrix-org/matrix-spec/issues/492 + * @param {any} kstate + * @param {(_: any) => Promise} callback must return room ID + * @returns {Promise} room ID + */ +async function postApplyPowerLevels(kstate, callback) { + const powerLevelContent = kstate["m.room.power_levels/"] + const kstateWithoutPowerLevels = {...kstate} + delete kstateWithoutPowerLevels["m.room.power_levels/"] + + /** @type {string} */ + const roomID = await callback(kstateWithoutPowerLevels) + + // Now *really* apply the power level overrides on top of what Synapse *really* set + if (powerLevelContent) { + const newRoomKState = await roomToKState(roomID) + const newRoomPowerLevelsDiff = ks.diffKState(newRoomKState, {"m.room.power_levels/": powerLevelContent}) + await applyKStateDiffToRoom(roomID, newRoomPowerLevelsDiff) + } + + return roomID +} + /** * @param {DiscordTypes.APIGuildChannel} channel */ @@ -290,5 +327,6 @@ module.exports.createAllForGuild = createAllForGuild module.exports.channelToKState = channelToKState module.exports.roomToKState = roomToKState module.exports.applyKStateDiffToRoom = applyKStateDiffToRoom +module.exports.postApplyPowerLevels = postApplyPowerLevels module.exports._convertNameAndTopic = convertNameAndTopic module.exports._unbridgeRoom = _unbridgeRoom diff --git a/d2m/actions/create-space.js b/d2m/actions/create-space.js index 34aa25f..46fa71f 100644 --- a/d2m/actions/create-space.js +++ b/d2m/actions/create-space.js @@ -21,23 +21,24 @@ const ks = sync.require("../../matrix/kstate") async function createSpace(guild, kstate) { const name = kstate["m.room.name/"].name const topic = kstate["m.room.topic/"]?.topic || undefined - assert(name) - const roomID = await api.createRoom({ - name, - preset: "private_chat", // cannot join space unless invited - visibility: "private", - power_level_content_override: { - events_default: 100, // space can only be managed by bridge - invite: 0 // any existing member can invite others - }, - invite: ["@cadence:cadence.moe"], // TODO - topic, - creation_content: { - type: "m.space" - }, - initial_state: ks.kstateToState(kstate) + const roomID = await createRoom.postApplyPowerLevels(kstate, async kstate => { + return api.createRoom({ + name, + preset: "private_chat", // cannot join space unless invited + visibility: "private", + power_level_content_override: { + events_default: 100, // space can only be managed by bridge + invite: 0 // any existing member can invite others + }, + invite: ["@cadence:cadence.moe"], // TODO + topic, + creation_content: { + type: "m.space" + }, + initial_state: ks.kstateToState(kstate) + }) }) db.prepare("INSERT INTO guild_space (guild_id, space_id) VALUES (?, ?)").run(guild.id, roomID) return roomID diff --git a/matrix/kstate.js b/matrix/kstate.js index 469ec91..e840254 100644 --- a/matrix/kstate.js +++ b/matrix/kstate.js @@ -47,10 +47,8 @@ function diffKState(actual, target) { if (key === "m.room.power_levels/") { // Special handling for power levels, we want to deep merge the actual and target into the final state. - console.log(actual[key]) + if (!(key in actual)) throw new Error(`want to apply a power levels diff, but original power level data is missing\nstarted with: ${JSON.stringify(actual)}\nwant to apply: ${JSON.stringify(target)}`) const temp = mixin({}, actual[key], target[key]) - console.log(actual[key]) - console.log(temp) try { assert.deepEqual(actual[key], temp) } catch (e) { diff --git a/test/data.js b/test/data.js index 30d108a..6ed2f42 100644 --- a/test/data.js +++ b/test/data.js @@ -27,7 +27,7 @@ module.exports = { "m.room.guest_access/": {guest_access: "can_join"}, "m.room.history_visibility/": {history_visibility: "invited"}, "m.space.parent/!jjWAGMeQdNrVZSSfvz:cadence.moe": { - via: ["cadence.moe"], // TODO: put the proper server here + via: ["cadence.moe"], canonical: true }, "m.room.join_rules/": { @@ -40,6 +40,11 @@ module.exports = { "m.room.avatar/": { discord_path: "/icons/112760669178241024/a_f83622e09ead74f0c5c527fe241f8f8c.png?size=1024", url: "mxc://cadence.moe/zKXGZhmImMHuGQZWJEFKJbsF" + }, + "m.room.power_levels/": { + events: { + "m.room.avatar": 0 + } } } },