diff --git a/package-lock.json b/package-lock.json
index d58d72b..bf857a5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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"
}
diff --git a/package.json b/package.json
index 05a4220..64e6f77 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/d2m/converters/message-to-event.embeds.test.js b/src/d2m/converters/message-to-event.embeds.test.js
index 85a08cc..4895993 100644
--- a/src/d2m/converters/message-to-event.embeds.test.js
+++ b/src/d2m/converters/message-to-event.embeds.test.js
@@ -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: `https://youtu.be/kDMHHw8JqLE?si=NaqNjVTtXugHeG_E
Jutomi I'm gonna make these sounds in your walls tonight`,
+ "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, [{
diff --git a/src/d2m/converters/message-to-event.js b/src/d2m/converters/message-to-event.js
index 4ff1f4d..73fd691 100644
--- a/src/d2m/converters/message-to-event.js
+++ b/src/d2m/converters/message-to-event.js
@@ -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()
diff --git a/src/d2m/converters/message-to-event.test.js b/src/d2m/converters/message-to-event.test.js
index 3303b27..aba80d2 100644
--- a/src/d2m/converters/message-to-event.test.js
+++ b/src/d2m/converters/message-to-event.test.js
@@ -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: `beatrice`
+ }])
+})
+
test("message2event: very large attachment is linked instead of being uploaded", async t => {
const events = await messageToEvent({
content: "hey",