diff --git a/db/ooye-test-data.sql b/db/ooye-test-data.sql index bb789b21..ef665931 100644 --- a/db/ooye-test-data.sql +++ b/db/ooye-test-data.sql @@ -32,7 +32,9 @@ INSERT INTO message_channel (message_id, channel_id) VALUES ('1141619794500649020', '497161350934560778'), ('1143121514925928541', '1100319550446252084'), ('1144865310588014633', '687028734322147344'), -('1145688633186193479', '1100319550446252084'); +('1145688633186193479', '1100319550446252084'), +('1145688633186193480', '1100319550446252084'), +('1145688633186193481', '1100319550446252084'); INSERT INTO event_message (event_id, event_type, event_subtype, message_id, part, source) VALUES ('$X16nfVks1wsrhq4E9SSLiqrf2N8KD0erD0scZG7U5xg', 'm.room.message', 'm.text', '1126786462646550579', 0, 1), @@ -51,7 +53,9 @@ INSERT INTO event_message (event_id, event_type, event_subtype, message_id, part ('$f9cjKiacXI9qPF_nUAckzbiKnJEi0LM399kOkhdd8f8', 'm.sticker', NULL, '1106366167788044450', 0, 0), ('$Fxy8SMoJuTduwReVkHZ1uHif9EuvNx36Hg79cltiA04', 'm.room.message', 'm.text', '1144865310588014633', 0, 1), ('$v_Gtr-bzv9IVlSLBO5DstzwmiDd-GSFaNfHX66IupV8', 'm.room.message', 'm.text', '1144874214311067708', 0, 0), -('$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSs', 'm.room.message', 'm.text', '1145688633186193479', 0, 0); +('$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSs', 'm.room.message', 'm.text', '1145688633186193479', 0, 0), +('$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSt', 'm.room.message', 'm.text', '1145688633186193480', 0, 0), +('$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSt', 'm.room.message', 'm.text', '1145688633186193481', 1, 0); INSERT INTO file (discord_url, mxc_url) VALUES ('https://cdn.discordapp.com/attachments/497161332244742154/1124628646431297546/image.png', 'mxc://cadence.moe/qXoZktDqNtEGuOCZEADAMvhM'), @@ -66,7 +70,7 @@ INSERT INTO file (discord_url, mxc_url) VALUES INSERT INTO member_cache (room_id, mxid, displayname, avatar_url) VALUES ('!kLRqKKUQXcibIMtOpl:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL), -('!BpMdOUkWWhFxmTrENV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL), +('!BpMdOUkWWhFxmTrENV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'malformed mxc'), ('!fGgIymcYWOqjbSRUdV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU'), ('!PnyBKvUBOhjuCucEfk:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU'); diff --git a/m2d/converters/event-to-message.js b/m2d/converters/event-to-message.js index 9c221a5e..3e77560c 100644 --- a/m2d/converters/event-to-message.js +++ b/m2d/converters/event-to-message.js @@ -21,10 +21,6 @@ const BLOCK_ELEMENTS = [ "TFOOT", "TH", "THEAD", "TR", "UL" ] -function cleanAttribute (attribute) { - return attribute ? attribute.replace(/(\n+\s*)+/g, "\n") : "" -} - const turndownService = new TurndownService({ hr: "----", headingStyle: "atx", @@ -80,11 +76,9 @@ turndownService.addRule("inlineLink", { if (node.getAttribute("data-user-id")) return `<@${node.getAttribute("data-user-id")}>` if (node.getAttribute("data-channel-id")) return `<#${node.getAttribute("data-channel-id")}>` const href = node.getAttribute("href") - let title = cleanAttribute(node.getAttribute("title")) - if (title) title = ` "` + title + `"` let brackets = ["", ""] if (href.startsWith("https://matrix.to")) brackets = ["<", ">"] - return "[" + content + "](" + brackets[0] + href + title + brackets[1] + ")" + return "[" + content + "](" + brackets[0] + href + brackets[1] + ")" } }) @@ -304,6 +298,11 @@ async function eventToMessage(event, guild, di) { } } + // Ensure there is code coverage for adding, editing, and deleting + if (messagesToSend.length) void 0 + if (messagesToEdit.length) void 0 + if (messageIDsToEdit.length) void 0 + return { messagesToEdit, messagesToSend, diff --git a/m2d/converters/event-to-message.test.js b/m2d/converters/event-to-message.test.js index 41bb2e75..411d97c5 100644 --- a/m2d/converters/event-to-message.test.js +++ b/m2d/converters/event-to-message.test.js @@ -1,6 +1,8 @@ const {test} = require("supertape") const {eventToMessage} = require("./event-to-message") const data = require("../../test/data") +const {MatrixServerError} = require("../../matrix/mreq") +const {db} = require("../../passthrough") /** * @param {string} roomID @@ -97,7 +99,7 @@ test("event2message: basic html is converted to markdown", async t => { msgtype: "m.text", body: "wrong body", format: "org.matrix.custom.html", - formatted_body: "this is a test of formatting" + formatted_body: "this is a test of formatting" }, event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", origin_server_ts: 1688301929913, @@ -113,7 +115,7 @@ test("event2message: basic html is converted to markdown", async t => { messagesToEdit: [], messagesToSend: [{ username: "cadence [they]", - content: "this **is** a **_test_** of ~~formatting~~", + content: "this **is** a _**test** __of___ ~~formatting~~", avatar_url: undefined }] } @@ -426,6 +428,34 @@ test("event2message: quotes have an appropriate amount of whitespace", async t = ) }) +test("event2message: m.emote plaintext works", async t => { + t.deepEqual( + await eventToMessage({ + content: { + msgtype: "m.emote", + body: "tests an m.emote message" + }, + event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", + origin_server_ts: 1688301929913, + room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe", + sender: "@cadence:cadence.moe", + type: "m.room.message", + unsigned: { + age: 405299 + } + }), + { + messagesToDelete: [], + messagesToEdit: [], + messagesToSend: [{ + username: "cadence [they]", + content: "\\* cadence [they] tests an m.emote message", + avatar_url: undefined + }] + } + ) +}) + test("event2message: m.emote markdown syntax is escaped", async t => { t.deepEqual( await eventToMessage({ @@ -639,6 +669,112 @@ test("event2message: editing a plaintext body message", async t => { ) }) +test("event2message: editing a plaintext message to be longer", async t => { + t.deepEqual( + await eventToMessage({ + "type": "m.room.message", + "sender": "@cadence:cadence.moe", + "content": { + "msgtype": "m.text", + "body": " * " + "aaaaaaaaa ".repeat(198) + "well, I guess it's no longer brand new... it's existed for mere seconds..." + "aaaaaaaaa ".repeat(20), + "m.new_content": { + "msgtype": "m.text", + "body": "aaaaaaaaa ".repeat(198) + "well, I guess it's no longer brand new... it's existed for mere seconds..." + "aaaaaaaaa ".repeat(20) + }, + "m.relates_to": { + "rel_type": "m.replace", + "event_id": "$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSs" + } + }, + "origin_server_ts": 1693223873912, + "unsigned": { + "age": 42, + "transaction_id": "m1693223873796.842" + }, + "event_id": "$KxGwvVNzNcmlVbiI2m5kX-jMFNi3Jle71-uu1j7P7vM", + "room_id": "!PnyBKvUBOhjuCucEfk:cadence.moe" + }, data.guild.general, { + api: { + getEvent: mockGetEvent(t, "!PnyBKvUBOhjuCucEfk:cadence.moe", "$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSs", { + type: "m.room.message", + sender: "@cadence:cadence.moe", + content: { + msgtype: "m.text", + body: "brand new, never before seen message", + } + }) + } + }), + { + messagesToDelete: [], + messagesToEdit: [{ + id: "1145688633186193479", + message: { + content: "aaaaaaaaa ".repeat(198) + "well, I guess it's", + username: "cadence [they]", + avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/azCAhThKTojXSZJRoWwZmhvU" + } + }], + messagesToSend: [{ + content: "no longer brand new... it's existed for mere seconds..." + ("aaaaaaaaa ".repeat(20)).slice(0, -1), + username: "cadence [they]", + avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/azCAhThKTojXSZJRoWwZmhvU" + }] + } + ) +}) + +test("event2message: editing a plaintext message to be shorter", async t => { + t.deepEqual( + await eventToMessage({ + "type": "m.room.message", + "sender": "@cadence:cadence.moe", + "content": { + "msgtype": "m.text", + "body": " * well, I guess it's no longer brand new... it's existed for mere seconds...", + "m.new_content": { + "msgtype": "m.text", + "body": "well, I guess it's no longer brand new... it's existed for mere seconds..." + }, + "m.relates_to": { + "rel_type": "m.replace", + "event_id": "$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSt" + } + }, + "origin_server_ts": 1693223873912, + "unsigned": { + "age": 42, + "transaction_id": "m1693223873796.842" + }, + "event_id": "$KxGwvVNzNcmlVbiI2m5kX-jMFNi3Jle71-uu1j7P7vM", + "room_id": "!PnyBKvUBOhjuCucEfk:cadence.moe" + }, data.guild.general, { + api: { + getEvent: mockGetEvent(t, "!PnyBKvUBOhjuCucEfk:cadence.moe", "$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSt", { + type: "m.room.message", + sender: "@cadence:cadence.moe", + content: { + msgtype: "m.text", + body: "aaaaaaaaa ".repeat(198) + "well, I guess it's no longer brand new... it's existed for mere seconds..." + "aaaaaaaaa ".repeat(20) + } + }) + } + }), + { + messagesToDelete: ["1145688633186193481"], + messagesToEdit: [{ + id: "1145688633186193480", + message: { + username: "cadence [they]", + content: "well, I guess it's no longer brand new... it's existed for mere seconds...", + avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/azCAhThKTojXSZJRoWwZmhvU" + } + }], + messagesToSend: [] + } + ) +}) + test("event2message: editing a formatted body message", async t => { t.deepEqual( await eventToMessage({ @@ -865,7 +1001,7 @@ test("event2message: mentioning bridged rooms works", async t => { msgtype: "m.text", body: "wrong body", format: "org.matrix.custom.html", - formatted_body: `I'm just testing mentions` + formatted_body: `I'm just worm-form testing channel mentions` }, event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", origin_server_ts: 1688301929913, @@ -881,9 +1017,96 @@ test("event2message: mentioning bridged rooms works", async t => { messagesToEdit: [], messagesToSend: [{ username: "cadence [they]", - content: "I'm just [▲]() testing mentions", + content: "I'm just <#1100319550446252084> testing channel mentions", avatar_url: undefined }] } ) }) + +test("event2message: caches the member if the member is not known", async t => { + let called = 0 + t.deepEqual( + await eventToMessage({ + content: { + body: "testing the member state cache", + msgtype: "m.text" + }, + event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", + origin_server_ts: 1688301929913, + room_id: "!should_be_newly_cached:cadence.moe", + sender: "@should_be_newly_cached:cadence.moe", + type: "m.room.message", + unsigned: { + age: 405299 + } + }, {}, { + api: { + getStateEvent: async (roomID, type, stateKey) => { + called++ + t.equal(roomID, "!should_be_newly_cached:cadence.moe") + t.equal(type, "m.room.member") + t.equal(stateKey, "@should_be_newly_cached:cadence.moe") + return { + displayname: "this is the username", + avatar_url: undefined + } + } + } + }), + { + messagesToDelete: [], + messagesToEdit: [], + messagesToSend: [{ + username: "this is the username", + content: "testing the member state cache", + avatar_url: undefined + }] + } + ) + t.deepEqual(db.prepare("SELECT avatar_url, displayname, mxid FROM member_cache WHERE room_id = '!should_be_newly_cached:cadence.moe'").all(), [ + {avatar_url: null, displayname: "this is the username", mxid: "@should_be_newly_cached:cadence.moe"} + ]) + t.equal(called, 1, "getStateEvent should be called once") +}) + +test("event2message: skips caching the member if the member does not exist, somehow", async t => { + let called = 0 + t.deepEqual( + await eventToMessage({ + content: { + body: "should honestly never happen", + msgtype: "m.text" + }, + event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", + origin_server_ts: 1688301929913, + room_id: "!not_real:cadence.moe", + sender: "@not_real:cadence.moe", + type: "m.room.message", + unsigned: { + age: 405299 + } + }, {}, { + api: { + getStateEvent: async (roomID, type, stateKey) => { + called++ + t.equal(roomID, "!not_real:cadence.moe") + t.equal(type, "m.room.member") + t.equal(stateKey, "@not_real:cadence.moe") + throw new MatrixServerError("State event doesn't exist or something") + } + } + }), + { + messagesToDelete: [], + messagesToEdit: [], + messagesToSend: [{ + username: "not_real", + content: "should honestly never happen", + avatar_url: undefined + }] + } + ) + t.deepEqual(db.prepare("SELECT avatar_url, displayname, mxid FROM member_cache WHERE room_id = '!not_real:cadence.moe'").all(), []) + t.equal(called, 1, "getStateEvent should be called once") +})