Compare commits

...
Sign in to create a new pull request.

2 commits
main ... main

Author SHA1 Message Date
a968bacffd Update discord-markdown
Interpret channel URLs the same as a channel #mention
2025-09-03 00:00:02 +12:00
c71044fdec Only edit events if the text has changed 2025-08-29 00:09:18 +12:00
4 changed files with 114 additions and 62 deletions

58
package-lock.json generated
View file

@ -10,7 +10,7 @@
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@chriscdn/promise-semaphore": "^3.0.1", "@chriscdn/promise-semaphore": "^3.0.1",
"@cloudrac3r/discord-markdown": "^2.6.5", "@cloudrac3r/discord-markdown": "^2.6.6",
"@cloudrac3r/giframe": "^0.4.3", "@cloudrac3r/giframe": "^0.4.3",
"@cloudrac3r/html-template-tag": "^5.0.1", "@cloudrac3r/html-template-tag": "^5.0.1",
"@cloudrac3r/in-your-element": "^1.1.1", "@cloudrac3r/in-your-element": "^1.1.1",
@ -35,7 +35,7 @@
"lru-cache": "^11.0.2", "lru-cache": "^11.0.2",
"prettier-bytes": "^1.0.4", "prettier-bytes": "^1.0.4",
"sharp": "^0.33.4", "sharp": "^0.33.4",
"snowtransfer": "^0.14.2", "snowtransfer": "^0.15.0",
"stream-mime-type": "^1.0.2", "stream-mime-type": "^1.0.2",
"try-to-catch": "^3.0.1", "try-to-catch": "^3.0.1",
"uqr": "^0.1.2", "uqr": "^0.1.2",
@ -119,9 +119,9 @@
} }
}, },
"node_modules/@chriscdn/promise-semaphore": { "node_modules/@chriscdn/promise-semaphore": {
"version": "3.0.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/@chriscdn/promise-semaphore/-/promise-semaphore-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@chriscdn/promise-semaphore/-/promise-semaphore-3.1.1.tgz",
"integrity": "sha512-fVlCnoYE4hDzpcYRPtmN7dmcpmd2zxyPWjyfjIKI9Y+gsI7rwZSkjtuwMi8HFtlkSmNh8L7Zr37hdqeL13sYrw==", "integrity": "sha512-ALLLLYlPfd/QZLptcVi6HQRK1zaCDWZoqYYw+axLmCatFs4gVTSZ5nqlyxwFe4qwR/K84HvOMa9hxda881FqMA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@cloudcmd/stub": { "node_modules/@cloudcmd/stub": {
@ -225,9 +225,9 @@
} }
}, },
"node_modules/@cloudrac3r/discord-markdown": { "node_modules/@cloudrac3r/discord-markdown": {
"version": "2.6.5", "version": "2.6.6",
"resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.5.tgz", "resolved": "https://registry.npmjs.org/@cloudrac3r/discord-markdown/-/discord-markdown-2.6.6.tgz",
"integrity": "sha512-B4uQNsyva5JNW0CVYkcunMQwWfrok1Hd5FYww/cWcvb98zp/pJdJfE3hoRl9EbnxNK2l62IJQ9j8HmssMFHJ9Q==", "integrity": "sha512-4FNO7WmACPvcTrQjeLQLr9WRuP7JDUVUGFrRJvmAjiMs2UlUAsShfSRuU2SCqz3QqmX8vyJ06wy2hkjTTyRtbw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"simple-markdown": "^0.7.3" "simple-markdown": "^0.7.3"
@ -949,9 +949,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@stackoverflow/stacks": { "node_modules/@stackoverflow/stacks": {
"version": "2.8.3", "version": "2.8.4",
"resolved": "https://registry.npmjs.org/@stackoverflow/stacks/-/stacks-2.8.3.tgz", "resolved": "https://registry.npmjs.org/@stackoverflow/stacks/-/stacks-2.8.4.tgz",
"integrity": "sha512-ZGBeuXJC7moK/f+lgl2dCAW85etD/RO0DNubocdH2qzpJMuuGXX0GMeEAfrTOe+B00I8E1OqTnS1cpkqGdHBdQ==", "integrity": "sha512-FfA7Bw7a0AQrMw3/bG6G4BUrZ698F7Cdk6HkR9T7jdaufORkiX5d16wI4j4b5Sqm1FwkaZAF+ZSKLL1w0tAsew==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@hotwired/stimulus": "^3.2.2", "@hotwired/stimulus": "^3.2.2",
@ -1107,9 +1107,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.17.1", "version": "22.18.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.0.tgz",
"integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==", "integrity": "sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -1452,13 +1452,13 @@
} }
}, },
"node_modules/cloudstorm": { "node_modules/cloudstorm": {
"version": "0.14.0", "version": "0.14.1",
"resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.14.0.tgz", "resolved": "https://registry.npmjs.org/cloudstorm/-/cloudstorm-0.14.1.tgz",
"integrity": "sha512-EgjMGxb2Z+L6Acti6DzL/bEbR495AIqPThyW4DaG6Jpvd0ZuM5eC13EiyxV8wlqAME612QO2LjqbhkdXn/327Q==", "integrity": "sha512-x95WCKg818E1rE1Ru45NPD3RoIq0pg3WxwvF0GE7Eq07pAeLcjSRqM1lUmbmfjdOqZrWdSRYA1NETVZ8QhVrIA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"discord-api-types": "^0.38.12", "discord-api-types": "^0.38.21",
"snowtransfer": "^0.14.2" "snowtransfer": "^0.15.0"
}, },
"engines": { "engines": {
"node": ">=22.0.0" "node": ">=22.0.0"
@ -1616,9 +1616,9 @@
} }
}, },
"node_modules/discord-api-types": { "node_modules/discord-api-types": {
"version": "0.38.19", "version": "0.38.22",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.19.tgz", "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.22.tgz",
"integrity": "sha512-NUNMTgjYrgxt7wrTNEqnEez4hIAYbfyBpsjxT5gW7+82GjQCPDZvN+em6t+4/P5kGWnnwDa4ci070BV7eI6GbA==", "integrity": "sha512-2gnYrgXN3yTlv2cKBISI/A8btZwsSZLwKpIQXeI1cS8a7W7wP3sFVQOm3mPuuinTD8jJCKGPGNH399zE7Un1kA==",
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
"scripts/actions/documentation" "scripts/actions/documentation"
@ -2719,12 +2719,12 @@
} }
}, },
"node_modules/snowtransfer": { "node_modules/snowtransfer": {
"version": "0.14.2", "version": "0.15.0",
"resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.14.2.tgz", "resolved": "https://registry.npmjs.org/snowtransfer/-/snowtransfer-0.15.0.tgz",
"integrity": "sha512-Fi8OdRmaIgeCj58oVej+tQAoY2I+Xp/6PAYV8X93jE/2E6Anc87SbTbDV6WZXCnuzTQz3gty8JOGz02qI7Qs9A==", "integrity": "sha512-kEDGKtFiH5nSkHsDZonEUuDx99lUasJoZ7AGrgvE8HzVG59vjvqc//C+pjWj4DuJqTj4Q+Z1L/M/MYNim8F2VA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"discord-api-types": "^0.38.8" "discord-api-types": "^0.38.21"
}, },
"engines": { "engines": {
"node": ">=16.15.0" "node": ">=16.15.0"
@ -3447,9 +3447,9 @@
} }
}, },
"node_modules/zod": { "node_modules/zod": {
"version": "4.0.17", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.0.17.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.5.tgz",
"integrity": "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==", "integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"

View file

@ -19,7 +19,7 @@
}, },
"dependencies": { "dependencies": {
"@chriscdn/promise-semaphore": "^3.0.1", "@chriscdn/promise-semaphore": "^3.0.1",
"@cloudrac3r/discord-markdown": "^2.6.5", "@cloudrac3r/discord-markdown": "^2.6.6",
"@cloudrac3r/giframe": "^0.4.3", "@cloudrac3r/giframe": "^0.4.3",
"@cloudrac3r/html-template-tag": "^5.0.1", "@cloudrac3r/html-template-tag": "^5.0.1",
"@cloudrac3r/in-your-element": "^1.1.1", "@cloudrac3r/in-your-element": "^1.1.1",
@ -44,7 +44,7 @@
"lru-cache": "^11.0.2", "lru-cache": "^11.0.2",
"prettier-bytes": "^1.0.4", "prettier-bytes": "^1.0.4",
"sharp": "^0.33.4", "sharp": "^0.33.4",
"snowtransfer": "^0.14.2", "snowtransfer": "^0.15.0",
"stream-mime-type": "^1.0.2", "stream-mime-type": "^1.0.2",
"try-to-catch": "^3.0.1", "try-to-catch": "^3.0.1",
"uqr": "^0.1.2", "uqr": "^0.1.2",

View file

@ -22,6 +22,10 @@ function eventCanBeEdited(ev) {
return true return true
} }
function eventIsText(ev) {
return ev.old.event_type === "m.room.message" && (ev.old.event_subtype === "m.text" || ev.old.event_subtype === "m.notice")
}
/** /**
* @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message * @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message
* @param {import("discord-api-types/v10").APIGuild} guild * @param {import("discord-api-types/v10").APIGuild} guild
@ -121,6 +125,20 @@ async function editToChanges(message, guild, api) {
unchangedEvents.push(...eventsToReplace.filter(ev => !eventCanBeEdited(ev))) // Move them from eventsToRedact to unchangedEvents. unchangedEvents.push(...eventsToReplace.filter(ev => !eventCanBeEdited(ev))) // Move them from eventsToRedact to unchangedEvents.
eventsToReplace = eventsToReplace.filter(eventCanBeEdited) eventsToReplace = eventsToReplace.filter(eventCanBeEdited)
// Now, everything in eventsToReplace has the potential to have changed, but did it actually?
// (Example: if a URL preview was generated or updated, the message text won't have changed.)
// Only way to detect this is by text content. So we'll remove text events from eventsToReplace that have the same new text as text currently in the event.
for (let i = eventsToReplace.length; i--;) { // move backwards through array
const event = eventsToReplace[i]
if (!eventIsText(event)) continue // not text, can't analyse
const oldEvent = await api.getEvent(roomID, eventsToReplace[i].old.event_id)
const oldEventBodyWithoutQuotedReply = oldEvent.content.body?.replace(/^(>.*\n)*\n*/sm, "")
if (oldEventBodyWithoutQuotedReply !== event.newInnerContent.body) continue // event changed, must replace it
// Move it from eventsToRedact to unchangedEvents.
unchangedEvents.push(...eventsToReplace.filter(ev => ev.old.event_id === event.old.event_id))
eventsToReplace = eventsToReplace.filter(ev => ev.old.event_id !== event.old.event_id)
}
// We want to maintain exactly one part = 0 and one reaction_part = 0 database row at all times. // We want to maintain exactly one part = 0 and one reaction_part = 0 database row at all times.
// This would be disrupted if existing events that are (reaction_)part = 0 will be redacted. // This would be disrupted if existing events that are (reaction_)part = 0 will be redacted.
// If that is the case, pick a different existing or newly sent event to be (reaction_)part = 0. // If that is the case, pick a different existing or newly sent event to be (reaction_)part = 0.
@ -193,4 +211,3 @@ function makeReplacementEventContent(oldID, newFallbackContent, newInnerContent)
} }
module.exports.editToChanges = editToChanges module.exports.editToChanges = editToChanges
module.exports.makeReplacementEventContent = makeReplacementEventContent

