diff --git a/d2m/actions/speedbump.js b/d2m/actions/speedbump.js index e782ae0a..7c3109b1 100644 --- a/d2m/actions/speedbump.js +++ b/d2m/actions/speedbump.js @@ -50,8 +50,7 @@ async function doSpeedbump(messageID) { async function maybeDoSpeedbump(channelID, messageID) { let row = select("channel_room", ["thread_parent", "speedbump_id", "speedbump_webhook_id"], {channel_id: channelID}).get() if (row?.thread_parent) row = select("channel_room", ["thread_parent", "speedbump_id", "speedbump_webhook_id"], {channel_id: row.thread_parent}).get() // webhooks belong to the channel, not the thread - if (!row) return {affected: false, row: null}// not affected, no speedbump - // Edits need to go through the speedbump as well. If the message is delayed but the edit isn't, we don't have anything to edit from. + if (!row?.speedbump_webhook_id) return {affected: false, row: null} // not affected, no speedbump const affected = await doSpeedbump(messageID) return {affected, row} // maybe affected, and there is a speedbump } diff --git a/d2m/converters/message-to-event.js b/d2m/converters/message-to-event.js index 814fda2c..1b21be47 100644 --- a/d2m/converters/message-to-event.js +++ b/d2m/converters/message-to-event.js @@ -39,15 +39,15 @@ function getDiscordParseCallbacks(message, guild, useHTML) { return `@${username}:` } }, - /** @param {{id: string, type: "discordChannel"}} node */ + // FIXME: type + /** @param {{id: string, type: "discordChannel", row: any, via: URLSearchParams}} node */ channel: node => { - const row = select("channel_room", ["room_id", "name", "nick"], {channel_id: node.id}).get() - if (!row) { + if (!node.row) { return `#[channel-from-an-unknown-server]` // fallback for when this channel is not bridged } else if (useHTML) { - return `#${row.nick || row.name}` + return `#${node.row.nick || node.row.name}` } else { - return `#${row.nick || row.name}` + return `#${node.row.nick || node.row.name}` } }, /** @param {{animated: boolean, name: string, id: string, type: "discordEmoji"}} node */ @@ -332,12 +332,27 @@ async function messageToEvent(message, guild, options = {}, di) { return emojiToKey.emojiToKey({id, name, animated}) // Register the custom emoji if needed })) - let html = markdown.toHTML(content, { + async function transformParsedVia(parsed) { + for (const node of parsed) { + if (node.type === "discordChannel") { + node.row = select("channel_room", ["room_id", "name", "nick"], {channel_id: node.id}).get() + if (node.row?.room_id) { + node.via = await mxUtils.getViaServersQuery(node.row.room_id, di.api) + } + } + if (Array.isArray(node.content)) { + await transformParsedVia(node.content) + } + } + return parsed + } + + let html = await markdown.toHtmlWithPostParser(content, transformParsedVia, { discordCallback: getDiscordParseCallbacks(message, guild, true), ...customOptions }, customParser, customHtmlOutput) - let body = markdown.toHTML(content, { + let body = await markdown.toHtmlWithPostParser(content, transformParsedVia, { discordCallback: getDiscordParseCallbacks(message, guild, false), discordOnly: true, escapeHTML: false, @@ -347,6 +362,7 @@ async function messageToEvent(message, guild, options = {}, di) { return {body, html} } + // FIXME: What was the scanMentions parameter supposed to activate? It's unused. async function addTextEvent(body, html, msgtype, {scanMentions}) { // Star * prefix for fallback edits if (options.includeEditFallbackStar) { diff --git a/d2m/event-dispatcher.js b/d2m/event-dispatcher.js index 52bed725..e7690537 100644 --- a/d2m/event-dispatcher.js +++ b/d2m/event-dispatcher.js @@ -264,6 +264,7 @@ module.exports = { if (row) return // The message was sent by the bridge's own webhook on discord. We don't want to reflect this back, so just drop it. } + // Edits need to go through the speedbump as well. If the message is delayed but the edit isn't, we don't have anything to edit from. const {affected, row} = await speedbump.maybeDoSpeedbump(data.channel_id, data.id) if (affected) return @@ -273,6 +274,7 @@ module.exports = { /** @type {DiscordTypes.GatewayMessageCreateDispatchData} */ // @ts-ignore const message = data + const channel = client.channels.get(message.channel_id) if (!channel || !("guild_id" in channel) || !channel.guild_id) return // Nothing we can do in direct messages. const guild = client.guilds.get(channel.guild_id) diff --git a/m2d/converters/utils.js b/m2d/converters/utils.js index 5707aec4..9d7a5e2a 100644 --- a/m2d/converters/utils.js +++ b/m2d/converters/utils.js @@ -128,7 +128,9 @@ class MatrixStringBuilder { } /** - * Room IDs are not routable on their own. Room permalinks need a list of servers to try. The client is responsible for coming up with a list of servers. + * Context: Room IDs are not routable on their own. Room permalinks need a list of servers to try. The client is responsible for coming up with a list of servers. + * ASSUMPTION 1: The bridge bot is a member of the target room and can therefore access its power levels and member list for calculation. + * ASSUMPTION 2: Because the bridge bot is a member of the target room, the target room is bridged. * https://spec.matrix.org/v1.9/appendices/#routing * https://gitdab.com/cadence/out-of-your-element/issues/11 * @param {string} roomID @@ -192,9 +194,29 @@ async function getViaServers(roomID, api) { return candidates } +/** + * Context: Room IDs are not routable on their own. Room permalinks need a list of servers to try. The client is responsible for coming up with a list of servers. + * ASSUMPTION 1: The bridge bot is a member of the target room and can therefore access its power levels and member list for calculation. + * ASSUMPTION 2: Because the bridge bot is a member of the target room, the target room is bridged. + * https://spec.matrix.org/v1.9/appendices/#routing + * https://gitdab.com/cadence/out-of-your-element/issues/11 + * @param {string} roomID + * @param {{[K in "getStateEvent" | "getJoinedMembers"]: import("../../matrix/api")[K]}} api + * @returns {Promise} + */ +async function getViaServersQuery(roomID, api) { + const list = await getViaServers(roomID, api) + const qs = new URLSearchParams() + for (const server of list) { + qs.append("via", server) + } + return qs +} + module.exports.BLOCK_ELEMENTS = BLOCK_ELEMENTS module.exports.eventSenderIsFromDiscord = eventSenderIsFromDiscord module.exports.getPublicUrlForMxc = getPublicUrlForMxc module.exports.getEventIDHash = getEventIDHash module.exports.MatrixStringBuilder = MatrixStringBuilder module.exports.getViaServers = getViaServers +module.exports.getViaServersQuery = getViaServersQuery diff --git a/package-lock.json b/package-lock.json index 9c4f1bfc..27e04e36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "chunk-text": "^2.0.1", "cloudstorm": "^0.10.7", "deep-equal": "^2.2.3", - "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de519353668c87ecc8c543e9749093481bc72ff8", + "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de44d1666a9115a3763f45ccdcd414c3bf4dbbef", "entities": "^4.5.0", "get-stream": "^6.0.1", "giframe": "github:cloudrac3r/giframe#v0.4.1", @@ -352,14 +352,14 @@ } }, "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/react": { - "version": "18.2.20", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.20.tgz", - "integrity": "sha512-WKNtmsLWJM/3D5mG4U84cysVY31ivmyw85dE84fOCk5Hx78wezB/XEjVPWl2JTZ5FkEeaTJf+VgUAUn3PE7Isw==", + "version": "18.2.55", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz", + "integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -367,9 +367,9 @@ } }, "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/abort-controller": { "version": "3.0.0", @@ -927,9 +927,9 @@ } }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/data-uri-to-buffer": { "version": "2.0.2", @@ -1075,9 +1075,8 @@ "integrity": "sha512-c0rHc5YGNIXQkI+V7QwP8y77wxo74ITNeZmMwxtKC/l01aIF/gKBG/U2MKhUt2iaeRH9XwAt9PT3AI9JQVvKVA==" }, "node_modules/discord-markdown": { - "version": "2.5.1", - "resolved": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de519353668c87ecc8c543e9749093481bc72ff8", - "license": "MIT", + "version": "2.6.0", + "resolved": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de44d1666a9115a3763f45ccdcd414c3bf4dbbef", "dependencies": { "simple-markdown": "^0.7.2" } diff --git a/package.json b/package.json index 5b22a4fc..5ca5f326 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "chunk-text": "^2.0.1", "cloudstorm": "^0.10.7", "deep-equal": "^2.2.3", - "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de519353668c87ecc8c543e9749093481bc72ff8", + "discord-markdown": "git+https://git.sr.ht/~cadence/nodejs-discord-markdown#de44d1666a9115a3763f45ccdcd414c3bf4dbbef", "entities": "^4.5.0", "get-stream": "^6.0.1", "giframe": "github:cloudrac3r/giframe#v0.4.1",