diff --git a/src/d2m/converters/edit-to-changes.test.js b/src/d2m/converters/edit-to-changes.test.js
index cb1fb5a..842c24e 100644
--- a/src/d2m/converters/edit-to-changes.test.js
+++ b/src/d2m/converters/edit-to-changes.test.js
@@ -78,7 +78,7 @@ test("edit2changes: bot response", async t => {
newContent: {
$type: "m.room.message",
msgtype: "m.text",
- body: "* :ae_botrac4r: [@cadence](https://matrix.to/#/@cadence:cadence.moe) asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
+ body: "* :ae_botrac4r: @cadence asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
format: "org.matrix.custom.html",
formatted_body: '*
@cadence asked , I respond: Stop drinking paint. (No)
Hit
to reroll.',
"m.mentions": {
@@ -87,7 +87,7 @@ test("edit2changes: bot response", async t => {
// *** Replaced With: ***
"m.new_content": {
msgtype: "m.text",
- body: ":ae_botrac4r: [@cadence](https://matrix.to/#/@cadence:cadence.moe) asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
+ body: ":ae_botrac4r: @cadence asked ````, I respond: Stop drinking paint. (No)\n\nHit :bn_re: to reroll.",
format: "org.matrix.custom.html",
formatted_body: '
@cadence asked , I respond: Stop drinking paint. (No)
Hit
to reroll.',
"m.mentions": {
diff --git a/src/d2m/converters/find-mentions.js b/src/d2m/converters/find-mentions.js
index 8726830..8107459 100644
--- a/src/d2m/converters/find-mentions.js
+++ b/src/d2m/converters/find-mentions.js
@@ -146,10 +146,18 @@ function findMention(pjr, maximumWrittenSection, baseOffset, prefix, content) {
// Highlight the relevant part of the message
const start = baseOffset + best.scored.matchedInputTokens[0].index
const end = baseOffset + prefix.length + best.scored.matchedInputTokens.slice(-1)[0].end
- const newContent = content.slice(0, start) + "[" + content.slice(start, end) + "](https://matrix.to/#/" + best.mxid + ")" + content.slice(end)
+ const newNodes = [{
+ type: "text", content: content.slice(0, start)
+ }, {
+ type: "link", target: `https://matrix.to/#/${best.mxid}`, content: [
+ {type: "text", content: content.slice(start, end)}
+ ]
+ }, {
+ type: "text", content: content.slice(end)
+ }]
return {
mxid: best.mxid,
- newContent
+ newNodes
}
}
}
diff --git a/src/d2m/converters/message-to-event.js b/src/d2m/converters/message-to-event.js
index 81c821f..05a376a 100644
--- a/src/d2m/converters/message-to-event.js
+++ b/src/d2m/converters/message-to-event.js
@@ -519,29 +519,60 @@ async function messageToEvent(message, guild, options = {}, di) {
return emojiToKey.emojiToKey({id, name, animated}, message.id) // Register the custom emoji if needed
}))
- async function transformParsedVia(parsed) {
- for (const node of parsed) {
+ async function transformParsedVia(parsed, scanTextForMentions) {
+ for (let n = 0; n < parsed.length; n++) {
+ const node = parsed[n]
if (node.type === "discordChannel" || node.type === "discordChannelLink") {
node.row = select("channel_room", ["room_id", "name", "nick"], {channel_id: node.id}).get()
if (node.row?.room_id) {
node.via = await getViaServersMemo(node.row.room_id)
}
}
+ else if (node.type === "text" && typeof node.content === "string") {
+ // Merge adjacent text nodes into this one
+ while (parsed[n+1]?.type === "text" && typeof parsed[n+1].content === "string") {
+ node.content += parsed[n+1].content
+ parsed.splice(n+1, 1)
+ }
+ // Mentions scenario 3: scan the message content for written @mentions of matrix users. Allows for up to one space between @ and mention.
+ if (scanTextForMentions) {
+ let content = node.content
+ const matches = [...content.matchAll(/(@ ?)([a-z0-9_.#$][^@\n]+)/gi)]
+ for (let i = matches.length; i--;) {
+ const m = matches[i]
+ const prefix = m[1]
+ const maximumWrittenSection = m[2].toLowerCase()
+ if (m.index > 0 && !content[m.index-1].match(/ |\(|\n/)) continue // must have space before it
+ if (maximumWrittenSection.match(/^everyone\b/) || maximumWrittenSection.match(/^here\b/)) continue // ignore @everyone/@here
+
+ var roomID = roomID ?? select("channel_room", "room_id", {channel_id: message.channel_id}).pluck().get()
+ assert(roomID)
+ var pjr = pjr ?? findMentions.processJoined(Object.entries((await di.api.getJoinedMembers(roomID)).joined).map(([mxid, ev]) => ({mxid, displayname: ev.display_name})))
+
+ const found = findMentions.findMention(pjr, maximumWrittenSection, m.index, prefix, content)
+ if (found) {
+ addMention(found.mxid)
+ parsed.splice(n, 1, ...found.newNodes)
+ content = found.newNodes[0].content
+ }
+ }
+ }
+ }
for (const maybeChildNodesArray of [node, node.content, node.items]) {
if (Array.isArray(maybeChildNodesArray)) {
- await transformParsedVia(maybeChildNodesArray)
+ await transformParsedVia(maybeChildNodesArray, scanTextForMentions && ["blockQuote", "list", "paragraph", "em", "strong", "u", "del", "text"].includes(node.type))
}
}
}
return parsed
}
- let html = await markdown.toHtmlWithPostParser(content, transformParsedVia, {
+ let html = await markdown.toHtmlWithPostParser(content, parsed => transformParsedVia(parsed, customOptions.isTheMessageContent && options.scanTextForMentions !== false), {
discordCallback: getDiscordParseCallbacks(message, guild, true, spoilers),
...customOptions
}, customParser, customHtmlOutput)
- let body = await markdown.toHtmlWithPostParser(content, transformParsedVia, {
+ let body = await markdown.toHtmlWithPostParser(content, parsed => transformParsedVia(parsed, false), { // not scanning plaintext body for mentions as we don't parse whether they're in code
discordCallback: getDiscordParseCallbacks(message, guild, false),
discordOnly: true,
escapeHTML: false,
@@ -735,35 +766,12 @@ async function messageToEvent(message, guild, options = {}, di) {
// Then text content
if (message.content && !isOnlyKlipyGIF && !isThinkingInteraction) {
- // Mentions scenario 3: scan the message content for written @mentions of matrix users. Allows for up to one space between @ and mention.
- let content = message.content
- if (options.scanTextForMentions !== false) {
- const matches = [...content.matchAll(/(@ ?)([a-z0-9_.#$][^@\n]+)/gi)]
- for (let i = matches.length; i--;) {
- const m = matches[i]
- const prefix = m[1]
- const maximumWrittenSection = m[2].toLowerCase()
- if (m.index > 0 && !content[m.index-1].match(/ |\(|\n/)) continue // must have space before it
- if (maximumWrittenSection.match(/^everyone\b/) || maximumWrittenSection.match(/^here\b/)) continue // ignore @everyone/@here
-
- var roomID = roomID ?? select("channel_room", "room_id", {channel_id: message.channel_id}).pluck().get()
- assert(roomID)
- var pjr = pjr ?? findMentions.processJoined(Object.entries((await di.api.getJoinedMembers(roomID)).joined).map(([mxid, ev]) => ({mxid, displayname: ev.display_name})))
-
- const found = findMentions.findMention(pjr, maximumWrittenSection, m.index, prefix, content)
- if (found) {
- addMention(found.mxid)
- content = found.newContent
- }
- }
- }
-
// Scan the content for emojihax and replace them with real emojis
- content = content.replaceAll(/\[([a-zA-Z0-9_-]{2,32})(?:~[0-9]+)?\]\(https:\/\/cdn\.discordapp\.com\/emojis\/([0-9]+)\.[^ \n)`]+\)/g, (_, name, id) => {
+ let content = message.content.replaceAll(/\[([a-zA-Z0-9_-]{2,32})(?:~[0-9]+)?\]\(https:\/\/cdn\.discordapp\.com\/emojis\/([0-9]+)\.[^ \n)`]+\)/g, (_, name, id) => {
return `<:${name}:${id}>`
})
- const {body, html} = await transformContent(content)
+ const {body, html} = await transformContent(content, {isTheMessageContent: true})
await addTextEvent(body, html, msgtype)
}
diff --git a/src/d2m/converters/message-to-event.test.js b/src/d2m/converters/message-to-event.test.js
index f071417..4728c2b 100644
--- a/src/d2m/converters/message-to-event.test.js
+++ b/src/d2m/converters/message-to-event.test.js
@@ -789,7 +789,7 @@ test("message2event: simple written @mention for matrix user", async t => {
]
},
msgtype: "m.text",
- body: "[@ash](https://matrix.to/#/@she_who_brings_destruction:cadence.moe) do you need anything from the store btw as I'm heading there after gym",
+ body: "@ash do you need anything from the store btw as I'm heading there after gym",
format: "org.matrix.custom.html",
formatted_body: `@ash do you need anything from the store btw as I'm heading there after gym`
}])
@@ -838,7 +838,7 @@ test("message2event: many written @mentions for matrix users", async t => {
]
},
msgtype: "m.text",
- body: "[@Cadence](https://matrix.to/#/@cadence:cadence.moe), tell me about @Phil, the creator of the Chin Trick, who has become ever more powerful under the mentorship of @botrac4r and [@huck](https://matrix.to/#/@huckleton:cadence.moe)",
+ body: "@Cadence, tell me about @Phil, the creator of the Chin Trick, who has become ever more powerful under the mentorship of @botrac4r and @huck",
format: "org.matrix.custom.html",
formatted_body: `@Cadence, tell me about @Phil, the creator of the Chin Trick, who has become ever more powerful under the mentorship of @botrac4r and @huck`
}])
@@ -890,7 +890,7 @@ test("message2event: written @mentions may match part of the name", async t => {
]
},
msgtype: "m.text",
- body: "I wonder if [@cadence](https://matrix.to/#/@secret:cadence.moe) saw this?",
+ body: "I wonder if @cadence saw this?",
format: "org.matrix.custom.html",
formatted_body: `I wonder if @cadence saw this?`
}])
@@ -941,7 +941,7 @@ test("message2event: written @mentions may match part of the mxid", async t => {
]
},
msgtype: "m.text",
- body: "I wonder if [@huck](https://matrix.to/#/@huckleton:cadence.moe) saw this?",
+ body: "I wonder if @huck saw this?",
format: "org.matrix.custom.html",
formatted_body: `I wonder if @huck saw this?`
}])
@@ -962,6 +962,21 @@ test("message2event: written @mentions do not match in URLs", async t => {
}])
})
+test("message2event: written @mentions do not match in inline code", async t => {
+ const events = await messageToEvent({
+ ...data.message.advanced_written_at_mention_for_matrix,
+ content: "`public @Nullable EntityType>`"
+ }, data.guild.general, {}, {})
+ t.deepEqual(events, [{
+ $type: "m.room.message",
+ "m.mentions": {},
+ msgtype: "m.text",
+ body: "`public @Nullable EntityType>`",
+ format: "org.matrix.custom.html",
+ formatted_body: `public @Nullable EntityType<?>`
+ }])
+})
+
test("message2event: entire message may match elaborate display name", async t => {
let called = 0
const events = await messageToEvent({
@@ -1007,7 +1022,7 @@ test("message2event: entire message may match elaborate display name", async t =
]
},
msgtype: "m.text",
- body: "[@Cadence, Maid of Creation, Eye of Clarity, Empress of Hope ☆](https://matrix.to/#/@wa:cadence.moe)",
+ body: "@Cadence, Maid of Creation, Eye of Clarity, Empress of Hope ☆",
format: "org.matrix.custom.html",
formatted_body: `@Cadence, Maid of Creation, Eye of Clarity, Empress of Hope ☆`
}])