View file

@ -4,7 +4,14 @@ const data = require("../../../test/data")
const Ty = require("../../types") const Ty = require("../../types")
test("edit2changes: edit by webhook", async t => { test("edit2changes: edit by webhook", async t => {
const {senderMxid, eventsToRedact, eventsToReplace, eventsToSend, promotions} = await editToChanges(data.message_update.edit_by_webhook, data.guild.general, {}) let called = 0
const {senderMxid, eventsToRedact, eventsToReplace, eventsToSend, promotions} = await editToChanges(data.message_update.edit_by_webhook, data.guild.general, {
getEvent(roomID, eventID) {
called++
t.equal(eventID, "$zXSlyI78DQqQwwfPUSzZ1b-nXzbUrCDljJgnGDdoI10")
return {content: {body: "dummy"}}
}
})
t.deepEqual(eventsToRedact, []) t.deepEqual(eventsToRedact, [])
t.deepEqual(eventsToSend, []) t.deepEqual(eventsToSend, [])
t.deepEqual(eventsToReplace, [{ t.deepEqual(eventsToReplace, [{
@ -28,10 +35,15 @@ test("edit2changes: edit by webhook", async t => {
}]) }])
t.equal(senderMxid, null) t.equal(senderMxid, null)
t.deepEqual(promotions, []) t.deepEqual(promotions, [])
t.equal(called, 1)
}) })
test("edit2changes: bot response", async t => { test("edit2changes: bot response", async t => {
const {senderMxid, eventsToRedact, eventsToReplace, eventsToSend, promotions} = await editToChanges(data.message_update.bot_response, data.guild.general, { const {senderMxid, eventsToRedact, eventsToReplace, eventsToSend, promotions} = await editToChanges(data.message_update.bot_response, data.guild.general, {
getEvent(roomID, eventID) {
t.equal(eventID, "$fdD9OZ55xg3EAsfvLZza5tMhtjUO91Wg3Otuo96TplY")
return {content: {body: "dummy"}}
},
async getJoinedMembers(roomID) { async getJoinedMembers(roomID) {
t.equal(roomID, "!hYnGGlPHlbujVVfktC:cadence.moe") t.equal(roomID, "!hYnGGlPHlbujVVfktC:cadence.moe")
return new Promise(resolve => { return new Promise(resolve => {
@ -123,7 +135,14 @@ test("edit2changes: add caption back to that image (due to it having a reaction,
}) })
test("edit2changes: stickers and attachments are not changed, only the content can be edited", async t => { 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, {}) let called = 0
const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.edited_content_with_sticker_and_attachments, data.guild.general, {
getEvent(roomID, eventID) {
called++
t.equal(eventID, "$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qdFv4")
return {content: {body: "dummy"}}
}
})
t.deepEqual(eventsToRedact, []) t.deepEqual(eventsToRedact, [])
t.deepEqual(eventsToSend, []) t.deepEqual(eventsToSend, [])
t.deepEqual(eventsToReplace, [{ t.deepEqual(eventsToReplace, [{
@ -145,10 +164,16 @@ test("edit2changes: stickers and attachments are not changed, only the content c
} }
} }
}]) }])
t.equal(called, 1)
}) })
test("edit2changes: edit of reply to skull webp attachment with content", async t => { 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, {}) const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.edit_of_reply_to_skull_webp_attachment_with_content, data.guild.general, {
getEvent(roomID, eventID) {
t.equal(eventID, "$vgTKOR5ZTYNMKaS7XvgEIDaOWZtVCEyzLLi5Pc5Gz4M")
return {content: {body: "dummy"}}
}
})
t.deepEqual(eventsToRedact, []) t.deepEqual(eventsToRedact, [])
t.deepEqual(eventsToSend, []) t.deepEqual(eventsToSend, [])
t.deepEqual(eventsToReplace, [{ t.deepEqual(eventsToReplace, [{
@ -177,7 +202,12 @@ test("edit2changes: edit of reply to skull webp attachment with content", async
}) })
test("edit2changes: edits the text event when multiple rows have part = 0 (should never happen in real life, but make sure the safety net works)", async t => { test("edit2changes: edits the text event when multiple rows have part = 0 (should never happen in real life, but make sure the safety net works)", async t => {
const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.edited_content_with_sticker_and_attachments_but_all_parts_equal_0, data.guild.general, {}) const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.edited_content_with_sticker_and_attachments_but_all_parts_equal_0, data.guild.general, {
getEvent(roomID, eventID) {
t.equal(eventID, "$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qd999")
return {content: {body: "dummy"}}
}
})
t.deepEqual(eventsToRedact, []) t.deepEqual(eventsToRedact, [])
t.deepEqual(eventsToSend, []) t.deepEqual(eventsToSend, [])
t.deepEqual(eventsToReplace, [{ t.deepEqual(eventsToReplace, [{
@ -202,7 +232,12 @@ test("edit2changes: edits the text event when multiple rows have part = 0 (shoul
}) })
test("edit2changes: promotes the text event when multiple rows have part = 1 (should never happen in real life, but make sure the safety net works)", async t => { test("edit2changes: promotes the text event when multiple rows have part = 1 (should never happen in real life, but make sure the safety net works)", async t => {
const {eventsToRedact, eventsToReplace, eventsToSend, promotions} = await editToChanges(data.message_update.edited_content_with_sticker_and_attachments_but_all_parts_equal_1, data.guild.general, {}) const {eventsToRedact, eventsToReplace, eventsToSend, promotions} = await editToChanges(data.message_update.edited_content_with_sticker_and_attachments_but_all_parts_equal_1, data.guild.general, {
getEvent(roomID, eventID) {
t.equal(eventID, "$lnAF9IosAECTnlv9p2e18FG8rHn-JgYKHEHIh5qd111")
return {content: {body: "dummy"}}
}
})
t.deepEqual(eventsToRedact, []) t.deepEqual(eventsToRedact, [])
t.deepEqual(eventsToSend, []) t.deepEqual(eventsToSend, [])
t.deepEqual(eventsToReplace, [{ t.deepEqual(eventsToReplace, [{
@ -279,32 +314,31 @@ test("edit2changes: generated embed", async t => {
}) })
test("edit2changes: generated embed on a reply", async t => { test("edit2changes: generated embed on a reply", async t => {
const {senderMxid, eventsToRedact, eventsToReplace, eventsToSend, promotions} = await editToChanges(data.message_update.embed_generated_on_reply, data.guild.general, {}) let called = 0
t.deepEqual(eventsToRedact, []) const {senderMxid, eventsToRedact, eventsToReplace, eventsToSend, promotions} = await editToChanges(data.message_update.embed_generated_on_reply, data.guild.general, {
t.deepEqual(eventsToReplace, [{ getEvent(roomID, eventID) {
oldID: "$UTqiL3Zj3FC4qldxRLggN1fhygpKl8sZ7XGY5f9MNbF", called++
newContent: { t.equal(eventID, "$UTqiL3Zj3FC4qldxRLggN1fhygpKl8sZ7XGY5f9MNbF")
$type: "m.room.message", return {
type: "m.room.message",
content: {
// Unfortunately the edited message doesn't include the message_reference field. Fine. Whatever. It looks normal if you're using a good client. // Unfortunately the edited message doesn't include the message_reference field. Fine. Whatever. It looks normal if you're using a good client.
body: "> a Discord user: [Replied-to message content wasn't provided by Discord]" body: "> a Discord user: [Replied-to message content wasn't provided by Discord]"
+ "\n\n* https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM", + "\n\nhttps://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM",
format: "org.matrix.custom.html", format: "org.matrix.custom.html",
formatted_body: "<mx-reply><blockquote><a href=\"https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM\">In reply to</a> a Discord user<br>[Replied-to message content wasn't provided by Discord]</blockquote></mx-reply>* <a href=\"https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM\">https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM</a>", formatted_body: "<mx-reply><blockquote><a href=\"https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM\">In reply to</a> a Discord user<br>[Replied-to message content wasn't provided by Discord]</blockquote></mx-reply><a href=\"https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM\">https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM</a>",
"m.mentions": {}, "m.mentions": {},
"m.new_content": {
body: "https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM",
format: "org.matrix.custom.html",
formatted_body: "<a href=\"https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM\">https://matrix.to/#/!BnKuBPCvyfOkhcUjEu:cadence.moe/$aLVZyiC3HlOu-prCSIaXlQl68I8leUdnPFiCwkgn6qM</a>",
"m.mentions": {},
msgtype: "m.text",
},
"m.relates_to": { "m.relates_to": {
event_id: "$UTqiL3Zj3FC4qldxRLggN1fhygpKl8sZ7XGY5f9MNbF", event_id: "$UTqiL3Zj3FC4qldxRLggN1fhygpKl8sZ7XGY5f9MNbF",
rel_type: "m.replace", rel_type: "m.replace",
}, },
msgtype: "m.text", msgtype: "m.text",
}, }
}]) }
}
})
t.deepEqual(eventsToRedact, [])
t.deepEqual(eventsToReplace, [])
t.deepEqual(eventsToSend, [{ t.deepEqual(eventsToSend, [{
$type: "m.room.message", $type: "m.room.message",
msgtype: "m.notice", msgtype: "m.notice",
@ -324,4 +358,5 @@ test("edit2changes: generated embed on a reply", async t => {
"nextEvent": true, "nextEvent": true,
}]) }])
t.equal(senderMxid, "@_ooye_cadence:cadence.moe") t.equal(senderMxid, "@_ooye_cadence:cadence.moe")
t.equal(called, 1)
}) })