Add unlink space feature

Squashed commit of the following:

commit bd9fd5cd3cf3f1301df18074c997ec537a81b4f5
Author: Elliu <elliu@hashi.re>
Date:   Sat Nov 15 15:32:18 2025 +0900

    Revert "fix matrix / db resource cleanup on space unlink"

    This reverts commit ccc10564f1e33ab277bc15f360b8c65f2d0ea867.

commit eec559293861305394770343d501389905fe1650
Author: Cadence Ember <cadence@disroot.org>
Date:   Sat Nov 8 13:01:59 2025 +1300

    Dependency inject snow for testing

commit b45eeb150e0702c201b8f710a3bdaa8e9f7d90be
Author: Elliu <elliu@hashi.re>
Date:   Wed Nov 5 00:20:20 2025 +0900

    manually revert 3597a3b: "Factorize some of the space link/unlink sanity checks"

commit 0f2e575df21bf940e4780c30d2701da989f62471
Author: Elliu <elliu@hashi.re>
Date:   Wed Nov 5 00:04:38 2025 +0900

    on unbriding room, also demote powel level of bridge user in matrix room

commit ccc10564f1e33ab277bc15f360b8c65f2d0ea867
Author: Elliu <elliu@hashi.re>
Date:   Wed Nov 5 00:04:13 2025 +0900

    fix matrix / db resource cleanup on space unlink

commit f4c1ea7c7f7d5a265b84ce464cd8e9e26d934a32
Author: Elliu <elliu@hashi.re>
Date:   Tue Nov 4 23:54:41 2025 +0900

    /unlink-space: properly leave guild and clean DB

commit 5f0ec3b2c861cc8b9edc51389d6176c7a22a1135
Author: Cadence Ember <cadence@disroot.org>
Date:   Sun Nov 2 22:31:14 2025 +1300

    Improve HTML to a state I'm happy with

commit 16309f26b3dd72927e05454cee8c63504b447b7f
Author: Elliu <elliu@hashi.re>
Date:   Sat Nov 1 22:24:51 2025 +0900

    add tests from /unlink-space endpoint

commit 5aff6f9048330a86eda3b2d1862f42df8d2bad84
Author: Elliu <elliu@hashi.re>
Date:   Sat Sep 6 20:05:18 2025 +0900

    Add /api/unlink-space implementation

commit dfc61594f68db4b52b3553ac7d3561ae9ce13b49
Author: Elliu <elliu@hashi.re>
Date:   Sat Sep 6 19:59:44 2025 +0900

    Extract /api/unlink code to its own function

commit 3597a3b5ce9dde3a9ddfe0853253bfda91a38335
Author: Elliu <elliu@hashi.re>
Date:   Sat Sep 6 19:28:42 2025 +0900

    Factorize some of the space link/unlink sanity checks

commit 05d788e26394106d9be24cef8b38f6c6f1e4c984
Author: Elliu <elliu@hashi.re>
Date:   Sat Sep 6 18:23:01 2025 +0900

    Add button to unlink a space

Co-authored-by: Cadence Ember <cadence@disroot.org>
This commit is contained in:
Elliu
2026-02-13 19:12:01 +13:00
committed by Cadence Ember
parent 1defd83fde
commit 35e9c9e1ea
9 changed files with 322 additions and 55 deletions

View File

@@ -439,19 +439,11 @@ function syncRoom(channelID) {
return _syncRoom(channelID, true)
}
async function unbridgeChannel(channelID) {
/** @ts-ignore @type {DiscordTypes.APIGuildChannel} */
const channel = discord.channels.get(channelID)
assert.ok(channel)
assert.ok(channel.guild_id)
return unbridgeDeletedChannel(channel, channel.guild_id)
}
/**
* @param {{id: string, topic?: string?}} channel channel-ish (just needs an id, topic is optional)
* @param {string} guildID
*/
async function unbridgeDeletedChannel(channel, guildID) {
async function unbridgeChannel(channel, guildID) {
const roomID = select("channel_room", "room_id", {channel_id: channel.id}).pluck().get()
assert.ok(roomID)
const row = from("guild_space").join("guild_active", "guild_id").select("space_id", "autocreate").where({guild_id: guildID}).get()
@@ -488,14 +480,13 @@ async function unbridgeDeletedChannel(channel, guildID) {
if (!botInRoom) return
// demote admins in room
/** @type {Ty.Event.M_Power_Levels} */
const powerLevelContent = await api.getStateEvent(roomID, "m.room.power_levels", "")
powerLevelContent.users ??= {}
for (const mxid of Object.keys(powerLevelContent.users)) {
if (powerLevelContent.users[mxid] >= 100 && mUtils.eventSenderIsFromDiscord(mxid) && mxid !== mUtils.bot) {
delete powerLevelContent.users[mxid]
await api.sendState(roomID, "m.room.power_levels", "", powerLevelContent, mxid)
// demote discord sim admins in room
const {powerLevels, allCreators} = await mUtils.getEffectivePower(roomID, [], api)
const powerLevelsUsers = (powerLevels.users ||= {})
for (const mxid of Object.keys(powerLevelsUsers)) {
if (powerLevelsUsers[mxid] >= (powerLevels.state_default ?? 50) && !allCreators.includes(mxid) && mUtils.eventSenderIsFromDiscord(mxid) && mxid !== mUtils.bot) {
delete powerLevelsUsers[mxid]
await api.sendState(roomID, "m.room.power_levels", "", powerLevels, mxid) // done individually because each user must demote themselves
}
}
@@ -526,6 +517,7 @@ async function unbridgeDeletedChannel(channel, guildID) {
}
// leave room
await mUtils.setUserPower(roomID, mUtils.bot, 0, api)
await api.leaveRoom(roomID)
}
@@ -589,6 +581,5 @@ module.exports.postApplyPowerLevels = postApplyPowerLevels
module.exports._convertNameAndTopic = convertNameAndTopic
module.exports._syncSpaceMember = _syncSpaceMember
module.exports.unbridgeChannel = unbridgeChannel
module.exports.unbridgeDeletedChannel = unbridgeDeletedChannel
module.exports.existsOrAutocreatable = existsOrAutocreatable
module.exports.assertExistsOrAutocreatable = assertExistsOrAutocreatable

View File

@@ -203,7 +203,7 @@ async function syncSpaceFully(guildID) {
if (discord.channels.has(channelID)) {
await createRoom.syncRoom(channelID)
} else {
await createRoom.unbridgeDeletedChannel({id: channelID}, guildID)
await createRoom.unbridgeChannel({id: channelID}, guildID)
}
}

View File

@@ -250,7 +250,7 @@ module.exports = {
const roomID = select("channel_room", "room_id", {channel_id: channel.id}).pluck().get()
if (!roomID) return // channel wasn't being bridged in the first place
// @ts-ignore
await createRoom.unbridgeDeletedChannel(channel, guildID)
await createRoom.unbridgeChannel(channel, guildID)
},
/**