From 749f721aac6637df1a01504c2cbfcb15c005755f Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 16 Aug 2023 20:44:38 +1200 Subject: [PATCH 1/3] record more update events --- d2m/actions/send-message.js | 2 +- d2m/converters/edit-to-changes.js | 47 +++++----- d2m/converters/edit-to-changes.test.js | 9 +- d2m/converters/message-to-event.js | 20 +++- d2m/converters/message-to-event.test.js | 116 +++++++++++++++--------- scripts/events.db | Bin 8192 -> 98304 bytes 6 files changed, 122 insertions(+), 72 deletions(-) diff --git a/d2m/actions/send-message.js b/d2m/actions/send-message.js index 258efcf..cf87d35 100644 --- a/d2m/actions/send-message.js +++ b/d2m/actions/send-message.js @@ -27,7 +27,7 @@ async function sendMessage(message, guild) { await registerUser.syncUser(message.author, message.member, message.guild_id, roomID) } - const events = await messageToEvent.messageToEvent(message, guild, api) + const events = await messageToEvent.messageToEvent(message, guild, {}, {api}) const eventIDs = [] let eventPart = 0 // 0 is primary, 1 is supporting for (const event of events) { diff --git a/d2m/converters/edit-to-changes.js b/d2m/converters/edit-to-changes.js index 87e769b..4afa3ce 100644 --- a/d2m/converters/edit-to-changes.js +++ b/d2m/converters/edit-to-changes.js @@ -29,7 +29,9 @@ async function editToChanges(message, guild) { // Figure out what we will be replacing them with - const newEvents = await messageToEvent.messageToEvent(message, guild, api) + const newFallbackContent = await messageToEvent.messageToEvent(message, guild, {includeEditFallbackStar: true}, {api}) + const newInnerContent = await messageToEvent.messageToEvent(message, guild, {includeReplyFallback: false}, {api}) + assert.ok(newFallbackContent.length === newInnerContent.length) // Match the new events to the old events @@ -47,21 +49,27 @@ async function editToChanges(message, guild) { let eventsToSend = [] // 4. Events that are matched and have definitely not changed, so they don't need to be edited or replaced at all. This is represented as nothing. + function shift() { + newFallbackContent.shift() + newInnerContent.shift() + } + // For each old event... - outer: while (newEvents.length) { - const newe = newEvents[0] + outer: while (newFallbackContent.length) { + const newe = newFallbackContent[0] // 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)) { + 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({ old: olde, - new: newe + newFallbackContent: newFallbackContent[0], + newInnerContent: newInnerContent[0] }) // These events have been handled now, so remove them from the source arrays - newEvents.shift() + shift() oldEventRows.splice(i, 1) // Go all the way back to the start of the next iteration of the outer loop continue outer @@ -69,7 +77,7 @@ async function editToChanges(message, guild) { } // If we got this far, we could not pair it to an existing event, so it'll have to be a new one eventsToSend.push(newe) - newEvents.shift() + shift() } // Anything remaining in oldEventRows is present in the old version only and should be redacted. eventsToRedact = oldEventRows @@ -92,7 +100,7 @@ async function editToChanges(message, guild) { // Removing unnecessary properties before returning eventsToRedact = eventsToRedact.map(e => e.event_id) - eventsToReplace = eventsToReplace.map(e => ({oldID: e.old.event_id, new: eventToReplacementEvent(e.old.event_id, e.new)})) + eventsToReplace = eventsToReplace.map(e => ({oldID: e.old.event_id, new: eventToReplacementEvent(e.old.event_id, e.newFallbackContent, e.newInnerContent)})) return {eventsToReplace, eventsToRedact, eventsToSend} } @@ -100,31 +108,26 @@ async function editToChanges(message, guild) { /** * @template T * @param {string} oldID - * @param {T} content + * @param {T} newFallbackContent + * @param {T} newInnerContent * @returns {import("../../types").Event.ReplacementContent} content */ -function eventToReplacementEvent(oldID, content) { - const newContent = { - ...content, +function eventToReplacementEvent(oldID, newFallbackContent, newInnerContent) { + const content = { + ...newFallbackContent, "m.mentions": {}, "m.new_content": { - ...content + ...newInnerContent }, "m.relates_to": { rel_type: "m.replace", event_id: oldID } } - if (typeof newContent.body === "string") { - newContent.body = "* " + newContent.body - } - if (typeof newContent.formatted_body === "string") { - newContent.formatted_body = "* " + newContent.formatted_body - } - delete newContent["m.new_content"]["$type"] + delete content["m.new_content"]["$type"] // Client-Server API spec 11.37.3: Any m.relates_to property within m.new_content is ignored. - delete newContent["m.new_content"]["m.relates_to"] - return newContent + delete content["m.new_content"]["m.relates_to"] + return content } module.exports.editToChanges = editToChanges diff --git a/d2m/converters/edit-to-changes.test.js b/d2m/converters/edit-to-changes.test.js index b3e6e0c..f6ecc8d 100644 --- a/d2m/converters/edit-to-changes.test.js +++ b/d2m/converters/edit-to-changes.test.js @@ -47,9 +47,13 @@ test("edit2changes: edit of reply to skull webp attachment with content", async oldID: "$vgTKOR5ZTYNMKaS7XvgEIDaOWZtVCEyzLLi5Pc5Gz4M", new: { $type: "m.room.message", - // TODO: read "edits of replies" in the spec!!! msgtype: "m.text", - body: "* Edit", + body: "> Extremity: Image\n\n* Edit", + format: "org.matrix.custom.html", + formatted_body: + '
In reply to Extremity' + + '
Image
' + + '* Edit', "m.mentions": {}, "m.new_content": { msgtype: "m.text", @@ -60,7 +64,6 @@ test("edit2changes: edit of reply to skull webp attachment with content", async rel_type: "m.replace", event_id: "$vgTKOR5ZTYNMKaS7XvgEIDaOWZtVCEyzLLi5Pc5Gz4M" } - // TODO: read "edits of replies" in the spec!!! } }]) }) diff --git a/d2m/converters/message-to-event.js b/d2m/converters/message-to-event.js index a2c4915..c128595 100644 --- a/d2m/converters/message-to-event.js +++ b/d2m/converters/message-to-event.js @@ -55,9 +55,12 @@ function getDiscordParseCallbacks(message, useHTML) { /** * @param {import("discord-api-types/v10").APIMessage} message * @param {import("discord-api-types/v10").APIGuild} guild - * @param {import("../../matrix/api")} api simple-as-nails dependency injection for the matrix API + * @param {{includeReplyFallback?: boolean, includeEditFallbackStar?: boolean}} options default values: + * - includeReplyFallback: true + * - includeEditFallbackStar: false + * @param {{api: import("../../matrix/api")}} di simple-as-nails dependency injection for the matrix API */ -async function messageToEvent(message, guild, api) { +async function messageToEvent(message, guild, options = {}, di) { const events = [] /** @@ -99,7 +102,7 @@ async function messageToEvent(message, guild, api) { } if (repliedToEventOriginallyFromMatrix) { // Need to figure out who sent that event... - const event = await api.getEvent(repliedToEventRoomId, repliedToEventId) + const event = await di.api.getEvent(repliedToEventRoomId, repliedToEventId) repliedToEventSenderMxid = event.sender // Need to add the sender to m.mentions addMention(repliedToEventSenderMxid) @@ -133,7 +136,7 @@ async function messageToEvent(message, guild, api) { if (matches.length && matches.some(m => m[1].match(/[a-z]/i))) { const writtenMentionsText = matches.map(m => m[1].toLowerCase()) const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(message.channel_id) - const {joined} = await api.getJoinedMembers(roomID) + const {joined} = await di.api.getJoinedMembers(roomID) for (const [mxid, member] of Object.entries(joined)) { if (!userRegex.some(rx => mxid.match(rx))) { const localpart = mxid.match(/@([^:]*)/) @@ -143,8 +146,15 @@ async function messageToEvent(message, guild, api) { } } + // Star * prefix for fallback edits + if (options.includeEditFallbackStar) { + body = "* " + body + html = "* " + html + } + // Fallback body/formatted_body for replies - if (repliedToEventId) { + // This branch is optional - do NOT change anything apart from the reply fallback, since it may not be run + if (repliedToEventId && options.includeReplyFallback !== false) { let repliedToDisplayName let repliedToUserHtml if (repliedToEventOriginallyFromMatrix && repliedToEventSenderMxid) { diff --git a/d2m/converters/message-to-event.test.js b/d2m/converters/message-to-event.test.js index 17079e5..4200afe 100644 --- a/d2m/converters/message-to-event.test.js +++ b/d2m/converters/message-to-event.test.js @@ -30,7 +30,7 @@ function mockGetEvent(t, roomID_in, eventID_in, outer) { } test("message2event: simple plaintext", async t => { - const events = await messageToEvent(data.message.simple_plaintext, data.guild.general) + const events = await messageToEvent(data.message.simple_plaintext, data.guild.general, {}) t.deepEqual(events, [{ $type: "m.room.message", "m.mentions": {}, @@ -40,7 +40,7 @@ test("message2event: simple plaintext", async t => { }) test("message2event: simple user mention", async t => { - const events = await messageToEvent(data.message.simple_user_mention, data.guild.general) + const events = await messageToEvent(data.message.simple_user_mention, data.guild.general, {}) t.deepEqual(events, [{ $type: "m.room.message", "m.mentions": {}, @@ -52,7 +52,7 @@ test("message2event: simple user mention", async t => { }) test("message2event: simple room mention", async t => { - const events = await messageToEvent(data.message.simple_room_mention, data.guild.general) + const events = await messageToEvent(data.message.simple_room_mention, data.guild.general, {}) t.deepEqual(events, [{ $type: "m.room.message", "m.mentions": {}, @@ -64,7 +64,7 @@ test("message2event: simple room mention", async t => { }) test("message2event: simple message link", async t => { - const events = await messageToEvent(data.message.simple_message_link, data.guild.general) + const events = await messageToEvent(data.message.simple_message_link, data.guild.general, {}) t.deepEqual(events, [{ $type: "m.room.message", "m.mentions": {}, @@ -76,7 +76,7 @@ test("message2event: simple message link", async t => { }) test("message2event: attachment with no content", async t => { - const events = await messageToEvent(data.message.attachment_no_content, data.guild.general) + const events = await messageToEvent(data.message.attachment_no_content, data.guild.general, {}) t.deepEqual(events, [{ $type: "m.room.message", "m.mentions": {}, @@ -94,7 +94,7 @@ test("message2event: attachment with no content", async t => { }) test("message2event: stickers", async t => { - const events = await messageToEvent(data.message.sticker, data.guild.general) + const events = await messageToEvent(data.message.sticker, data.guild.general, {}) t.deepEqual(events, [{ $type: "m.room.message", "m.mentions": {}, @@ -127,7 +127,7 @@ test("message2event: stickers", async t => { }) test("message2event: skull webp attachment with content", async t => { - const events = await messageToEvent(data.message.skull_webp_attachment_with_content, data.guild.general) + const events = await messageToEvent(data.message.skull_webp_attachment_with_content, data.guild.general, {}) t.deepEqual(events, [{ $type: "m.room.message", "m.mentions": {}, @@ -150,7 +150,7 @@ test("message2event: skull webp attachment with content", async t => { }) test("message2event: reply to skull webp attachment with content", async t => { - const events = await messageToEvent(data.message.reply_to_skull_webp_attachment_with_content, data.guild.general) + const events = await messageToEvent(data.message.reply_to_skull_webp_attachment_with_content, data.guild.general, {}) t.deepEqual(events, [{ $type: "m.room.message", "m.relates_to": { @@ -183,15 +183,17 @@ test("message2event: reply to skull webp attachment with content", async t => { }) test("message2event: simple reply to matrix user", async t => { - const events = await messageToEvent(data.message.simple_reply_to_matrix_user, data.guild.general, { - getEvent: mockGetEvent(t, "!kLRqKKUQXcibIMtOpl:cadence.moe", "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4", { - type: "m.room.message", - content: { - msgtype: "m.text", - body: "so can you reply to my webhook uwu" - }, - sender: "@cadence:cadence.moe" - }) + const events = await messageToEvent(data.message.simple_reply_to_matrix_user, data.guild.general, {}, { + api: { + getEvent: mockGetEvent(t, "!kLRqKKUQXcibIMtOpl:cadence.moe", "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4", { + type: "m.room.message", + content: { + msgtype: "m.text", + body: "so can you reply to my webhook uwu" + }, + sender: "@cadence:cadence.moe" + }) + } }) t.deepEqual(events, [{ $type: "m.room.message", @@ -215,34 +217,66 @@ test("message2event: simple reply to matrix user", async t => { }]) }) +test("message2event: simple reply to matrix user, reply fallbacks disabled", async t => { + const events = await messageToEvent(data.message.simple_reply_to_matrix_user, data.guild.general, {includeReplyFallback: false}, { + api: { + getEvent: mockGetEvent(t, "!kLRqKKUQXcibIMtOpl:cadence.moe", "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4", { + type: "m.room.message", + content: { + msgtype: "m.text", + body: "so can you reply to my webhook uwu" + }, + sender: "@cadence:cadence.moe" + }) + } + }) + t.deepEqual(events, [{ + $type: "m.room.message", + "m.relates_to": { + "m.in_reply_to": { + event_id: "$Ij3qo7NxMA4VPexlAiIx2CB9JbsiGhJeyt-2OvkAUe4" + } + }, + "m.mentions": { + user_ids: [ + "@cadence:cadence.moe" + ] + }, + msgtype: "m.text", + body: "Reply" + }]) +}) + test("message2event: simple written @mention for matrix user", async t => { - const events = await messageToEvent(data.message.simple_written_at_mention_for_matrix, data.guild.general, { - async getJoinedMembers(roomID) { - t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe") - return new Promise(resolve => { - setTimeout(() => { - resolve({ - joined: { - "@cadence:cadence.moe": { - display_name: "cadence [they]", - avatar_url: "whatever" - }, - "@huckleton:cadence.moe": { - display_name: "huck", - avatar_url: "whatever" - }, - "@_ooye_botrac4r:cadence.moe": { - display_name: "botrac4r", - avatar_url: "whatever" - }, - "@_ooye_bot:cadence.moe": { - display_name: "Out Of Your Element", - avatar_url: "whatever" + const events = await messageToEvent(data.message.simple_written_at_mention_for_matrix, data.guild.general, {}, { + api: { + async getJoinedMembers(roomID) { + t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe") + return new Promise(resolve => { + setTimeout(() => { + resolve({ + joined: { + "@cadence:cadence.moe": { + display_name: "cadence [they]", + avatar_url: "whatever" + }, + "@huckleton:cadence.moe": { + display_name: "huck", + avatar_url: "whatever" + }, + "@_ooye_botrac4r:cadence.moe": { + display_name: "botrac4r", + avatar_url: "whatever" + }, + "@_ooye_bot:cadence.moe": { + display_name: "Out Of Your Element", + avatar_url: "whatever" + } } - } + }) }) }) - }) + } } }) t.deepEqual(events, [{ diff --git a/scripts/events.db b/scripts/events.db index c8f5bad88858b0ddc7a3b54ec36577c94791c001..3356cbe2ba67631f1efdf34292de8256ab6ee024 100644 GIT binary patch literal 98304 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 RzZa(6kc%W!yx3`f{{Qk~$=Lt^ delta 59 zcmZo@U~6!gAkE6kz`(#XQNf;(bz{N;ejZ*Bm*X)5?<$VR8w;;W-ZNYYyh}%5On|m From 75414678c85441cda41af86630769ebe002eac0f Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 17 Aug 2023 12:35:34 +1200 Subject: [PATCH 2/3] test for editing a new caption onto an image --- d2m/actions/edit-message.js | 2 +- d2m/converters/edit-to-changes.js | 9 +- d2m/converters/edit-to-changes.test.js | 45 ++++++- db/ooye.db | Bin 360448 -> 360448 bytes test/data.js | 156 +++++++++++++++++++++++++ 5 files changed, 202 insertions(+), 10 deletions(-) diff --git a/d2m/actions/edit-message.js b/d2m/actions/edit-message.js index 933267c..6823602 100644 --- a/d2m/actions/edit-message.js +++ b/d2m/actions/edit-message.js @@ -9,7 +9,7 @@ async function editMessage() { // 3. Send all the things. // old code lies here - let eventPart = 0 // TODO: what to do about eventPart when editing? probably just need to make sure that exactly 1 value of '1' remains in the database? + let eventPart = 0 // TODO: what to do about eventPart when editing? probably just need to make sure that exactly 1 value of '0' remains in the database? for (const event of events) { const eventType = event.$type /** @type {Pick> & { $type?: string }} */ diff --git a/d2m/converters/edit-to-changes.js b/d2m/converters/edit-to-changes.js index 4afa3ce..f055c90 100644 --- a/d2m/converters/edit-to-changes.js +++ b/d2m/converters/edit-to-changes.js @@ -6,8 +6,6 @@ const passthrough = require("../../passthrough") const { discord, sync, db } = passthrough /** @type {import("./message-to-event")} */ const messageToEvent = sync.require("../converters/message-to-event") -/** @type {import("../../matrix/api")} */ -const api = sync.require("../../matrix/api") /** @type {import("../actions/register-user")} */ const registerUser = sync.require("../actions/register-user") /** @type {import("../actions/create-room")} */ @@ -18,8 +16,9 @@ const createRoom = sync.require("../actions/create-room") * IMPORTANT: This may not have all the normal fields! The API documentation doesn't provide possible types, just says it's all optional! * Since I don't have a spec, I will have to capture some real traffic and add it as test cases... I hope they don't change anything later... * @param {import("discord-api-types/v10").APIGuild} guild + * @param {import("../../matrix/api")} api simple-as-nails dependency injection for the matrix API */ -async function editToChanges(message, guild) { +async function editToChanges(message, guild, api) { // Figure out what events we will be replacing const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(message.channel_id) @@ -76,7 +75,7 @@ async function editToChanges(message, guild) { } } // If we got this far, we could not pair it to an existing event, so it'll have to be a new one - eventsToSend.push(newe) + eventsToSend.push(newInnerContent[0]) shift() } // Anything remaining in oldEventRows is present in the old version only and should be redacted. @@ -102,7 +101,7 @@ async function editToChanges(message, guild) { eventsToRedact = eventsToRedact.map(e => e.event_id) eventsToReplace = eventsToReplace.map(e => ({oldID: e.old.event_id, new: eventToReplacementEvent(e.old.event_id, e.newFallbackContent, e.newInnerContent)})) - return {eventsToReplace, eventsToRedact, eventsToSend} + return {eventsToReplace, eventsToRedact, eventsToSend, senderMxid} } /** diff --git a/d2m/converters/edit-to-changes.test.js b/d2m/converters/edit-to-changes.test.js index f6ecc8d..8385cd0 100644 --- a/d2m/converters/edit-to-changes.test.js +++ b/d2m/converters/edit-to-changes.test.js @@ -1,12 +1,30 @@ -// @ts-check - const {test} = require("supertape") const {editToChanges} = require("./edit-to-changes") const data = require("../../test/data") const Ty = require("../../types") test("edit2changes: bot response", async t => { - const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.bot_response, data.guild.general) + const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.bot_response, data.guild.general, { + async getJoinedMembers(roomID) { + t.equal(roomID, "!uCtjHhfGlYbVnPVlkG:cadence.moe") + return new Promise(resolve => { + setTimeout(() => { + resolve({ + joined: { + "@cadence:cadence.moe": { + display_name: "cadence [they]", + avatar_url: "whatever" + }, + "@_ooye_botrac4r:cadence.moe": { + display_name: "botrac4r", + avatar_url: "whatever" + } + } + }) + }) + }) + } + }) t.deepEqual(eventsToRedact, []) t.deepEqual(eventsToSend, []) t.deepEqual(eventsToReplace, [{ @@ -39,8 +57,27 @@ test("edit2changes: bot response", async t => { }]) }) +test("edit2changes: remove caption from image", async t => { + const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.removed_caption_from_image, data.guild.general, {}) + t.deepEqual(eventsToRedact, ["$mtR8cJqM4fKno1bVsm8F4wUVqSntt2sq6jav1lyavuA"]) + t.deepEqual(eventsToSend, []) + t.deepEqual(eventsToReplace, []) +}) + +test("edit2changes: add caption back to that image", async t => { + const {eventsToRedact, eventsToReplace, eventsToSend} = await editToChanges(data.message_update.added_caption_to_image, data.guild.general, {}) + t.deepEqual(eventsToRedact, []) + t.deepEqual(eventsToSend, [{ + $type: "m.room.message", + msgtype: "m.text", + body: "some text", + "m.mentions": {} + }]) + t.deepEqual(eventsToReplace, []) +}) + 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, {}) t.deepEqual(eventsToRedact, []) t.deepEqual(eventsToSend, []) t.deepEqual(eventsToReplace, [{ diff --git a/db/ooye.db b/db/ooye.db index a916aee82643b6a8873edfde0eaf2fe46c59b001..56b1c29cbc6ff0cfe587c1443c6ab74d0ad26249 100644 GIT binary patch delta 31509 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 diff --git a/test/data.js b/test/data.js index e23d42f..bb2570b 100644 --- a/test/data.js +++ b/test/data.js @@ -867,6 +867,162 @@ module.exports = { tts: false, type: 0 }, + removed_caption_from_image: { + attachments: [ + { + content_type: "image/png", + filename: "piper_2.png", + height: 163, + id: "1141501302497615912", + proxy_url: "https://media.discordapp.net/attachments/112760669178241024/1141501302497615912/piper_2.png", + size: 43231, + url: "https://cdn.discordapp.com/attachments/112760669178241024/1141501302497615912/piper_2.png", + width: 188 + } + ], + author: { + avatar: "47db1be7ab77e1d812a4573177af0692", + avatar_decoration: null, + discriminator: "0", + global_name: "wing", + id: "112890272819507200", + public_flags: 0, + username: ".wing." + }, + channel_id: "112760669178241024", + components: [], + content: "", + edited_timestamp: "2023-08-16T22:38:43.075298+00:00", + embeds: [], + flags: 0, + guild_id: "112760669178241024", + id: "1141501302736695316", + member: { + avatar: null, + communication_disabled_until: null, + deaf: false, + flags: 0, + joined_at: "2015-11-08T12:25:38.461000+00:00", + mute: false, + nick: "windfucker", + pending: false, + premium_since: null, + roles: [ + "204427286542417920", + "118924814567211009", + "222168467627835392", + "265239342648131584", + "303273332248412160", + "303319030163439616", + "305775031223320577", + "318243902521868288", + "349185088157777920", + "378402925128712193", + "391076926573510656", + "230462991751970827", + "392141548932038658", + "397533096012152832", + "454567553738473472", + "482658335536185357", + "482860581670486028", + "495384759074160642", + "638988388740890635", + "764071315388629012", + "373336013109461013", + "872274377150980116", + "1034022405275910164", + "790724320824655873", + "1040735082610167858", + "1123730787653660742", + "1070177137367208036" + ] + }, + mention_everyone: false, + mention_roles: [], + mentions: [], + pinned: false, + timestamp: "2023-08-16T22:38:38.641000+00:00", + tts: false, + type: 0 + }, + added_caption_to_image: { + attachments: [ + { + content_type: "image/png", + filename: "piper_2.png", + height: 163, + id: "1141501302497615912", + proxy_url: "https://media.discordapp.net/attachments/112760669178241024/1141501302497615912/piper_2.png", + size: 43231, + url: "https://cdn.discordapp.com/attachments/112760669178241024/1141501302497615912/piper_2.png", + width: 188 + } + ], + author: { + avatar: "47db1be7ab77e1d812a4573177af0692", + avatar_decoration: null, + discriminator: "0", + global_name: "wing", + id: "112890272819507200", + public_flags: 0, + username: ".wing." + }, + channel_id: "112760669178241024", + components: [], + content: "some text", + edited_timestamp: "2023-08-17T00:13:18.620975+00:00", + embeds: [], + flags: 0, + guild_id: "112760669178241024", + id: "1141501302736695317", + member: { + avatar: null, + communication_disabled_until: null, + deaf: false, + flags: 0, + joined_at: "2015-11-08T12:25:38.461000+00:00", + mute: false, + nick: "windfucker", + pending: false, + premium_since: null, + roles: [ + "204427286542417920", + "118924814567211009", + "222168467627835392", + "265239342648131584", + "303273332248412160", + "303319030163439616", + "305775031223320577", + "318243902521868288", + "349185088157777920", + "378402925128712193", + "391076926573510656", + "230462991751970827", + "392141548932038658", + "397533096012152832", + "454567553738473472", + "482658335536185357", + "482860581670486028", + "495384759074160642", + "638988388740890635", + "764071315388629012", + "373336013109461013", + "872274377150980116", + "1034022405275910164", + "790724320824655873", + "1040735082610167858", + "1123730787653660742", + "1070177137367208036" + ] + }, + mention_everyone: false, + mention_roles: [], + mentions: [], + pinned: false, + timestamp: "2023-08-16T22:38:38.641000+00:00", + tts: false, + type: 0 + }, edit_of_reply_to_skull_webp_attachment_with_content: { type: 19, tts: false, From 6a452bd935a54eafe9423a341776012210ae5e33 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Thu, 17 Aug 2023 16:41:28 +1200 Subject: [PATCH 3/3] write editMessage action, connected to dispatcher --- d2m/actions/edit-message.js | 70 ++++++++++++++++++-------- d2m/actions/register-user.js | 4 +- d2m/converters/edit-to-changes.js | 10 ++-- d2m/converters/edit-to-changes.test.js | 4 +- d2m/event-dispatcher.js | 22 ++++++++ matrix/api.js | 14 +++++- types.d.ts | 4 ++ 7 files changed, 95 insertions(+), 33 deletions(-) diff --git a/d2m/actions/edit-message.js b/d2m/actions/edit-message.js index 6823602..9a329b6 100644 --- a/d2m/actions/edit-message.js +++ b/d2m/actions/edit-message.js @@ -1,28 +1,54 @@ -async function editMessage() { - // Action time! +// @ts-check + +const passthrough = require("../../passthrough") +const { sync, db } = passthrough +/** @type {import("../converters/edit-to-changes")} */ +const editToChanges = sync.require("../converters/edit-to-changes") +/** @type {import("../../matrix/api")} */ +const api = sync.require("../../matrix/api") + +/** + * @param {import("discord-api-types/v10").GatewayMessageCreateDispatchData} message + * @param {import("discord-api-types/v10").APIGuild} guild + */ +async function editMessage(message, guild) { + console.log(`*** applying edit for message ${message.id} in channel ${message.channel_id}`) + const {roomID, eventsToRedact, eventsToReplace, eventsToSend, senderMxid} = await editToChanges.editToChanges(message, guild, api) + console.log("making these changes:", {eventsToRedact, eventsToReplace, eventsToSend}) // 1. Replace all the things. + for (const {oldID, newContent} of eventsToReplace) { + const eventType = newContent.$type + /** @type {Pick> & { $type?: string }} */ + const newContentWithoutType = {...newContent} + delete newContentWithoutType.$type - - // 2. Redact all the things. - - // 3. Send all the things. - - // old code lies here - let eventPart = 0 // TODO: what to do about eventPart when editing? probably just need to make sure that exactly 1 value of '0' remains in the database? - for (const event of events) { - const eventType = event.$type - /** @type {Pick> & { $type?: string }} */ - const eventWithoutType = {...event} - delete eventWithoutType.$type - - const eventID = await api.sendEvent(roomID, eventType, event, senderMxid) - db.prepare("INSERT INTO event_message (event_id, message_id, channel_id, part, source) VALUES (?, ?, ?, ?, 1)").run(eventID, message.id, message.channel_id, eventPart) // source 1 = discord - - eventPart = 1 // TODO: use more intelligent algorithm to determine whether primary or supporting - eventIDs.push(eventID) + 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 + db.prepare("UPDATE event_message SET event_subtype = ? WHERE event_id = ?").run(subtype, oldID) } - return eventIDs + // 2. Redact all the things. + // Not redacting as the last action because the last action is likely to be shown in the room preview in clients, and we don't want it to look like somebody actually deleted a message. + for (const eventID of eventsToRedact) { + await api.redactEvent(roomID, eventID, senderMxid) + // TODO: I should almost certainly remove the redacted event from our database now, shouldn't I? I mean, it's literally not there any more... you can't do anything else with it... + // TODO: If I just redacted part = 0, I should update one of the other events to make it the new part = 0, right? + // TODO: Consider whether this code could be reused between edited messages and deleted messages. + } -{eventsToReplace, eventsToRedact, eventsToSend} + // 3. Send all the things. + for (const content of eventsToSend) { + const eventType = content.$type + /** @type {Pick> & { $type?: string }} */ + const contentWithoutType = {...content} + delete contentWithoutType.$type + + const eventID = await api.sendEvent(roomID, eventType, contentWithoutType, senderMxid) + db.prepare("INSERT INTO event_message (event_id, event_type, event_subtype, message_id, channel_id, part, source) VALUES (?, ?, ?, ?, ?, 1, 1)").run(eventID, eventType, content.msgtype || null, message.id, message.channel_id) // part 1 = supporting; source 1 = discord + } +} + +module.exports.editMessage = editMessage diff --git a/d2m/actions/register-user.js b/d2m/actions/register-user.js index beb24bd..1455360 100644 --- a/d2m/actions/register-user.js +++ b/d2m/actions/register-user.js @@ -43,7 +43,7 @@ async function createSim(user) { * Ensure a sim is registered for the user. * If there is already a sim, use that one. If there isn't one yet, register a new sim. * @param {import("discord-api-types/v10").APIUser} user - * @returns mxid + * @returns {Promise} mxid */ async function ensureSim(user) { let mxid = null @@ -60,7 +60,7 @@ async function ensureSim(user) { * Ensure a sim is registered for the user and is joined to the room. * @param {import("discord-api-types/v10").APIUser} user * @param {string} roomID - * @returns mxid + * @returns {Promise} mxid */ async function ensureSimJoined(user, roomID) { // Ensure room ID is really an ID, not an alias diff --git a/d2m/converters/edit-to-changes.js b/d2m/converters/edit-to-changes.js index f055c90..4e6892d 100644 --- a/d2m/converters/edit-to-changes.js +++ b/d2m/converters/edit-to-changes.js @@ -82,7 +82,7 @@ async function editToChanges(message, guild, api) { eventsToRedact = oldEventRows // Now, everything in eventsToSend and eventsToRedact is a real change, but everything in eventsToReplace might not have actually changed! - // (Consider a MESSAGE_UPDATE for a text+image message - Discord does not allow the image to be changed, but the text might have been.) + // (Example: a MESSAGE_UPDATE for a text+image message - Discord does not allow the image to be changed, but the text might have been.) // So we'll remove entries from eventsToReplace that *definitely* cannot have changed. (This is category 4 mentioned above.) Everything remaining *may* have changed. eventsToReplace = eventsToReplace.filter(ev => { // Discord does not allow files, images, attachments, or videos to be edited. @@ -99,9 +99,9 @@ async function editToChanges(message, guild, api) { // Removing unnecessary properties before returning eventsToRedact = eventsToRedact.map(e => e.event_id) - eventsToReplace = eventsToReplace.map(e => ({oldID: e.old.event_id, new: eventToReplacementEvent(e.old.event_id, e.newFallbackContent, e.newInnerContent)})) + eventsToReplace = eventsToReplace.map(e => ({oldID: e.old.event_id, newContent: makeReplacementEventContent(e.old.event_id, e.newFallbackContent, e.newInnerContent)})) - return {eventsToReplace, eventsToRedact, eventsToSend, senderMxid} + return {roomID, eventsToReplace, eventsToRedact, eventsToSend, senderMxid} } /** @@ -111,7 +111,7 @@ async function editToChanges(message, guild, api) { * @param {T} newInnerContent * @returns {import("../../types").Event.ReplacementContent} content */ -function eventToReplacementEvent(oldID, newFallbackContent, newInnerContent) { +function makeReplacementEventContent(oldID, newFallbackContent, newInnerContent) { const content = { ...newFallbackContent, "m.mentions": {}, @@ -130,4 +130,4 @@ function eventToReplacementEvent(oldID, newFallbackContent, newInnerContent) { } module.exports.editToChanges = editToChanges -module.exports.eventToReplacementEvent = eventToReplacementEvent +module.exports.makeReplacementEventContent = makeReplacementEventContent diff --git a/d2m/converters/edit-to-changes.test.js b/d2m/converters/edit-to-changes.test.js index 8385cd0..bb3f3ec 100644 --- a/d2m/converters/edit-to-changes.test.js +++ b/d2m/converters/edit-to-changes.test.js @@ -29,7 +29,7 @@ test("edit2changes: bot response", async t => { t.deepEqual(eventsToSend, []) t.deepEqual(eventsToReplace, [{ oldID: "$fdD9OZ55xg3EAsfvLZza5tMhtjUO91Wg3Otuo96TplY", - new: { + newContent: { $type: "m.room.message", msgtype: "m.text", body: "* :ae_botrac4r: @cadence asked ``­``, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.", @@ -82,7 +82,7 @@ test("edit2changes: edit of reply to skull webp attachment with content", async t.deepEqual(eventsToSend, []) t.deepEqual(eventsToReplace, [{ oldID: "$vgTKOR5ZTYNMKaS7XvgEIDaOWZtVCEyzLLi5Pc5Gz4M", - new: { + newContent: { $type: "m.room.message", msgtype: "m.text", body: "> Extremity: Image\n\n* Edit", diff --git a/d2m/event-dispatcher.js b/d2m/event-dispatcher.js index 1686b5f..4bb94ff 100644 --- a/d2m/event-dispatcher.js +++ b/d2m/event-dispatcher.js @@ -3,6 +3,9 @@ const {sync, db} = require("../passthrough") /** @type {import("./actions/send-message")}) */ const sendMessage = sync.require("./actions/send-message") +/** @type {import("./actions/edit-message")}) */ +const editMessage = sync.require("./actions/edit-message") + /** @type {import("./actions/add-reaction")}) */ const addReaction = sync.require("./actions/add-reaction") @@ -29,6 +32,25 @@ module.exports = { sendMessage.sendMessage(message, guild) }, + /** + * @param {import("./discord-client")} client + * @param {import("discord-api-types/v10").GatewayMessageUpdateDispatchData} message + */ + onMessageUpdate(client, data) { + // Based on looking at data they've sent me over the gateway, this is the best way to check for meaningful changes. + // If the message content is a string then it includes all interesting fields and is meaningful. + if (typeof data.content === "string") { + /** @type {import("discord-api-types/v10").GatewayMessageCreateDispatchData} */ + const message = data + /** @type {import("discord-api-types/v10").APIGuildChannel} */ + const channel = client.channels.get(message.channel_id) + if (!channel.guild_id) return // Nothing we can do in direct messages. + const guild = client.guilds.get(channel.guild_id) + if (message.guild_id !== "112760669178241024" && message.guild_id !== "497159726455455754") return // TODO: activate on other servers (requires the space creation flow to be done first) + editMessage.editMessage(message, guild) + } + }, + /** * @param {import("./discord-client")} client * @param {import("discord-api-types/v10").GatewayMessageReactionAddDispatchData} data diff --git a/matrix/api.js b/matrix/api.js index 9111909..ef3e199 100644 --- a/matrix/api.js +++ b/matrix/api.js @@ -14,7 +14,7 @@ const makeTxnId = sync.require("./txnid") /** * @param {string} p endpoint to access - * @param {string} [mxid] optional: user to act as, for the ?user_id parameter + * @param {string?} [mxid] optional: user to act as, for the ?user_id parameter * @param {{[x: string]: any}} [otherParams] optional: any other query parameters to add * @returns {string} the new endpoint */ @@ -119,7 +119,7 @@ async function sendState(roomID, type, stateKey, content, mxid) { * @param {string} roomID * @param {string} type * @param {any} content - * @param {string} [mxid] + * @param {string?} [mxid] * @param {number} [timestamp] timestamp of the newly created event, in unix milliseconds */ async function sendEvent(roomID, type, content, mxid, timestamp) { @@ -129,6 +129,15 @@ async function sendEvent(roomID, type, content, mxid, timestamp) { return root.event_id } +/** + * @returns {Promise} room ID + */ +async function redactEvent(roomID, eventID, mxid) { + /** @type {Ty.R.EventRedacted} */ + const root = await mreq.mreq("PUT", path(`/client/v3/rooms/${roomID}/redact/${eventID}/${makeTxnId.makeTxnId()}`, mxid)) + return root.event_id +} + async function profileSetDisplayname(mxid, displayname) { await mreq.mreq("PUT", path(`/client/v3/profile/${mxid}/displayname`, mxid), { displayname @@ -152,5 +161,6 @@ module.exports.getAllState = getAllState module.exports.getJoinedMembers = getJoinedMembers module.exports.sendState = sendState module.exports.sendEvent = sendEvent +module.exports.redactEvent = redactEvent module.exports.profileSetDisplayname = profileSetDisplayname module.exports.profileSetAvatarUrl = profileSetAvatarUrl diff --git a/types.d.ts b/types.d.ts index 76d3bd1..b9f7ed6 100644 --- a/types.d.ts +++ b/types.d.ts @@ -112,4 +112,8 @@ namespace R { export type EventSent = { event_id: string } + + export type EventRedacted = { + event_id: string + } }