workaround synapse bug that would make broken PLs
This commit is contained in:
		
							parent
							
								
									8d536d5ef2
								
							
						
					
					
						commit
						a8fab062a4
					
				
					 4 changed files with 72 additions and 30 deletions
				
			
		| 
						 | 
					@ -95,6 +95,11 @@ async function channelToKState(channel, guild) {
 | 
				
			||||||
				type: "m.room_membership",
 | 
									type: "m.room_membership",
 | 
				
			||||||
				room_id: spaceID
 | 
									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
 | 
						if (channel.type === DiscordTypes.ChannelType.PublicThread) threadParent = channel.parent_id
 | 
				
			||||||
	const invite = threadParent ? [] : ["@cadence:cadence.moe"] // TODO
 | 
						const invite = threadParent ? [] : ["@cadence:cadence.moe"] // TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const [convertedName, convertedTopic] = convertNameAndTopic(channel, guild, null)
 | 
						const roomID = await postApplyPowerLevels(kstate, async kstate => {
 | 
				
			||||||
	const roomID = await api.createRoom({
 | 
							const [convertedName, convertedTopic] = convertNameAndTopic(channel, guild, null)
 | 
				
			||||||
		name: convertedName,
 | 
							const roomID = await api.createRoom({
 | 
				
			||||||
		topic: convertedTopic,
 | 
								name: convertedName,
 | 
				
			||||||
		preset: "private_chat",
 | 
								topic: convertedTopic,
 | 
				
			||||||
		visibility: "private",
 | 
								preset: "private_chat",
 | 
				
			||||||
		invite,
 | 
								visibility: "private",
 | 
				
			||||||
		initial_state: ks.kstateToState(kstate)
 | 
								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, no need to await this
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Put the newly created child into the space
 | 
					 | 
				
			||||||
	_syncSpaceMember(channel, spaceID, roomID)
 | 
						_syncSpaceMember(channel, spaceID, roomID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 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<string>} callback must return room ID
 | 
				
			||||||
 | 
					 * @returns {Promise<string>} 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
 | 
					 * @param {DiscordTypes.APIGuildChannel} channel
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -290,5 +327,6 @@ module.exports.createAllForGuild = createAllForGuild
 | 
				
			||||||
module.exports.channelToKState = channelToKState
 | 
					module.exports.channelToKState = channelToKState
 | 
				
			||||||
module.exports.roomToKState = roomToKState
 | 
					module.exports.roomToKState = roomToKState
 | 
				
			||||||
module.exports.applyKStateDiffToRoom = applyKStateDiffToRoom
 | 
					module.exports.applyKStateDiffToRoom = applyKStateDiffToRoom
 | 
				
			||||||
 | 
					module.exports.postApplyPowerLevels = postApplyPowerLevels
 | 
				
			||||||
module.exports._convertNameAndTopic = convertNameAndTopic
 | 
					module.exports._convertNameAndTopic = convertNameAndTopic
 | 
				
			||||||
module.exports._unbridgeRoom = _unbridgeRoom
 | 
					module.exports._unbridgeRoom = _unbridgeRoom
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,23 +21,24 @@ const ks = sync.require("../../matrix/kstate")
 | 
				
			||||||
async function createSpace(guild, kstate) {
 | 
					async function createSpace(guild, kstate) {
 | 
				
			||||||
	const name = kstate["m.room.name/"].name
 | 
						const name = kstate["m.room.name/"].name
 | 
				
			||||||
	const topic = kstate["m.room.topic/"]?.topic || undefined
 | 
						const topic = kstate["m.room.topic/"]?.topic || undefined
 | 
				
			||||||
 | 
					 | 
				
			||||||
	assert(name)
 | 
						assert(name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const roomID = await api.createRoom({
 | 
						const roomID = await createRoom.postApplyPowerLevels(kstate, async kstate => {
 | 
				
			||||||
		name,
 | 
							return api.createRoom({
 | 
				
			||||||
		preset: "private_chat", // cannot join space unless invited
 | 
								name,
 | 
				
			||||||
		visibility: "private",
 | 
								preset: "private_chat", // cannot join space unless invited
 | 
				
			||||||
		power_level_content_override: {
 | 
								visibility: "private",
 | 
				
			||||||
			events_default: 100, // space can only be managed by bridge
 | 
								power_level_content_override: {
 | 
				
			||||||
			invite: 0 // any existing member can invite others
 | 
									events_default: 100, // space can only be managed by bridge
 | 
				
			||||||
		},
 | 
									invite: 0 // any existing member can invite others
 | 
				
			||||||
		invite: ["@cadence:cadence.moe"], // TODO
 | 
								},
 | 
				
			||||||
		topic,
 | 
								invite: ["@cadence:cadence.moe"], // TODO
 | 
				
			||||||
		creation_content: {
 | 
								topic,
 | 
				
			||||||
			type: "m.space"
 | 
								creation_content: {
 | 
				
			||||||
		},
 | 
									type: "m.space"
 | 
				
			||||||
		initial_state: ks.kstateToState(kstate)
 | 
								},
 | 
				
			||||||
 | 
								initial_state: ks.kstateToState(kstate)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	db.prepare("INSERT INTO guild_space (guild_id, space_id) VALUES (?, ?)").run(guild.id, roomID)
 | 
						db.prepare("INSERT INTO guild_space (guild_id, space_id) VALUES (?, ?)").run(guild.id, roomID)
 | 
				
			||||||
	return roomID
 | 
						return roomID
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,10 +47,8 @@ function diffKState(actual, target) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (key === "m.room.power_levels/") {
 | 
							if (key === "m.room.power_levels/") {
 | 
				
			||||||
			// Special handling for power levels, we want to deep merge the actual and target into the final state.
 | 
								// 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])
 | 
								const temp = mixin({}, actual[key], target[key])
 | 
				
			||||||
			console.log(actual[key])
 | 
					 | 
				
			||||||
			console.log(temp)
 | 
					 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				assert.deepEqual(actual[key], temp)
 | 
									assert.deepEqual(actual[key], temp)
 | 
				
			||||||
			} catch (e) {
 | 
								} catch (e) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ module.exports = {
 | 
				
			||||||
			"m.room.guest_access/": {guest_access: "can_join"},
 | 
								"m.room.guest_access/": {guest_access: "can_join"},
 | 
				
			||||||
			"m.room.history_visibility/": {history_visibility: "invited"},
 | 
								"m.room.history_visibility/": {history_visibility: "invited"},
 | 
				
			||||||
			"m.space.parent/!jjWAGMeQdNrVZSSfvz:cadence.moe": {
 | 
								"m.space.parent/!jjWAGMeQdNrVZSSfvz:cadence.moe": {
 | 
				
			||||||
				via: ["cadence.moe"], // TODO: put the proper server here
 | 
									via: ["cadence.moe"],
 | 
				
			||||||
				canonical: true
 | 
									canonical: true
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			"m.room.join_rules/": {
 | 
								"m.room.join_rules/": {
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,11 @@ module.exports = {
 | 
				
			||||||
			"m.room.avatar/": {
 | 
								"m.room.avatar/": {
 | 
				
			||||||
				discord_path: "/icons/112760669178241024/a_f83622e09ead74f0c5c527fe241f8f8c.png?size=1024",
 | 
									discord_path: "/icons/112760669178241024/a_f83622e09ead74f0c5c527fe241f8f8c.png?size=1024",
 | 
				
			||||||
				url: "mxc://cadence.moe/zKXGZhmImMHuGQZWJEFKJbsF"
 | 
									url: "mxc://cadence.moe/zKXGZhmImMHuGQZWJEFKJbsF"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"m.room.power_levels/": {
 | 
				
			||||||
 | 
									events: {
 | 
				
			||||||
 | 
										"m.room.avatar": 0
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue