mirror of
https://gitdab.com/cadence/out-of-your-element.git
synced 2025-09-10 12:22:50 +02:00
Fully support unlinking channels
This commit is contained in:
parent
5c0e830658
commit
b1b9124052
4 changed files with 84 additions and 26 deletions
|
@ -6,7 +6,7 @@ const Ty = require("../../types")
|
|||
const {reg} = require("../../matrix/read-registration")
|
||||
|
||||
const passthrough = require("../../passthrough")
|
||||
const {discord, sync, db, select} = passthrough
|
||||
const {discord, sync, db, select, from} = passthrough
|
||||
/** @type {import("../../matrix/file")} */
|
||||
const file = sync.require("../../matrix/file")
|
||||
/** @type {import("../../matrix/api")} */
|
||||
|
@ -14,7 +14,9 @@ const api = sync.require("../../matrix/api")
|
|||
/** @type {import("../../matrix/kstate")} */
|
||||
const ks = sync.require("../../matrix/kstate")
|
||||
/** @type {import("../../discord/utils")} */
|
||||
const utils = sync.require("../../discord/utils")
|
||||
const dUtils = sync.require("../../discord/utils")
|
||||
/** @type {import("../../m2d/converters/utils")} */
|
||||
const mUtils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("./create-space")} */
|
||||
const createSpace = sync.require("./create-space")
|
||||
|
||||
|
@ -114,8 +116,8 @@ async function channelToKState(channel, guild, di) {
|
|||
join_rules = {join_rule: PRIVACY_ENUMS.ROOM_JOIN_RULES[privacyLevel]}
|
||||
}
|
||||
|
||||
const everyonePermissions = utils.getPermissions([], guild.roles, undefined, channel.permission_overwrites)
|
||||
const everyoneCanMentionEveryone = utils.hasAllPermissions(everyonePermissions, ["MentionEveryone"])
|
||||
const everyonePermissions = dUtils.getPermissions([], guild.roles, undefined, channel.permission_overwrites)
|
||||
const everyoneCanMentionEveryone = dUtils.hasAllPermissions(everyonePermissions, ["MentionEveryone"])
|
||||
|
||||
const globalAdmins = select("member_power", ["mxid", "power_level"], {room_id: "*"}).all()
|
||||
const globalAdminPower = globalAdmins.reduce((a, c) => (a[c.mxid] = c.power_level, a), {})
|
||||
|
@ -392,7 +394,7 @@ function syncRoom(channelID) {
|
|||
return _syncRoom(channelID, true)
|
||||
}
|
||||
|
||||
async function _unbridgeRoom(channelID) {
|
||||
async function unbridgeChannel(channelID) {
|
||||
/** @ts-ignore @type {DiscordTypes.APIGuildChannel} */
|
||||
const channel = discord.channels.get(channelID)
|
||||
assert.ok(channel)
|
||||
|
@ -407,12 +409,8 @@ async function _unbridgeRoom(channelID) {
|
|||
async function unbridgeDeletedChannel(channel, guildID) {
|
||||
const roomID = select("channel_room", "room_id", {channel_id: channel.id}).pluck().get()
|
||||
assert.ok(roomID)
|
||||
const spaceID = select("guild_space", "space_id", {guild_id: guildID}).pluck().get()
|
||||
assert.ok(spaceID)
|
||||
|
||||
// remove room from being a space member
|
||||
await api.sendState(roomID, "m.space.parent", spaceID, {})
|
||||
await api.sendState(spaceID, "m.space.child", roomID, {})
|
||||
const row = from("guild_space").join("guild_active", "guild_id").select("space_id", "autocreate").get()
|
||||
assert.ok(row)
|
||||
|
||||
// remove declaration that the room is bridged
|
||||
await api.sendState(roomID, "uk.half-shot.bridge", `moe.cadence.ooye://discord/${guildID}/${channel.id}`, {})
|
||||
|
@ -421,15 +419,6 @@ async function unbridgeDeletedChannel(channel, guildID) {
|
|||
await api.sendState(roomID, "m.room.topic", "", {topic: channel.topic || ""})
|
||||
}
|
||||
|
||||
// send a notification in the room
|
||||
await api.sendEvent(roomID, "m.room.message", {
|
||||
msgtype: "m.notice",
|
||||
body: "⚠️ This room was removed from the bridge."
|
||||
})
|
||||
|
||||
// leave room
|
||||
await api.leaveRoom(roomID)
|
||||
|
||||
// delete webhook on discord
|
||||
const webhook = select("webhook", ["webhook_id", "webhook_token"], {channel_id: channel.id}).get()
|
||||
if (webhook) {
|
||||
|
@ -439,7 +428,48 @@ async function unbridgeDeletedChannel(channel, guildID) {
|
|||
|
||||
// delete room from database
|
||||
db.prepare("DELETE FROM member_cache WHERE room_id = ?").run(roomID)
|
||||
db.prepare("DELETE FROM channel_room WHERE room_id = ? AND channel_id = ?").run(roomID, channel.id)
|
||||
db.prepare("DELETE FROM channel_room WHERE room_id = ? AND channel_id = ?").run(roomID, channel.id) // cascades to most other tables, like messages
|
||||
|
||||
// demote admins in room
|
||||
/** @type {Ty.Event.M_Power_Levels} */
|
||||
const powerLevelContent = await api.getStateEvent(roomID, "m.room.power_levels", "")
|
||||
powerLevelContent.users ??= {}
|
||||
const bot = `@${reg.sender_localpart}:${reg.ooye.server_name}`
|
||||
for (const mxid of Object.keys(powerLevelContent.users)) {
|
||||
if (mUtils.eventSenderIsFromDiscord(mxid) && mxid !== bot) {
|
||||
delete powerLevelContent.users[mxid]
|
||||
await api.sendState(roomID, "m.room.power_levels", "", powerLevelContent, mxid)
|
||||
}
|
||||
}
|
||||
|
||||
// send a notification in the room
|
||||
await api.sendEvent(roomID, "m.room.message", {
|
||||
msgtype: "m.notice",
|
||||
body: "⚠️ This room was removed from the bridge."
|
||||
})
|
||||
|
||||
// if it is an easy mode room, clean up the room from the managed space and make it clear it's not being bridged
|
||||
// (don't do this for self-service rooms, because they might continue to be used on Matrix or linked somewhere else later)
|
||||
if (row.autocreate === 1) {
|
||||
// remove room from being a space member
|
||||
await api.sendState(roomID, "m.space.parent", row.space_id, {})
|
||||
await api.sendState(row.space_id, "m.space.child", roomID, {})
|
||||
|
||||
// leave room
|
||||
await api.leaveRoom(roomID)
|
||||
}
|
||||
|
||||
// if it is a self-service room, remove sim members
|
||||
// (the room can be used with less clutter and the member list makes sense if it's bridged somewhere else)
|
||||
if (row.autocreate === 0) {
|
||||
// remove sim members
|
||||
const members = select("sim_member", "mxid", {room_id: roomID}).pluck().all()
|
||||
const preparedDelete = db.prepare("DELETE FROM sim_member WHERE room_id = ? AND mxid = ?")
|
||||
for (const mxid of members) {
|
||||
await api.leaveRoom(roomID, mxid)
|
||||
preparedDelete.run(roomID, mxid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -488,7 +518,7 @@ module.exports.createAllForGuild = createAllForGuild
|
|||
module.exports.channelToKState = channelToKState
|
||||
module.exports.postApplyPowerLevels = postApplyPowerLevels
|
||||
module.exports._convertNameAndTopic = convertNameAndTopic
|
||||
module.exports._unbridgeRoom = _unbridgeRoom
|
||||
module.exports.unbridgeChannel = unbridgeChannel
|
||||
module.exports.unbridgeDeletedChannel = unbridgeDeletedChannel
|
||||
module.exports.existsOrAutocreatable = existsOrAutocreatable
|
||||
module.exports.assertExistsOrAutocreatable = assertExistsOrAutocreatable
|
||||
|
|
|
@ -78,12 +78,13 @@ block body
|
|||
h3.mt32.fs-category Linked channels
|
||||
|
||||
.s-card.bs-sm.p0
|
||||
.s-table-container
|
||||
form.s-table-container(method="post" action="/api/unlink" hx-post="/api/unlink" hx-trigger="submit" hx-disabled-elt="this" hx-confirm="Unlink these channels?\nIt may take a moment to clean up Matrix resources.")
|
||||
input(type="hidden" name="guild_id" value=guild_id)
|
||||
table.s-table.s-table__bx-simple
|
||||
each row in linkedChannelsWithDetails
|
||||
tr
|
||||
td.w40: +discord(row.channel)
|
||||
td.p2: button.s-btn.s-btn__muted.s-btn__xs!= icons.Icons.IconLinkSm
|
||||
td.p2: button.s-btn.s-btn__muted.s-btn__xs(name="channel_id" value=row.channel.id)!= icons.Icons.IconLinkSm
|
||||
td: +matrix(row)
|
||||
else
|
||||
tr
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @ts-check
|
||||
|
||||
const {z} = require("zod")
|
||||
const {defineEventHandler, useSession, createError, readValidatedBody} = require("h3")
|
||||
const {defineEventHandler, useSession, createError, readValidatedBody, setResponseHeader} = require("h3")
|
||||
const Ty = require("../../types")
|
||||
|
||||
const {discord, db, as, sync, select, from} = require("../../passthrough")
|
||||
|
@ -19,6 +19,10 @@ const schema = {
|
|||
guild_id: z.string(),
|
||||
matrix: z.string(),
|
||||
discord: z.string()
|
||||
}),
|
||||
unlink: z.object({
|
||||
guild_id: z.string(),
|
||||
channel_id: z.string()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -61,3 +65,26 @@ as.router.post("/api/link", defineEventHandler(async event => {
|
|||
|
||||
return null // 204
|
||||
}))
|
||||
|
||||
as.router.post("/api/unlink", defineEventHandler(async event => {
|
||||
const {channel_id, guild_id} = await readValidatedBody(event, schema.unlink.parse)
|
||||
const session = await useSession(event, {password: reg.as_token})
|
||||
|
||||
// Check guild ID or nonce
|
||||
if (!(session.data.managedGuilds || []).includes(guild_id)) throw createError({status: 403, message: "Forbidden", data: "Can't edit a guild you don't have Manage Server permissions in"})
|
||||
|
||||
// Check channel is part of this guild
|
||||
const channel = discord.channels.get(channel_id)
|
||||
if (!channel) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${channel_id} does not exist`})
|
||||
if (!("guild_id" in channel) || channel.guild_id !== guild_id) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${channel_id} is not part of guild ${guild_id}`})
|
||||
|
||||
// Check channel is currently bridged
|
||||
const row = select("channel_room", "channel_id", {channel_id: channel_id}).get()
|
||||
if (!row) throw createError({status: 400, message: "Bad Request", data: `Channel ID ${channel_id} is not currently bridged`})
|
||||
|
||||
// Do it
|
||||
await createRoom.unbridgeDeletedChannel(channel, guild_id)
|
||||
|
||||
setResponseHeader(event, "HX-Refresh", "true")
|
||||
return null // 204
|
||||
}))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue