diff --git a/db/data-for-test.sql b/db/data-for-test.sql index b8da73c..8c564df 100644 --- a/db/data-for-test.sql +++ b/db/data-for-test.sql @@ -47,6 +47,13 @@ CREATE TABLE IF NOT EXISTS "event_message" ( "source" INTEGER NOT NULL, PRIMARY KEY("event_id","message_id") ); +CREATE TABLE IF NOT EXISTS "member_cache" ( + "room_id" TEXT NOT NULL, + "mxid" TEXT NOT NULL, + "displayname" TEXT, + "avatar_url" TEXT, + PRIMARY KEY("room_id", "mxid") +); COMMIT; @@ -101,4 +108,9 @@ INSERT INTO file (discord_url, mxc_url) VALUES ('https://cdn.discordapp.com/icons/112760669178241024/a_f83622e09ead74f0c5c527fe241f8f8c.png?size=1024', 'mxc://cadence.moe/zKXGZhmImMHuGQZWJEFKJbsF'), ('https://cdn.discordapp.com/avatars/113340068197859328/b48302623a12bc7c59a71328f72ccb39.png?size=1024', 'mxc://cadence.moe/UpAeIqeclhKfeiZNdIWNcXXL'); +INSERT INTO member_cache (room_id, mxid, displayname, avatar_url) VALUES +('!kLRqKKUQXcibIMtOpl:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL), +('!BpMdOUkWWhFxmTrENV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', NULL), +('!fGgIymcYWOqjbSRUdV:cadence.moe', '@cadence:cadence.moe', 'cadence [they]', 'mxc://cadence.moe/azCAhThKTojXSZJRoWwZmhvU'); + COMMIT; diff --git a/m2d/converters/event-to-message.js b/m2d/converters/event-to-message.js index cf34705..8363d8f 100644 --- a/m2d/converters/event-to-message.js +++ b/m2d/converters/event-to-message.js @@ -9,6 +9,8 @@ const passthrough = require("../../passthrough") const { sync, db, discord } = passthrough /** @type {import("../../matrix/file")} */ const file = sync.require("../../matrix/file") +/** @type {import("../converters/utils")} */ +const utils = sync.require("../converters/utils") const BLOCK_ELEMENTS = [ "ADDRESS", "ARTICLE", "ASIDE", "AUDIO", "BLOCKQUOTE", "BODY", "CANVAS", @@ -69,6 +71,22 @@ turndownService.addRule("fencedCodeBlock", { } }) +/** + * @param {string} roomID + * @param {string} mxid + * @returns {Promise<{displayname?: string?, avatar_url?: string?}>} + */ +async function getMemberFromCacheOrHomeserver(roomID, mxid, api) { + const row = db.prepare("SELECT displayname, avatar_url FROM member_cache WHERE room_id = ? AND mxid = ?").get(roomID, mxid) + if (row) return row + return api.getStateEvent(roomID, "m.room.member", mxid).then(event => { + db.prepare("INSERT INTO member_cache (room_id, mxid, displayname, avatar_url) VALUES (?, ?, ?, ?)").run(roomID, mxid, event?.displayname || null, event?.avatar_url || null) + return event + }).catch(() => { + return {displayname: null, avatar_url: null} + }) +} + /** * @param {Ty.Event.Outer} event * @param {import("discord-api-types/v10").APIGuild} guild @@ -81,11 +99,13 @@ async function eventToMessage(event, guild, di) { let displayName = event.sender let avatarURL = undefined let replyLine = "" + // Extract a basic display name from the sender const match = event.sender.match(/^@(.*?):/) - if (match) { - displayName = match[1] - // TODO: get the media repo domain and the avatar url from the matrix member event - } + if (match) displayName = match[1] + // 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 = utils.getPublicUrlForMxc(member.avatar_url) // Convert content depending on what the message is let content = event.content.body // ultimate fallback diff --git a/m2d/converters/event-to-message.test.js b/m2d/converters/event-to-message.test.js index 33c4d71..b595be8 100644 --- a/m2d/converters/event-to-message.test.js +++ b/m2d/converters/event-to-message.test.js @@ -51,7 +51,7 @@ test("event2message: body is used when there is no formatted_body", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: "testing plaintext", avatar_url: undefined }] @@ -75,7 +75,7 @@ test("event2message: any markdown in body is escaped", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: "testing \\*\\*special\\*\\* \\~\\~things\\~\\~ which \\_should\\_ \\*not\\* \\`trigger\\` @any ", avatar_url: undefined }] @@ -101,7 +101,7 @@ test("event2message: basic html is converted to markdown", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: "this **is** a **_test_** of ~~formatting~~", avatar_url: undefined }] @@ -127,7 +127,7 @@ test("event2message: markdown syntax is escaped", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: "this \\*\\*is\\*\\* an **_extreme_** \\\\\\*test\\\\\\* of", avatar_url: undefined }] @@ -153,7 +153,7 @@ test("event2message: html lines are bridged correctly", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: "paragraph one\nline _two_\nline three\n\nparagraph two\nline _two_\nline three\n\nparagraph three\n\nparagraph four\nline two\nline three\nline four\n\nparagraph five", avatar_url: undefined }] @@ -179,7 +179,7 @@ test("event2message: html lines are bridged correctly", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: "line one: test test\nline two: **test** **test**\nline three: **test test**\nline four: test test\n line five", avatar_url: undefined }] @@ -206,7 +206,7 @@ test("event2message: whitespace is collapsed", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: "line one: test test\nline two: **test** **test**\nline three: **test test**\nline four: test test\nline five", avatar_url: undefined }] @@ -234,7 +234,7 @@ test("event2message: lists are bridged correctly", async t => { "room_id": "!BpMdOUkWWhFxmTrENV:cadence.moe" }), [{ - username: "cadence", + username: "cadence [they]", content: "* line one\n* line two\n* line three\n * nested one\n * nested two\n* line four", avatar_url: undefined }] @@ -258,11 +258,11 @@ test("event2message: long messages are split", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: (("a".repeat(130) + " ").repeat(15)).slice(0, -1), avatar_url: undefined }, { - username: "cadence", + username: "cadence [they]", content: (("a".repeat(130) + " ").repeat(4)).slice(0, -1), avatar_url: undefined }] @@ -288,7 +288,7 @@ test("event2message: code blocks work", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: "preceding\n\n```\ncode block\n```\n\nfollowing `code` is inline", avatar_url: undefined }] @@ -315,7 +315,7 @@ test("event2message: code block contents are formatted correctly and not escaped "room_id": "!BpMdOUkWWhFxmTrENV:cadence.moe" }), [{ - username: "cadence", + username: "cadence [they]", content: "```\ninput = input.replace(/(<\\/?([^ >]+)[^>]*>)?\\n(<\\/?([^ >]+)[^>]*>)?/g,\n_input_ = input = input.replace(/(<\\/?([^ >]+)[^>]*>)?\\n(<\\/?([^ >]+)[^>]*>)?/g,\n```\n\n`input = input.replace(/(<\\/?([^ >]+)[^>]*>)?\\n(<\\/?([^ >]+)[^>]*>)?/g,`", avatar_url: undefined }] @@ -341,7 +341,7 @@ test("event2message: quotes have an appropriate amount of whitespace", async t = } }), [{ - username: "cadence", + username: "cadence [they]", content: "> Chancellor of Germany Angela Merkel, on March 17, 2017: they did not shake hands\n🤨", avatar_url: undefined }] @@ -367,8 +367,8 @@ test("event2message: m.emote markdown syntax is escaped", async t => { } }), [{ - username: "cadence", - content: "\\* cadence shows you \\*\\*her\\*\\* **_extreme_** \\\\\\*test\\\\\\* of", + username: "cadence [they]", + content: "\\* cadence \\[they\\] shows you \\*\\*her\\*\\* **_extreme_** \\\\\\*test\\\\\\* of", avatar_url: undefined }] ) @@ -410,9 +410,9 @@ test("event2message: rich reply to a sim user", async t => { } }), [{ - username: "cadence", + username: "cadence [they]", content: "<:L1:1144820033948762203><:L2:1144820084079087647>https://discord.com/channels/112760669178241024/687028734322147344/1144865310588014633 <@111604486476181504>: Slow news day.\nTesting this reply, ignore", - avatar_url: undefined + avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/azCAhThKTojXSZJRoWwZmhvU" }] ) }) @@ -455,9 +455,9 @@ test("event2message: rich reply to a matrix user's long message with formatting" } }), [{ - username: "cadence", + username: "cadence [they]", content: "<:L1:1144820033948762203><:L2:1144820084079087647>https://discord.com/channels/112760669178241024/687028734322147344/1144865310588014633 Ⓜ️**cadence**: i should have a little...\n**no you can't!!!**", - avatar_url: undefined + avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/azCAhThKTojXSZJRoWwZmhvU" }] ) }) @@ -500,9 +500,9 @@ test("event2message: with layered rich replies, the preview should only be the r } }), [{ - username: "cadence", + username: "cadence [they]", content: "<:L1:1144820033948762203><:L2:1144820084079087647>https://discord.com/channels/112760669178241024/687028734322147344/1144865310588014633 Ⓜ️**cadence**: two\nthree", - avatar_url: undefined + avatar_url: "https://matrix.cadence.moe/_matrix/media/r0/download/cadence.moe/azCAhThKTojXSZJRoWwZmhvU" }] ) }) diff --git a/m2d/converters/utils.js b/m2d/converters/utils.js index 7b9c504..02ec147 100644 --- a/m2d/converters/utils.js +++ b/m2d/converters/utils.js @@ -19,4 +19,15 @@ function eventSenderIsFromDiscord(sender) { return false } +/** + * @param {string} mxc + * @returns {string?} + */ +function getPublicUrlForMxc(mxc) { + const avatarURLParts = mxc?.match(/^mxc:\/\/([^/]+)\/(\w+)$/) + if (avatarURLParts) return `https://matrix.cadence.moe/_matrix/media/r0/download/${avatarURLParts[1]}/${avatarURLParts[2]}` + else return null +} + module.exports.eventSenderIsFromDiscord = eventSenderIsFromDiscord +module.exports.getPublicUrlForMxc = getPublicUrlForMxc