getting edits closer to working
This commit is contained in:
		
							parent
							
								
									cae591e5fd
								
							
						
					
					
						commit
						b1ca71f37c
					
				
					 10 changed files with 259 additions and 51 deletions
				
			
		| 
						 | 
				
			
			@ -9,7 +9,7 @@ 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("./register-user")
 | 
			
		||||
const registerUser = sync.require("../actions/register-user")
 | 
			
		||||
/** @type {import("../actions/create-room")} */
 | 
			
		||||
const createRoom = sync.require("../actions/create-room")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ const createRoom = sync.require("../actions/create-room")
 | 
			
		|||
async function editToChanges(message, guild) {
 | 
			
		||||
	// Figure out what events we will be replacing
 | 
			
		||||
 | 
			
		||||
	const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").get(message.channel_id)
 | 
			
		||||
	const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(message.channel_id)
 | 
			
		||||
	const senderMxid = await registerUser.ensureSimJoined(message.author, roomID)
 | 
			
		||||
	/** @type {{event_id: string, event_type: string, event_subtype: string?, part: number}[]} */
 | 
			
		||||
   const oldEventRows = db.prepare("SELECT event_id, event_type, event_subtype, part FROM event_message WHERE message_id = ?").all(message.id)
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ async function editToChanges(message, guild) {
 | 
			
		|||
		Rules:
 | 
			
		||||
			+ The events must have the same type.
 | 
			
		||||
			+ The events must have the same subtype.
 | 
			
		||||
		Events will therefore be divided into three categories:
 | 
			
		||||
		Events will therefore be divided into four categories:
 | 
			
		||||
	*/
 | 
			
		||||
	/** 1. Events that are matched, and should be edited by sending another m.replace event */
 | 
			
		||||
	let eventsToReplace = []
 | 
			
		||||
| 
						 | 
				
			
			@ -45,12 +45,12 @@ async function editToChanges(message, guild) {
 | 
			
		|||
	let eventsToRedact = []
 | 
			
		||||
	/** 3. Events that are present in the new version only, and should be sent as new, with references back to the context */
 | 
			
		||||
	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.
 | 
			
		||||
 | 
			
		||||
	// For each old event...
 | 
			
		||||
	outer: while (newEvents.length) {
 | 
			
		||||
		const newe = newEvents[0]
 | 
			
		||||
		// Find a new event to pair it with...
 | 
			
		||||
		let handled = false
 | 
			
		||||
		for (let i = 0; i < oldEventRows.length; i++) {
 | 
			
		||||
			const olde = oldEventRows[i]
 | 
			
		||||
			if (olde.event_type === newe.$type && olde.event_subtype === (newe.msgtype || null)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +76,7 @@ async function editToChanges(message, guild) {
 | 
			
		|||
 | 
			
		||||
	// 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.)
 | 
			
		||||
	// So we'll remove entries from eventsToReplace that *definitely* cannot have changed. Everything remaining *may* have changed.
 | 
			
		||||
	// 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.
 | 
			
		||||
		if (ev.old.event_type === "m.room.message" && ev.old.event_subtype !== "m.text" && ev.old.event_subtype !== "m.emote") {
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +90,42 @@ async function editToChanges(message, guild) {
 | 
			
		|||
		return true
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// 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)}))
 | 
			
		||||
 | 
			
		||||
	return {eventsToReplace, eventsToRedact, eventsToSend}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports.editMessage = editMessage
 | 
			
		||||
/**
 | 
			
		||||
 * @template T
 | 
			
		||||
 * @param {string} oldID
 | 
			
		||||
 * @param {T} content
 | 
			
		||||
 * @returns {import("../../types").Event.ReplacementContent<T>} content
 | 
			
		||||
 */
 | 
			
		||||
function eventToReplacementEvent(oldID, content) {
 | 
			
		||||
	const newContent = {
 | 
			
		||||
		...content,
 | 
			
		||||
		"m.mentions": {},
 | 
			
		||||
		"m.new_content": {
 | 
			
		||||
			...content
 | 
			
		||||
		},
 | 
			
		||||
		"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"]
 | 
			
		||||
	// 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports.editToChanges = editToChanges
 | 
			
		||||
module.exports.eventToReplacementEvent = eventToReplacementEvent
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										66
									
								
								d2m/converters/edit-to-changes.test.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								d2m/converters/edit-to-changes.test.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
// @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)
 | 
			
		||||
   t.deepEqual(eventsToRedact, [])
 | 
			
		||||
   t.deepEqual(eventsToSend, [])
 | 
			
		||||
   t.deepEqual(eventsToReplace, [{
 | 
			
		||||
      oldID: "$fdD9OZ55xg3EAsfvLZza5tMhtjUO91Wg3Otuo96TplY",
 | 
			
		||||
      new: {
 | 
			
		||||
         $type: "m.room.message",
 | 
			
		||||
         msgtype: "m.text",
 | 
			
		||||
         body: "* :ae_botrac4r: @cadence asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
 | 
			
		||||
         format: "org.matrix.custom.html",
 | 
			
		||||
         formatted_body: '* <img src="mxc://cadence.moe/551636841284108289" data-mx-emoticon alt=":ae_botrac4r:" title=":ae_botrac4r:" height="24"> @cadence asked <code></code>, I respond: Stop drinking paint. (No)<br><br>Hit <img src="mxc://cadence.moe/362741439211503616" data-mx-emoticon alt=":bn_re:" title=":bn_re:" height="24"> to reroll.',
 | 
			
		||||
         "m.mentions": {
 | 
			
		||||
            // Client-Server API spec 11.37.7: Copy Discord's behaviour by not re-notifying anyone that an *edit occurred*
 | 
			
		||||
         },
 | 
			
		||||
         // *** Replaced With: ***
 | 
			
		||||
         "m.new_content": {
 | 
			
		||||
            msgtype: "m.text",
 | 
			
		||||
            body: ":ae_botrac4r: @cadence asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
 | 
			
		||||
            format: "org.matrix.custom.html",
 | 
			
		||||
            formatted_body: '<img src="mxc://cadence.moe/551636841284108289" data-mx-emoticon alt=":ae_botrac4r:" title=":ae_botrac4r:" height="24"> @cadence asked <code></code>, I respond: Stop drinking paint. (No)<br><br>Hit <img src="mxc://cadence.moe/362741439211503616" data-mx-emoticon alt=":bn_re:" title=":bn_re:" height="24"> to reroll.',
 | 
			
		||||
            "m.mentions": {
 | 
			
		||||
               // Client-Server API spec 11.37.7: This should contain the mentions for the final version of the event
 | 
			
		||||
               "user_ids": ["@cadence:cadence.moe"]
 | 
			
		||||
            }
 | 
			
		||||
         },
 | 
			
		||||
         "m.relates_to": {
 | 
			
		||||
            rel_type: "m.replace",
 | 
			
		||||
            event_id: "$fdD9OZ55xg3EAsfvLZza5tMhtjUO91Wg3Otuo96TplY"
 | 
			
		||||
         }
 | 
			
		||||
      }
 | 
			
		||||
   }])
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
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, [])
 | 
			
		||||
   t.deepEqual(eventsToSend, [])
 | 
			
		||||
   t.deepEqual(eventsToReplace, [{
 | 
			
		||||
      oldID: "$vgTKOR5ZTYNMKaS7XvgEIDaOWZtVCEyzLLi5Pc5Gz4M",
 | 
			
		||||
      new: {
 | 
			
		||||
         $type: "m.room.message",
 | 
			
		||||
         // TODO: read "edits of replies" in the spec!!!
 | 
			
		||||
         msgtype: "m.text",
 | 
			
		||||
         body: "* Edit",
 | 
			
		||||
         "m.mentions": {},
 | 
			
		||||
         "m.new_content": {
 | 
			
		||||
            msgtype: "m.text",
 | 
			
		||||
            body: "Edit",
 | 
			
		||||
            "m.mentions": {}
 | 
			
		||||
         },
 | 
			
		||||
         "m.relates_to": {
 | 
			
		||||
            rel_type: "m.replace",
 | 
			
		||||
            event_id: "$vgTKOR5ZTYNMKaS7XvgEIDaOWZtVCEyzLLi5Pc5Gz4M"
 | 
			
		||||
         }
 | 
			
		||||
         // TODO: read "edits of replies" in the spec!!!
 | 
			
		||||
      }
 | 
			
		||||
   }])
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ const userRegex = reg.namespaces.users.map(u => new RegExp(u.regex))
 | 
			
		|||
 | 
			
		||||
function getDiscordParseCallbacks(message, useHTML) {
 | 
			
		||||
	return {
 | 
			
		||||
		/** @param {{id: string, type: "discordUser"}} node */
 | 
			
		||||
		user: node => {
 | 
			
		||||
			const mxid = db.prepare("SELECT mxid FROM sim WHERE discord_id = ?").pluck().get(node.id)
 | 
			
		||||
			const username = message.mentions.find(ment => ment.id === node.id)?.username || node.id
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +25,7 @@ function getDiscordParseCallbacks(message, useHTML) {
 | 
			
		|||
				return `@${username}:`
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		/** @param {{id: string, type: "discordChannel"}} node */
 | 
			
		||||
		channel: node => {
 | 
			
		||||
			const {room_id, name, nick} = db.prepare("SELECT room_id, name, nick FROM channel_room WHERE channel_id = ?").get(node.id)
 | 
			
		||||
			if (room_id && useHTML) {
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +34,15 @@ function getDiscordParseCallbacks(message, useHTML) {
 | 
			
		|||
				return `#${nick || name}`
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		/** @param {{animated: boolean, name: string, id: string, type: "discordEmoji"}} node */
 | 
			
		||||
		emoji: node => {
 | 
			
		||||
			if (useHTML) {
 | 
			
		||||
				// TODO: upload the emoji and actually use the right mxc!!
 | 
			
		||||
				return `<img src="mxc://cadence.moe/${node.id}" data-mx-emoticon alt=":${node.name}:" title=":${node.name}:" height="24">`
 | 
			
		||||
			} else {
 | 
			
		||||
				return `:${node.name}:`
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		role: node =>
 | 
			
		||||
			"@&" + node.id,
 | 
			
		||||
		everyone: node =>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue