finalise message editing
This commit is contained in:
		
							parent
							
								
									53b5438756
								
							
						
					
					
						commit
						56fe710392
					
				
					 4 changed files with 157 additions and 63 deletions
				
			
		| 
						 | 
					@ -58,6 +58,20 @@ async function sendMessageWithWebhook(channelID, data, threadID) {
 | 
				
			||||||
	return result
 | 
						return result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @param {string} channelID
 | 
				
			||||||
 | 
					 * @param {string} messageID
 | 
				
			||||||
 | 
					 * @param {DiscordTypes.RESTPatchAPIWebhookWithTokenMessageJSONBody & {files?: {name: string, file: Buffer}[]}} data
 | 
				
			||||||
 | 
					 * @param {string} [threadID]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					async function editMessageWithWebhook(channelID, messageID, data, threadID) {
 | 
				
			||||||
 | 
						const result = await withWebhook(channelID, async webhook => {
 | 
				
			||||||
 | 
							return discord.snow.webhook.editWebhookMessage(webhook.id, webhook.token, messageID, {...data, thread_id: threadID})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports.ensureWebhook = ensureWebhook
 | 
					module.exports.ensureWebhook = ensureWebhook
 | 
				
			||||||
module.exports.withWebhook = withWebhook
 | 
					module.exports.withWebhook = withWebhook
 | 
				
			||||||
module.exports.sendMessageWithWebhook = sendMessageWithWebhook
 | 
					module.exports.sendMessageWithWebhook = sendMessageWithWebhook
 | 
				
			||||||
 | 
					module.exports.editMessageWithWebhook = editMessageWithWebhook
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,9 +34,11 @@ async function sendEvent(event) {
 | 
				
			||||||
	/** @type {DiscordTypes.APIMessage[]} */
 | 
						/** @type {DiscordTypes.APIMessage[]} */
 | 
				
			||||||
	const messageResponses = []
 | 
						const messageResponses = []
 | 
				
			||||||
	let eventPart = 0 // 0 is primary, 1 is supporting
 | 
						let eventPart = 0 // 0 is primary, 1 is supporting
 | 
				
			||||||
	// for (const message of messagesToEdit) {
 | 
						for (const data of messagesToEdit) {
 | 
				
			||||||
	//	eventPart = 1
 | 
							const messageResponse = await channelWebhook.editMessageWithWebhook(channelID, data.id, data.message, threadID)
 | 
				
			||||||
	//	TODO ...
 | 
							eventPart = 1
 | 
				
			||||||
 | 
							messageResponses.push(messageResponse)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	for (const message of messagesToSend) {
 | 
						for (const message of messagesToSend) {
 | 
				
			||||||
		const messageResponse = await channelWebhook.sendMessageWithWebhook(channelID, message, threadID)
 | 
							const messageResponse = await channelWebhook.sendMessageWithWebhook(channelID, message, threadID)
 | 
				
			||||||
		db.prepare("REPLACE INTO message_channel (message_id, channel_id) VALUES (?, ?)").run(messageResponse.id, channelID)
 | 
							db.prepare("REPLACE INTO message_channel (message_id, channel_id) VALUES (?, ?)").run(messageResponse.id, channelID)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -141,19 +141,10 @@ async function eventToMessage(event, guild, di) {
 | 
				
			||||||
	if (member.displayname) displayName = member.displayname
 | 
						if (member.displayname) displayName = member.displayname
 | 
				
			||||||
	if (member.avatar_url) avatarURL = utils.getPublicUrlForMxc(member.avatar_url)
 | 
						if (member.avatar_url) avatarURL = utils.getPublicUrlForMxc(member.avatar_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Convert content depending on what the message is
 | 
					 | 
				
			||||||
	let content = event.content.body // ultimate fallback
 | 
						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"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Convert content depending on what the message is
 | 
				
			||||||
 | 
						if (event.content.msgtype === "m.text" || event.content.msgtype === "m.emote") {
 | 
				
			||||||
		// Handling edits. If the edit was an edit of a reply, edits do not include the reply reference, so we need to fetch up to 2 more events.
 | 
							// Handling edits. If the edit was an edit of a reply, edits do not include the reply reference, so we need to fetch up to 2 more events.
 | 
				
			||||||
		// this event ---is an edit of--> original event ---is a reply to--> past event
 | 
							// this event ---is an edit of--> original event ---is a reply to--> past event
 | 
				
			||||||
		await (async () => {
 | 
							await (async () => {
 | 
				
			||||||
| 
						 | 
					@ -167,16 +158,26 @@ async function eventToMessage(event, guild, di) {
 | 
				
			||||||
			if (!originalEventId) return
 | 
								if (!originalEventId) return
 | 
				
			||||||
			messageIDsToEdit = db.prepare("SELECT message_id FROM event_message WHERE event_id = ? ORDER BY part").pluck().all(originalEventId)
 | 
								messageIDsToEdit = db.prepare("SELECT message_id FROM event_message WHERE event_id = ? ORDER BY part").pluck().all(originalEventId)
 | 
				
			||||||
			if (!messageIDsToEdit.length) return
 | 
								if (!messageIDsToEdit.length) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Ok, it's an edit.
 | 
				
			||||||
 | 
								event.content = event.content["m.new_content"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Is it editing a reply? We need special handling if it is.
 | 
				
			||||||
			// Get the original event, then check if it was a reply
 | 
								// Get the original event, then check if it was a reply
 | 
				
			||||||
			const originalEvent = await di.api.getEvent(event.room_id, originalEventId)
 | 
								const originalEvent = await di.api.getEvent(event.room_id, originalEventId)
 | 
				
			||||||
			if (!originalEvent) return
 | 
								if (!originalEvent) return
 | 
				
			||||||
			const repliedToEventId = originalEvent.content["m.relates_to"]?.["m.in_reply_to"]?.event_id
 | 
								const repliedToEventId = originalEvent.content["m.relates_to"]?.["m.in_reply_to"]?.event_id
 | 
				
			||||||
			if (!repliedToEventId) return
 | 
								if (!repliedToEventId) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// After all that, it's an edit of a reply.
 | 
								// After all that, it's an edit of a reply.
 | 
				
			||||||
			// We'll be sneaky and prepare the message data so that everything else can handle it just like original messages.
 | 
								// We'll be sneaky and prepare the message data so that the next steps can handle it just like original messages.
 | 
				
			||||||
			Object.assign(event.content, event.content["m.new_content"])
 | 
								Object.assign(event.content, {
 | 
				
			||||||
			input = event.content.formatted_body || event.content.body
 | 
									"m.relates_to": {
 | 
				
			||||||
			relatesTo["m.in_reply_to"] = {event_id: repliedToEventId}
 | 
										"m.in_reply_to": {
 | 
				
			||||||
 | 
											event_id: repliedToEventId
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
		})()
 | 
							})()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Handling replies. We'll look up the data of the replied-to event from the Matrix homeserver.
 | 
							// Handling replies. We'll look up the data of the replied-to event from the Matrix homeserver.
 | 
				
			||||||
| 
						 | 
					@ -206,51 +207,69 @@ async function eventToMessage(event, guild, di) {
 | 
				
			||||||
			replyLine = `> ${replyLine}\n> ${contentPreview}\n`
 | 
								replyLine = `> ${replyLine}\n> ${contentPreview}\n`
 | 
				
			||||||
		})()
 | 
							})()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Handling mentions of Discord users
 | 
							if (event.content.format === "org.matrix.custom.html" && event.content.formatted_body) {
 | 
				
			||||||
		input = input.replace(/("https:\/\/matrix.to\/#\/(@[^"]+)")>/g, (whole, attributeValue, mxid) => {
 | 
								let input = event.content.formatted_body
 | 
				
			||||||
			if (!utils.eventSenderIsFromDiscord(mxid)) return whole
 | 
								if (event.content.msgtype === "m.emote") {
 | 
				
			||||||
			const userID = db.prepare("SELECT discord_id FROM sim WHERE mxid = ?").pluck().get(mxid)
 | 
									input = `* ${displayName} ${input}`
 | 
				
			||||||
			if (!userID) return whole
 | 
					 | 
				
			||||||
			return `${attributeValue} data-user-id="${userID}">`
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Handling mentions of Discord rooms
 | 
					 | 
				
			||||||
		input = input.replace(/("https:\/\/matrix.to\/#\/(![^"]+)")>/g, (whole, attributeValue, roomID) => {
 | 
					 | 
				
			||||||
			const channelID = db.prepare("SELECT channel_id FROM channel_room WHERE room_id = ?").pluck().get(roomID)
 | 
					 | 
				
			||||||
			if (!channelID) return whole
 | 
					 | 
				
			||||||
			return `${attributeValue} data-channel-id="${channelID}">`
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Element adds a bunch of <br> before </blockquote> but doesn't render them. I can't figure out how this even works in the browser, so let's just delete those.
 | 
					 | 
				
			||||||
		input = input.replace(/(?:\n|<br ?\/?>\s*)*<\/blockquote>/g, "</blockquote>")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 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) => {
 | 
					 | 
				
			||||||
			// console.error(beforeContext, beforeTag, afterContext, afterTag)
 | 
					 | 
				
			||||||
			if (typeof beforeTag !== "string" && typeof afterTag !== "string") {
 | 
					 | 
				
			||||||
				return "<br>"
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			beforeContext = beforeContext || ""
 | 
					
 | 
				
			||||||
			beforeTag = beforeTag || ""
 | 
								// Handling mentions of Discord users
 | 
				
			||||||
			afterContext = afterContext || ""
 | 
								input = input.replace(/("https:\/\/matrix.to\/#\/(@[^"]+)")>/g, (whole, attributeValue, mxid) => {
 | 
				
			||||||
			afterTag = afterTag || ""
 | 
									if (!utils.eventSenderIsFromDiscord(mxid)) return whole
 | 
				
			||||||
			if (!BLOCK_ELEMENTS.includes(beforeTag.toUpperCase()) && !BLOCK_ELEMENTS.includes(afterTag.toUpperCase())) {
 | 
									const userID = db.prepare("SELECT discord_id FROM sim WHERE mxid = ?").pluck().get(mxid)
 | 
				
			||||||
				return beforeContext + "<br>" + afterContext
 | 
									if (!userID) return whole
 | 
				
			||||||
			} else {
 | 
									return `${attributeValue} data-user-id="${userID}">`
 | 
				
			||||||
				return whole
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Handling mentions of Discord rooms
 | 
				
			||||||
 | 
								input = input.replace(/("https:\/\/matrix.to\/#\/(![^"]+)")>/g, (whole, attributeValue, roomID) => {
 | 
				
			||||||
 | 
									const channelID = db.prepare("SELECT channel_id FROM channel_room WHERE room_id = ?").pluck().get(roomID)
 | 
				
			||||||
 | 
									if (!channelID) return whole
 | 
				
			||||||
 | 
									return `${attributeValue} data-channel-id="${channelID}">`
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Element adds a bunch of <br> before </blockquote> but doesn't render them. I can't figure out how this even works in the browser, so let's just delete those.
 | 
				
			||||||
 | 
								input = input.replace(/(?:\n|<br ?\/?>\s*)*<\/blockquote>/g, "</blockquote>")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 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) => {
 | 
				
			||||||
 | 
									// console.error(beforeContext, beforeTag, afterContext, afterTag)
 | 
				
			||||||
 | 
									if (typeof beforeTag !== "string" && typeof afterTag !== "string") {
 | 
				
			||||||
 | 
										return "<br>"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									beforeContext = beforeContext || ""
 | 
				
			||||||
 | 
									beforeTag = beforeTag || ""
 | 
				
			||||||
 | 
									afterContext = afterContext || ""
 | 
				
			||||||
 | 
									afterTag = afterTag || ""
 | 
				
			||||||
 | 
									if (!BLOCK_ELEMENTS.includes(beforeTag.toUpperCase()) && !BLOCK_ELEMENTS.includes(afterTag.toUpperCase())) {
 | 
				
			||||||
 | 
										return beforeContext + "<br>" + afterContext
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										return whole
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// @ts-ignore bad type from turndown
 | 
				
			||||||
 | 
								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")
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Looks like we're using the plaintext body!
 | 
				
			||||||
 | 
								content = event.content.body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (event.content.msgtype === "m.emote") {
 | 
				
			||||||
 | 
									content = `* ${displayName} ${content}`
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// @ts-ignore bad type from turndown
 | 
								// Markdown needs to be escaped
 | 
				
			||||||
		content = turndownService.turndown(input)
 | 
								content = content.replace(/([*_~`#])/g, `\\$1`)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		// It's optimised for commonmark, we need to replace the space-space-newline with just newline
 | 
					 | 
				
			||||||
		content = content.replace(/  \n/g, "\n")
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		// Looks like we're using the plaintext body!
 | 
					 | 
				
			||||||
		// Markdown needs to be escaped
 | 
					 | 
				
			||||||
		content = content.replace(/([*_~`#])/g, `\\$1`)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	content = replyLine + content
 | 
						content = replyLine + content
 | 
				
			||||||
| 
						 | 
					@ -266,8 +285,10 @@ async function eventToMessage(event, guild, di) {
 | 
				
			||||||
	const messagesToEdit = []
 | 
						const messagesToEdit = []
 | 
				
			||||||
	const messagesToSend = []
 | 
						const messagesToSend = []
 | 
				
			||||||
	for (let i = 0; i < messages.length; i++) {
 | 
						for (let i = 0; i < messages.length; i++) {
 | 
				
			||||||
		if (messageIDsToEdit.length) {
 | 
							const next = messageIDsToEdit[0]
 | 
				
			||||||
			messagesToEdit.push({id: messageIDsToEdit.shift(), message: messages[i]})
 | 
							if (next) {
 | 
				
			||||||
 | 
								messagesToEdit.push({id: next, message: messages[i]})
 | 
				
			||||||
 | 
								messageIDsToEdit.shift()
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			messagesToSend.push(messages[i])
 | 
								messagesToSend.push(messages[i])
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -584,7 +584,7 @@ test("event2message: editing a plaintext body message", async t => {
 | 
				
			||||||
			"room_id": "!PnyBKvUBOhjuCucEfk:cadence.moe"
 | 
								"room_id": "!PnyBKvUBOhjuCucEfk:cadence.moe"
 | 
				
			||||||
		}, data.guild.general, {
 | 
							}, data.guild.general, {
 | 
				
			||||||
			api: {
 | 
								api: {
 | 
				
			||||||
				getEvent: mockGetEvent(t, "!fGgIymcYWOqjbSRUdV:cadence.moe", "$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSs", {
 | 
									getEvent: mockGetEvent(t, "!PnyBKvUBOhjuCucEfk:cadence.moe", "$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSs", {
 | 
				
			||||||
					type: "m.room.message",
 | 
										type: "m.room.message",
 | 
				
			||||||
					sender: "@cadence:cadence.moe",
 | 
										sender: "@cadence:cadence.moe",
 | 
				
			||||||
					content: {
 | 
										content: {
 | 
				
			||||||
| 
						 | 
					@ -609,6 +609,63 @@ test("event2message: editing a plaintext body message", async t => {
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test("event2message: editing a formatted body message", async t => {
 | 
				
			||||||
 | 
						t.deepEqual(
 | 
				
			||||||
 | 
							await eventToMessage({
 | 
				
			||||||
 | 
								"type": "m.room.message",
 | 
				
			||||||
 | 
								"sender": "@cadence:cadence.moe",
 | 
				
			||||||
 | 
								"content": {
 | 
				
			||||||
 | 
									"msgtype": "m.text",
 | 
				
			||||||
 | 
									"body": " * **well, I guess it's no longer brand new... it's existed for mere seconds...**",
 | 
				
			||||||
 | 
									"format": "org.matrix.custom.html",
 | 
				
			||||||
 | 
									"formatted_body": "* <strong>well, I guess it's no longer brand new... it's existed for mere seconds...</strong>",
 | 
				
			||||||
 | 
									"m.new_content": {
 | 
				
			||||||
 | 
										"msgtype": "m.text",
 | 
				
			||||||
 | 
										"body": "**well, I guess it's no longer brand new... it's existed for mere seconds...**",
 | 
				
			||||||
 | 
										"format": "org.matrix.custom.html",
 | 
				
			||||||
 | 
										"formatted_body": "<strong>well, I guess it's no longer brand new... it's existed for mere seconds...</strong>"
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									"m.relates_to": {
 | 
				
			||||||
 | 
										"rel_type": "m.replace",
 | 
				
			||||||
 | 
										"event_id": "$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSs"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"origin_server_ts": 1693223873912,
 | 
				
			||||||
 | 
								"unsigned": {
 | 
				
			||||||
 | 
									"age": 42,
 | 
				
			||||||
 | 
									"transaction_id": "m1693223873796.842"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"event_id": "$KxGwvVNzNcmlVbiI2m5kX-jMFNi3Jle71-uu1j7P7vM",
 | 
				
			||||||
 | 
								"room_id": "!PnyBKvUBOhjuCucEfk:cadence.moe"
 | 
				
			||||||
 | 
							}, data.guild.general, {
 | 
				
			||||||
 | 
								api: {
 | 
				
			||||||
 | 
									getEvent: mockGetEvent(t, "!PnyBKvUBOhjuCucEfk:cadence.moe", "$7LIdiJCEqjcWUrpzWzS8TELOlFfBEe4ytgS7zn2lbSs", {
 | 
				
			||||||
 | 
										type: "m.room.message",
 | 
				
			||||||
 | 
										sender: "@cadence:cadence.moe",
 | 
				
			||||||
 | 
										content: {
 | 
				
			||||||
 | 
											msgtype: "m.text",
 | 
				
			||||||
 | 
											body: "**brand new, never before seen message**",
 | 
				
			||||||
 | 
											format: "org.matrix.custom.html",
 | 
				
			||||||
 | 
											formatted_body: "<strong>brand new, never before seen message</strong>"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								messagesToDelete: [],
 | 
				
			||||||
 | 
								messagesToEdit: [{
 | 
				
			||||||
 | 
									id: "1145688633186193479",
 | 
				
			||||||
 | 
									message: {
 | 
				
			||||||
 | 
										username: "cadence [they]",
 | 
				
			||||||
 | 
										content: "**well, I guess it's no longer brand new... it's existed for mere seconds...**",
 | 
				
			||||||
 | 
										avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/azCAhThKTojXSZJRoWwZmhvU"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}],
 | 
				
			||||||
 | 
								messagesToSend: []
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test("event2message: rich reply to a matrix user's long message with formatting", async t => {
 | 
					test("event2message: rich reply to a matrix user's long message with formatting", async t => {
 | 
				
			||||||
	t.deepEqual(
 | 
						t.deepEqual(
 | 
				
			||||||
		await eventToMessage({
 | 
							await eventToMessage({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue