setUserPower should account for room version 12
This commit is contained in:
@@ -37,7 +37,9 @@ const createRoom = sync.require("../d2m/actions/create-room")
|
||||
/** @type {import("../src/matrix/mreq")} */
|
||||
const mreq = sync.require("../matrix/mreq")
|
||||
/** @type {import("../src/matrix/api")} */
|
||||
const api = sync.require("../matrix/api")
|
||||
const api = sync.require("../src/matrix/api")
|
||||
/** @type {import("../src/matrix/utils")} */
|
||||
const utils = sync.require("../src/matrix/utils")
|
||||
|
||||
const sema = new Semaphore()
|
||||
|
||||
@@ -89,7 +91,7 @@ async function migrateGuild(guild) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
await api.setUserPower(roomID, newBridgeMxid, 100)
|
||||
await utils.setUserPower(roomID, newBridgeMxid, 100, api)
|
||||
})
|
||||
await api.joinRoom(roomID)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const passthrough = require("../src/passthrough")
|
||||
Object.assign(passthrough, {db, sync})
|
||||
|
||||
const api = require("../src/matrix/api")
|
||||
const utils = require("../src/matrix/utils")
|
||||
const mreq = require("../src/matrix/mreq")
|
||||
|
||||
const rooms = db.prepare("select room_id from channel_room").pluck().all()
|
||||
@@ -25,7 +26,7 @@ const rooms = db.prepare("select room_id from channel_room").pluck().all()
|
||||
await api.leaveRoom(roomID, mxid)
|
||||
}
|
||||
}
|
||||
await api.setUserPower(roomID, "@_discord_bot:cadence.moe", 0)
|
||||
await utils.setUserPower(roomID, "@_discord_bot:cadence.moe", 0, api)
|
||||
await api.leaveRoom(roomID)
|
||||
} catch (e) {
|
||||
if (e.message.includes("Appservice not in room")) {
|
||||
|
||||
@@ -17,8 +17,8 @@ const mreq = sync.require("../../matrix/mreq")
|
||||
const ks = sync.require("../../matrix/kstate")
|
||||
/** @type {import("../../discord/utils")} */
|
||||
const dUtils = sync.require("../../discord/utils")
|
||||
/** @type {import("../../m2d/converters/utils")} */
|
||||
const mUtils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const mUtils = sync.require("../../matrix/utils")
|
||||
/** @type {import("./create-space")} */
|
||||
const createSpace = sync.require("./create-space")
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@ const api = sync.require("../../matrix/api")
|
||||
/** @type {import("../../matrix/file")} */
|
||||
const file = sync.require("../../matrix/file")
|
||||
/** @type {import("../../discord/utils")} */
|
||||
const utils = sync.require("../../discord/utils")
|
||||
const dUtils = sync.require("../../discord/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const mxUtils = sync.require("../../matrix/utils")
|
||||
/** @type {import("../converters/user-to-mxid")} */
|
||||
const userToMxid = sync.require("../converters/user-to-mxid")
|
||||
/** @type {import("./create-room")} */
|
||||
@@ -159,8 +161,8 @@ async function memberToStateContent(user, member, guildID) {
|
||||
function memberToPowerLevel(user, member, guild, channel) {
|
||||
if (!member) return 0
|
||||
|
||||
const permissions = utils.getPermissions(member.roles, guild.roles, user.id, channel.permission_overwrites)
|
||||
const everyonePermissions = utils.getPermissions([], guild.roles, undefined, channel.permission_overwrites)
|
||||
const permissions = dUtils.getPermissions(member.roles, guild.roles, user.id, channel.permission_overwrites)
|
||||
const everyonePermissions = dUtils.getPermissions([], guild.roles, undefined, channel.permission_overwrites)
|
||||
/*
|
||||
* PL 100 = Administrator = People who can brick the room. RATIONALE:
|
||||
* - Administrator.
|
||||
@@ -169,7 +171,7 @@ function memberToPowerLevel(user, member, guild, channel) {
|
||||
* - Manage Channels: People who can manage the channel can delete it.
|
||||
* (Setting sim users to PL 100 is safe because even though we can't demote the sims we can use code to make the sims demote themselves.)
|
||||
*/
|
||||
if (guild.owner_id === user.id || utils.hasSomePermissions(permissions, ["Administrator", "ManageWebhooks", "ManageGuild", "ManageChannels"])) return 100
|
||||
if (guild.owner_id === user.id || dUtils.hasSomePermissions(permissions, ["Administrator", "ManageWebhooks", "ManageGuild", "ManageChannels"])) return 100
|
||||
/*
|
||||
* PL 50 = Moderator = People who can manage people and messages in many ways. RATIONALE:
|
||||
* - Manage Messages: Can moderate by pinning or deleting the conversation.
|
||||
@@ -179,14 +181,14 @@ function memberToPowerLevel(user, member, guild, channel) {
|
||||
* - Mute Members & Deafen Members: Can moderate by silencing disruptive people in ways they can't undo.
|
||||
* - Moderate Members.
|
||||
*/
|
||||
if (utils.hasSomePermissions(permissions, ["ManageMessages", "ManageNicknames", "ManageThreads", "KickMembers", "BanMembers", "MuteMembers", "DeafenMembers", "ModerateMembers"])) return 50
|
||||
if (dUtils.hasSomePermissions(permissions, ["ManageMessages", "ManageNicknames", "ManageThreads", "KickMembers", "BanMembers", "MuteMembers", "DeafenMembers", "ModerateMembers"])) return 50
|
||||
/* PL 50 = if room is read-only but the user has been specially allowed to send messages */
|
||||
const everyoneCanSend = utils.hasPermission(everyonePermissions, DiscordTypes.PermissionFlagsBits.SendMessages)
|
||||
const userCanSend = utils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.SendMessages)
|
||||
const everyoneCanSend = dUtils.hasPermission(everyonePermissions, DiscordTypes.PermissionFlagsBits.SendMessages)
|
||||
const userCanSend = dUtils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.SendMessages)
|
||||
if (!everyoneCanSend && userCanSend) return createRoom.READ_ONLY_ROOM_EVENTS_DEFAULT_POWER
|
||||
/* PL 20 = Mention Everyone for technical reasons. */
|
||||
const everyoneCanMentionEveryone = utils.hasPermission(everyonePermissions, DiscordTypes.PermissionFlagsBits.MentionEveryone)
|
||||
const userCanMentionEveryone = utils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.MentionEveryone)
|
||||
const everyoneCanMentionEveryone = dUtils.hasPermission(everyonePermissions, DiscordTypes.PermissionFlagsBits.MentionEveryone)
|
||||
const userCanMentionEveryone = dUtils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.MentionEveryone)
|
||||
if (!everyoneCanMentionEveryone && userCanMentionEveryone) return 20
|
||||
return 0
|
||||
}
|
||||
@@ -247,7 +249,7 @@ async function _sendSyncUser(roomID, mxid, content, powerLevel, options) {
|
||||
actions.push(api.sendState(roomID, "m.room.member", mxid, content, mxid))
|
||||
// Update power levels
|
||||
if (powerLevel != null) {
|
||||
actions.push(api.setUserPower(roomID, mxid, powerLevel))
|
||||
actions.push(mxUtils.setUserPower(roomID, mxid, powerLevel, api))
|
||||
}
|
||||
// Update global profile (if supported by server)
|
||||
if (await supportsMsc4069) {
|
||||
|
||||
@@ -6,8 +6,8 @@ const passthrough = require("../../passthrough")
|
||||
const {sync, select, from} = passthrough
|
||||
/** @type {import("./message-to-event")} */
|
||||
const messageToEvent = sync.require("../converters/message-to-event")
|
||||
/** @type {import("../../m2d/converters/utils")} */
|
||||
const utils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const utils = sync.require("../../matrix/utils")
|
||||
|
||||
function eventCanBeEdited(ev) {
|
||||
// Discord does not allow files, images, attachments, or videos to be edited.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const {test} = require("supertape")
|
||||
const {messageToEvent} = require("./message-to-event")
|
||||
const data = require("../../../test/data")
|
||||
const {mockGetEffectivePower} = require("../../m2d/converters/utils.test")
|
||||
const {mockGetEffectivePower} = require("../../matrix/utils.test")
|
||||
const {db} = require("../../passthrough")
|
||||
|
||||
test("message2event embeds: nothing but a field", async t => {
|
||||
|
||||
@@ -14,8 +14,8 @@ const file = sync.require("../../matrix/file")
|
||||
const emojiToKey = sync.require("./emoji-to-key")
|
||||
/** @type {import("../actions/lottie")} */
|
||||
const lottie = sync.require("../actions/lottie")
|
||||
/** @type {import("../../m2d/converters/utils")} */
|
||||
const mxUtils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const mxUtils = sync.require("../../matrix/utils")
|
||||
/** @type {import("../../discord/utils")} */
|
||||
const dUtils = sync.require("../../discord/utils")
|
||||
const {reg} = require("../../matrix/read-registration")
|
||||
|
||||
@@ -2,7 +2,7 @@ const {test} = require("supertape")
|
||||
const {messageToEvent} = require("./message-to-event")
|
||||
const {MatrixServerError} = require("../../matrix/mreq")
|
||||
const data = require("../../../test/data")
|
||||
const {mockGetEffectivePower} = require("../../m2d/converters/utils.test")
|
||||
const {mockGetEffectivePower} = require("../../matrix/utils.test")
|
||||
const Ty = require("../../types")
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,8 @@ const DiscordTypes = require("discord-api-types/v10")
|
||||
|
||||
const passthrough = require("../../passthrough")
|
||||
const {discord, sync, select} = passthrough
|
||||
/** @type {import("../../m2d/converters/utils")} */
|
||||
const utils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const utils = sync.require("../../matrix/utils")
|
||||
|
||||
/**
|
||||
* @typedef ReactionRemoveRequest
|
||||
|
||||
@@ -4,8 +4,8 @@ const assert = require("assert").strict
|
||||
|
||||
const passthrough = require("../../passthrough")
|
||||
const {discord, sync, db, select} = passthrough
|
||||
/** @type {import("../../m2d/converters/utils")} */
|
||||
const mxUtils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const mxUtils = sync.require("../../matrix/utils")
|
||||
const {reg} = require("../../matrix/read-registration.js")
|
||||
|
||||
const userRegex = reg.namespaces.users.map(u => new RegExp(u.regex))
|
||||
|
||||
@@ -2,7 +2,7 @@ const {test} = require("supertape")
|
||||
const {threadToAnnouncement} = require("./thread-to-announcement")
|
||||
const data = require("../../../test/data")
|
||||
const Ty = require("../../types")
|
||||
const {mockGetEffectivePower} = require("../../m2d/converters/utils.test")
|
||||
const {mockGetEffectivePower} = require("../../matrix/utils.test")
|
||||
|
||||
/**
|
||||
* @param {string} roomID
|
||||
|
||||
@@ -9,8 +9,8 @@ const {InteractionMethods} = require("snowtransfer")
|
||||
|
||||
/** @type {import("../../matrix/api")} */
|
||||
const api = sync.require("../../matrix/api")
|
||||
/** @type {import("../../m2d/converters/utils")} */
|
||||
const utils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const utils = sync.require("../../matrix/utils")
|
||||
|
||||
/**
|
||||
* @param {DiscordTypes.APIContextMenuGuildInteraction} interaction
|
||||
@@ -126,7 +126,7 @@ async function* _interactEdit({data, guild_id, message}, {api}) {
|
||||
assert(spaceID)
|
||||
|
||||
// Do it
|
||||
await api.setUserPowerCascade(spaceID, mxid, power)
|
||||
await utils.setUserPowerCascade(spaceID, mxid, power, api)
|
||||
|
||||
// ACK
|
||||
yield {editOriginalInteractionResponse: {
|
||||
|
||||
@@ -2,7 +2,7 @@ const {test} = require("supertape")
|
||||
const DiscordTypes = require("discord-api-types/v10")
|
||||
const {select, db} = require("../../passthrough")
|
||||
const {_interact, _interactEdit} = require("./permissions")
|
||||
const {mockGetEffectivePower} = require("../../m2d/converters/utils.test")
|
||||
const {mockGetEffectivePower} = require("../../matrix/utils.test")
|
||||
|
||||
/**
|
||||
* @template T
|
||||
@@ -156,7 +156,7 @@ test("permissions: reports permissions of selected matrix user (admin v11 cannot
|
||||
})
|
||||
|
||||
test("permissions: can update user to moderator", async t => {
|
||||
let called = 0
|
||||
let called = []
|
||||
const msgs = await fromAsync(_interactEdit({
|
||||
data: {
|
||||
target_id: "1128118177155526666",
|
||||
@@ -168,22 +168,48 @@ test("permissions: can update user to moderator", async t => {
|
||||
guild_id: "112760669178241024"
|
||||
}, {
|
||||
api: {
|
||||
async setUserPowerCascade(roomID, mxid, power) {
|
||||
called++
|
||||
t.equal(roomID, "!jjmvBegULiLucuWEHU:cadence.moe") // space ID
|
||||
t.equal(mxid, "@cadence:cadence.moe")
|
||||
t.equal(power, 50)
|
||||
async getStateEvent(roomID, type, key) {
|
||||
called.push("get power levels")
|
||||
t.equal(type, "m.room.power_levels")
|
||||
return {}
|
||||
},
|
||||
async getStateEventOuter(roomID, type, key) {
|
||||
called.push("get room create")
|
||||
return {
|
||||
type: "m.room.create",
|
||||
state_key: "",
|
||||
sender: "@_ooye_bot:cadence.moe",
|
||||
event_id: "$create",
|
||||
origin_server_ts: 0,
|
||||
room_id: roomID,
|
||||
content: {
|
||||
room_version: "11"
|
||||
}
|
||||
}
|
||||
},
|
||||
async *generateFullHierarchy(spaceID) {
|
||||
called.push("generate full hierarchy")
|
||||
},
|
||||
async sendState(roomID, type, key, content) {
|
||||
called.push("set power levels")
|
||||
t.ok(["!hierarchy", "!jjmvBegULiLucuWEHU:cadence.moe"].includes(roomID), `expected room ID to be in hierarchy, but was ${roomID}`)
|
||||
t.equal(type, "m.room.power_levels")
|
||||
t.equal(key, "")
|
||||
t.deepEqual(content, {
|
||||
users: {"@cadence:cadence.moe": 50}
|
||||
})
|
||||
return "$updated"
|
||||
}
|
||||
}
|
||||
}))
|
||||
t.equal(msgs.length, 2)
|
||||
t.equal(msgs[0].createInteractionResponse.data.content, "Updating `@cadence:cadence.moe` to **moderator**, please wait...")
|
||||
t.equal(msgs[1].editOriginalInteractionResponse.content, "Updated `@cadence:cadence.moe` to **moderator**.")
|
||||
t.equal(called, 1)
|
||||
t.deepEqual(called, ["generate full hierarchy", "get room create", "get power levels", "set power levels"])
|
||||
})
|
||||
|
||||
test("permissions: can update user to default", async t => {
|
||||
let called = 0
|
||||
let called = []
|
||||
const msgs = await fromAsync(_interactEdit({
|
||||
data: {
|
||||
target_id: "1128118177155526666",
|
||||
@@ -195,16 +221,44 @@ test("permissions: can update user to default", async t => {
|
||||
guild_id: "112760669178241024"
|
||||
}, {
|
||||
api: {
|
||||
async setUserPowerCascade(roomID, mxid, power) {
|
||||
called++
|
||||
t.equal(roomID, "!jjmvBegULiLucuWEHU:cadence.moe") // space ID
|
||||
t.equal(mxid, "@cadence:cadence.moe")
|
||||
t.equal(power, 0)
|
||||
async getStateEvent(roomID, type, key) {
|
||||
called.push("get power levels")
|
||||
t.equal(type, "m.room.power_levels")
|
||||
return {
|
||||
users: {"@cadence:cadence.moe": 50}
|
||||
}
|
||||
},
|
||||
async getStateEventOuter(roomID, type, key) {
|
||||
called.push("get room create")
|
||||
return {
|
||||
type: "m.room.create",
|
||||
state_key: "",
|
||||
sender: "@_ooye_bot:cadence.moe",
|
||||
event_id: "$create",
|
||||
origin_server_ts: 0,
|
||||
room_id: roomID,
|
||||
content: {
|
||||
room_version: "11"
|
||||
}
|
||||
}
|
||||
},
|
||||
async *generateFullHierarchy(spaceID) {
|
||||
called.push("generate full hierarchy")
|
||||
},
|
||||
async sendState(roomID, type, key, content) {
|
||||
called.push("set power levels")
|
||||
t.ok(["!hierarchy", "!jjmvBegULiLucuWEHU:cadence.moe"].includes(roomID), `expected room ID to be in hierarchy, but was ${roomID}`)
|
||||
t.equal(type, "m.room.power_levels")
|
||||
t.equal(key, "")
|
||||
t.deepEqual(content, {
|
||||
users: {}
|
||||
})
|
||||
return "$updated"
|
||||
}
|
||||
}
|
||||
}))
|
||||
t.equal(msgs.length, 2)
|
||||
t.equal(msgs[0].createInteractionResponse.data.content, "Updating `@cadence:cadence.moe` to **default**, please wait...")
|
||||
t.equal(msgs[1].editOriginalInteractionResponse.content, "Updated `@cadence:cadence.moe` to **default**.")
|
||||
t.equal(called, 1)
|
||||
t.deepEqual(called, ["generate full hierarchy", "get room create", "get power levels", "set power levels"])
|
||||
})
|
||||
|
||||
@@ -7,8 +7,8 @@ const {InteractionMethods} = require("snowtransfer")
|
||||
|
||||
/** @type {import("../../matrix/api")} */
|
||||
const api = sync.require("../../matrix/api")
|
||||
/** @type {import("../../m2d/converters/utils")} */
|
||||
const utils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const utils = sync.require("../../matrix/utils")
|
||||
|
||||
/**
|
||||
* @param {DiscordTypes.APIMessageApplicationCommandGuildInteraction} interaction
|
||||
|
||||
@@ -5,8 +5,8 @@ const Ty = require("../../types")
|
||||
|
||||
const passthrough = require("../../passthrough")
|
||||
const {discord, sync, db, select} = passthrough
|
||||
/** @type {import("../converters/utils")} */
|
||||
const utils = sync.require("../converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const utils = sync.require("../../matrix/utils")
|
||||
/** @type {import("../converters/emoji")} */
|
||||
const emoji = sync.require("../converters/emoji")
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ const Ty = require("../../types")
|
||||
|
||||
const passthrough = require("../../passthrough")
|
||||
const {discord, sync, db, select, from} = passthrough
|
||||
/** @type {import("../converters/utils")} */
|
||||
const utils = sync.require("../converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const utils = sync.require("../../matrix/utils")
|
||||
|
||||
/**
|
||||
* @param {Ty.Event.Outer_M_Room_Redaction} event
|
||||
|
||||
@@ -14,8 +14,8 @@ const {tag} = require("@cloudrac3r/html-template-tag")
|
||||
const passthrough = require("../../passthrough")
|
||||
const {sync, db, discord, select, from} = passthrough
|
||||
const {reg} = require("../../matrix/read-registration")
|
||||
/** @type {import("../converters/utils")} */
|
||||
const mxUtils = sync.require("../converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const mxUtils = sync.require("../../matrix/utils")
|
||||
/** @type {import("../../discord/utils")} */
|
||||
const dUtils = sync.require("../../discord/utils")
|
||||
/** @type {import("../../matrix/file")} */
|
||||
|
||||
@@ -20,8 +20,8 @@ const redact = sync.require("./actions/redact")
|
||||
const updatePins = sync.require("./actions/update-pins")
|
||||
/** @type {import("../matrix/matrix-command-handler")} */
|
||||
const matrixCommandHandler = sync.require("../matrix/matrix-command-handler")
|
||||
/** @type {import("./converters/utils")} */
|
||||
const utils = sync.require("./converters/utils")
|
||||
/** @type {import("../matrix/utils")} */
|
||||
const utils = sync.require("../matrix/utils")
|
||||
/** @type {import("../matrix/api")}) */
|
||||
const api = sync.require("../matrix/api")
|
||||
/** @type {import("../d2m/actions/create-room")} */
|
||||
|
||||
@@ -358,55 +358,6 @@ async function profileSetAvatarUrl(mxid, avatar_url, inhibitPropagate) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user's power level within a room.
|
||||
* @param {string} roomID
|
||||
* @param {string} mxid
|
||||
* @param {number} newPower
|
||||
*/
|
||||
async function setUserPower(roomID, mxid, newPower) {
|
||||
assert(roomID[0] === "!")
|
||||
assert(mxid[0] === "@")
|
||||
// Yes there's no shortcut https://github.com/matrix-org/matrix-appservice-bridge/blob/2334b0bae28a285a767fe7244dad59f5a5963037/src/components/intent.ts#L352
|
||||
const power = await getStateEvent(roomID, "m.room.power_levels", "")
|
||||
power.users = power.users || {}
|
||||
|
||||
// Check if it has really changed to avoid sending a useless state event
|
||||
// (Can't diff kstate here because of (a) circular imports (b) kstate has special behaviour diffing power levels)
|
||||
const oldPowerLevel = power.users?.[mxid] ?? power.users_default ?? 0
|
||||
if (oldPowerLevel === newPower) return
|
||||
|
||||
// Bridge bot can't demote equal power users, so need to decide which user will send the event
|
||||
const botPowerLevel = power.users?.[`@${reg.sender_localpart}:${reg.ooye.server_name}`] ?? power.users_default ?? 0
|
||||
const eventSender = oldPowerLevel >= botPowerLevel ? mxid : undefined
|
||||
|
||||
// Update the event content
|
||||
if (newPower == null || newPower === (power.users_default ?? 0)) {
|
||||
delete power.users[mxid]
|
||||
} else {
|
||||
power.users[mxid] = newPower
|
||||
}
|
||||
|
||||
await sendState(roomID, "m.room.power_levels", "", power, eventSender)
|
||||
return power
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user's power level for a whole room hierarchy.
|
||||
* @param {string} spaceID
|
||||
* @param {string} mxid
|
||||
* @param {number} power
|
||||
*/
|
||||
async function setUserPowerCascade(spaceID, mxid, power) {
|
||||
assert(spaceID[0] === "!")
|
||||
assert(mxid[0] === "@")
|
||||
const rooms = await getFullHierarchy(spaceID)
|
||||
await setUserPower(spaceID, mxid, power)
|
||||
for (const room of rooms) {
|
||||
await setUserPower(room.room_id, mxid, power)
|
||||
}
|
||||
}
|
||||
|
||||
async function ping() {
|
||||
// not using mreq so that we can read the status code
|
||||
const res = await fetch(`${mreq.baseUrl}/client/v1/appservice/${reg.id}/ping`, {
|
||||
@@ -579,8 +530,6 @@ module.exports.redactEvent = redactEvent
|
||||
module.exports.sendTyping = sendTyping
|
||||
module.exports.profileSetDisplayname = profileSetDisplayname
|
||||
module.exports.profileSetAvatarUrl = profileSetAvatarUrl
|
||||
module.exports.setUserPower = setUserPower
|
||||
module.exports.setUserPowerCascade = setUserPowerCascade
|
||||
module.exports.ping = ping
|
||||
module.exports.getMedia = getMedia
|
||||
module.exports.sendReadReceipt = sendReadReceipt
|
||||
|
||||
@@ -10,8 +10,8 @@ const {sync} = passthrough
|
||||
const file = sync.require("./file")
|
||||
/** @type {import("./api")} */
|
||||
const api = sync.require("./api")
|
||||
/** @type {import("../m2d/converters/utils")} */
|
||||
const utils = sync.require("../m2d/converters/utils")
|
||||
/** @type {import("./utils")} */
|
||||
const utils = sync.require("./utils")
|
||||
|
||||
/** Mutates the input. Not recursive - can only include or exclude entire state events. */
|
||||
function kstateStripConditionals(kstate) {
|
||||
|
||||
@@ -8,8 +8,8 @@ const sharp = require("sharp")
|
||||
const {discord, sync, db, select} = require("../passthrough")
|
||||
/** @type {import("./api")}) */
|
||||
const api = sync.require("./api")
|
||||
/** @type {import("../m2d/converters/utils")} */
|
||||
const mxUtils = sync.require("../m2d/converters/utils")
|
||||
/** @type {import("./utils")} */
|
||||
const mxUtils = sync.require("./utils")
|
||||
/** @type {import("../discord/utils")} */
|
||||
const dUtils = sync.require("../discord/utils")
|
||||
/** @type {import("./kstate")} */
|
||||
|
||||
@@ -10,8 +10,8 @@ const {discord, db, sync, as, select, from} = require("../passthrough")
|
||||
const api = sync.require("./api")
|
||||
/** @type {import("../d2m/actions/create-room")}) */
|
||||
const createRoom = sync.require("../d2m/actions/create-room")
|
||||
/** @type {import("../m2d/converters/utils")}) */
|
||||
const utils = sync.require("../m2d/converters/utils")
|
||||
/** @type {import("./utils")}) */
|
||||
const utils = sync.require("./utils")
|
||||
|
||||
const roomUpgradeSema = new Semaphore()
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// @ts-check
|
||||
|
||||
const assert = require("assert").strict
|
||||
const Ty = require("../../types")
|
||||
const passthrough = require("../../passthrough")
|
||||
const Ty = require("../types")
|
||||
const passthrough = require("../passthrough")
|
||||
const {db} = passthrough
|
||||
|
||||
const {reg} = require("../../matrix/read-registration")
|
||||
const {reg} = require("./read-registration")
|
||||
const userRegex = reg.namespaces.users.map(u => new RegExp(u.regex))
|
||||
|
||||
/** @type {import("xxhash-wasm").XXHashAPI} */ // @ts-ignore
|
||||
@@ -129,7 +129,7 @@ class MatrixStringBuilder {
|
||||
* https://spec.matrix.org/v1.9/appendices/#routing
|
||||
* https://gitdab.com/cadence/out-of-your-element/issues/11
|
||||
* @param {string} roomID
|
||||
* @param {{[K in "getStateEvent" | "getStateEventOuter" | "getJoinedMembers"]: import("../../matrix/api")[K]} | {getEffectivePower: (roomID: string, mxids: string[], api: any) => Promise<{powers: Record<string, number>, allCreators: string[], tombstone: number, roomCreate: Ty.Event.StateOuter<Ty.Event.M_Room_Create>, powerLevels: Ty.Event.M_Power_Levels}>, getJoinedMembers: import("../../matrix/api")["getJoinedMembers"]}} api
|
||||
* @param {{[K in "getStateEvent" | "getStateEventOuter" | "getJoinedMembers"]: import("./api")[K]} | {getEffectivePower: (roomID: string, mxids: string[], api: any) => Promise<{powers: Record<string, number>, allCreators: string[], tombstone: number, roomCreate: Ty.Event.StateOuter<Ty.Event.M_Room_Create>, powerLevels: Ty.Event.M_Power_Levels}>, getJoinedMembers: import("./api")["getJoinedMembers"]}} api
|
||||
*/
|
||||
async function getViaServers(roomID, api) {
|
||||
const candidates = []
|
||||
@@ -188,7 +188,7 @@ async function getViaServers(roomID, api) {
|
||||
* https://spec.matrix.org/v1.9/appendices/#routing
|
||||
* https://gitdab.com/cadence/out-of-your-element/issues/11
|
||||
* @param {string} roomID
|
||||
* @param {{[K in "getStateEvent" | "getStateEventOuter" | "getJoinedMembers"]: import("../../matrix/api")[K]}} api
|
||||
* @param {{[K in "getStateEvent" | "getStateEventOuter" | "getJoinedMembers"]: import("./api")[K]}} api
|
||||
* @returns {Promise<URLSearchParams>}
|
||||
*/
|
||||
async function getViaServersQuery(roomID, api) {
|
||||
@@ -273,7 +273,7 @@ function removeCreatorsFromPowerLevels(roomCreateOuter, powerLevels) {
|
||||
* @template {string} T
|
||||
* @param {string} roomID
|
||||
* @param {T[]} mxids
|
||||
* @param {{[K in "getStateEvent" | "getStateEventOuter"]: import("../../matrix/api")[K]}} api
|
||||
* @param {{[K in "getStateEvent" | "getStateEventOuter"]: import("./api")[K]}} api
|
||||
* @returns {Promise<{powers: Record<T, number>, allCreators: string[], tombstone: number, roomCreate: Ty.Event.StateOuter<Ty.Event.M_Room_Create>, powerLevels: Ty.Event.M_Power_Levels}>}
|
||||
*/
|
||||
async function getEffectivePower(roomID, mxids, api) {
|
||||
@@ -300,6 +300,56 @@ async function getEffectivePower(roomID, mxids, api) {
|
||||
return {powers, allCreators, tombstone, roomCreate, powerLevels}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user's power level within a room.
|
||||
* @param {string} roomID
|
||||
* @param {string} mxid
|
||||
* @param {number} newPower
|
||||
* @param {{[K in "getStateEvent" | "getStateEventOuter" | "sendState"]: import("./api")[K]}} api
|
||||
*/
|
||||
async function setUserPower(roomID, mxid, newPower, api) {
|
||||
assert(roomID[0] === "!")
|
||||
assert(mxid[0] === "@")
|
||||
// Yes there's no shortcut https://github.com/matrix-org/matrix-appservice-bridge/blob/2334b0bae28a285a767fe7244dad59f5a5963037/src/components/intent.ts#L352
|
||||
const {powerLevels, powers: {[mxid]: oldPowerLevel, [bot]: botPowerLevel}} = await getEffectivePower(roomID, [mxid, bot], api)
|
||||
|
||||
// Check if it has really changed to avoid sending a useless state event
|
||||
if (oldPowerLevel === newPower) return
|
||||
|
||||
// Bridge bot can't demote equal power users, so need to decide which user will send the event
|
||||
const eventSender = oldPowerLevel >= botPowerLevel ? mxid : undefined
|
||||
|
||||
// Update the event content
|
||||
powerLevels.users ??= {}
|
||||
if (newPower == null || newPower === (powerLevels.users_default ?? 0)) {
|
||||
delete powerLevels.users[mxid]
|
||||
} else {
|
||||
powerLevels.users[mxid] = newPower
|
||||
}
|
||||
|
||||
await api.sendState(roomID, "m.room.power_levels", "", powerLevels, eventSender)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user's power level for a whole room hierarchy.
|
||||
* @param {string} spaceID
|
||||
* @param {string} mxid
|
||||
* @param {number} power
|
||||
* @param {{[K in "getStateEvent" | "getStateEventOuter" | "sendState" | "generateFullHierarchy"]: import("./api")[K]}} api
|
||||
*/
|
||||
async function setUserPowerCascade(spaceID, mxid, power, api) {
|
||||
assert(spaceID[0] === "!")
|
||||
assert(mxid[0] === "@")
|
||||
let seenSpace = false
|
||||
for await (const room of api.generateFullHierarchy(spaceID)) {
|
||||
if (room.room_id === spaceID) seenSpace = true
|
||||
await setUserPower(room.room_id, mxid, power, api)
|
||||
}
|
||||
if (!seenSpace) {
|
||||
await setUserPower(spaceID, mxid, power, api)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.bot = bot
|
||||
module.exports.BLOCK_ELEMENTS = BLOCK_ELEMENTS
|
||||
module.exports.eventSenderIsFromDiscord = eventSenderIsFromDiscord
|
||||
@@ -311,3 +361,5 @@ module.exports.getViaServersQuery = getViaServersQuery
|
||||
module.exports.roomHasAtLeastVersion = roomHasAtLeastVersion
|
||||
module.exports.removeCreatorsFromPowerLevels = removeCreatorsFromPowerLevels
|
||||
module.exports.getEffectivePower = getEffectivePower
|
||||
module.exports.setUserPower = setUserPower
|
||||
module.exports.setUserPowerCascade = setUserPowerCascade
|
||||
@@ -1,7 +1,5 @@
|
||||
// @ts-check
|
||||
|
||||
const e = new Error("Custom error")
|
||||
|
||||
const {test} = require("supertape")
|
||||
const {eventSenderIsFromDiscord, getEventIDHash, MatrixStringBuilder, getViaServers, roomHasAtLeastVersion} = require("./utils")
|
||||
const util = require("util")
|
||||
@@ -41,8 +39,14 @@ test("event hash: hash is different for different inputs", t => {
|
||||
})
|
||||
|
||||
test("MatrixStringBuilder: add, addLine, add same text", t => {
|
||||
const e = {
|
||||
stack: "Error: Custom error\n at ./example.test.js:3:11)",
|
||||
toString() {
|
||||
return "Error: Custom error"
|
||||
}
|
||||
}
|
||||
const gatewayMessage = {t: "MY_MESSAGE", d: {display: "Custom message data"}}
|
||||
let stackLines = e.stack?.split("\n")
|
||||
let stackLines = e.stack.split("\n")
|
||||
|
||||
const builder = new MatrixStringBuilder()
|
||||
builder.addLine("\u26a0 Bridged event from Discord not delivered", "\u26a0 <strong>Bridged event from Discord not delivered</strong>")
|
||||
@@ -63,12 +67,12 @@ test("MatrixStringBuilder: add, addLine, add same text", t => {
|
||||
+ "\nError: Custom error"
|
||||
+ "\nError trace:"
|
||||
+ "\nError: Custom error"
|
||||
+ "\n at ./m2d/converters/utils.test.js:3:11)\n",
|
||||
+ "\n at ./example.test.js:3:11)\n",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: "\u26a0 <strong>Bridged event from Discord not delivered</strong>"
|
||||
+ "<br>Gateway event: MY_MESSAGE"
|
||||
+ "<br>Error: Custom error"
|
||||
+ "<br><details><summary>Error trace</summary><pre>Error: Custom error\n at ./m2d/converters/utils.test.js:3:11)</pre></details>"
|
||||
+ "<br><details><summary>Error trace</summary><pre>Error: Custom error\n at ./example.test.js:3:11)</pre></details>"
|
||||
+ `<details><summary>Original payload</summary><pre>{ display: 'Custom message data' }</pre></details>`
|
||||
})
|
||||
})
|
||||
@@ -18,7 +18,9 @@ const createSpace = sync.require("../../d2m/actions/create-space")
|
||||
/** @type {import("../auth")} */
|
||||
const auth = require("../auth")
|
||||
/** @type {import("../../discord/utils")} */
|
||||
const utils = sync.require("../../discord/utils")
|
||||
const dUtils = sync.require("../../discord/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const mxUtils = sync.require("../../matrix/utils")
|
||||
const {reg} = require("../../matrix/read-registration")
|
||||
|
||||
const schema = {
|
||||
@@ -102,8 +104,8 @@ function getChannelRoomsLinks(guild, rooms, roles) {
|
||||
let unlinkedChannels = unlinkedChannelIDs.map(c => discord.channels.get(c))
|
||||
let removedWrongTypeChannels = filterTo(unlinkedChannels, c => c && [0, 5].includes(c.type))
|
||||
let removedPrivateChannels = filterTo(unlinkedChannels, c => {
|
||||
const permissions = utils.getPermissions(roles, guild.roles, botID, c["permission_overwrites"])
|
||||
return utils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.ViewChannel)
|
||||
const permissions = dUtils.getPermissions(roles, guild.roles, botID, c["permission_overwrites"])
|
||||
return dUtils.hasPermission(permissions, DiscordTypes.PermissionFlagsBits.ViewChannel)
|
||||
})
|
||||
unlinkedChannels.sort((a, b) => getPosition(a) - getPosition(b))
|
||||
|
||||
@@ -228,7 +230,7 @@ as.router.post("/api/invite", defineEventHandler(async event => {
|
||||
( parsedBody.permissions === "admin" ? 100
|
||||
: parsedBody.permissions === "moderator" ? 50
|
||||
: 0)
|
||||
if (powerLevel) await api.setUserPowerCascade(spaceID, parsedBody.mxid, powerLevel)
|
||||
if (powerLevel) await mxUtils.setUserPowerCascade(spaceID, parsedBody.mxid, powerLevel, api)
|
||||
|
||||
if (parsedBody.guild_id) {
|
||||
setResponseHeader(event, "HX-Refresh", true)
|
||||
|
||||
@@ -4,8 +4,8 @@ const {z} = require("zod")
|
||||
const {defineEventHandler, getValidatedQuery, H3Event} = require("h3")
|
||||
const {as, from, sync, select} = require("../../passthrough")
|
||||
|
||||
/** @type {import("../../m2d/converters/utils")} */
|
||||
const mUtils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("../../matrix/utils")} */
|
||||
const mUtils = sync.require("../../matrix/utils")
|
||||
|
||||
/**
|
||||
* @param {H3Event} event
|
||||
|
||||
@@ -10,8 +10,8 @@ const {discord, db, as, sync, select, from} = require("../../passthrough")
|
||||
const auth = sync.require("../auth")
|
||||
/** @type {import("../../matrix/mreq")} */
|
||||
const mreq = sync.require("../../matrix/mreq")
|
||||
/** @type {import("../../m2d/converters/utils")}*/
|
||||
const utils = sync.require("../../m2d/converters/utils")
|
||||
/** @type {import("../../matrix/utils")}*/
|
||||
const utils = sync.require("../../matrix/utils")
|
||||
const {reg} = require("../../matrix/read-registration")
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,8 +13,8 @@ const reg = require("../matrix/read-registration")
|
||||
const {sync, discord, as, select} = require("../passthrough")
|
||||
/** @type {import("./pug-sync")} */
|
||||
const pugSync = sync.require("./pug-sync")
|
||||
/** @type {import("../m2d/converters/utils")} */
|
||||
const mUtils = sync.require("../m2d/converters/utils")
|
||||
/** @type {import("../matrix/utils")} */
|
||||
const mUtils = sync.require("../matrix/utils")
|
||||
const {id} = require("../../addbot")
|
||||
|
||||
// Pug
|
||||
|
||||
@@ -147,6 +147,7 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not
|
||||
require("../src/matrix/mreq.test")
|
||||
require("../src/matrix/read-registration.test")
|
||||
require("../src/matrix/txnid.test")
|
||||
require("../src/matrix/utils.test")
|
||||
require("../src/d2m/actions/create-room.test")
|
||||
require("../src/d2m/actions/create-space.test")
|
||||
require("../src/d2m/actions/register-user.test")
|
||||
@@ -164,7 +165,6 @@ file._actuallyUploadDiscordFileToMxc = function(url, res) { throw new Error(`Not
|
||||
require("../src/m2d/converters/diff-pins.test")
|
||||
require("../src/m2d/converters/event-to-message.test")
|
||||
require("../src/m2d/converters/emoji.test")
|
||||
require("../src/m2d/converters/utils.test")
|
||||
require("../src/m2d/converters/emoji-sheet.test")
|
||||
require("../src/discord/interactions/invite.test")
|
||||
require("../src/discord/interactions/matrix-info.test")
|
||||
|
||||
Reference in New Issue
Block a user