diff --git a/m2d/converters/event-to-message.js b/m2d/converters/event-to-message.js
index 2dcb2e8..ce5cfcd 100644
--- a/m2d/converters/event-to-message.js
+++ b/m2d/converters/event-to-message.js
@@ -33,6 +33,12 @@ const markdownEscapes = [
[/^>/g, '\\>'],
[/_/g, '\\_'],
[/^(\d+)\. /g, '$1\\. ']
+ /*
+ Strikethrough is deliberately not escaped. Usually when Matrix users type ~~ it's not because they wanted to send ~~,
+ it's because they wanted strikethrough and it didn't work because their client doesn't support it.
+ As bridge developers, we can choose between "messages should look as similar as possible" vs "it was most likely intended to be strikethrough".
+ I went with the latter. Even though the appearance doesn't match, I'd rather it displayed as originally intended for 80% of the readers than for 0%.
+ */
]
const turndownService = new TurndownService({
@@ -87,11 +93,15 @@ turndownService.addRule("blockquote", {
turndownService.addRule("spoiler", {
filter: function (node, options) {
- return node.hasAttribute("data-mx-spoiler")
+ return node.tagName === "SPAN" && node.hasAttribute("data-mx-spoiler")
},
replacement: function (content, node) {
- return "||" + content + "||"
+ if (node.getAttribute("data-mx-spoiler")) {
+ // escape parentheses so it can't become a link
+ return `\\(${node.getAttribute("data-mx-spoiler")}\\) ||${content}||`
+ }
+ return `||${content}||`
}
})
@@ -509,6 +519,8 @@ async function eventToMessage(event, guild, di) {
const fileReplyContentAlternative = attachmentEmojis.get(repliedToEvent.content.msgtype)
if (fileReplyContentAlternative) {
contentPreview = " " + fileReplyContentAlternative
+ } else if (repliedToEvent.unsigned?.redacted_because) {
+ contentPreview = " (in reply to a deleted message)"
} else {
const repliedToContent = repliedToEvent.content.formatted_body || repliedToEvent.content.body
const contentPreviewChunks = chunk(
@@ -628,6 +640,9 @@ async function eventToMessage(event, guild, di) {
// It's designed for commonmark, we need to replace the space-space-newline with just newline
content = content.replace(/ \n/g, "\n")
+ // If there's a blockquote at the start of the message body and this message is a reply, they should be visually separated
+ if (replyLine && content.startsWith("> ")) content = "\n" + content
+
// SPRITE SHEET EMOJIS FEATURE:
content = await uploadEndOfMessageSpriteSheet(content, attachments, pendingFiles)
} else {
diff --git a/m2d/converters/event-to-message.test.js b/m2d/converters/event-to-message.test.js
index f116f8c..fb21098 100644
--- a/m2d/converters/event-to-message.test.js
+++ b/m2d/converters/event-to-message.test.js
@@ -234,6 +234,37 @@ test("event2message: spoilers work", async t => {
)
})
+test("event2message: spoiler reasons work", async t => {
+ t.deepEqual(
+ await eventToMessage({
+ content: {
+ msgtype: "m.text",
+ body: "wrong body",
+ format: "org.matrix.custom.html",
+ formatted_body: `zoe kills a 5 letter noun at the end`
+ },
+ event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU",
+ origin_server_ts: 1688301929913,
+ room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe",
+ sender: "@cadence:cadence.moe",
+ type: "m.room.message",
+ unsigned: {
+ age: 405299
+ }
+ }),
+ {
+ ensureJoined: [],
+ messagesToDelete: [],
+ messagesToEdit: [],
+ messagesToSend: [{
+ username: "cadence [they]",
+ content: "\\(cw crossword spoilers you'll never believe. don't tell anybody\\) ||zoe kills a 5 letter noun at the end||",
+ avatar_url: undefined
+ }]
+ }
+ )
+})
+
test("event2message: markdown syntax is escaped", async t => {
t.deepEqual(
await eventToMessage({
@@ -1778,6 +1809,115 @@ test("event2message: with layered rich replies, the preview should only be the r
)
})
+test("event2message: if event is a reply and starts with a quote, they should be separated by a blank line, so that they don't visually merge together", async t => {
+ t.deepEqual(
+ await eventToMessage({
+ type: "m.room.message",
+ sender: "@aflower:syndicated.gay",
+ content: {
+ body: "> <@aflower:syndicated.gay> i have a feeling that clients are *meant to* strip these reply fallbacks too, just that none of them, in reality, do\n\n>To strip the fallback on the body
, the client should iterate over each line of the string, removing any lines that start with the fallback prefix ("> “, including the space, without quotes) and stopping when a line is encountered without the prefix. This prefix is known as the “fallback prefix sequence”.",
+ format: "org.matrix.custom.html",
+ formatted_body: "In reply to @aflower:syndicated.gay
i have a feeling that clients are meant to strip these reply fallbacks too, just that none of them, in reality, do
\n", + "im.nheko.relations.v1.relations": [ + { + event_id: "$tTYQcke93fwocsc1K6itwUq85EG0RZ0ksCuIglKioks", + rel_type: "im.nheko.relations.v1.in_reply_to" + } + ], + "m.relates_to": { + "m.in_reply_to": { + event_id: "$tTYQcke93fwocsc1K6itwUq85EG0RZ0ksCuIglKioks" + } + }, + msgtype: "m.text" + }, + room_id: "!TqlyQmifxGUggEmdBN:cadence.moe", + event_id: "$nCvtZeBFedYuEavt4OftloCHc0kaFW2ktHCfIOklhjU", + }, data.guild.general, { + api: { + getEvent: mockGetEvent(t, "!TqlyQmifxGUggEmdBN:cadence.moe", "$tTYQcke93fwocsc1K6itwUq85EG0RZ0ksCuIglKioks", { + sender: "@aflower:syndicated.gay", + type: "m.room.message", + content: { + body: "i have a feeling that clients are *meant to* strip these reply fallbacks too, just that none of them, in reality, do", + format: "org.matrix.custom.html", + formatted_body: "i have a feeling that clients are meant to strip these reply fallbacks too, just that none of them, in reality, do", + msgtype: "m.text" + } + }) + } + }), + { + ensureJoined: [], + messagesToDelete: [], + messagesToEdit: [], + messagesToSend: [{ + username: "Rose", + content: "> <:L1:1144820033948762203><:L2:1144820084079087647>Ⓜ️**Rose**:" + + "\n> i have a feeling that clients are meant to strip..." + + "\n" + + "\n> To strip the fallback on the `body`, the client should iterate over each line of the string, removing any lines that start with the fallback prefix (\"> “, including the space, without quotes) and stopping when a line is encountered without the prefix. This prefix is known as the “fallback prefix sequence”.", + avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/syndicated.gay/ZkBUPXCiXTjdJvONpLJmcbKP" + }] + } + ) +}) + +test("event2message: rich reply to a deleted event", async t => { + t.deepEqual( + await eventToMessage({ + type: "m.room.message", + sender: "@ampflower:matrix.org", + content: { + msgtype: "m.text", + body: "> <@ampflower:matrix.org> \n\nHuh it did the same thing here too", + format: "org.matrix.custom.html", + formatted_body: "To strip the fallback on the
\nbody
, the client should iterate over each line of the string, removing any lines that start with the fallback prefix ("> “, including the space, without quotes) and stopping when a line is encountered without the prefix. This prefix is known as the “fallback prefix sequence”.
In reply to @ampflower:matrix.org