Better text spoilers
Remove spoiler content from plaintext body Don't bridge embeds if their link is spoilered (deliberately imprecise)
This commit is contained in:
37
package-lock.json
generated
37
package-lock.json
generated
@@ -10,7 +10,7 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@chriscdn/promise-semaphore": "^3.0.1",
|
||||
"@cloudrac3r/discord-markdown": "^2.6.7",
|
||||
"@cloudrac3r/discord-markdown": "^2.6.10",
|
||||
"@cloudrac3r/giframe": "^0.4.3",
|
||||
"@cloudrac3r/html-template-tag": "^5.0.1",
|
||||
"@cloudrac3r/in-your-element": "^1.1.1",
|
||||
@@ -242,9 +242,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudrac3r/discord-markdown": {
|
||||
"version": "2.6.8",
|
||||
"resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.8.tgz",
|
||||
"integrity": "sha512-ZrSimHqmLqXR+W3U1n6ge6poAjmQaMzXyWrTkT36znrgKhfuQAYxLBtKTf7m+cmr3VlaDVM2P+iPdSeTeaM0qg==",
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.10.tgz",
|
||||
"integrity": "sha512-E+F9UYDUHP2kHDCciX63SBzgsUnHpu2Pp/h98x9Zo+vKuzXjCQ5PcFNdUlH6M18bvHDZPoIsKVmjnON8UYaAPQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"simple-markdown": "^0.7.3"
|
||||
@@ -1227,26 +1227,15 @@
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
||||
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.2.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz",
|
||||
"integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==",
|
||||
"version": "19.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz",
|
||||
"integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
"csstype": "^3.0.2"
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/scheduler": {
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
|
||||
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
@@ -1668,9 +1657,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "2.0.2",
|
||||
@@ -2825,6 +2815,7 @@
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/simple-markdown/-/simple-markdown-0.7.3.tgz",
|
||||
"integrity": "sha512-uGXIc13NGpqfPeFJIt/7SHHxd6HekEJYtsdoCM06mEBPL9fQH/pSD7LRM6PZ7CKchpSvxKL4tvwMamqAaNDAyg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react": ">=16.0.0"
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@chriscdn/promise-semaphore": "^3.0.1",
|
||||
"@cloudrac3r/discord-markdown": "^2.6.7",
|
||||
"@cloudrac3r/discord-markdown": "^2.6.10",
|
||||
"@cloudrac3r/giframe": "^0.4.3",
|
||||
"@cloudrac3r/html-template-tag": "^5.0.1",
|
||||
"@cloudrac3r/in-your-element": "^1.1.1",
|
||||
@@ -64,6 +64,7 @@
|
||||
"scripts": {
|
||||
"start": "node --enable-source-maps start.js",
|
||||
"setup": "node --enable-source-maps scripts/setup.js",
|
||||
"build": "mkdir -p dist/out-of-your-element && cp -R src dist/out-of-your-element && cp -R docs dist/out-of-your-element && npx tsdown",
|
||||
"addbot": "node addbot.js",
|
||||
"test": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap --no-worker test/test.js | tap-dot",
|
||||
"test-slow": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap --no-worker test/test.js -- --slow | tap-dot",
|
||||
|
||||
@@ -312,6 +312,21 @@ test("message2event embeds: youtube video", async t => {
|
||||
}])
|
||||
})
|
||||
|
||||
test("message2event embeds: embed not bridged if its link was spoilered", async t => {
|
||||
const events = await messageToEvent({
|
||||
...data.message_with_embeds.youtube_video,
|
||||
content: "||https://youtu.be/kDMHHw8JqLE?si=NaqNjVTtXugHeG_E\n\n\nJutomi I'm gonna make these sounds in your walls tonight||"
|
||||
}, data.guild.general)
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
msgtype: "m.text",
|
||||
body: "[spoiler]",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: `<span data-mx-spoiler=""><a href="https://youtu.be/kDMHHw8JqLE?si=NaqNjVTtXugHeG_E">https://youtu.be/kDMHHw8JqLE?si=NaqNjVTtXugHeG_E</a><br><br><br>Jutomi I'm gonna make these sounds in your walls tonight</span>`,
|
||||
"m.mentions": {}
|
||||
}])
|
||||
})
|
||||
|
||||
test("message2event embeds: tenor gif should show a video link without a provider", async t => {
|
||||
const events = await messageToEvent(data.message_with_embeds.tenor_gif, data.guild.general, {}, {})
|
||||
t.deepEqual(events, [{
|
||||
|
||||
@@ -26,8 +26,9 @@ const userRegex = reg.namespaces.users.map(u => new RegExp(u.regex))
|
||||
* @param {DiscordTypes.APIMessage} message
|
||||
* @param {DiscordTypes.APIGuild} guild
|
||||
* @param {boolean} useHTML
|
||||
* @param {string[]} spoilers
|
||||
*/
|
||||
function getDiscordParseCallbacks(message, guild, useHTML) {
|
||||
function getDiscordParseCallbacks(message, guild, useHTML, spoilers = []) {
|
||||
return {
|
||||
/** @param {{id: string, type: "discordUser"}} node */
|
||||
user: node => {
|
||||
@@ -90,6 +91,10 @@ function getDiscordParseCallbacks(message, guild, useHTML) {
|
||||
here: () => {
|
||||
if (message.mention_everyone) return "@room"
|
||||
return "@here"
|
||||
},
|
||||
spoiler: node => {
|
||||
spoilers.push(node.raw)
|
||||
return useHTML
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,6 +397,7 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||
return content.replace(/https:\/\/(cdn|media)\.discordapp\.(?:com|net)\/attachments\/([0-9]+)\/([0-9]+)\/([-A-Za-z0-9_.,]+)/g, url => dUtils.getPublicUrlForCdn(url))
|
||||
}
|
||||
|
||||
const spoilers = []
|
||||
/**
|
||||
* Translate links and emojis and mentions and stuff. Give back the text and HTML so they can be combined into bigger events.
|
||||
* @param {string} content Partial or complete Discord message content
|
||||
@@ -431,7 +437,7 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||
}
|
||||
|
||||
let html = await markdown.toHtmlWithPostParser(content, transformParsedVia, {
|
||||
discordCallback: getDiscordParseCallbacks(message, guild, true),
|
||||
discordCallback: getDiscordParseCallbacks(message, guild, true, spoilers),
|
||||
...customOptions
|
||||
}, customParser, customHtmlOutput)
|
||||
|
||||
@@ -692,6 +698,13 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||
continue // If discord creates an embed preview for a discord channel link, don't copy that embed
|
||||
}
|
||||
|
||||
if (embed.url && spoilers.some(sp => sp.match(/\bhttps?:\/\/[a-z]/))) {
|
||||
// If the original message had spoilered URLs, don't generate any embeds for links.
|
||||
// This logic is the same as the Discord desktop client. It doesn't match specific embeds to specific spoilered text, it's all or nothing.
|
||||
// It's not easy to do much better because posting a link like youtu.be generates an embed.url with youtube.com/watch, so you can't match up the text without making at least that a special case.
|
||||
continue
|
||||
}
|
||||
|
||||
// Start building up a replica ("rep") of the embed in Discord-markdown format, which we will convert into both plaintext and formatted body at once
|
||||
const rep = new mxUtils.MatrixStringBuilder()
|
||||
|
||||
|
||||
@@ -862,6 +862,20 @@ test("message2event: advanced written @mentions for matrix users", async t => {
|
||||
t.equal(called, 1, "should only look up the member list once")
|
||||
})
|
||||
|
||||
test("message2event: spoilers are removed from plaintext body", async t => {
|
||||
const events = await messageToEvent({
|
||||
content: "||**beatrice**||"
|
||||
})
|
||||
t.deepEqual(events, [{
|
||||
$type: "m.room.message",
|
||||
"m.mentions": {},
|
||||
msgtype: "m.text",
|
||||
body: "[spoiler]",
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: `<span data-mx-spoiler=""><strong>beatrice</strong></span>`
|
||||
}])
|
||||
})
|
||||
|
||||
test("message2event: very large attachment is linked instead of being uploaded", async t => {
|
||||
const events = await messageToEvent({
|
||||
content: "hey",
|
||||
|
||||
Reference in New Issue
Block a user