Convert @room to @everyone using permissions
This commit is contained in:
parent
25cd8cb289
commit
f5ffc09fab
3 changed files with 201 additions and 14 deletions
|
@ -57,7 +57,7 @@ async function withWebhook(channelID, callback) {
|
||||||
*/
|
*/
|
||||||
async function sendMessageWithWebhook(channelID, data, threadID) {
|
async function sendMessageWithWebhook(channelID, data, threadID) {
|
||||||
const result = await withWebhook(channelID, async webhook => {
|
const result = await withWebhook(channelID, async webhook => {
|
||||||
return discord.snow.webhook.executeWebhook(webhook.id, webhook.token, data, {wait: true, thread_id: threadID, disableEveryone: true})
|
return discord.snow.webhook.executeWebhook(webhook.id, webhook.token, data, {wait: true, thread_id: threadID})
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -384,19 +384,35 @@ async function handleRoomOrMessageLinks(input, di) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} content
|
* @param {string} content
|
||||||
|
* @param {string} senderMxid
|
||||||
|
* @param {string} roomID
|
||||||
* @param {DiscordTypes.APIGuild} guild
|
* @param {DiscordTypes.APIGuild} guild
|
||||||
* @param {{api: import("../../matrix/api"), snow: import("snowtransfer").SnowTransfer, fetch: import("node-fetch")["default"]}} di
|
* @param {{api: import("../../matrix/api"), snow: import("snowtransfer").SnowTransfer, fetch: import("node-fetch")["default"]}} di
|
||||||
*/
|
*/
|
||||||
async function checkWrittenMentions(content, guild, di) {
|
async function checkWrittenMentions(content, senderMxid, roomID, guild, di) {
|
||||||
let writtenMentionMatch = content.match(/(?:^|[^"[<>/A-Za-z0-9])@([A-Za-z][A-Za-z0-9._\[\]\(\)-]+):?/d) // /d flag for indices requires node.js 16+
|
let writtenMentionMatch = content.match(/(?:^|[^"[<>/A-Za-z0-9])@([A-Za-z][A-Za-z0-9._\[\]\(\)-]+):?/d) // /d flag for indices requires node.js 16+
|
||||||
if (writtenMentionMatch) {
|
if (writtenMentionMatch) {
|
||||||
|
if (writtenMentionMatch[1] === "room") { // convert @room to @everyone
|
||||||
|
const powerLevels = await di.api.getStateEvent(roomID, "m.room.power_levels", "")
|
||||||
|
const userPower = powerLevels.users?.[senderMxid] || 0
|
||||||
|
if (userPower >= powerLevels.notifications?.room) {
|
||||||
|
return {
|
||||||
|
// @ts-ignore - typescript doesn't know about indices yet
|
||||||
|
content: content.slice(0, writtenMentionMatch.indices[1][0]-1) + `@everyone` + content.slice(writtenMentionMatch.indices[1][1]),
|
||||||
|
ensureJoined: [],
|
||||||
|
allowedMentionsParse: ["everyone"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const results = await di.snow.guild.searchGuildMembers(guild.id, {query: writtenMentionMatch[1]})
|
const results = await di.snow.guild.searchGuildMembers(guild.id, {query: writtenMentionMatch[1]})
|
||||||
if (results[0]) {
|
if (results[0]) {
|
||||||
assert(results[0].user)
|
assert(results[0].user)
|
||||||
return {
|
return {
|
||||||
// @ts-ignore - typescript doesn't know about indices yet
|
// @ts-ignore - typescript doesn't know about indices yet
|
||||||
content: content.slice(0, writtenMentionMatch.indices[1][0]-1) + `<@${results[0].user.id}>` + content.slice(writtenMentionMatch.indices[1][1]),
|
content: content.slice(0, writtenMentionMatch.indices[1][0]-1) + `<@${results[0].user.id}>` + content.slice(writtenMentionMatch.indices[1][1]),
|
||||||
ensureJoined: results[0].user
|
ensureJoined: [results[0].user],
|
||||||
|
allowedMentionsParse: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,6 +443,7 @@ const attachmentEmojis = new Map([
|
||||||
async function eventToMessage(event, guild, di) {
|
async function eventToMessage(event, guild, di) {
|
||||||
let displayName = event.sender
|
let displayName = event.sender
|
||||||
let avatarURL = undefined
|
let avatarURL = undefined
|
||||||
|
const allowedMentionsParse = ["users", "roles"]
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
let messageIDsToEdit = []
|
let messageIDsToEdit = []
|
||||||
let replyLine = ""
|
let replyLine = ""
|
||||||
|
@ -656,10 +673,11 @@ async function eventToMessage(event, guild, di) {
|
||||||
for (; node; node = node.nextSibling) {
|
for (; node; node = node.nextSibling) {
|
||||||
// Check written mentions
|
// Check written mentions
|
||||||
if (node.nodeType === 3 && node.nodeValue.includes("@") && !nodeIsChildOf(node, ["A", "CODE", "PRE"])) {
|
if (node.nodeType === 3 && node.nodeValue.includes("@") && !nodeIsChildOf(node, ["A", "CODE", "PRE"])) {
|
||||||
const result = await checkWrittenMentions(node.nodeValue, guild, di)
|
const result = await checkWrittenMentions(node.nodeValue, event.sender, event.room_id, guild, di)
|
||||||
if (result) {
|
if (result) {
|
||||||
node.nodeValue = result.content
|
node.nodeValue = result.content
|
||||||
ensureJoined.push(result.ensureJoined)
|
ensureJoined.push(...result.ensureJoined)
|
||||||
|
allowedMentionsParse.push(...result.allowedMentionsParse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check for incompatible backticks in code blocks
|
// Check for incompatible backticks in code blocks
|
||||||
|
@ -727,10 +745,11 @@ async function eventToMessage(event, guild, di) {
|
||||||
content = await handleRoomOrMessageLinks(content, di) // Replace matrix.to links with discord.com equivalents where possible
|
content = await handleRoomOrMessageLinks(content, di) // Replace matrix.to links with discord.com equivalents where possible
|
||||||
content = content.replace(/\bhttps?:\/\/matrix\.to\/[^ )]*/, "<$&>") // Put < > around any surviving matrix.to links to hide the URL previews
|
content = content.replace(/\bhttps?:\/\/matrix\.to\/[^ )]*/, "<$&>") // Put < > around any surviving matrix.to links to hide the URL previews
|
||||||
|
|
||||||
const result = await checkWrittenMentions(content, guild, di)
|
const result = await checkWrittenMentions(content, event.sender, event.room_id, guild, di)
|
||||||
if (result) {
|
if (result) {
|
||||||
content = result.content
|
content = result.content
|
||||||
ensureJoined.push(result.ensureJoined)
|
ensureJoined.push(...result.ensureJoined)
|
||||||
|
allowedMentionsParse.push(...result.allowedMentionsParse)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Markdown needs to be escaped, though take care not to escape the middle of links
|
// Markdown needs to be escaped, though take care not to escape the middle of links
|
||||||
|
@ -787,7 +806,7 @@ async function eventToMessage(event, guild, di) {
|
||||||
const messages = chunks.map(content => ({
|
const messages = chunks.map(content => ({
|
||||||
content,
|
content,
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
parse: ["users", "roles"]
|
parse: allowedMentionsParse
|
||||||
},
|
},
|
||||||
username: displayNameShortened,
|
username: displayNameShortened,
|
||||||
avatar_url: avatarURL
|
avatar_url: avatarURL
|
||||||
|
|
|
@ -3786,6 +3786,174 @@ test("event2message: guessed @mentions work with other matrix bridge old users",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("event2message: @room converts to @everyone and is allowed when the room doesn't restrict who can use it (plaintext body)", async t => {
|
||||||
|
let called = 0
|
||||||
|
t.deepEqual(
|
||||||
|
await eventToMessage({
|
||||||
|
type: "m.room.message",
|
||||||
|
sender: "@cadence:cadence.moe",
|
||||||
|
content: {
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "@room dinner's ready",
|
||||||
|
},
|
||||||
|
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe",
|
||||||
|
event_id: "$SiXetU9h9Dg-M9Frcw_C6ahnoXZ3QPZe3MVJR5tcB9A"
|
||||||
|
}, data.guild.general, {
|
||||||
|
api: {
|
||||||
|
getStateEvent(roomID, type, key) {
|
||||||
|
called++
|
||||||
|
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
|
||||||
|
t.equal(type, "m.room.power_levels")
|
||||||
|
t.equal(key, "")
|
||||||
|
return {
|
||||||
|
users: {},
|
||||||
|
notifications: {
|
||||||
|
room: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
messagesToDelete: [],
|
||||||
|
messagesToEdit: [],
|
||||||
|
messagesToSend: [{
|
||||||
|
username: "cadence [they]",
|
||||||
|
content: "@everyone dinner's ready",
|
||||||
|
avatar_url: undefined,
|
||||||
|
allowed_mentions: {
|
||||||
|
parse: ["users", "roles", "everyone"]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
ensureJoined: []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("event2message: @room converts to @everyone but is not allowed when the room restricts who can use it", async t => {
|
||||||
|
let called = 0
|
||||||
|
t.deepEqual(
|
||||||
|
await eventToMessage({
|
||||||
|
type: "m.room.message",
|
||||||
|
sender: "@cadence:cadence.moe",
|
||||||
|
content: {
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "wrong body",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: "@room dinner's ready"
|
||||||
|
},
|
||||||
|
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe",
|
||||||
|
event_id: "$SiXetU9h9Dg-M9Frcw_C6ahnoXZ3QPZe3MVJR5tcB9A"
|
||||||
|
}, data.guild.general, {
|
||||||
|
api: {
|
||||||
|
getStateEvent(roomID, type, key) {
|
||||||
|
called++
|
||||||
|
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
|
||||||
|
t.equal(type, "m.room.power_levels")
|
||||||
|
t.equal(key, "")
|
||||||
|
return {
|
||||||
|
users: {},
|
||||||
|
notifications: {
|
||||||
|
room: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
messagesToDelete: [],
|
||||||
|
messagesToEdit: [],
|
||||||
|
messagesToSend: [{
|
||||||
|
username: "cadence [they]",
|
||||||
|
content: "@room dinner's ready",
|
||||||
|
avatar_url: undefined,
|
||||||
|
allowed_mentions: {
|
||||||
|
parse: ["users", "roles"]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
ensureJoined: []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("event2message: @room converts to @everyone and is allowed if the user has sufficient power to use it", async t => {
|
||||||
|
let called = 0
|
||||||
|
t.deepEqual(
|
||||||
|
await eventToMessage({
|
||||||
|
type: "m.room.message",
|
||||||
|
sender: "@cadence:cadence.moe",
|
||||||
|
content: {
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "wrong body",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: "@room dinner's ready"
|
||||||
|
},
|
||||||
|
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe",
|
||||||
|
event_id: "$SiXetU9h9Dg-M9Frcw_C6ahnoXZ3QPZe3MVJR5tcB9A"
|
||||||
|
}, data.guild.general, {
|
||||||
|
api: {
|
||||||
|
getStateEvent(roomID, type, key) {
|
||||||
|
called++
|
||||||
|
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
|
||||||
|
t.equal(type, "m.room.power_levels")
|
||||||
|
t.equal(key, "")
|
||||||
|
return {
|
||||||
|
users: {
|
||||||
|
"@cadence:cadence.moe": 20
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
room: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
messagesToDelete: [],
|
||||||
|
messagesToEdit: [],
|
||||||
|
messagesToSend: [{
|
||||||
|
username: "cadence [they]",
|
||||||
|
content: "@everyone dinner's ready",
|
||||||
|
avatar_url: undefined,
|
||||||
|
allowed_mentions: {
|
||||||
|
parse: ["users", "roles", "everyone"]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
ensureJoined: []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("event2message: @room in the middle of a link is not converted", async t => {
|
||||||
|
t.deepEqual(
|
||||||
|
await eventToMessage({
|
||||||
|
type: "m.room.message",
|
||||||
|
sender: "@cadence:cadence.moe",
|
||||||
|
content: {
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "wrong body",
|
||||||
|
format: "org.matrix.custom.html",
|
||||||
|
formatted_body: `<a href="https://github.com/@room/repositories">https://github.com/@room/repositories</a> https://github.com/@room/repositories`
|
||||||
|
},
|
||||||
|
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe",
|
||||||
|
event_id: "$SiXetU9h9Dg-M9Frcw_C6ahnoXZ3QPZe3MVJR5tcB9A"
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
messagesToDelete: [],
|
||||||
|
messagesToEdit: [],
|
||||||
|
messagesToSend: [{
|
||||||
|
username: "cadence [they]",
|
||||||
|
content: "https://github.com/@room/repositories https://github.com/@room/repositories",
|
||||||
|
avatar_url: undefined,
|
||||||
|
allowed_mentions: {
|
||||||
|
parse: ["users", "roles"]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
ensureJoined: []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
slow()("event2message: unknown emoji at the end is reuploaded as a sprite sheet", async t => {
|
slow()("event2message: unknown emoji at the end is reuploaded as a sprite sheet", async t => {
|
||||||
const messages = await eventToMessage({
|
const messages = await eventToMessage({
|
||||||
type: "m.room.message",
|
type: "m.room.message",
|
||||||
|
|
Loading…
Reference in a new issue