Handle replies to state events with no body
This commit is contained in:
		
							parent
							
								
									8ad0117fd2
								
							
						
					
					
						commit
						69e3d64905
					
				
					 2 changed files with 66 additions and 19 deletions
				
			
		|  | @ -476,7 +476,7 @@ async function eventToMessage(event, guild, di) { | |||
| 	// Try to extract an accurate display name and avatar URL from the member event
 | ||||
| 	const member = await getMemberFromCacheOrHomeserver(event.room_id, event.sender, di?.api) | ||||
| 	if (member.displayname) displayName = member.displayname | ||||
| 	if (member.avatar_url) avatarURL = mxUtils.getPublicUrlForMxc(member.avatar_url) || undefined | ||||
| 	if (member.avatar_url) avatarURL = mxUtils.getPublicUrlForMxc(member.avatar_url) | ||||
| 	// If the display name is too long to be put into the webhook (80 characters is the maximum),
 | ||||
| 	// put the excess characters into displayNameRunoff, later to be put at the top of the message
 | ||||
| 	let [displayNameShortened, displayNameRunoff] = splitDisplayName(displayName) | ||||
|  | @ -512,8 +512,7 @@ async function eventToMessage(event, guild, di) { | |||
| 			// Is it editing a reply? We need special handling if it is.
 | ||||
| 			// Get the original event, then check if it was a reply
 | ||||
| 			const originalEvent = await di.api.getEvent(event.room_id, originalEventId) | ||||
| 			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 | ||||
| 
 | ||||
| 			// After all that, it's an edit of a reply.
 | ||||
|  | @ -576,34 +575,27 @@ async function eventToMessage(event, guild, di) { | |||
| 			if (row) { | ||||
| 				replyLine += `https://discord.com/channels/${guild.id}/${row.channel_id}/${row.message_id} ` | ||||
| 			} | ||||
| 			const sender = repliedToEvent.sender | ||||
| 			const authorID = getUserOrProxyOwnerID(sender) | ||||
| 			if (authorID) { | ||||
| 				replyLine += `<@${authorID}>` | ||||
| 			} else { | ||||
| 				let senderName = select("member_cache", "displayname", {mxid: sender}).pluck().get() | ||||
| 				if (!senderName) { | ||||
| 					const match = sender.match(/@([^:]*)/) | ||||
| 					assert(match) | ||||
| 					senderName = match[1] | ||||
| 				} | ||||
| 				replyLine += `**Ⓜ${senderName}**` | ||||
| 			} | ||||
| 			// If the event has been edited, the homeserver will include the relation in `unsigned`.
 | ||||
| 			if (repliedToEvent.unsigned?.["m.relations"]?.["m.replace"]?.content?.["m.new_content"]) { | ||||
| 				repliedToEvent = repliedToEvent.unsigned["m.relations"]["m.replace"] // Note: this changes which event_id is in repliedToEvent.
 | ||||
| 				repliedToEvent.content = repliedToEvent.content["m.new_content"] | ||||
| 			} | ||||
| 			let contentPreview | ||||
| 			/** @type {string} */ | ||||
| 			let repliedToContent = repliedToEvent.content.formatted_body || repliedToEvent.content.body | ||||
| 			const fileReplyContentAlternative = attachmentEmojis.get(repliedToEvent.content.msgtype) | ||||
| 			let contentPreview | ||||
| 			if (fileReplyContentAlternative) { | ||||
| 				contentPreview = " " + fileReplyContentAlternative | ||||
| 			} else if (repliedToEvent.unsigned?.redacted_because) { | ||||
| 				contentPreview = " (in reply to a deleted message)" | ||||
| 			} else if (typeof repliedToContent !== "string") { | ||||
| 				// in reply to a weird metadata event like m.room.name, m.room.member...
 | ||||
| 				// I'm not implementing text fallbacks for arbitrary room events. this should cover most cases
 | ||||
| 				// this has never ever happened in the wild anyway
 | ||||
| 				repliedToEvent.sender = "" | ||||
| 				contentPreview = " (channel details edited)" | ||||
| 			} else { | ||||
| 				// Generate a reply preview for a standard message
 | ||||
| 				/** @type {string} */ | ||||
| 				let repliedToContent = repliedToEvent.content.formatted_body || repliedToEvent.content.body | ||||
| 				repliedToContent = repliedToContent.replace(/.*<\/mx-reply>/s, "") // Remove everything before replies, so just use the actual message body | ||||
| 				repliedToContent = repliedToContent.replace(/^\s*<blockquote>.*?<\/blockquote>(.....)/s, "$1") // If the message starts with a blockquote, don't count it and use the message body afterwards | ||||
| 				repliedToContent = repliedToContent.replace(/(?:\n|<br>)+/g, " ") // Should all be on one line
 | ||||
|  | @ -624,6 +616,15 @@ async function eventToMessage(event, guild, di) { | |||
| 					contentPreview = "" | ||||
| 				} | ||||
| 			} | ||||
| 			const sender = repliedToEvent.sender | ||||
| 			const authorID = getUserOrProxyOwnerID(sender) | ||||
| 			if (authorID) { | ||||
| 				replyLine += `<@${authorID}>` | ||||
| 			} else { | ||||
| 				let senderName = select("member_cache", "displayname", {mxid: sender}).pluck().get() | ||||
| 				if (!senderName) senderName = sender.match(/@([^:]*)/)?.[1] | ||||
| 				if (senderName) replyLine += `**Ⓜ${senderName}**` | ||||
| 			} | ||||
| 			replyLine = `-# > ${replyLine}${contentPreview}\n` | ||||
| 		})() | ||||
| 
 | ||||
|  |  | |||
|  | @ -2625,6 +2625,52 @@ test("event2message: rich reply to a deleted event", async t => { | |||
| 	) | ||||
| }) | ||||
| 
 | ||||
