Make permissions command apply recursively

This commit is contained in:
Cadence Ember 2024-08-28 00:17:34 +12:00
parent 3a84658e8b
commit ddb211f8f3
5 changed files with 69 additions and 36 deletions

View file

@ -57,6 +57,9 @@ class DiscordClient {
addEventLogger("error", "Error") addEventLogger("error", "Error")
addEventLogger("disconnected", "Disconnected") addEventLogger("disconnected", "Disconnected")
addEventLogger("ready", "Ready") addEventLogger("ready", "Ready")
this.snow.requestHandler.on("requestError", (requestID, error) => {
console.error("request error:", error)
})
} }
} }

View file

@ -5,23 +5,27 @@ const Ty = require("../../types")
const {discord, sync, db, select, from} = require("../../passthrough") const {discord, sync, db, select, from} = require("../../passthrough")
const assert = require("assert/strict") const assert = require("assert/strict")
/** @type {import("../../matrix/api")} */ /** @type {import("../../matrix/api")} */
const api = sync.require("../../matrix/api") const api = sync.require("../../matrix/api")
/** @param {DiscordTypes.APIContextMenuGuildInteraction} interaction */ /**
async function interact({data, channel, id, token, guild_id}) { * @param {DiscordTypes.APIContextMenuGuildInteraction} interaction
* @returns {Promise<DiscordTypes.APIInteractionResponse>}
*/
async function _interact({data, channel, guild_id}) {
const row = select("event_message", ["event_id", "source"], {message_id: data.target_id}).get() const row = select("event_message", ["event_id", "source"], {message_id: data.target_id}).get()
assert(row) assert(row)
// Can't operate on Discord users // Can't operate on Discord users
if (row.source === 1) { // discord if (row.source === 1) { // discord
return discord.snow.interaction.createInteractionResponse(id, token, { return {
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
data: { data: {
content: `This command is only meaningful for Matrix users.`, content: `This command is only meaningful for Matrix users.`,
flags: DiscordTypes.MessageFlags.Ephemeral flags: DiscordTypes.MessageFlags.Ephemeral
} }
}) }
} }
// Get the message sender, the person that will be inspected/edited // Get the message sender, the person that will be inspected/edited
@ -42,16 +46,16 @@ async function interact({data, channel, id, token, guild_id}) {
// Administrators equal to the bot cannot be demoted // Administrators equal to the bot cannot be demoted
if (userPower >= 100) { if (userPower >= 100) {
return discord.snow.interaction.createInteractionResponse(id, token, { return {
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
data: { data: {
content: `\`${sender}\` has administrator permissions. This cannot be edited.`, content: `\`${sender}\` has administrator permissions. This cannot be edited.`,
flags: DiscordTypes.MessageFlags.Ephemeral flags: DiscordTypes.MessageFlags.Ephemeral
} }
}) }
} }
await discord.snow.interaction.createInteractionResponse(id, token, { return {
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource,
data: { data: {
content: `Showing permissions for \`${sender}\`. Click to edit.`, content: `Showing permissions for \`${sender}\`. Click to edit.`,
@ -79,30 +83,47 @@ async function interact({data, channel, id, token, guild_id}) {
} }
] ]
} }
}) }
} }
/** @param {DiscordTypes.APIMessageComponentSelectMenuInteraction} interaction */ /**
async function interactEdit({data, channel, id, token, guild_id, message}) { * @param {DiscordTypes.APIMessageComponentSelectMenuInteraction} interaction
*/
async function interactEdit({data, id, token, guild_id, message}) {
// Get the person that will be inspected/edited // Get the person that will be inspected/edited
const mxid = message.content.match(/`(@(?:[^:]+):(?:[a-z0-9:-]+\.[a-z0-9.:-]+))`/)?.[1] const mxid = message.content.match(/`(@(?:[^:]+):(?:[a-z0-9:-]+\.[a-z0-9.:-]+))`/)?.[1]
assert(mxid) assert(mxid)
const permission = data.values[0]
const power = permission === "moderator" ? 50 : 0
await discord.snow.interaction.createInteractionResponse(id, token, {
type: DiscordTypes.InteractionResponseType.UpdateMessage,
data: {
content: `Updating \`${mxid}\` to **${permission}**, please wait...`,
components: []
}
})
// Get the space, where the power levels will be inspected/edited // Get the space, where the power levels will be inspected/edited
const spaceID = select("guild_space", "space_id", {guild_id}).pluck().get() const spaceID = select("guild_space", "space_id", {guild_id}).pluck().get()
assert(spaceID) assert(spaceID)
// Do it // Do it
const permission = data.values[0] await api.setUserPowerCascade(spaceID, mxid, power)
const power = permission === "moderator" ? 50 : 0
await api.setUserPower(spaceID, mxid, power)
// TODO: Cascade permissions through room hierarchy (make a helper for this already, geez...)
// ACK // ACK
await discord.snow.interaction.createInteractionResponse(id, token, { await discord.snow.interaction.editOriginalInteractionResponse(discord.application.id, token, {
type: DiscordTypes.InteractionResponseType.DeferredMessageUpdate content: `Updated \`${mxid}\` to **${permission}**.`,
components: []
}) })
} }
/** @param {DiscordTypes.APIContextMenuGuildInteraction} interaction */
async function interact(interaction) {
await discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, await _interact(interaction))
}
module.exports.interact = interact module.exports.interact = interact
module.exports.interactEdit = interactEdit module.exports.interactEdit = interactEdit
module.exports._interact = _interact

View file

@ -48,23 +48,23 @@ discord.snow.interaction.bulkOverwriteApplicationCommands(id, [{
}]) }])
async function dispatchInteraction(interaction) { async function dispatchInteraction(interaction) {
const id = interaction.data.custom_id || interaction.data.name const interactionId = interaction.data.custom_id || interaction.data.name
try { try {
console.log(interaction) console.log(interaction)
if (id === "Matrix info") { if (interactionId === "Matrix info") {
await matrixInfo.interact(interaction) await matrixInfo.interact(interaction)
} else if (id === "invite") { } else if (interactionId === "invite") {
await invite.interact(interaction) await invite.interact(interaction)
} else if (id === "invite_channel") { } else if (interactionId === "invite_channel") {
await invite.interactButton(interaction) await invite.interactButton(interaction)
} else if (id === "Permissions") { } else if (interactionId === "Permissions") {
await permissions.interact(interaction) await permissions.interact(interaction)
} else if (id === "permissions_edit") { } else if (interactionId === "permissions_edit") {
await permissions.interactEdit(interaction) await permissions.interactEdit(interaction)
} else if (id === "bridge") { } else if (interactionId === "bridge") {
await bridge.interact(interaction) await bridge.interact(interaction)
} else { } else {
throw new Error(`Unknown interaction ${id}`) throw new Error(`Unknown interaction ${interactionId}`)
} }
} catch (e) { } catch (e) {
let stackLines = null let stackLines = null
@ -75,14 +75,11 @@ async function dispatchInteraction(interaction) {
stackLines = stackLines.slice(0, cloudstormLine - 2) stackLines = stackLines.slice(0, cloudstormLine - 2)
} }
} }
discord.snow.interaction.createInteractionResponse(interaction.id, interaction.token, { await discord.snow.interaction.createFollowupMessage(id, interaction.token, {
type: DiscordTypes.InteractionResponseType.ChannelMessageWithSource, content: `Interaction failed: **${interactionId}**`
data: { + `\nError trace:\n\`\`\`\n${stackLines.join("\n")}\`\`\``
content: `Interaction failed: **${id}**` + `Interaction data:\n\`\`\`\n${JSON.stringify(interaction.data, null, 2)}\`\`\``,
+ `\nError trace:\n\`\`\`\n${stackLines.join("\n")}\`\`\`` flags: DiscordTypes.MessageFlags.Ephemeral
+ `Interaction data:\n\`\`\`\n${JSON.stringify(interaction.data, null, 2)}\`\`\``,
flags: DiscordTypes.MessageFlags.Ephemeral
}
}) })
} }
} }

View file

@ -260,6 +260,21 @@ async function setUserPower(roomID, mxid, power) {
return powerLevels return powerLevels
} }
/**
* Set a user's power level for a whole room hierarchy.
* @param {string} roomID
* @param {string} mxid
* @param {number} power
*/
async function setUserPowerCascade(roomID, mxid, power) {
assert(roomID[0] === "!")
assert(mxid[0] === "@")
const rooms = await getFullHierarchy(roomID)
for (const room of rooms) {
await setUserPower(room.room_id, mxid, power)
}
}
module.exports.path = path module.exports.path = path
module.exports.register = register module.exports.register = register
module.exports.createRoom = createRoom module.exports.createRoom = createRoom
@ -281,3 +296,4 @@ module.exports.sendTyping = sendTyping
module.exports.profileSetDisplayname = profileSetDisplayname module.exports.profileSetDisplayname = profileSetDisplayname
module.exports.profileSetAvatarUrl = profileSetAvatarUrl module.exports.profileSetAvatarUrl = profileSetAvatarUrl
module.exports.setUserPower = setUserPower module.exports.setUserPower = setUserPower
module.exports.setUserPowerCascade = setUserPowerCascade

View file

@ -27,10 +27,6 @@ passthrough.select = orm.select
sync.require("./m2d/event-dispatcher") sync.require("./m2d/event-dispatcher")
discord.snow.requestHandler.on("requestError", data => {
console.error("request error", data)
})
;(async () => { ;(async () => {
await migrate.migrate(db) await migrate.migrate(db)
await discord.cloud.connect() await discord.cloud.connect()