Compare commits
3 commits
043f178d1e
...
a190e690b1
Author | SHA1 | Date | |
---|---|---|---|
a190e690b1 | |||
12d85c982e | |||
0f1cf7a20c |
8 changed files with 77 additions and 18 deletions
|
@ -12,6 +12,8 @@ const file = sync.require("../../matrix/file")
|
||||||
const api = sync.require("../../matrix/api")
|
const api = sync.require("../../matrix/api")
|
||||||
/** @type {import("../../matrix/kstate")} */
|
/** @type {import("../../matrix/kstate")} */
|
||||||
const ks = sync.require("../../matrix/kstate")
|
const ks = sync.require("../../matrix/kstate")
|
||||||
|
/** @type {import("../../discord/utils")} */
|
||||||
|
const utils = sync.require("../../discord/utils")
|
||||||
/** @type {import("./create-space")}) */
|
/** @type {import("./create-space")}) */
|
||||||
const createSpace = sync.require("./create-space") // watch out for the require loop
|
const createSpace = sync.require("./create-space") // watch out for the require loop
|
||||||
|
|
||||||
|
@ -119,6 +121,9 @@ async function channelToKState(channel, guild) {
|
||||||
join_rules = {join_rule: PRIVACY_ENUMS.ROOM_JOIN_RULES[privacyLevel]}
|
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 channelKState = {
|
const channelKState = {
|
||||||
"m.room.name/": {name: convertedName},
|
"m.room.name/": {name: convertedName},
|
||||||
"m.room.topic/": {topic: convertedTopic},
|
"m.room.topic/": {topic: convertedTopic},
|
||||||
|
@ -136,7 +141,7 @@ async function channelToKState(channel, guild) {
|
||||||
"m.room.avatar": 0
|
"m.room.avatar": 0
|
||||||
},
|
},
|
||||||
notifications: {
|
notifications: {
|
||||||
room: 20 // TODO: Matrix users should have the same abilities as unprivileged Discord members. So make this automatically configured based on the guild or channel's default mention everyone permissions. That way if unprivileged Discord members can mention everyone, Matrix users can too.
|
room: everyoneCanMentionEveryone ? 0 : 20
|
||||||
},
|
},
|
||||||
users: reg.ooye.invite.reduce((a, c) => (a[c] = 100, a), {})
|
users: reg.ooye.invite.reduce((a, c) => (a[c] = 100, a), {})
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
const mixin = require("mixin-deep")
|
||||||
const {channelToKState, _convertNameAndTopic} = require("./create-room")
|
const {channelToKState, _convertNameAndTopic} = require("./create-room")
|
||||||
const {kstateStripConditionals} = require("../../matrix/kstate")
|
const {kstateStripConditionals} = require("../../matrix/kstate")
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
|
@ -39,6 +40,16 @@ test("channel2room: invite-only privacy room", async t => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("channel2room: room where limited people can mention everyone", async t => {
|
||||||
|
const limitedGuild = mixin({}, testData.guild.general)
|
||||||
|
limitedGuild.roles[0].permissions = (BigInt(limitedGuild.roles[0].permissions) - 131072n).toString()
|
||||||
|
const limitedRoom = mixin({}, testData.room.general, {"m.room.power_levels/": {notifications: {room: 20}}})
|
||||||
|
t.deepEqual(
|
||||||
|
kstateStripConditionals(await channelToKState(testData.channel.general, limitedGuild).then(x => x.channelKState)),
|
||||||
|
limitedRoom
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test("convertNameAndTopic: custom name and topic", t => {
|
test("convertNameAndTopic: custom name and topic", t => {
|
||||||
t.deepEqual(
|
t.deepEqual(
|
||||||
_convertNameAndTopic({id: "123", name: "the-twilight-zone", topic: "Spooky stuff here. :ghost:", type: 0}, {id: "456"}, "hauntings"),
|
_convertNameAndTopic({id: "123", name: "the-twilight-zone", topic: "Spooky stuff here. :ghost:", type: 0}, {id: "456"}, "hauntings"),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const assert = require("assert")
|
const assert = require("assert").strict
|
||||||
const reg = require("../../matrix/read-registration")
|
const reg = require("../../matrix/read-registration")
|
||||||
const DiscordTypes = require("discord-api-types/v10")
|
const DiscordTypes = require("discord-api-types/v10")
|
||||||
const mixin = require("mixin-deep")
|
const mixin = require("mixin-deep")
|
||||||
|
@ -180,11 +180,12 @@ function _hashProfileContent(content, powerLevel) {
|
||||||
* 5. If the state content or power level have changed, send them to Matrix and update them in the database for next time
|
* 5. If the state content or power level have changed, send them to Matrix and update them in the database for next time
|
||||||
* @param {DiscordTypes.APIUser} user
|
* @param {DiscordTypes.APIUser} user
|
||||||
* @param {Omit<DiscordTypes.APIGuildMember, "user">} member
|
* @param {Omit<DiscordTypes.APIGuildMember, "user">} member
|
||||||
* @param {DiscordTypes.APIGuild} guild
|
|
||||||
* @param {DiscordTypes.APIGuildChannel} channel
|
* @param {DiscordTypes.APIGuildChannel} channel
|
||||||
|
* @param {DiscordTypes.APIGuild} guild
|
||||||
|
* @param {string} roomID
|
||||||
* @returns {Promise<string>} mxid of the updated sim
|
* @returns {Promise<string>} mxid of the updated sim
|
||||||
*/
|
*/
|
||||||
async function syncUser(user, member, guild, channel, roomID) {
|
async function syncUser(user, member, channel, guild, roomID) {
|
||||||
const mxid = await ensureSimJoined(user, roomID)
|
const mxid = await ensureSimJoined(user, roomID)
|
||||||
const content = await memberToStateContent(user, member, guild.id)
|
const content = await memberToStateContent(user, member, guild.id)
|
||||||
const powerLevel = memberToPowerLevel(user, member, guild, channel)
|
const powerLevel = memberToPowerLevel(user, member, guild, channel)
|
||||||
|
@ -204,6 +205,9 @@ async function syncUser(user, member, guild, channel, roomID) {
|
||||||
return mxid
|
return mxid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} roomID
|
||||||
|
*/
|
||||||
async function syncAllUsersInRoom(roomID) {
|
async function syncAllUsersInRoom(roomID) {
|
||||||
const mxids = select("sim_member", "mxid", {room_id: roomID}).pluck().all()
|
const mxids = select("sim_member", "mxid", {room_id: roomID}).pluck().all()
|
||||||
|
|
||||||
|
@ -228,7 +232,7 @@ async function syncAllUsersInRoom(roomID) {
|
||||||
assert.ok(user)
|
assert.ok(user)
|
||||||
|
|
||||||
console.log(`[user sync] to matrix: ${user.username} in ${channel.name}`)
|
console.log(`[user sync] to matrix: ${user.username} in ${channel.name}`)
|
||||||
await syncUser(user, member, guild, channel, roomID)
|
await syncUser(user, member, channel, guild, roomID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
const assert = require("assert")
|
const assert = require("assert").strict
|
||||||
|
const DiscordTypes = require("discord-api-types/v10")
|
||||||
|
|
||||||
const passthrough = require("../../passthrough")
|
const passthrough = require("../../passthrough")
|
||||||
const { discord, sync, db } = passthrough
|
const { discord, sync, db } = passthrough
|
||||||
|
@ -18,17 +19,18 @@ const createRoom = sync.require("../actions/create-room")
|
||||||
const dUtils = sync.require("../../discord/utils")
|
const dUtils = sync.require("../../discord/utils")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message
|
* @param {DiscordTypes.GatewayMessageCreateDispatchData} message
|
||||||
* @param {import("discord-api-types/v10").APIGuild} guild
|
* @param {DiscordTypes.APIGuildChannel} channel
|
||||||
|
* @param {DiscordTypes.APIGuild} guild
|
||||||
* @param {{speedbump_id: string, speedbump_webhook_id: string} | null} row data about the webhook which is proxying messages in this channel
|
* @param {{speedbump_id: string, speedbump_webhook_id: string} | null} row data about the webhook which is proxying messages in this channel
|
||||||
*/
|
*/
|
||||||
async function sendMessage(message, guild, row) {
|
async function sendMessage(message, channel, guild, row) {
|
||||||
const roomID = await createRoom.ensureRoom(message.channel_id)
|
const roomID = await createRoom.ensureRoom(message.channel_id)
|
||||||
|
|
||||||
let senderMxid = null
|
let senderMxid = null
|
||||||
if (!dUtils.isWebhookMessage(message)) {
|
if (!dUtils.isWebhookMessage(message)) {
|
||||||
if (message.member) { // available on a gateway message create event
|
if (message.member) { // available on a gateway message create event
|
||||||
senderMxid = await registerUser.syncUser(message.author, message.member, message.guild_id, roomID)
|
senderMxid = await registerUser.syncUser(message.author, message.member, channel, guild, roomID)
|
||||||
} else { // well, good enough...
|
} else { // well, good enough...
|
||||||
senderMxid = await registerUser.ensureSimJoined(message.author, roomID)
|
senderMxid = await registerUser.ensureSimJoined(message.author, roomID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,7 +248,7 @@ module.exports = {
|
||||||
if (affected) return
|
if (affected) return
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await sendMessage.sendMessage(message, guild, row),
|
await sendMessage.sendMessage(message, channel, guild, row),
|
||||||
await discordCommandHandler.execute(message, channel, guild)
|
await discordCommandHandler.execute(message, channel, guild)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ function getPermissions(userRoles, guildRoles, userID, channelOverwrites) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channelOverwrites) {
|
if (channelOverwrites) {
|
||||||
/** @type {((overwrite: Required<DiscordTypes.APIGuildChannel>["permission_overwrites"][0]) => any)[]} */
|
/** @type {((overwrite: Required<DiscordTypes.APIOverwrite>) => any)[]} */
|
||||||
const actions = [
|
const actions = [
|
||||||
// Channel @everyone deny
|
// Channel @everyone deny
|
||||||
overwrite => overwrite.id === everyoneID && (allowed &= ~BigInt(overwrite.deny)),
|
overwrite => overwrite.id === everyoneID && (allowed &= ~BigInt(overwrite.deny)),
|
||||||
|
@ -63,7 +63,7 @@ function getPermissions(userRoles, guildRoles, userID, channelOverwrites) {
|
||||||
*/
|
*/
|
||||||
function hasPermission(resolvedPermissions, permissionToCheckFor) {
|
function hasPermission(resolvedPermissions, permissionToCheckFor) {
|
||||||
// Make sure permissionToCheckFor has exactly one permission in it
|
// Make sure permissionToCheckFor has exactly one permission in it
|
||||||
assert.equal(permissionToCheckFor.toString(2).match(/1/g), 1)
|
assert.equal(permissionToCheckFor.toString(2).match(/1/g)?.length, 1)
|
||||||
// Do the actual calculation
|
// Do the actual calculation
|
||||||
return (resolvedPermissions & permissionToCheckFor) === permissionToCheckFor
|
return (resolvedPermissions & permissionToCheckFor) === permissionToCheckFor
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
const DiscordTypes = require("discord-api-types/v10")
|
||||||
const {test} = require("supertape")
|
const {test} = require("supertape")
|
||||||
const data = require("../test/data")
|
const data = require("../test/data")
|
||||||
const utils = require("./utils")
|
const utils = require("./utils")
|
||||||
|
@ -82,3 +83,27 @@ test("getPermissions: channel overwrite to allow role works", t => {
|
||||||
const want = BigInt(1 << 10 | 1 << 16)
|
const want = BigInt(1 << 10 | 1 << 16)
|
||||||
t.equal((permissions & want), want)
|
t.equal((permissions & want), want)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("hasSomePermissions: detects the permission", t => {
|
||||||
|
const userPermissions = DiscordTypes.PermissionFlagsBits.MentionEveryone | DiscordTypes.PermissionFlagsBits.BanMembers
|
||||||
|
const canRemoveMembers = utils.hasSomePermissions(userPermissions, ["KickMembers", "BanMembers"])
|
||||||
|
t.equal(canRemoveMembers, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("hasSomePermissions: doesn't detect not the permission", t => {
|
||||||
|
const userPermissions = DiscordTypes.PermissionFlagsBits.MentionEveryone | DiscordTypes.PermissionFlagsBits.SendMessages
|
||||||
|
const canRemoveMembers = utils.hasSomePermissions(userPermissions, ["KickMembers", "BanMembers"])
|
||||||
|
t.equal(canRemoveMembers, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("hasAllPermissions: detects the permissions", t => {
|
||||||
|
const userPermissions = DiscordTypes.PermissionFlagsBits.KickMembers | DiscordTypes.PermissionFlagsBits.BanMembers | DiscordTypes.PermissionFlagsBits.MentionEveryone
|
||||||
|
const canRemoveMembers = utils.hasAllPermissions(userPermissions, ["KickMembers", "BanMembers"])
|
||||||
|
t.equal(canRemoveMembers, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("hasAllPermissions: doesn't detect not the permissions", t => {
|
||||||
|
const userPermissions = DiscordTypes.PermissionFlagsBits.MentionEveryone | DiscordTypes.PermissionFlagsBits.SendMessages | DiscordTypes.PermissionFlagsBits.KickMembers
|
||||||
|
const canRemoveMembers = utils.hasAllPermissions(userPermissions, ["KickMembers", "BanMembers"])
|
||||||
|
t.equal(canRemoveMembers, false)
|
||||||
|
})
|
||||||
|
|
22
test/data.js
22
test/data.js
|
@ -47,6 +47,9 @@ module.exports = {
|
||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
"@test_auto_invite:example.org": 100
|
"@test_auto_invite:example.org": 100
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
room: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chat.schildi.hide_ui/read_receipts": {hidden: true},
|
"chat.schildi.hide_ui/read_receipts": {hidden: true},
|
||||||
|
@ -98,7 +101,6 @@ module.exports = {
|
||||||
icon: "a_f83622e09ead74f0c5c527fe241f8f8c",
|
icon: "a_f83622e09ead74f0c5c527fe241f8f8c",
|
||||||
emojis: [
|
emojis: [
|
||||||
{
|
{
|
||||||
version: 0,
|
|
||||||
roles: [],
|
roles: [],
|
||||||
require_colons: true,
|
require_colons: true,
|
||||||
name: "hippo",
|
name: "hippo",
|
||||||
|
@ -108,7 +110,6 @@ module.exports = {
|
||||||
animated: false
|
animated: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
version: 0,
|
|
||||||
roles: [],
|
roles: [],
|
||||||
require_colons: true,
|
require_colons: true,
|
||||||
name: "hipposcope",
|
name: "hipposcope",
|
||||||
|
@ -121,7 +122,20 @@ module.exports = {
|
||||||
premium_subscription_count: 14,
|
premium_subscription_count: 14,
|
||||||
roles: [
|
roles: [
|
||||||
{
|
{
|
||||||
version: 1696964862461,
|
unicode_emoji: null,
|
||||||
|
tags: {},
|
||||||
|
position: 0,
|
||||||
|
permissions: '559623605575360',
|
||||||
|
name: '@everyone',
|
||||||
|
mentionable: false,
|
||||||
|
managed: false,
|
||||||
|
id: '112760669178241024',
|
||||||
|
icon: null,
|
||||||
|
hoist: false,
|
||||||
|
flags: 0,
|
||||||
|
color: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
unicode_emoji: null,
|
unicode_emoji: null,
|
||||||
tags: {},
|
tags: {},
|
||||||
position: 22,
|
position: 22,
|
||||||
|
@ -135,7 +149,6 @@ module.exports = {
|
||||||
flags: 0,
|
flags: 0,
|
||||||
color: 0
|
color: 0
|
||||||
}, {
|
}, {
|
||||||
version: 1696964862776,
|
|
||||||
unicode_emoji: null,
|
unicode_emoji: null,
|
||||||
tags: {},
|
tags: {},
|
||||||
position: 131,
|
position: 131,
|
||||||
|
@ -149,7 +162,6 @@ module.exports = {
|
||||||
flags: 0,
|
flags: 0,
|
||||||
color: 11076095
|
color: 11076095
|
||||||
}, {
|
}, {
|
||||||
version: 1696964862698,
|
|
||||||
unicode_emoji: '🍂',
|
unicode_emoji: '🍂',
|
||||||
tags: {},
|
tags: {},
|
||||||
position: 102,
|
position: 102,
|
||||||
|
|
Loading…
Reference in a new issue