diff --git a/d2m/actions/create-room.js b/d2m/actions/create-room.js index 2095130..e778d22 100644 --- a/d2m/actions/create-room.js +++ b/d2m/actions/create-room.js @@ -283,6 +283,7 @@ async function _unbridgeRoom(channelID) { assert.ok(spaceID) // remove room from being a space member + await api.sendState(roomID, "m.space.parent", spaceID, {}) await api.sendState(spaceID, "m.space.child", roomID, {}) // send a notification in the room diff --git a/d2m/actions/edit-message.js b/d2m/actions/edit-message.js index fa152cf..d7cb81d 100644 --- a/d2m/actions/edit-message.js +++ b/d2m/actions/edit-message.js @@ -24,7 +24,7 @@ async function editMessage(message, guild) { await api.sendEvent(roomID, eventType, newContentWithoutType, senderMxid) // Ensure the database is up to date. // The columns are event_id, event_type, event_subtype, message_id, channel_id, part, source. Only event_subtype could potentially be changed by a replacement event. - const subtype = newContentWithoutType.msgtype ?? null + const subtype = newContentWithoutType.msgtype || null db.prepare("UPDATE event_message SET event_subtype = ? WHERE event_id = ?").run(subtype, oldID) } diff --git a/d2m/converters/edit-to-changes.js b/d2m/converters/edit-to-changes.js index 3f4b2d2..814bb97 100644 --- a/d2m/converters/edit-to-changes.js +++ b/d2m/converters/edit-to-changes.js @@ -23,7 +23,7 @@ async function editToChanges(message, guild, api) { const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(message.channel_id) /** @type {string?} */ - let senderMxid = db.prepare("SELECT mxid FROM sim WHERE discord_id = ?").pluck().get(message.author.id) ?? null + let senderMxid = db.prepare("SELECT mxid FROM sim WHERE discord_id = ?").pluck().get(message.author.id) || null if (senderMxid) { const senderIsInRoom = db.prepare("SELECT * FROM sim_member WHERE room_id = ? and mxid = ?").get(roomID, senderMxid) if (!senderIsInRoom) { @@ -66,7 +66,7 @@ async function editToChanges(message, guild, api) { // Find a new event to pair it with... for (let i = 0; i < oldEventRows.length; i++) { const olde = oldEventRows[i] - if (olde.event_type === newe.$type && olde.event_subtype === (newe.msgtype ?? null)) { // The spec does allow subtypes to change, so I can change this condition later if I want to + if (olde.event_type === newe.$type && olde.event_subtype === (newe.msgtype || null)) { // The spec does allow subtypes to change, so I can change this condition later if I want to // Found one! // Set up the pairing eventsToReplace.push({ diff --git a/d2m/converters/edit-to-changes.test.js b/d2m/converters/edit-to-changes.test.js index 674cb15..022b396 100644 --- a/d2m/converters/edit-to-changes.test.js +++ b/d2m/converters/edit-to-changes.test.js @@ -103,6 +103,32 @@ test("edit2changes: add caption back to that image", async t => { t.deepEqual(eventsToReplace, []) }) +test("edit2changes: stickers and attachments are not changed, only the content can be edited", async t => { + const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.edited_content_with_sticker_and_attachments, data.guild.general, {}) + t.deepEqual(eventsToRedact, []) + t.deepEqual(eventsToSend, []) + t.deepEqual(eventsToReplace, [{ + oldID: "$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qdFv4", + newContent: { + $type: "m.room.message", + msgtype: "m.text", + body: "* only the content can be edited", + "m.mentions": {}, + // *** Replaced With: *** + "m.new_content": { + msgtype: "m.text", + body: "only the content can be edited", + "m.mentions": {} + }, + "m.relates_to": { + rel_type: "m.replace", + event_id: "$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qdFv4" + } + } + }]) +}) + + test("edit2changes: edit of reply to skull webp attachment with content", async t => { const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.edit_of_reply_to_skull_webp_attachment_with_content, data.guild.general, {}) t.deepEqual(eventsToRedact, []) diff --git a/db/data-for-test.sql b/db/data-for-test.sql index e88b967..7148b19 100644 --- a/db/data-for-test.sql +++ b/db/data-for-test.sql @@ -83,7 +83,10 @@ INSERT INTO event_message (event_id, event_type, event_subtype, message_id, chan ('$vgTKOR5ZTYNMKaS7XvgEIDaOWZtVCEyzLLi5Pc5Gz4M', 'm.room.message', 'm.text', '1128084851279536279', '112760669178241024', 0, 1), ('$YUJFa5j0ZJe7PUvD2DykRt9g51RoadUEYmuJLdSEbJ0', 'm.room.message', 'm.image', '1128084851279536279', '112760669178241024', 1, 1), ('$oLyUTyZ_7e_SUzGNWZKz880ll9amLZvXGbArJCKai2Q', 'm.room.message', 'm.text', '1128084748338741392', '112760669178241024', 0, 1), -('$FchUVylsOfmmbj-VwEs5Z9kY49_dt2zd0vWfylzy5Yo', 'm.room.message', 'm.text', '1143121514925928541', '1100319550446252084', 0, 1); +('$FchUVylsOfmmbj-VwEs5Z9kY49_dt2zd0vWfylzy5Yo', 'm.room.message', 'm.text', '1143121514925928541', '1100319550446252084', 0, 1), +('$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qdFv4', 'm.room.message', 'm.text', '1106366167788044450', '122155380120748034', 0, 1), +('$Ijf1MFCD39ktrNHxrA-i2aKoRWNYdAV2ZXYQeiZIgEU', 'm.room.message', 'm.image', '1106366167788044450', '122155380120748034', 0, 0), +('$f9cjKiacXI9qPF_nUAckzbiKnJEi0LM399kOkhdd8f8', 'm.sticker', NULL, '1106366167788044450', '122155380120748034', 0, 0); INSERT INTO file (discord_url, mxc_url) VALUES ('https://cdn.discordapp.com/attachments/497161332244742154/1124628646431297546/image.png', 'mxc://cadence.moe/qXoZktDqNtEGuOCZEADAMvhM'), diff --git a/m2d/converters/event-to-message.js b/m2d/converters/event-to-message.js index dde77b7..f793f85 100644 --- a/m2d/converters/event-to-message.js +++ b/m2d/converters/event-to-message.js @@ -2,19 +2,41 @@ const Ty = require("../../types") const DiscordTypes = require("discord-api-types/v10") -const markdown = require("discord-markdown") +const chunk = require("chunk-text") +const TurndownService = require("turndown") const passthrough = require("../../passthrough") const { sync, db, discord } = passthrough /** @type {import("../../matrix/file")} */ const file = sync.require("../../matrix/file") +// https://github.com/mixmark-io/turndown/blob/97e4535ca76bb2e70d9caa2aa4d4686956b06d44/src/utilities.js#L26C28-L33C2 +const BLOCK_ELEMENTS = [ + "ADDRESS", "ARTICLE", "ASIDE", "AUDIO", "BLOCKQUOTE", "BODY", "CANVAS", + "CENTER", "DD", "DETAILS", "DIR", "DIV", "DL", "DT", "FIELDSET", "FIGCAPTION", "FIGURE", + "FOOTER", "FORM", "FRAMESET", "H1", "H2", "H3", "H4", "H5", "H6", "HEADER", + "HGROUP", "HR", "HTML", "ISINDEX", "LI", "MAIN", "MENU", "NAV", "NOFRAMES", + "NOSCRIPT", "OL", "OUTPUT", "P", "PRE", "SECTION", "SUMMARY", "TABLE", "TBODY", "TD", + "TFOOT", "TH", "THEAD", "TR", "UL" +] + +const turndownService = new TurndownService({ + hr: "----" +}) + +turndownService.addRule("strikethrough", { + filter: ["del", "s", "strike"], + replacement: function (content) { + return "~~" + content + "~~" + } +}) + /** * @param {Ty.Event.Outer} event */ function eventToMessage(event) { /** @type {(DiscordTypes.RESTPostAPIWebhookWithTokenJSONBody & {files?: {name: string, file: Buffer}[]})[]} */ - const messages = [] + let messages = [] let displayName = event.sender let avatarURL = undefined @@ -24,20 +46,51 @@ function eventToMessage(event) { // TODO: get the media repo domain and the avatar url from the matrix member event } - if (event.content.msgtype === "m.text") { - messages.push({ - content: event.content.body, - username: displayName, - avatar_url: avatarURL - }) - } else if (event.content.msgtype === "m.emote") { - messages.push({ - content: `\* _${displayName} ${event.content.body}_`, - username: displayName, - avatar_url: avatarURL + // Convert content depending on what the message is + let content = event.content.body // ultimate fallback + if (event.content.format === "org.matrix.custom.html" && event.content.formatted_body) { + let input = event.content.formatted_body + if (event.content.msgtype === "m.emote") { + input = `* ${displayName} ${input}` + } + + // Note: Element's renderers on Web and Android currently collapse whitespace, like the browser does. Turndown also collapses whitespace which is good for me. + // If later I'm using a client that doesn't collapse whitespace and I want turndown to follow suit, uncomment the following line of code, and it Just Works: + // input = input.replace(/ /g, " ") + // There is also a corresponding test to uncomment, named "event2message: whitespace is retained" + + // The matrix spec hasn't decided whether \n counts as a newline or not, but I'm going to count it, because if it's in the data it's there for a reason. + // But I should not count it if it's between block elements. + input = input.replace(/(<\/?([^ >]+)[^>]*>)?\n(<\/?([^ >]+)[^>]*>)?/g, (whole, beforeContext, beforeTag, afterContext, afterTag) => { + if (typeof beforeTag !== "string" && typeof afterTag !== "string") { + return "
" + } + beforeContext = beforeContext || "" + beforeTag = beforeTag || "" + afterContext = afterContext || "" + afterTag = afterTag || "" + if (!BLOCK_ELEMENTS.includes(beforeTag.toUpperCase()) && !BLOCK_ELEMENTS.includes(afterTag.toUpperCase())) { + return beforeContext + "
" + afterContext + } else { + return whole + } }) + + // @ts-ignore + content = turndownService.turndown(input) + + // It's optimised for commonmark, we need to replace the space-space-newline with just newline + content = content.replace(/ \n/g, "\n") } + // Split into 2000 character chunks + const chunks = chunk(content, 2000) + messages = messages.concat(chunks.map(content => ({ + content, + username: displayName, + avatar_url: avatarURL + }))) + return messages } diff --git a/m2d/converters/event-to-message.test.js b/m2d/converters/event-to-message.test.js index a45c23b..ac62bf3 100644 --- a/m2d/converters/event-to-message.test.js +++ b/m2d/converters/event-to-message.test.js @@ -4,6 +4,12 @@ const {test} = require("supertape") const {eventToMessage} = require("./event-to-message") const data = require("../../test/data") +function sameFirstContentAndWhitespace(t, a, b) { + const a2 = JSON.stringify(a[0].content) + const b2 = JSON.stringify(b[0].content) + t.equal(a2, b2) +} + test("event2message: janky test", t => { t.deepEqual( eventToMessage({ @@ -28,6 +34,165 @@ test("event2message: janky test", t => { ) }) +test("event2message: basic html is converted to markdown", t => { + t.deepEqual( + eventToMessage({ + content: { + msgtype: "m.text", + body: "wrong body", + format: "org.matrix.custom.html", + formatted_body: "this is a test of formatting" + }, + event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", + origin_server_ts: 1688301929913, + room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe", + sender: "@cadence:cadence.moe", + type: "m.room.message", + unsigned: { + age: 405299 + } + }), + [{ + username: "cadence", + content: "this **is** a **_test_** of ~~formatting~~", + avatar_url: undefined + }] + ) +}) + +test("event2message: markdown syntax is escaped", t => { + t.deepEqual( + eventToMessage({ + content: { + msgtype: "m.text", + body: "wrong body", + format: "org.matrix.custom.html", + formatted_body: "this **is** an extreme \\*test\\* of" + }, + event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", + origin_server_ts: 1688301929913, + room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe", + sender: "@cadence:cadence.moe", + type: "m.room.message", + unsigned: { + age: 405299 + } + }), + [{ + username: "cadence", + content: "this \\*\\*is\\*\\* an **_extreme_** \\\\\\*test\\\\\\* of", + avatar_url: undefined + }] + ) +}) + +test("event2message: html lines are bridged correctly", t => { + t.deepEqual( + eventToMessage({ + content: { + msgtype: "m.text", + body: "wrong body", + format: "org.matrix.custom.html", + formatted_body: "

paragraph one
line two
line three

paragraph two\nline two\nline three\n\nparagraph three

paragraph four\nline two
line three\nline four

paragraph five" + }, + event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", + origin_server_ts: 1688301929913, + room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe", + sender: "@cadence:cadence.moe", + type: "m.room.message", + unsigned: { + age: 405299 + } + }), + [{ + username: "cadence", + content: "paragraph one\nline _two_\nline three\n\nparagraph two\nline _two_\nline three\n\nparagraph three\n\nparagraph four\nline two\nline three\nline four\n\nparagraph five", + avatar_url: undefined + }] + ) +}) + +/*test("event2message: whitespace is retained", t => { + t.deepEqual( + eventToMessage({ + content: { + msgtype: "m.text", + body: "wrong body", + format: "org.matrix.custom.html", + formatted_body: "line one: test test
line two: test test
line three: test test
line four: test test
line five" + }, + event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", + origin_server_ts: 1688301929913, + room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe", + sender: "@cadence:cadence.moe", + type: "m.room.message", + unsigned: { + age: 405299 + } + }), + [{ + username: "cadence", + content: "line one: test test\nline two: **test** **test**\nline three: **test test**\nline four: test test\n line five", + avatar_url: undefined + }] + ) +})*/ + +test("event2message: whitespace is collapsed", t => { + sameFirstContentAndWhitespace( + t, + eventToMessage({ + content: { + msgtype: "m.text", + body: "wrong body", + format: "org.matrix.custom.html", + formatted_body: "line one: test test
line two: test test
line three: test test
line four: test test
line five" + }, + event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", + origin_server_ts: 1688301929913, + room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe", + sender: "@cadence:cadence.moe", + type: "m.room.message", + unsigned: { + age: 405299 + } + }), + [{ + username: "cadence", + content: "line one: test test\nline two: **test** **test**\nline three: **test test**\nline four: test test\nline five", + avatar_url: undefined + }] + ) +}) + +test("event2message: lists are bridged correctly", t => { + sameFirstContentAndWhitespace( + t, + eventToMessage({ + "type": "m.room.message", + "sender": "@cadence:cadence.moe", + "content": { + "msgtype": "m.text", + "body": "* line one\n* line two\n* line three\n * nested one\n * nested two\n* line four", + "format": "org.matrix.custom.html", + "formatted_body": "\n" + }, + "origin_server_ts": 1692967314062, + "unsigned": { + "age": 112, + "transaction_id": "m1692967313951.441" + }, + "event_id": "$l-xQPY5vNJo3SNxU9d8aOWNVD1glMslMyrp4M_JEF70", + "room_id": "!BpMdOUkWWhFxmTrENV:cadence.moe" + }), + [{ + username: "cadence", + content: "* line one\n* line two\n* line three\n * nested one\n * nested two\n* line four", + avatar_url: undefined + }] + ) +}) + test("event2message: long messages are split", t => { t.deepEqual( eventToMessage({ @@ -55,3 +220,29 @@ test("event2message: long messages are split", t => { }] ) }) + +test("event2message: m.emote markdown syntax is escaped", t => { + t.deepEqual( + eventToMessage({ + content: { + msgtype: "m.emote", + body: "wrong body", + format: "org.matrix.custom.html", + formatted_body: "shows you **her** extreme \\*test\\* of" + }, + event_id: "$g07oYSZFWBkxohNEfywldwgcWj1hbhDzQ1sBAKvqOOU", + origin_server_ts: 1688301929913, + room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe", + sender: "@cadence:cadence.moe", + type: "m.room.message", + unsigned: { + age: 405299 + } + }), + [{ + username: "cadence", + content: "\\* cadence shows you \\*\\*her\\*\\* **_extreme_** \\\\\\*test\\\\\\* of", + avatar_url: undefined + }] + ) +}) diff --git a/package-lock.json b/package-lock.json index e808e1f..dfd21ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "better-sqlite3": "^8.3.0", + "chunk-text": "^2.0.1", "cloudstorm": "^0.8.0", "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#440130ef343c8183a81c7c09809731484aa3a182", "heatsync": "^2.4.1", @@ -20,7 +21,8 @@ "node-fetch": "^2.6.7", "prettier-bytes": "^1.0.4", "snowtransfer": "^0.8.0", - "try-to-catch": "^3.0.1" + "try-to-catch": "^3.0.1", + "turndown": "^7.1.2" }, "devDependencies": { "@types/node": "^18.16.0", @@ -29,7 +31,7 @@ "cross-env": "^7.0.3", "discord-api-types": "^0.37.53", "supertape": "^8.3.0", - "tap-dot": "github:cloudrac3r/tap-dot#223a4e67a6f7daf015506a12a7af74605f06c7f4" + "tap-dot": "github:cloudrac3r/tap-dot#9dd7750ececeae3a96afba91905be812b6b2cc2d" } }, "node_modules/@babel/runtime": { @@ -732,6 +734,18 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, + "node_modules/chunk-text": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunk-text/-/chunk-text-2.0.1.tgz", + "integrity": "sha512-ER6TSpe2DT4wjOVOKJ3FFAYv7wE77HA/Ztz88Peiv3lq/2oVMsItYJJsVVI0xNZM8cdImOOTNqlw+LQz7gYdJg==", + "dependencies": { + "runes": "^0.4.3" + }, + "bin": { + "chunk": "bin/server.js", + "chunk-text": "bin/server.js" + } + }, "node_modules/ci-info": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", @@ -1057,6 +1071,11 @@ "simple-markdown": "^0.7.2" } }, + "node_modules/domino": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", + "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2518,15 +2537,16 @@ "dev": true }, "node_modules/readable-stream": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz", - "integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", + "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", "dev": true, "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", - "process": "^0.11.10" + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2645,6 +2665,14 @@ "node": "*" } }, + "node_modules/runes": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/runes/-/runes-0.4.3.tgz", + "integrity": "sha512-K6p9y4ZyL9wPzA+PMDloNQPfoDGTiFYDvdlXznyGKgD10BJpcAosvATKrExRKOrNLgD8E7Um7WGW0lxsnOuNLg==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3043,8 +3071,8 @@ }, "node_modules/tap-dot": { "version": "2.0.0", - "resolved": "git+ssh://git@github.com/cloudrac3r/tap-dot.git#223a4e67a6f7daf015506a12a7af74605f06c7f4", - "integrity": "sha512-nhpVoX/s4IJJdm7OymbZ1rdZNlqt3l/yQ9Z9if06jcgRNto6QAZOrLIvdCILYQ6GE0mu+cyVA8s24amdwbvHiQ==", + "resolved": "git+ssh://git@github.com/cloudrac3r/tap-dot.git#9dd7750ececeae3a96afba91905be812b6b2cc2d", + "integrity": "sha512-SLg6KF3cSkKII+5hA/we9FjnMCrL5uk0wYap7RXD9KJziy7xqZolvEOamt3CJlm5LSzRXIGblm3nmhY/EBE3AA==", "dev": true, "license": "MIT", "dependencies": { @@ -3058,7 +3086,7 @@ "node_modules/tap-out": { "version": "3.2.1", "resolved": "git+ssh://git@github.com/cloudrac3r/tap-out.git#1b4ec6084aedb9f44ccaa0c7185ff9bfd83da771", - "integrity": "sha512-hyMMeN6jagEyeEOq7Xyg3GNIAR3iUDDocaoK5QRPjnEGbFZOYJ39Dkn7BsFUXyGVl+s4b3zPkDcTS38+6KTXCQ==", + "integrity": "sha512-55eUSaX5AeEOqJMRlj9XSqUlLV/yYPOPeC3kOFqjmorq6/jlH5kIeqpgLNW5PlPEAuggzYREYYXqrN8E37ZPfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3215,6 +3243,14 @@ "node": "*" } }, + "node_modules/turndown": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.2.tgz", + "integrity": "sha512-ntI9R7fcUKjqBP6QU8rBK2Ehyt8LAzt3UBT9JR9tgo6GtuKvyUzpayWmeMKJw1DPdXzktvtIT8m2mVXz+bL/Qg==", + "dependencies": { + "domino": "^2.1.6" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", diff --git a/package.json b/package.json index 67aeade..238b9ae 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "license": "MIT", "dependencies": { "better-sqlite3": "^8.3.0", + "chunk-text": "^2.0.1", "cloudstorm": "^0.8.0", "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#440130ef343c8183a81c7c09809731484aa3a182", "heatsync": "^2.4.1", @@ -26,7 +27,8 @@ "node-fetch": "^2.6.7", "prettier-bytes": "^1.0.4", "snowtransfer": "^0.8.0", - "try-to-catch": "^3.0.1" + "try-to-catch": "^3.0.1", + "turndown": "^7.1.2" }, "devDependencies": { "@types/node": "^18.16.0", @@ -35,7 +37,7 @@ "cross-env": "^7.0.3", "discord-api-types": "^0.37.53", "supertape": "^8.3.0", - "tap-dot": "github:cloudrac3r/tap-dot#223a4e67a6f7daf015506a12a7af74605f06c7f4" + "tap-dot": "github:cloudrac3r/tap-dot#9dd7750ececeae3a96afba91905be812b6b2cc2d" }, "scripts": { "test": "cross-env FORCE_COLOR=true supertape --no-check-assertions-count --format tap test/test.js | tap-dot", diff --git a/test/data.js b/test/data.js index a4d836d..32ee3b0 100644 --- a/test/data.js +++ b/test/data.js @@ -1250,6 +1250,47 @@ module.exports = { tts: false, type: 0 }, + edited_content_with_sticker_and_attachments: { + id: "1106366167788044450", + type: 0, + content: "only the content can be edited", + channel_id: "122155380120748034", + author: { + id: "113340068197859328", + username: "Cookie 🍪", + global_name: null, + display_name: null, + avatar: "b48302623a12bc7c59a71328f72ccb39", + discriminator: "7766", + public_flags: 128, + avatar_decoration: null + }, + attachments: [{ + id: "1106366167486038016", + filename: "image.png", + size: 127373, + url: "https://cdn.discordapp.com/attachments/122155380120748034/1106366167486038016/image.png", + proxy_url: "https://media.discordapp.net/attachments/122155380120748034/1106366167486038016/image.png", + width: 333, + height: 287, + content_type: "image/png" + }], + embeds: [], + mentions: [], + mention_roles: [], + pinned: false, + mention_everyone: false, + tts: false, + timestamp: "2023-05-11T23:44:09.690000+00:00", + edited_timestamp: "2023-05-11T23:44:19.690000+00:00", + flags: 0, + components: [], + sticker_items: [{ + id: "1106323941183717586", + format_type: 1, + name: "pomu puff" + }] + }, edit_of_reply_to_skull_webp_attachment_with_content: { type: 19, tts: false, diff --git a/types.d.ts b/types.d.ts index 2ba8d1d..badbbab 100644 --- a/types.d.ts +++ b/types.d.ts @@ -67,10 +67,10 @@ export namespace Event { } export type M_Room_Message = { - msgtype: "m.text" + msgtype: "m.text" | "m.emote" body: string - formatted_body?: "org.matrix.custom.html" - format?: string + format?: "org.matrix.custom.html" + formatted_body?: string } export type M_Room_Member = {