diff --git a/d2m/converters/message-to-event.test.js b/d2m/converters/message-to-event.test.js index 5524543..94b67ff 100644 --- a/d2m/converters/message-to-event.test.js +++ b/d2m/converters/message-to-event.test.js @@ -363,7 +363,7 @@ test("message2event: thread start message reference", async t => { api: { getEvent: mockGetEvent(t, "!PnyBKvUBOhjuCucEfk:cadence.moe", "$FchUVylsOfmmbj-VwEs5Z9kY49_dt2zd0vWfylzy5Yo", { "type": "m.room.message", - "sender": "@_ooye_cadence:cadence.moe", + "sender": "@_ooye_kyuugryphon:cadence.moe", "content": { "m.mentions": {}, "msgtype": "m.text", diff --git a/m2d/converters/event-to-message.js b/m2d/converters/event-to-message.js index 91dea5e..75e04f9 100644 --- a/m2d/converters/event-to-message.js +++ b/m2d/converters/event-to-message.js @@ -227,7 +227,7 @@ async function eventToMessage(event, guild, di) { await (async () => { const repliedToEventId = event.content["m.relates_to"]?.["m.in_reply_to"]?.event_id if (!repliedToEventId) return - const repliedToEvent = await di.api.getEvent(event.room_id, repliedToEventId) + let repliedToEvent = await di.api.getEvent(event.room_id, repliedToEventId) if (!repliedToEvent) return const row = db.prepare("SELECT channel_id, message_id FROM event_message INNER JOIN message_channel USING (message_id) WHERE event_id = ? ORDER BY part").get(repliedToEventId) if (row) { @@ -243,6 +243,11 @@ async function eventToMessage(event, guild, di) { } else { replyLine += `Ⓜ️**${senderName}**` } + // If the event has been edited, the homeserver will include the relation in `unsigned`. + if (repliedToEvent.unsigned?.["m.relations"]?.["m.replace"]?.content?.["m.new_content"]) { + repliedToEvent = repliedToEvent.unsigned["m.relations"]["m.replace"] // Note: this changes which event_id is in repliedToEvent. + repliedToEvent.content = repliedToEvent.content["m.new_content"] + } let contentPreview const fileReplyContentAlternative = ( repliedToEvent.content.msgtype === "m.image" ? "🖼️" @@ -254,7 +259,12 @@ async function eventToMessage(event, guild, di) { contentPreview = " " + fileReplyContentAlternative } else { const repliedToContent = repliedToEvent.content.formatted_body || repliedToEvent.content.body - const contentPreviewChunks = chunk(repliedToContent.replace(/.*<\/mx-reply>/, "").replace(/.*?<\/blockquote>/, "").replace(/(?:\n|
)+/g, " ").replace(/<[^>]+>/g, ""), 50) + const contentPreviewChunks = chunk( + repliedToContent.replace(/.*<\/mx-reply>/, "") // Remove everything before replies, so just use the actual message body + .replace(/.*?<\/blockquote>/, "") // If the message starts with a blockquote, don't count it and use the message body afterwards + .replace(/(?:\n|
)+/g, " ") // Should all be on one line + .replace(/]*data-mx-spoiler\b[^>]*>.*?<\/span>/g, "[spoiler]") // Good enough method of removing spoiler content. (I don't want to break out the HTML parser unless I have to.) + .replace(/<[^>]+>/g, ""), 50) // Completely strip all other formatting. contentPreview = ":\n> " contentPreview += contentPreviewChunks.length > 1 ? contentPreviewChunks[0] + "..." : contentPreviewChunks[0] } diff --git a/m2d/converters/event-to-message.test.js b/m2d/converters/event-to-message.test.js index 4d85092..e7d2314 100644 --- a/m2d/converters/event-to-message.test.js +++ b/m2d/converters/event-to-message.test.js @@ -595,6 +595,82 @@ test("event2message: rich reply to a sim user", async t => { ) }) +test("event2message: rich reply to an already-edited message will quote the new message content", async t => { + t.deepEqual( + await eventToMessage({ + "type": "m.room.message", + "sender": "@cadence:cadence.moe", + "content": { + "msgtype": "m.text", + "body": "> <@_ooye_kyuugryphon:cadence.moe> this is the new content. heya!\n\nhiiiii....", + "format": "org.matrix.custom.html", + "formatted_body": "
In reply to @_ooye_kyuugryphon:cadence.moe
this is the new content. heya!
hiiiii....", + "m.relates_to": { + "m.in_reply_to": { + "event_id": "$DSQvWxOBB2DYaei6b83-fb33dQGYt5LJd_s8Nl2a43Q" + } + } + }, + "origin_server_ts": 1693029683016, + "unsigned": { + "age": 91, + "transaction_id": "m1693029682894.510" + }, + "event_id": "$v_Gtr-bzv9IVlSLBO5DstzwmiDd-GSFaNfHX66IupV8", + "room_id": "!fGgIymcYWOqjbSRUdV:cadence.moe" + }, data.guild.general, { + api: { + getEvent: mockGetEvent(t, "!fGgIymcYWOqjbSRUdV:cadence.moe", "$DSQvWxOBB2DYaei6b83-fb33dQGYt5LJd_s8Nl2a43Q", { + type: "m.room.message", + room_id: "!fGgIymcYWOqjbSRUdV:cadence.moe", + sender: "@_ooye_kyuugryphon:cadence.moe", + content: { + "m.mentions": {}, + msgtype: "m.text", + body: "this is the old content. don't use this!" + }, + unsigned: { + "m.relations": { + "m.replace": { + type: "m.room.message", + room_id: "!fGgIymcYWOqjbSRUdV:cadence.moe", + sender: "@_ooye_kyuugryphon:cadence.moe", + content: { + "m.mentions": {}, + msgtype: "m.text", + body: "* this is the new content. heya!", + "m.new_content": { + "m.mentions": {}, + msgtype: "m.text", + body: "this is the new content. heya!" + }, + "m.relates_to": { + rel_type: "m.replace", + event_id: "$DSQvWxOBB2DYaei6b83-fb33dQGYt5LJd_s8Nl2a43Q" + } + }, + event_id: "$JOrl8ycWpo7NIAxZ4u-VJmANVrZFBF41LXyp30y8VvU", + user_id: "@_ooye_kyuugryphon:cadence.moe", + } + } + } + }) + } + }), + { + messagesToDelete: [], + messagesToEdit: [], + messagesToSend: [{ + username: "cadence [they]", + content: "> <:L1:1144820033948762203><:L2:1144820084079087647><@111604486476181504>:" + + "\n> this is the new content. heya!" + + "\nhiiiii....", + avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/azCAhThKTojXSZJRoWwZmhvU" + }] + } + ) +}) + test("event2message: should avoid using blockquote contents as reply preview in rich reply to a sim user", async t => { t.deepEqual( await eventToMessage({ @@ -642,8 +718,6 @@ test("event2message: should avoid using blockquote contents as reply preview in ) }) - - test("event2message: editing a rich reply to a sim user", async t => { const eventsFetched = [] t.deepEqual( @@ -947,20 +1021,20 @@ test("event2message: rich reply to a matrix user's long message with formatting" "type": "m.room.message", "sender": "@cadence:cadence.moe", "content": { - "msgtype": "m.text", - "body": "> <@cadence:cadence.moe> ```\n> i should have a little happy test\n> ```\n> * list **bold** _em_ ~~strike~~\n> # heading 1\n> ## heading 2\n> ### heading 3\n> https://cadence.moe\n> [legit website](https://cadence.moe)\n\nno you can't!!!", - "format": "org.matrix.custom.html", - "formatted_body": "
In reply to @cadence:cadence.moe
i should have a little happy test\n
\n
    \n
  • list bold em ~~strike~~
  • \n
\n

heading 1

\n

heading 2

\n

heading 3

\n

https://cadence.moe
legit website

\n
no you can't!!!", - "m.relates_to": { - "m.in_reply_to": { - "event_id": "$Fxy8SMoJuTduwReVkHZ1uHif9EuvNx36Hg79cltiA04" - } - } + "msgtype": "m.text", + "body": "> <@cadence:cadence.moe> ```\n> i should have a little happy test\n> ```\n> * list **bold** _em_ ~~strike~~\n> # heading 1\n> ## heading 2\n> ### heading 3\n> https://cadence.moe\n> [legit website](https://cadence.moe)\n\nno you can't!!!", + "format": "org.matrix.custom.html", + "formatted_body": "
In reply to @cadence:cadence.moe
i should have a little happy test\n
\n
    \n
  • list bold em ~~strike~~
  • \n
\n

heading 1

\n

heading 2

\n

heading 3

\n

https://cadence.moe
legit website

\n
no you can't!!!", + "m.relates_to": { + "m.in_reply_to": { + "event_id": "$Fxy8SMoJuTduwReVkHZ1uHif9EuvNx36Hg79cltiA04" + } + } }, "origin_server_ts": 1693037401693, "unsigned": { - "age": 381, - "transaction_id": "m1693037401592.521" + "age": 381, + "transaction_id": "m1693037401592.521" }, "event_id": "$v_Gtr-bzv9IVlSLBO5DstzwmiDd-GSFaNfHX66IupV8", "room_id": "!fGgIymcYWOqjbSRUdV:cadence.moe" @@ -998,20 +1072,20 @@ test("event2message: rich reply to an image", async t => { "type": "m.room.message", "sender": "@cadence:cadence.moe", "content": { - "msgtype": "m.text", - "body": "> <@cadence:cadence.moe> sent an image.\n\nCaught in 8K UHD VR QLED Epic Edition", - "format": "org.matrix.custom.html", - "formatted_body": "
In reply to @cadence:cadence.moe
sent an image.
Caught in 8K UHD VR QLED Epic Edition", - "m.relates_to": { - "m.in_reply_to": { - "event_id": "$Fxy8SMoJuTduwReVkHZ1uHif9EuvNx36Hg79cltiA04" - } - } + "msgtype": "m.text", + "body": "> <@cadence:cadence.moe> sent an image.\n\nCaught in 8K UHD VR QLED Epic Edition", + "format": "org.matrix.custom.html", + "formatted_body": "
In reply to @cadence:cadence.moe
sent an image.
Caught in 8K UHD VR QLED Epic Edition", + "m.relates_to": { + "m.in_reply_to": { + "event_id": "$Fxy8SMoJuTduwReVkHZ1uHif9EuvNx36Hg79cltiA04" + } + } }, "origin_server_ts": 1693037401693, "unsigned": { - "age": 381, - "transaction_id": "m1693037401592.521" + "age": 381, + "transaction_id": "m1693037401592.521" }, "event_id": "$v_Gtr-bzv9IVlSLBO5DstzwmiDd-GSFaNfHX66IupV8", "room_id": "!fGgIymcYWOqjbSRUdV:cadence.moe" @@ -1050,6 +1124,58 @@ test("event2message: rich reply to an image", async t => { ) }) +test("event2message: rich reply to a spoiler should ensure the spoiler is hidden", async t => { + t.deepEqual( + await eventToMessage({ + "type": "m.room.message", + "sender": "@cadence:cadence.moe", + "content": { + "msgtype": "m.text", + "body": "> <@cadence:cadence.moe> ||zoe kills a 5 letter noun at the end. don't tell anybody|| cw crossword spoilers you'll never believe\n\nomg NO WAY!!", + "format": "org.matrix.custom.html", + "formatted_body": "
In reply to @cadence:cadence.moe
zoe kills a 5 letter noun at the end. don't tell anybody cw crossword spoilers you'll never believe
omg NO WAY!!", + "m.relates_to": { + "m.in_reply_to": { + "event_id": "$Fxy8SMoJuTduwReVkHZ1uHif9EuvNx36Hg79cltiA04" + } + } + }, + "origin_server_ts": 1693037401693, + "unsigned": { + "age": 381, + "transaction_id": "m1693037401592.521" + }, + "event_id": "$v_Gtr-bzv9IVlSLBO5DstzwmiDd-GSFaNfHX66IupV8", + "room_id": "!fGgIymcYWOqjbSRUdV:cadence.moe" + }, data.guild.general, { + api: { + getEvent: mockGetEvent(t, "!fGgIymcYWOqjbSRUdV:cadence.moe", "$Fxy8SMoJuTduwReVkHZ1uHif9EuvNx36Hg79cltiA04", { + type: "m.room.message", + sender: "@_ooye_kyuugryphon:cadence.moe", + content: { + "m.mentions": {}, + msgtype: "m.text", + body: "||zoe kills a 5 letter noun at the end. don't tell anybody|| cw crossword spoilers you'll never believe", + format: "org.matrix.custom.html", + formatted_body: `zoe kills a 5 letter noun at the end. don't tell anybody cw crossword spoilers you'll never believe` + } + }) + } + }), + { + messagesToDelete: [], + messagesToEdit: [], + messagesToSend: [{ + username: "cadence [they]", + content: "> <:L1:1144820033948762203><:L2:1144820084079087647>https://discord.com/channels/112760669178241024/687028734322147344/1144865310588014633 <@111604486476181504>:" + + "\n> [spoiler] cw crossword spoilers you'll never..." + + "\nomg NO WAY!!", + avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/azCAhThKTojXSZJRoWwZmhvU" + }] + } + ) +}) + test("event2message: with layered rich replies, the preview should only be the real text", async t => { t.deepEqual( await eventToMessage({