| test("event2message: rich reply to a state event with no body", async t => { | ||||
| 	t.deepEqual( | ||||
| 		await eventToMessage({ | ||||
| 			type: "m.room.message", | ||||
| 			sender: "@ampflower:matrix.org", | ||||
| 			content: { | ||||
| 				msgtype: "m.text", | ||||
| 				body: "> <@ampflower:matrix.org> changed the room topic\n\nnice room topic", | ||||
| 				format: "org.matrix.custom.html", | ||||
| 				formatted_body: "<mx-reply><blockquote><a href=\"https://matrix.to/#/!TqlyQmifxGUggEmdBN:cadence.moe/$f-noT-d-Eo_Xgpc05Ww89ErUXku4NwKWYGHLzWKo1kU?via=cadence.moe\">In reply to</a> <a href=\"https://matrix.to/#/@ampflower:matrix.org\">@ampflower:matrix.org</a> changed the room topic<br></blockquote></mx-reply>nice room topic", | ||||
| 				"m.relates_to": { | ||||
| 					"m.in_reply_to": { | ||||
| 						event_id: "$f-noT-d-Eo_Xgpc05Ww89ErUXku4NwKWYGHLzWKo1kU" | ||||
| 					} | ||||
| 				} | ||||
| 			}, | ||||
| 			event_id: "$v_Gtr-bzv9IVlSLBO5DstzwmiDd-GSFaNfHX66IupV8", | ||||
| 			room_id: "!TqlyQmifxGUggEmdBN:cadence.moe" | ||||
| 		 }, data.guild.general, { | ||||
| 			api: { | ||||
| 				getEvent: mockGetEvent(t, "!TqlyQmifxGUggEmdBN:cadence.moe", "$f-noT-d-Eo_Xgpc05Ww89ErUXku4NwKWYGHLzWKo1kU", { | ||||
| 					type: "m.room.topic", | ||||
| 					sender: "@ampflower:matrix.org", | ||||
| 					content: { | ||||
| 						topic: "you're cute" | ||||
| 					}, | ||||
| 					user_id: "@ampflower:matrix.org" | ||||
| 				}) | ||||
| 			} | ||||
| 		}), | ||||
| 		{ | ||||
| 			ensureJoined: [], | ||||
| 			messagesToDelete: [], | ||||
| 			messagesToEdit: [], | ||||
| 			messagesToSend: [{ | ||||
| 				username: "Ampflower 🌺", | ||||
| 				content: "-# > <:L1:1144820033948762203><:L2:1144820084079087647> (channel details edited)\nnice room topic", | ||||
| 				avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/PRfhXYBTOalvgQYtmCLeUXko", | ||||
| 				allowed_mentions: { | ||||
| 					parse: ["users", "roles"] | ||||
| 				} | ||||
| 			}] | ||||
| 		} | ||||
| 	) | ||||
| }) | ||||
| 
 | ||||
| test("event2message: raw mentioning discord users in plaintext body works", async t => { | ||||
| 	t.deepEqual( | ||||
| 		await eventToMessage({ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue